shadow-claw 1.34.5 → 1.35.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 (193) hide show
  1. package/.well-known/mcp/server-card.json +1 -1
  2. package/.well-known/mcp.json +1 -1
  3. package/README.md +5 -1
  4. package/dist/cli/chunk-BCLxzLO92.js +5 -0
  5. package/dist/cli/chunk-BHNCi3ui2.js +1 -0
  6. package/dist/cli/chunk-BMPhLSn8.js +1 -0
  7. package/dist/cli/chunk-BMW6QB5-2.js +27 -0
  8. package/dist/cli/chunk-BQCWtrzh.js +1 -0
  9. package/dist/cli/chunk-BQCWtrzh2.js +1 -0
  10. package/dist/cli/chunk-BdJUjMuz.js +1 -0
  11. package/dist/cli/chunk-C47WdxKx2.js +1 -0
  12. package/dist/cli/{chunk-D9dku_3t.js → chunk-CAqvQuGr.js} +1 -1
  13. package/dist/cli/{chunk-DoVxObGa2.js → chunk-CHzhUS1N2.js} +1 -1
  14. package/dist/cli/chunk-CUMlq7Jw2.js +263 -0
  15. package/dist/cli/chunk-CXi9LU9e2.js +110 -0
  16. package/dist/cli/chunk-ChefnX-r.js +110 -0
  17. package/dist/cli/{chunk-zvi71-77.js → chunk-D3JxbzjI2.js} +15 -15
  18. package/dist/cli/chunk-D7VYMl092.js +1 -0
  19. package/dist/cli/chunk-DNIduiJ82.js +91 -0
  20. package/dist/cli/{chunk-C4UfzPK4.js → chunk-DTbrldk_.js} +1 -1
  21. package/dist/cli/chunk-DZ8YBXax.js +969 -0
  22. package/dist/cli/chunk-DduRvw8B2.js +1 -0
  23. package/dist/cli/chunk-DnYxGjdu2.js +1 -0
  24. package/dist/cli/chunk-Ea7nXmgb2.js +1 -0
  25. package/dist/cli/{chunk-BRiGkTIi.js → chunk-EohNY8vW.js} +1 -1
  26. package/dist/cli/chunk-LIIpZlwP2.js +1 -0
  27. package/dist/cli/chunk-LIJZHJml2.js +1 -0
  28. package/dist/cli/chunk-MHBh2BgF2.js +91 -0
  29. package/dist/cli/{chunk-BQ7lHA7c.js → chunk-SKgLGyTY.js} +1 -1
  30. package/dist/cli/chunk-kcJSXcbq2.js +1 -0
  31. package/dist/cli/cli.js +12 -11
  32. package/dist/headless-agent.js +191 -187
  33. package/dist/lib/components/index.js +21 -21
  34. package/dist/lib/components/settings/shadow-claw-channel-config.js +1 -1
  35. package/dist/lib/components/settings/shadow-claw-control-plane.js +1 -1
  36. package/dist/lib/components/settings/shadow-claw-git.js +1 -1
  37. package/dist/lib/components/settings/shadow-claw-llm.js +1 -1
  38. package/dist/lib/components/settings/shadow-claw-networking.js +1 -1
  39. package/dist/lib/components/settings/shadow-claw-notifications.js +1 -1
  40. package/dist/lib/components/settings/shadow-claw-peerjs.js +1 -1
  41. package/dist/lib/components/settings/shadow-claw-storage.js +1 -1
  42. package/dist/lib/components/settings/shadow-claw-task-server.js +1 -1
  43. package/dist/lib/components/settings/shadow-claw-webvm.js +1 -1
  44. package/dist/lib/components/shadow-claw-channel-config.js +1 -1
  45. package/dist/lib/components/shadow-claw-channels.js +1 -1
  46. package/dist/lib/components/shadow-claw-chat.js +1 -1
  47. package/dist/lib/components/shadow-claw-control-plane.js +1 -1
  48. package/dist/lib/components/shadow-claw-conversations.js +1 -1
  49. package/dist/lib/components/shadow-claw-file-viewer.js +1 -1
  50. package/dist/lib/components/shadow-claw-files.js +1 -1
  51. package/dist/lib/components/shadow-claw-git.js +1 -1
  52. package/dist/lib/components/shadow-claw-llm.js +1 -1
  53. package/dist/lib/components/shadow-claw-networking.js +1 -1
  54. package/dist/lib/components/shadow-claw-notifications.js +1 -1
  55. package/dist/lib/components/shadow-claw-pages.js +1 -1
  56. package/dist/lib/components/shadow-claw-peerjs.js +1 -1
  57. package/dist/lib/components/shadow-claw-settings.js +1 -1
  58. package/dist/lib/components/shadow-claw-storage.js +1 -1
  59. package/dist/lib/components/shadow-claw-task-server.js +1 -1
  60. package/dist/lib/components/shadow-claw-tasks.js +1 -1
  61. package/dist/lib/components/shadow-claw-terminal.js +1 -1
  62. package/dist/lib/components/shadow-claw-tools.js +1 -1
  63. package/dist/lib/components/shadow-claw-webvm.js +1 -1
  64. package/dist/lib/components/shadow-claw.js +1 -1
  65. package/dist/lib/{configurePeerJs-DSRcNp3v.js → configurePeerJs-CdPzmpyI.js} +2 -2
  66. package/dist/lib/{configurePeerJs-DSRcNp3v.js.map → configurePeerJs-CdPzmpyI.js.map} +1 -1
  67. package/dist/lib/index.js +21 -21
  68. package/dist/lib/{initControlPlane-CWfE_Oan.js → initControlPlane-BLymRW34.js} +7 -7
  69. package/dist/lib/{initControlPlane-CWfE_Oan.js.map → initControlPlane-BLymRW34.js.map} +1 -1
  70. package/dist/lib/{orchestrator-BEUug1TW.js → orchestrator-DZkhJj7f.js} +13 -2
  71. package/dist/lib/{orchestrator-BEUug1TW.js.map → orchestrator-DZkhJj7f.js.map} +1 -1
  72. package/dist/lib/{orchestrator-CGUzfGbE.js → orchestrator-ZsQTRknI.js} +5 -5
  73. package/dist/lib/{orchestrator-CGUzfGbE.js.map → orchestrator-ZsQTRknI.js.map} +1 -1
  74. package/dist/lib/{peerjs-BSD88rT3.js → peerjs-CJYfnR5V.js} +3 -3
  75. package/dist/lib/{peerjs-BSD88rT3.js.map → peerjs-CJYfnR5V.js.map} +1 -1
  76. package/dist/lib/{push-client-CC3qifgc.js → push-client-C9hIPqLZ.js} +2 -2
  77. package/dist/lib/{push-client-CC3qifgc.js.map → push-client-C9hIPqLZ.js.map} +1 -1
  78. package/dist/lib/{shadow-claw-BWS-UVnE.js → shadow-claw-8qL2-QoK.js} +4 -4
  79. package/dist/lib/{shadow-claw-BWS-UVnE.js.map → shadow-claw-8qL2-QoK.js.map} +1 -1
  80. package/dist/lib/{shadow-claw-channel-config-DwMn3nFJ.js → shadow-claw-channel-config-CvnbqGBy.js} +3 -3
  81. package/dist/lib/{shadow-claw-channel-config-DwMn3nFJ.js.map → shadow-claw-channel-config-CvnbqGBy.js.map} +1 -1
  82. package/dist/lib/{shadow-claw-channels-BdE4cUlx.js → shadow-claw-channels-CkCvjWn1.js} +2 -2
  83. package/dist/lib/{shadow-claw-channels-BdE4cUlx.js.map → shadow-claw-channels-CkCvjWn1.js.map} +1 -1
  84. package/dist/lib/{shadow-claw-chat-DqaMoE9C.js → shadow-claw-chat-B1YEHKN3.js} +3 -3
  85. package/dist/lib/{shadow-claw-chat-DqaMoE9C.js.map → shadow-claw-chat-B1YEHKN3.js.map} +1 -1
  86. package/dist/lib/{shadow-claw-control-plane-D3DUDO29.js → shadow-claw-control-plane-jjo789IE.js} +6 -6
  87. package/dist/lib/{shadow-claw-control-plane-D3DUDO29.js.map → shadow-claw-control-plane-jjo789IE.js.map} +1 -1
  88. package/dist/lib/{shadow-claw-conversations-XT-OXogj.js → shadow-claw-conversations-MMT7wYH3.js} +2 -2
  89. package/dist/lib/{shadow-claw-conversations-XT-OXogj.js.map → shadow-claw-conversations-MMT7wYH3.js.map} +1 -1
  90. package/dist/lib/{shadow-claw-file-viewer-DJqxyd97.js → shadow-claw-file-viewer-qSkD1RQK.js} +2 -2
  91. package/dist/lib/{shadow-claw-file-viewer-DJqxyd97.js.map → shadow-claw-file-viewer-qSkD1RQK.js.map} +1 -1
  92. package/dist/lib/{shadow-claw-files-CUEJlA1o.js → shadow-claw-files-Cg8ycQ_N.js} +2 -2
  93. package/dist/lib/{shadow-claw-files-CUEJlA1o.js.map → shadow-claw-files-Cg8ycQ_N.js.map} +1 -1
  94. package/dist/lib/{shadow-claw-git-D_iVcb86.js → shadow-claw-git-gaClz-ZC.js} +3 -3
  95. package/dist/lib/{shadow-claw-git-D_iVcb86.js.map → shadow-claw-git-gaClz-ZC.js.map} +1 -1
  96. package/dist/lib/{shadow-claw-llm-C2msAsyU.js → shadow-claw-llm-DyOz0qzT.js} +2 -2
  97. package/dist/lib/{shadow-claw-llm-C2msAsyU.js.map → shadow-claw-llm-DyOz0qzT.js.map} +1 -1
  98. package/dist/lib/{shadow-claw-networking-D2NvodC-.js → shadow-claw-networking-B2xx0UUR.js} +2 -2
  99. package/dist/lib/{shadow-claw-networking-D2NvodC-.js.map → shadow-claw-networking-B2xx0UUR.js.map} +1 -1
  100. package/dist/lib/{shadow-claw-notifications-BcvVn_vu.js → shadow-claw-notifications-BjKfZy0k.js} +2 -2
  101. package/dist/lib/{shadow-claw-notifications-BcvVn_vu.js.map → shadow-claw-notifications-BjKfZy0k.js.map} +1 -1
  102. package/dist/lib/{shadow-claw-pages-CTMXRhEX.js → shadow-claw-pages-CJ2tsMtL.js} +2 -2
  103. package/dist/lib/{shadow-claw-pages-CTMXRhEX.js.map → shadow-claw-pages-CJ2tsMtL.js.map} +1 -1
  104. package/dist/lib/{shadow-claw-peerjs-C2YvypqM.js → shadow-claw-peerjs-CazCkXQ1.js} +3 -3
  105. package/dist/lib/{shadow-claw-peerjs-C2YvypqM.js.map → shadow-claw-peerjs-CazCkXQ1.js.map} +1 -1
  106. package/dist/lib/{shadow-claw-settings-B0hiDwEt.js → shadow-claw-settings-qro6pO9I.js} +2 -2
  107. package/dist/lib/{shadow-claw-settings-B0hiDwEt.js.map → shadow-claw-settings-qro6pO9I.js.map} +1 -1
  108. package/dist/lib/{shadow-claw-storage-Dt4yg4Q4.js → shadow-claw-storage-ewDdvCGa.js} +2 -2
  109. package/dist/lib/{shadow-claw-storage-Dt4yg4Q4.js.map → shadow-claw-storage-ewDdvCGa.js.map} +1 -1
  110. package/dist/lib/{shadow-claw-task-server-B2gbZaz4.js → shadow-claw-task-server-DpqQcNIs.js} +2 -2
  111. package/dist/lib/{shadow-claw-task-server-B2gbZaz4.js.map → shadow-claw-task-server-DpqQcNIs.js.map} +1 -1
  112. package/dist/lib/{shadow-claw-tasks-Bxq-niuM.js → shadow-claw-tasks-B2fMF4qE.js} +2 -2
  113. package/dist/lib/{shadow-claw-tasks-Bxq-niuM.js.map → shadow-claw-tasks-B2fMF4qE.js.map} +1 -1
  114. package/dist/lib/{shadow-claw-terminal-BfbW3xtg.js → shadow-claw-terminal-B25kQJC3.js} +2 -2
  115. package/dist/lib/{shadow-claw-terminal-BfbW3xtg.js.map → shadow-claw-terminal-B25kQJC3.js.map} +1 -1
  116. package/dist/lib/{shadow-claw-tools-BNSAaK0Y.js → shadow-claw-tools-B0y8JzoA.js} +10 -3
  117. package/dist/lib/{shadow-claw-tools-BNSAaK0Y.js.map → shadow-claw-tools-B0y8JzoA.js.map} +1 -1
  118. package/dist/lib/{shadow-claw-webvm-Dc2KmEXq.js → shadow-claw-webvm-CoCedIza.js} +2 -2
  119. package/dist/lib/{shadow-claw-webvm-Dc2KmEXq.js.map → shadow-claw-webvm-CoCedIza.js.map} +1 -1
  120. package/dist/lib/{syncWebMcpRegistration-BIL83P0Q.js → syncWebMcpRegistration-JKLALq4e.js} +2 -2
  121. package/dist/lib/{syncWebMcpRegistration-BIL83P0Q.js.map → syncWebMcpRegistration-JKLALq4e.js.map} +1 -1
  122. package/dist/lib/worker/utils/sandboxedEval.d.ts +8 -0
  123. package/dist/public/.well-known/mcp/server-card.json +1 -1
  124. package/dist/public/.well-known/mcp.json +1 -1
  125. package/dist/public/AGENTS.md +1 -1
  126. package/dist/public/README.md +5 -1
  127. package/dist/public/agent.worker.js +1 -1
  128. package/dist/public/components/shadow-claw-tools/shadow-claw-tools.html +1 -1
  129. package/dist/public/{configurePeerJs-B925t9C7.js → configurePeerJs-BrHeJGw1.js} +1 -1
  130. package/dist/public/docs/decisions/headless-cli-agent-participant.md +1 -1
  131. package/dist/public/docs/example/article/index.html +1 -1
  132. package/dist/public/docs/publishing/index.html +1 -1
  133. package/dist/public/docs/skill-creator/index.html +1 -1
  134. package/dist/public/docs/subsystems/cli.md +33 -23
  135. package/dist/public/docs/subsystems/skills.md +17 -0
  136. package/dist/public/docs/subsystems/tools.md +1 -1
  137. package/dist/public/e2e/README.md +2 -2
  138. package/dist/public/{e2e-bridge-CE1xleGn.js → e2e-bridge-D_lyH__p.js} +1 -1
  139. package/dist/public/{executeTool-DtTrsZHV.js → executeTool-b6DnRhcv.js} +2 -1
  140. package/dist/public/{file-viewer-LEEStSq-.js → file-viewer-CKrQoxZi.js} +1 -1
  141. package/dist/public/index.html +1 -1
  142. package/dist/public/index.js +1 -1
  143. package/dist/public/{initControlPlane-e2JgxXEb.js → initControlPlane-Dn-XFuxg.js} +1 -1
  144. package/dist/public/llms.txt +2 -0
  145. package/dist/public/main/index.html +1 -1
  146. package/dist/public/main/memory/index.html +1 -1
  147. package/dist/public/{orchestrator-Baif6kmu.js → orchestrator-0BnBtxHO.js} +3 -2
  148. package/dist/public/{orchestrator-TndREZPL.js → orchestrator-C3GKZriH.js} +1 -1
  149. package/dist/public/{peerjs-DE6WF3h2.js → peerjs-CgfLEp2N.js} +1 -1
  150. package/dist/public/{push-client-B_IkTpNm.js → push-client-D6DLiY1s.js} +1 -1
  151. package/dist/public/service-worker.js +1 -1
  152. package/dist/public/{shadow-claw-channels-BCGz36Zl.js → shadow-claw-channels-DSijinZ9.js} +1 -1
  153. package/dist/public/{shadow-claw-chat-H9U9jbqD.js → shadow-claw-chat-BeHlAjnA.js} +1 -1
  154. package/dist/public/{shadow-claw-control-plane-CeKh64s2.js → shadow-claw-control-plane-BfF_AZSg.js} +2 -2
  155. package/dist/public/{shadow-claw-conversations-C26t1HXN.js → shadow-claw-conversations-Bb_2bVLJ.js} +1 -1
  156. package/dist/public/{shadow-claw-DBeXILmk.js → shadow-claw-f71DlRdV.js} +2 -2
  157. package/dist/public/{shadow-claw-file-viewer-DWUoTjkr.js → shadow-claw-file-viewer-B67000Mm.js} +1 -1
  158. package/dist/public/{shadow-claw-files-BQ8DSX-z.js → shadow-claw-files-DeZZYt01.js} +1 -1
  159. package/dist/public/{shadow-claw-git-CIXyISw-.js → shadow-claw-git-Ccy25LuW.js} +2 -2
  160. package/dist/public/{shadow-claw-llm-D_GpOSZE.js → shadow-claw-llm-XjYcTlXT.js} +1 -1
  161. package/dist/public/{shadow-claw-networking-BYp5K3B7.js → shadow-claw-networking-efXQtydQ.js} +1 -1
  162. package/dist/public/{shadow-claw-notifications-ZiXmPweY.js → shadow-claw-notifications-Cq2ZSeJa.js} +1 -1
  163. package/dist/public/{shadow-claw-pages-BHXwf3BD.js → shadow-claw-pages-BVFNSSb1.js} +1 -1
  164. package/dist/public/{shadow-claw-settings-BfvDvt9c.js → shadow-claw-settings-B3sHxPh9.js} +2 -2
  165. package/dist/public/{shadow-claw-storage-Bvn85DII.js → shadow-claw-storage-DNdlzCgC.js} +1 -1
  166. package/dist/public/{shadow-claw-task-server-DZL_d8FC.js → shadow-claw-task-server-BJq0rpju.js} +1 -1
  167. package/dist/public/{shadow-claw-tasks-Cy8jzd8q.js → shadow-claw-tasks-CkF1uMeZ.js} +1 -1
  168. package/dist/public/{shadow-claw-terminal-BgEXr82T.js → shadow-claw-terminal-CnHhcpA-.js} +1 -1
  169. package/dist/public/{shadow-claw-tools-BjnssRSi.js → shadow-claw-tools-iw0fljud.js} +8 -1
  170. package/dist/public/{shadow-claw-webvm-tZ1VDoVA.js → shadow-claw-webvm-Bliu4V__.js} +1 -1
  171. package/dist/public/{syncWebMcpRegistration-CLMvJ4kV.js → syncWebMcpRegistration-D91usEMu.js} +1 -1
  172. package/package.json +1 -1
  173. package/src/cli/cli.ts +26 -2
  174. package/src/cli/commands/agent.test.ts +421 -0
  175. package/src/cli/commands/agent.ts +270 -20
  176. package/src/components/shadow-claw-tools/shadow-claw-tools.html +7 -0
  177. package/src/worker/headless-agent.ts +14 -0
  178. package/src/worker/headless-types.ts +39 -0
  179. package/src/worker/utils/native-eval-executor.test.ts +36 -0
  180. package/src/worker/utils/native-eval-executor.ts +58 -0
  181. package/src/worker/utils/sandboxedEval.ts +36 -0
  182. package/dist/cli/chunk-1BmpU7jO2.js +0 -1
  183. package/dist/cli/chunk-54Euq8Ji2.js +0 -1
  184. package/dist/cli/chunk-BTitwCgT2.js +0 -88
  185. package/dist/cli/chunk-Bjh76Dai.js +0 -1081
  186. package/dist/cli/chunk-C2HeZQ6K.js +0 -1
  187. package/dist/cli/chunk-CxdpafWg.js +0 -1
  188. package/dist/cli/chunk-DEy72ZNV2.js +0 -88
  189. package/dist/cli/chunk-Da1BZyqf.js +0 -1
  190. package/dist/cli/chunk-FHsJD-Sf2.js +0 -27
  191. package/dist/cli/chunk-aWgoVB7x2.js +0 -1
  192. package/dist/cli/chunk-hOceZVB92.js +0 -371
  193. package/dist/cli/chunk-pwNc6VtC2.js +0 -1
@@ -1,4 +1,4 @@
1
- import{_ as e,b as t,l as n,m as r,o as i,p as a,r as o}from"./config-DhI9BH7H.js";import{C as s,D as c,E as l,S as u,a as d,i as f,o as p,r as m,s as h,x as g}from"./executeNativeAiTask-DTuTs2Sj.js";import{i as _}from"./txPromise-C2iYogHX.js";import{t as v}from"./getConfig-4saf0Tkq.js";import{A as y,C as b,D as x,E as S,F as ee,I as te,L as ne,M as C,N as w,O as T,P as E,R as D,S as O,T as k,_ as A,a as j,b as re,c as ie,d as M,f as N,g as P,h as ae,i as F,j as I,k as L,l as R,m as oe,n as z,p as se,r as B,s as ce,t as V,u as H,v as le,w as ue,x as de,y as fe}from"./executeTool-DtTrsZHV.js";import{n as pe}from"./getAllTasks-JaDdWUpU.js";function me(e){return[...e,{role:`user`,content:`Please provide a concise summary of our entire conversation so far. Include all key facts, decisions, code discussed, and important context. This summary will replace the full history.`}]}function U(e,t,n,r){L(N(e,t,n,r))}const he=6e4,ge=new Map;function _e(e){let t=ge.get(e);if(t)return t;let n={recentCalls:[],nextAllowedAt:0,remoteLimit:null,remoteRemaining:null,remoteResetAt:null};return ge.set(e,n),n}function W(){return Date.now()}function G(e){if(!e)return null;let t=Number(e.trim());return Number.isFinite(t)?t:null}function ve(e){if(!e)return null;let t=e.trim(),n=Number(t);if(Number.isFinite(n))return n<=0?null:Math.round(n*1e3);let r=Date.parse(t);if(!Number.isFinite(r))return null;let i=r-W();return i>0?i:null}function ye(e){let t=G(e);return t===null||t<=0?null:t>=0xe8d4a51000?Math.round(t):t>=1e9?Math.round(t*1e3):W()+Math.round(t*1e3)}async function be(e,t){e<=0||await new Promise((n,r)=>{let i=setTimeout(()=>{o(),n()},e),a=()=>{o(),r(new DOMException(`Aborted`,`AbortError`))},o=()=>{i&&=(clearTimeout(i),null),t?.removeEventListener(`abort`,a)};if(t?.aborted){a();return}t?.addEventListener(`abort`,a)})}function xe(e,t){if(!Number.isFinite(t)||t<=0)return 0;let n=W();if(e.recentCalls=e.recentCalls.filter(e=>n-e<he),e.recentCalls.length<t)return 0;let r=e.recentCalls[0]||n;return Math.max(0,he-(n-r))}function Se(e){let t=W(),n=0;return e.nextAllowedAt>t&&(n=Math.max(n,e.nextAllowedAt-t)),e.remoteRemaining!==null&&e.remoteRemaining<=0&&e.remoteResetAt!==null&&e.remoteResetAt>t&&(n=Math.max(n,e.remoteResetAt-t)),n}async function Ce(e,t,n,r){let i=_e(e);for(;;){let e=xe(i,n.callsPerMinute),a=n.autoAdapt?Se(i):0,o=Math.max(e,a);if(o<=0){let e=W();if(n.callsPerMinute>0&&i.recentCalls.push(e),n.autoAdapt&&i.remoteRemaining!==null&&i.remoteRemaining>0&&i.remoteResetAt!==null&&i.remoteResetAt>e){let t=i.remoteResetAt-e,n=Math.max(1,i.remoteRemaining),r=Math.ceil(t/n);i.nextAllowedAt=Math.max(i.nextAllowedAt,e+r)}return}o>=1e3&&L({type:`thinking-log`,payload:{groupId:t,level:`info`,label:`Rate limiter`,message:`Waiting ${Math.ceil(o/1e3)}s before next provider call to avoid 429s.`,timestamp:Date.now()}}),await be(o,r)}}function we(e,t,n){if(!n.autoAdapt||!t||typeof t.get!=`function`)return;let r=_e(e),i=W(),a=G(t.get(`x-ratelimit-limit`)),o=G(t.get(`x-ratelimit-remaining`)),s=ye(t.get(`x-ratelimit-reset`));a!==null&&(r.remoteLimit=Math.max(0,Math.floor(a))),o!==null&&(r.remoteRemaining=Math.max(0,Math.floor(o))),s!==null&&(r.remoteResetAt=s);let c=ve(t.get(`retry-after`));c!==null&&(r.nextAllowedAt=Math.max(r.nextAllowedAt,i+c)),r.remoteRemaining!==null&&r.remoteRemaining<=0&&r.remoteResetAt!==null&&r.remoteResetAt>i&&(r.nextAllowedAt=Math.max(r.nextAllowedAt,r.remoteResetAt))}async function Te(e,n,r){let{groupId:i,messages:a,systemPrompt:o,apiKey:s,model:c,maxTokens:l=4096,provider:u,storageHandle:d,providerHeaders:p={},contextCompression:g=!1,rateLimitCallsPerMinute:_=0,rateLimitAutoAdapt:v=!0}=n,y={callsPerMinute:Number.isFinite(_)?Math.max(0,Math.floor(_)):0,autoAdapt:v!==!1};d&&D(d);let b=t(u);if(!b){L({type:`error`,payload:{groupId:i,error:`Unknown provider: ${u}`}});return}let x=b;L({type:`typing`,payload:{groupId:i}}),U(i,`info`,`Compacting context`,`Summarizing ${a.length} messages`);try{let e=f(x,me(a),[],{model:c,maxTokens:Math.min(l,4096),system:ie(o),contextCompression:g}),t={...m(x,s),...p};await Ce(u,i,y,r);let n=await fetch(x.baseUrl,{method:`POST`,headers:t,body:JSON.stringify(e),signal:r});if(we(u,n.headers,y),!n.ok){let e=await n.text();throw Error(`${x.name} API error ${n.status}: ${e}`)}let d=h(x,await n.json()).content.filter(e=>e.type===`text`).map(e=>e.text).join(``);U(i,`info`,`Compaction complete`,`Summary: ${d.length} chars`),L({type:`compact-done`,payload:{groupId:i,summary:d}})}catch(e){if(e instanceof Error&&e.name===`AbortError`)return;L({type:`error`,payload:{groupId:i,error:`Compaction failed: ${e instanceof Error?e.message:String(e)}`}})}}function Ee(e,t,n){let r=t.input_tokens||0,i=t.output_tokens||0,a=t.cache_read_input_tokens||0,o=t.cache_creation_input_tokens||0;return{type:`token-usage`,payload:{groupId:e,inputTokens:r,outputTokens:i,cacheReadTokens:a,cacheCreationTokens:o,totalTokens:r+i+a+o,contextLimit:n}}}async function*De(e,t){let n=e.getReader(),r=new TextDecoder,i=``;try{for(;!t?.aborted;){let{done:e,value:t}=await n.read();if(e)break;i+=r.decode(t,{stream:!0});let a=i.split(`
1
+ import{_ as e,b as t,l as n,m as r,o as i,p as a,r as o}from"./config-DhI9BH7H.js";import{C as s,D as c,E as l,S as u,a as d,i as f,o as p,r as m,s as h,x as g}from"./executeNativeAiTask-DTuTs2Sj.js";import{i as _}from"./txPromise-C2iYogHX.js";import{t as v}from"./getConfig-4saf0Tkq.js";import{A as y,C as b,D as x,E as S,F as ee,I as te,L as ne,M as C,N as w,O as T,P as E,R as D,S as O,T as k,_ as A,a as j,b as re,c as ie,d as M,f as N,g as P,h as ae,i as F,j as I,k as L,l as R,m as oe,n as z,p as se,r as B,s as ce,t as V,u as H,v as le,w as ue,x as de,y as fe}from"./executeTool-b6DnRhcv.js";import{n as pe}from"./getAllTasks-JaDdWUpU.js";function me(e){return[...e,{role:`user`,content:`Please provide a concise summary of our entire conversation so far. Include all key facts, decisions, code discussed, and important context. This summary will replace the full history.`}]}function U(e,t,n,r){L(N(e,t,n,r))}const he=6e4,ge=new Map;function _e(e){let t=ge.get(e);if(t)return t;let n={recentCalls:[],nextAllowedAt:0,remoteLimit:null,remoteRemaining:null,remoteResetAt:null};return ge.set(e,n),n}function W(){return Date.now()}function G(e){if(!e)return null;let t=Number(e.trim());return Number.isFinite(t)?t:null}function ve(e){if(!e)return null;let t=e.trim(),n=Number(t);if(Number.isFinite(n))return n<=0?null:Math.round(n*1e3);let r=Date.parse(t);if(!Number.isFinite(r))return null;let i=r-W();return i>0?i:null}function ye(e){let t=G(e);return t===null||t<=0?null:t>=0xe8d4a51000?Math.round(t):t>=1e9?Math.round(t*1e3):W()+Math.round(t*1e3)}async function be(e,t){e<=0||await new Promise((n,r)=>{let i=setTimeout(()=>{o(),n()},e),a=()=>{o(),r(new DOMException(`Aborted`,`AbortError`))},o=()=>{i&&=(clearTimeout(i),null),t?.removeEventListener(`abort`,a)};if(t?.aborted){a();return}t?.addEventListener(`abort`,a)})}function xe(e,t){if(!Number.isFinite(t)||t<=0)return 0;let n=W();if(e.recentCalls=e.recentCalls.filter(e=>n-e<he),e.recentCalls.length<t)return 0;let r=e.recentCalls[0]||n;return Math.max(0,he-(n-r))}function Se(e){let t=W(),n=0;return e.nextAllowedAt>t&&(n=Math.max(n,e.nextAllowedAt-t)),e.remoteRemaining!==null&&e.remoteRemaining<=0&&e.remoteResetAt!==null&&e.remoteResetAt>t&&(n=Math.max(n,e.remoteResetAt-t)),n}async function Ce(e,t,n,r){let i=_e(e);for(;;){let e=xe(i,n.callsPerMinute),a=n.autoAdapt?Se(i):0,o=Math.max(e,a);if(o<=0){let e=W();if(n.callsPerMinute>0&&i.recentCalls.push(e),n.autoAdapt&&i.remoteRemaining!==null&&i.remoteRemaining>0&&i.remoteResetAt!==null&&i.remoteResetAt>e){let t=i.remoteResetAt-e,n=Math.max(1,i.remoteRemaining),r=Math.ceil(t/n);i.nextAllowedAt=Math.max(i.nextAllowedAt,e+r)}return}o>=1e3&&L({type:`thinking-log`,payload:{groupId:t,level:`info`,label:`Rate limiter`,message:`Waiting ${Math.ceil(o/1e3)}s before next provider call to avoid 429s.`,timestamp:Date.now()}}),await be(o,r)}}function we(e,t,n){if(!n.autoAdapt||!t||typeof t.get!=`function`)return;let r=_e(e),i=W(),a=G(t.get(`x-ratelimit-limit`)),o=G(t.get(`x-ratelimit-remaining`)),s=ye(t.get(`x-ratelimit-reset`));a!==null&&(r.remoteLimit=Math.max(0,Math.floor(a))),o!==null&&(r.remoteRemaining=Math.max(0,Math.floor(o))),s!==null&&(r.remoteResetAt=s);let c=ve(t.get(`retry-after`));c!==null&&(r.nextAllowedAt=Math.max(r.nextAllowedAt,i+c)),r.remoteRemaining!==null&&r.remoteRemaining<=0&&r.remoteResetAt!==null&&r.remoteResetAt>i&&(r.nextAllowedAt=Math.max(r.nextAllowedAt,r.remoteResetAt))}async function Te(e,n,r){let{groupId:i,messages:a,systemPrompt:o,apiKey:s,model:c,maxTokens:l=4096,provider:u,storageHandle:d,providerHeaders:p={},contextCompression:g=!1,rateLimitCallsPerMinute:_=0,rateLimitAutoAdapt:v=!0}=n,y={callsPerMinute:Number.isFinite(_)?Math.max(0,Math.floor(_)):0,autoAdapt:v!==!1};d&&D(d);let b=t(u);if(!b){L({type:`error`,payload:{groupId:i,error:`Unknown provider: ${u}`}});return}let x=b;L({type:`typing`,payload:{groupId:i}}),U(i,`info`,`Compacting context`,`Summarizing ${a.length} messages`);try{let e=f(x,me(a),[],{model:c,maxTokens:Math.min(l,4096),system:ie(o),contextCompression:g}),t={...m(x,s),...p};await Ce(u,i,y,r);let n=await fetch(x.baseUrl,{method:`POST`,headers:t,body:JSON.stringify(e),signal:r});if(we(u,n.headers,y),!n.ok){let e=await n.text();throw Error(`${x.name} API error ${n.status}: ${e}`)}let d=h(x,await n.json()).content.filter(e=>e.type===`text`).map(e=>e.text).join(``);U(i,`info`,`Compaction complete`,`Summary: ${d.length} chars`),L({type:`compact-done`,payload:{groupId:i,summary:d}})}catch(e){if(e instanceof Error&&e.name===`AbortError`)return;L({type:`error`,payload:{groupId:i,error:`Compaction failed: ${e instanceof Error?e.message:String(e)}`}})}}function Ee(e,t,n){let r=t.input_tokens||0,i=t.output_tokens||0,a=t.cache_read_input_tokens||0,o=t.cache_creation_input_tokens||0;return{type:`token-usage`,payload:{groupId:e,inputTokens:r,outputTokens:i,cacheReadTokens:a,cacheCreationTokens:o,totalTokens:r+i+a+o,contextLimit:n}}}async function*De(e,t){let n=e.getReader(),r=new TextDecoder,i=``;try{for(;!t?.aborted;){let{done:e,value:t}=await n.read();if(e)break;i+=r.decode(t,{stream:!0});let a=i.split(`
2
2
 
3
3
  `);i=a.pop()||``;for(let e of a){let t=e.split(`
4
4
  `);for(let e of t)if(!(!e||e.startsWith(`:`))&&e.startsWith(`data: `)){let t=e.slice(6).trim();if(t===`[DONE]`)continue;try{yield JSON.parse(t)}catch{}}}}}finally{n.releaseLock()}}const Oe={"litert-community/gemma-4-E2B-it-litert-lm":`https://huggingface.co/litert-community/gemma-4-E2B-it-litert-lm/resolve/main/gemma-4-E2B-it-web.litertlm`,"litert-community/gemma-4-E4B-it-litert-lm":`https://huggingface.co/litert-community/gemma-4-E4B-it-litert-lm/resolve/main/gemma-4-E4B-it-web.litertlm`},ke=Object.keys(Oe);function Ae(){return typeof navigator<`u`&&`gpu`in navigator&&typeof WebAssembly<`u`&&`Suspending`in WebAssembly}let K=null,je=null,q=null,Me=null;const Ne=u;async function Pe(e,t,n){return g(e,t,n,{cacheName:`shadow-claw-litertlm-models`,chunkSize:Ne})}async function Fe(e,t,n){if(je===e&&K)return K;if(q&&Me===e)return q;Me=e,q=(async()=>{if(K){try{await K.delete?.()}catch{}K=null,je=null}let r=Oe[e];if(!r)throw Error(`LiteRT-LM: Model '${e} is not supported. Supported models: ${ke.join(`, `)}`);let i=await import(`./dist-DUy1CvmT.js`).catch(()=>{throw Error(`LiteRT-LM: The @litert-lm/core package is not installed. Run: npm install @litert-lm/core`)}),a=i.Engine??i.default?.Engine;if(typeof a?.create!=`function`)throw Error(`LiteRT-LM: Could not locate Engine.create in @litert-lm/core. Ensure you have the latest version installed.`);let o=await Pe(r,t??(()=>{}),n),s=await a.create({model:o,mainExecutorSettings:{maxNumTokens:8192}});return K=s,je=e,s})();try{return await q}finally{q=null,Me=null}}function Ie(e){if(!e)return null;let t=e.match(/<\|tool_call>call:([A-Za-z0-9_]+)(\{[\s\S]*?)(?:<tool_call\|>|$)/);if(!t)return null;let n=t[1],r=t[2];r=r.replace(/<\|"\|>/g,`"`);function i(e){return e.replace(/([{,]\s*)([A-Za-z_$][A-Za-z0-9_$]*)(\s*:)/g,`$1"$2"$3`)}let a={},o=[r,i(r)];for(let e of o)try{let t=JSON.parse(e);if(t&&typeof t==`object`&&!Array.isArray(t)){a=t;break}}catch{}if(Object.keys(a).length===0){let e=r.indexOf(`{`),t=r.lastIndexOf(`}`);if(e>=0&&t>e){let n=r.slice(e,t+1);for(let e of[n,i(n)])try{let t=JSON.parse(e);if(t&&typeof t==`object`&&!Array.isArray(t)){a=t;break}}catch{}}}return{name:n,input:a}}function Le(e){let t=s(String(e||``),`litert_lm`).trim();if(!t)return null;let n=e=>{try{let t=JSON.parse(e);if(t&&typeof t==`object`&&(t.type===`tool_use`||t.type===`response`))return t.tools_calls&&!t.tool_calls&&(t.tool_calls=t.tools_calls),{...t,format:`json`}}catch{}return null},r=n(t);if(r)return r;let i=t.indexOf(`{`),a=t.lastIndexOf(`}`);if(i>=0&&a>i&&(r=n(t.slice(i,a+1)),r))return r;if(t.includes(`"tool_use"`)){let e=/"name"\s*:\s*"([^"]+)"/.exec(t);if(e){let n={},r=/"input"\s*:\s*(\{.*?\})\s*(?:]|\}|"|$)/s.exec(t);if(r)try{n=JSON.parse(r[1])}catch{}return{type:`tool_use`,format:`json`,tool_calls:[{id:`litert_${Date.now()}`,name:e[1],input:n}]}}}let o=Ie(t);return o?{type:`tool_use`,format:`model_specific`,tool_calls:[{id:`litert_${Date.now()}`,name:o.name,input:o.input}]}:null}function Re(e,t,n){let r=[],i=e||``;if(n.length>0){let e=[``,`TOOL CALLING INSTRUCTIONS:`,`Return ONLY valid JSON (no markdown fences).`,`To call a tool: {"type":"tool_use","tool_calls":[{"name":"<tool>","input":{...}}]}`,`To reply: {"type":"response","response":"<your answer>"}`,`IMPORTANT: When you need to use a tool, output ONLY the JSON above — do NOT describe the call as text.`,``,`Available tools:`,n.map(e=>{let t=Object.keys(e.input_schema.properties||{}).join(`, `),n=e.description.split(`. `)[0];return`${e.name}(${t}): ${n}`}).join(`
@@ -1 +1 @@
1
- <template><section aria-label="Tool Configuration" class="tools"> <shadow-claw-page-header icon="🛠️" title="Tool Configuration"> <shadow-claw-page-header-action-button class="tools__back-btn" slot="actions"> ← Back to Settings </shadow-claw-page-header-action-button> <shadow-claw-page-header-action-button class="tools__add-btn" slot="actions" variant="primary"> + Add Tool </shadow-claw-page-header-action-button> <shadow-claw-page-header-action-button class="tools__backup-btn" slot="actions"> 💾 Backup </shadow-claw-page-header-action-button> <shadow-claw-page-header-action-button class="tools__restore-btn" slot="actions"> ♻️ Restore </shadow-claw-page-header-action-button> <shadow-claw-page-header-action-button class="tools__import-btn" slot="actions"> 🌐 Import </shadow-claw-page-header-action-button> </shadow-claw-page-header> <input accept=".json,application/json" aria-label="Restore tools config from JSON backup" class="tools__hidden-restore" hidden type="file"> <div class="tools__content"> <div class="tools__section"> <h3 class="tools__section-title">📋 Profiles</h3> <div class="tools__profile-bar"> <select aria-label="Select tool profile" class="tools__profile-select"> <option value>— No profile (manual config) —</option> </select> <div class="tools__profile-actions"> <button class="tools__profile-btn tools__profile-save-btn" type="button"> 💾 Save as Profile </button> <button class="tools__profile-btn tools__profile-new-btn" type="button"> + New </button> <button class="tools__profile-btn tools__profile-btn--danger tools__profile-delete-btn" type="button"> 🗑️ Delete </button> </div> </div> <div class="tools__prompt-hint"> Profiles save your tool selection, custom tools, and system prompt override. Associate a profile with a provider and model for quick switching. </div> </div> <div class="tools__section"> <h3 class="tools__section-title">Available Tools</h3> <div class="tools__toolbar"> <button class="tools__select-all-btn tools__toolbar-btn" type="button"> Select All </button> <button class="tools__select-none-btn tools__toolbar-btn" type="button"> Select None </button> <span class="tools__count"></span> </div> <div aria-live="polite" class="tools__list" role="list"></div> </div> <div class="tools__section"> <h3 class="tools__section-title">Internet Access</h3> <label class="tools__internet-optin" for="toolsInternetAccessOptIn"> <input class="tools__internet-access-toggle" id="toolsInternetAccessOptIn" type="checkbox"> Allow full internet access for Bash and JavaScript tools </label> <div class="tools__prompt-hint"> When enabled, both the <strong>bash</strong> and <strong>javascript</strong> tools can access the public internet. When disabled, external network access is restricted. </div> </div> <div class="tools__section"> <h3 class="tools__section-title">🔍 Search Files</h3> <div class="tools__searchfiles-row"> <label for="toolsSearchFilesMaxFileBytes">Max file size (bytes):</label> <input class="tools__searchfiles-max-file-bytes-input" id="toolsSearchFilesMaxFileBytes" min="1" placeholder="524288" type="number"> </div> <div class="tools__searchfiles-row"> <label for="toolsSearchFilesMaxFilesVisited">Max files visited:</label> <input class="tools__searchfiles-max-files-visited-input" id="toolsSearchFilesMaxFilesVisited" min="1" placeholder="1000" type="number"> </div> <div class="tools__searchfiles-row"> <label for="toolsSearchFilesSkipDirs">Skip directories (comma-separated):</label> <input class="tools__searchfiles-skip-dirs-input" id="toolsSearchFilesSkipDirs" placeholder=".git,node_modules,dist,…" type="text"> </div> <div class="tools__searchfiles-actions"> <button class="tools__save-btn tools__save-searchfiles-btn" type="button"> 💾 Save Search Files Settings </button> </div> <div class="tools__prompt-hint"> <strong>Max file size:</strong> Files larger than this (in bytes) are skipped to prevent out-of-memory crashes (default: <code>524288</code> = 512 KB).<br> <strong>Max files visited:</strong> Walk terminates after this many files are read, bounding CPU and memory use (default: <code>1000</code>).<br> <strong>Skip directories:</strong> Comma-separated directory names (not paths) that are never descended into, regardless of <code>path</code> or <code>file_glob</code> (default: <code>.git,node_modules,dist,…</code>). </div> </div> <div class="tools__section"> <h3 class="tools__section-title">🔍 Web Search</h3> <label class="tools__websearch-optin" for="toolsWebSearchUseProxy"> <input class="tools__websearch-proxy-toggle" id="toolsWebSearchUseProxy" type="checkbox"> Use local proxy for Web Search </label> <div class="tools__websearch-row"> <label for="toolsWebSearchProxyUrl">Proxy URL:</label> <input class="tools__websearch-proxy-url-input" id="toolsWebSearchProxyUrl" placeholder="/proxy" type="text"> </div> <div class="tools__websearch-row"> <label for="toolsWebSearchUrl">Web Search URL:</label> <input class="tools__websearch-url-input" id="toolsWebSearchUrl" placeholder="https://html.duckduckgo.com/html/?q={query}" type="text"> </div> <div class="tools__websearch-actions"> <button class="tools__save-btn tools__save-websearch-btn" type="button"> 💾 Save Web Search Settings </button> </div> <div class="tools__prompt-hint"> <strong>Proxy URL:</strong> Endpoint for the local proxy service (default: <code>/proxy</code>).<br> <strong>Web Search URL:</strong> Target search engine URL template. Use <code>{query}</code> as placeholder for search terms (default: <code>https://html.duckduckgo.com/html/?q={query}</code>). </div> </div> <div class="tools__section"> <h3 class="tools__section-title">WebMCP</h3> <label class="tools__webmcp-optin" for="toolsWebMcpOptIn"> <input class="tools__webmcp-toggle" id="toolsWebMcpOptIn" type="checkbox"> Enable WebMCP tool registration </label> <div class="tools__webmcp-mode-row"> <label for="toolsWebMcpMode">Mode:</label> <select class="tools__webmcp-mode" id="toolsWebMcpMode"> <option value="polyfill">Polyfill (recommended)</option> <option value="native">Native WebMCP flag (experimental)</option> </select> </div> <div class="tools__prompt-hint"> <strong>Polyfill</strong> uses @mcp-b/webmcp-polyfill — works in all browsers, no flags needed.<br> <strong>Native</strong> uses WebMCP API — requires chrome://flags/#enable-webmcp-testing (may crash in early Canary builds). </div> </div> <div class="tools__section"> <h3 class="tools__section-title">System Prompt Override</h3> <textarea aria-label="System prompt override" class="tools__prompt-area" placeholder="Leave empty to use the default system prompt. Any text here is appended to the default prompt."></textarea> <div class="tools__prompt-hint"> This text is appended to the built-in system prompt for all providers. Leave empty to use defaults only. </div> <div class="tools__prompt-actions"> <button class="tools__save-btn tools__save-prompt-btn" type="button"> 💾 Save Prompt </button> <button class="tools__clear-prompt-btn" type="button">Clear</button> </div> </div> </div> <shadow-claw-dialog dialog-aria-labelledby="toolsDialogTitle" dialog-class="tools__dialog"> <div class="tools__dialog-header"> <h3 class="tools__dialog-title" id="toolsDialogTitle"> Add Custom Tool </h3> <button aria-label="Close dialog" class="tools__dialog-close" type="button"> ✕ </button> </div> <form class="tools__dialog-form"> <div class="tools__dialog-body"> <div class="tools__form-group"> <label class="tools__form-label" for="toolNameInput"> Tool Name </label> <input class="tools__form-input" id="toolNameInput" name="name" pattern="[a-z][a-z0-9_]*" placeholder="my_tool" required type="text"> <div class="tools__form-hint"> Lowercase letters, digits, and underscores only. </div> </div> <div class="tools__form-group"> <label class="tools__form-label" for="toolDescInput"> Description </label> <textarea class="tools__form-textarea" id="toolDescInput" name="description" placeholder="Describe what this tool does..." required></textarea> </div> <div class="tools__form-group"> <label class="tools__form-label" for="toolSchemaInput"> Input Schema (JSON) </label> <textarea class="tools__form-textarea" id="toolSchemaInput" name="input_schema" placeholder='{"type":"object","properties":{}}'></textarea> <div class="tools__form-hint"> JSON Schema for the tool's input parameters. Leave empty for no parameters. </div> </div> </div> <div class="tools__dialog-footer"> <button class="tools__btn-cancel" type="button">Cancel</button> <button class="tools__btn-save" type="submit">Add Tool</button> </div> </form> </shadow-claw-dialog> <shadow-claw-dialog dialog-aria-labelledby="cloneDialogTitle" dialog-class="tools__clone-dialog"> <div class="tools__dialog-header"> <h3 class="tools__dialog-title" id="cloneDialogTitle">Clone Tool</h3> <button aria-label="Close dialog" class="tools__clone-dialog-close tools__dialog-close" type="button"> ✕ </button> </div> <form class="tools__clone-dialog-form"> <div class="tools__dialog-body"> <div class="tools__form-group"> <label class="tools__form-label">Source Tool</label> <input class="tools__form-input" name="source" readonly type="text"> </div> <div class="tools__form-group"> <label class="tools__form-label" for="cloneNameInput"> New Tool Name </label> <input class="tools__form-input" id="cloneNameInput" name="name" pattern="[a-z][a-z0-9_]*" placeholder="my_tool_v2" required type="text"> <div class="tools__form-hint"> Lowercase letters, digits, and underscores only. </div> </div> <div class="tools__form-group"> <label class="tools__form-label" for="cloneDescInput"> Description (optional) </label> <textarea class="tools__form-textarea" id="cloneDescInput" name="description" placeholder="Leave empty to copy the original description."></textarea> </div> </div> <div class="tools__dialog-footer"> <button class="tools__btn-cancel tools__clone-cancel-btn" type="button"> Cancel </button> <button class="tools__btn-save" type="submit">Clone Tool</button> </div> </form> </shadow-claw-dialog> <shadow-claw-dialog dialog-aria-labelledby="profileDialogTitle" dialog-class="tools__profile-dialog"> <div class="tools__dialog-header"> <h3 class="tools__dialog-title" id="profileDialogTitle"> Save Tool Profile </h3> <button aria-label="Close dialog" class="tools__dialog-close tools__profile-dialog-close" type="button"> ✕ </button> </div> <form class="tools__profile-dialog-form"> <div class="tools__dialog-body"> <div class="tools__form-group"> <label class="tools__form-label" for="profileNameInput"> Profile Name </label> <input class="tools__form-input" id="profileNameInput" name="name" placeholder="Claude Haiku Tools" required type="text"> </div> <div class="tools__form-group"> <label class="tools__form-label" for="profileProviderSelect"> Provider (optional) </label> <select class="tools__form-input" id="profileProviderSelect" name="providerId"> <option value>— Any provider —</option> </select> <div class="tools__form-hint"> Associate this profile with a specific provider. </div> </div> <div class="tools__form-group"> <label class="tools__form-label" for="profileModelInput"> Model (optional) </label> <input class="tools__form-input" id="profileModelInput" name="model" placeholder="e.g. anthropic/claude-haiku-4.5" type="text"> <div class="tools__form-hint"> Associate this profile with a specific model name. </div> </div> </div> <div class="tools__dialog-footer"> <button class="tools__btn-cancel tools__profile-cancel-btn" type="button"> Cancel </button> <button class="tools__btn-save" type="submit">Save Profile</button> </div> </form> </shadow-claw-dialog> <shadow-claw-dialog dialog-aria-labelledby="importDialogTitle" dialog-class="tools__import-dialog"> <div class="tools__dialog-header"> <h3 class="tools__dialog-title" id="importDialogTitle"> 🌐 Import Tools, Skills &amp; Scripts </h3> <button aria-label="Close dialog" class="tools__dialog-close tools__import-dialog-close" type="button"> ✕ </button> </div> <form class="tools__import-dialog-form"> <div class="tools__dialog-body"> <div class="tools__form-group"> <label class="tools__form-label" for="importUrlInput"> Site or Discovery URL </label> <div class="tools__import-url-row"> <input class="tools__form-input tools__import-url-input" id="importUrlInput" name="url" placeholder="https://kherrick.github.io/pwgen-knowledge-hub" required type="url"> <button class="tools__import-fetch-btn tools__toolbar-btn" type="button"> 🔍 Fetch </button> </div> <div class="tools__form-hint"> Enter any ShadowClaw site URL (e.g. GitHub Pages) or Discovery Index endpoint. </div> </div> <div class="tools__form-group"> <label class="tools__form-label"> Quick-Pick Presets </label> <div class="tools__import-presets"> <button class="tools__preset-btn" data-url="https://kherrick.github.io/pwgen-knowledge-hub" type="button"> 🔑 pwgen Knowledge Hub </button> <button class="tools__preset-btn" data-url="https://kherrick.github.io/block-garden-knowledge-hub" type="button"> 🌱 Block Garden Knowledge Hub </button> </div> </div> <div class="tools__import-manifest-status" hidden> <div class="tools__import-site-info"> <h4 class="tools__import-site-name"></h4> <p class="tools__import-site-desc"></p> </div> <div class="tools__import-catalog"> </div> <div class="tools__form-group tools__import-options"> <label class="tools__checkbox-label"> <input checked class="tools__import-auto-enable" type="checkbox"> Automatically enable imported tools </label> <label class="tools__checkbox-label"> <input class="tools__import-overwrite" type="checkbox"> Overwrite existing tools/skills if names match </label> </div> </div> </div> <div class="tools__dialog-footer"> <button class="tools__btn-cancel tools__import-cancel-btn" type="button"> Cancel </button> <button class="tools__btn-save tools__import-submit-btn" disabled type="submit"> Import Selected </button> </div> </form> </shadow-claw-dialog> </section></template>
1
+ <template><section aria-label="Tool Configuration" class="tools"> <shadow-claw-page-header icon="🛠️" title="Tool Configuration"> <shadow-claw-page-header-action-button class="tools__back-btn" slot="actions"> ← Back to Settings </shadow-claw-page-header-action-button> <shadow-claw-page-header-action-button class="tools__add-btn" slot="actions" variant="primary"> + Add Tool </shadow-claw-page-header-action-button> <shadow-claw-page-header-action-button class="tools__backup-btn" slot="actions"> 💾 Backup </shadow-claw-page-header-action-button> <shadow-claw-page-header-action-button class="tools__restore-btn" slot="actions"> ♻️ Restore </shadow-claw-page-header-action-button> <shadow-claw-page-header-action-button class="tools__import-btn" slot="actions"> 🌐 Import </shadow-claw-page-header-action-button> </shadow-claw-page-header> <input accept=".json,application/json" aria-label="Restore tools config from JSON backup" class="tools__hidden-restore" hidden type="file"> <div class="tools__content"> <div class="tools__section"> <h3 class="tools__section-title">📋 Profiles</h3> <div class="tools__profile-bar"> <select aria-label="Select tool profile" class="tools__profile-select"> <option value>— No profile (manual config) —</option> </select> <div class="tools__profile-actions"> <button class="tools__profile-btn tools__profile-save-btn" type="button"> 💾 Save as Profile </button> <button class="tools__profile-btn tools__profile-new-btn" type="button"> + New </button> <button class="tools__profile-btn tools__profile-btn--danger tools__profile-delete-btn" type="button"> 🗑️ Delete </button> </div> </div> <div class="tools__prompt-hint"> Profiles save your tool selection, custom tools, and system prompt override. Associate a profile with a provider and model for quick switching. </div> </div> <div class="tools__section"> <h3 class="tools__section-title">Available Tools</h3> <div class="tools__toolbar"> <button class="tools__select-all-btn tools__toolbar-btn" type="button"> Select All </button> <button class="tools__select-none-btn tools__toolbar-btn" type="button"> Select None </button> <span class="tools__count"></span> </div> <div aria-live="polite" class="tools__list" role="list"></div> </div> <div class="tools__section"> <h3 class="tools__section-title">Internet Access</h3> <label class="tools__internet-optin" for="toolsInternetAccessOptIn"> <input class="tools__internet-access-toggle" id="toolsInternetAccessOptIn" type="checkbox"> Allow full internet access for Bash and JavaScript tools </label> <div class="tools__prompt-hint"> When enabled, both the <strong>bash</strong> and <strong>javascript</strong> tools can access the public internet. When disabled, external network access is restricted. </div> </div> <div class="tools__section"> <h3 class="tools__section-title">🔍 Search Files</h3> <div class="tools__searchfiles-row"> <label for="toolsSearchFilesMaxFileBytes">Max file size (bytes):</label> <input class="tools__searchfiles-max-file-bytes-input" id="toolsSearchFilesMaxFileBytes" min="1" placeholder="524288" type="number"> </div> <div class="tools__searchfiles-row"> <label for="toolsSearchFilesMaxFilesVisited">Max files visited:</label> <input class="tools__searchfiles-max-files-visited-input" id="toolsSearchFilesMaxFilesVisited" min="1" placeholder="1000" type="number"> </div> <div class="tools__searchfiles-row"> <label for="toolsSearchFilesSkipDirs">Skip directories (comma-separated):</label> <input class="tools__searchfiles-skip-dirs-input" id="toolsSearchFilesSkipDirs" placeholder=".git,node_modules,dist,…" type="text"> </div> <div class="tools__searchfiles-actions"> <button class="tools__save-btn tools__save-searchfiles-btn" type="button"> 💾 Save Search Files Settings </button> </div> <div class="tools__prompt-hint"> <strong>Max file size:</strong> Files larger than this (in bytes) are skipped to prevent out-of-memory crashes (default: <code>524288</code> = 512 KB).<br> <strong>Max files visited:</strong> Walk terminates after this many files are read, bounding CPU and memory use (default: <code>1000</code>).<br> <strong>Skip directories:</strong> Comma-separated directory names (not paths) that are never descended into, regardless of <code>path</code> or <code>file_glob</code> (default: <code>.git,node_modules,dist,…</code>). </div> </div> <div class="tools__section"> <h3 class="tools__section-title">🔍 Web Search</h3> <label class="tools__websearch-optin" for="toolsWebSearchUseProxy"> <input class="tools__websearch-proxy-toggle" id="toolsWebSearchUseProxy" type="checkbox"> Use local proxy for Web Search </label> <div class="tools__websearch-row"> <label for="toolsWebSearchProxyUrl">Proxy URL:</label> <input class="tools__websearch-proxy-url-input" id="toolsWebSearchProxyUrl" placeholder="/proxy" type="text"> </div> <div class="tools__websearch-row"> <label for="toolsWebSearchUrl">Web Search URL:</label> <input class="tools__websearch-url-input" id="toolsWebSearchUrl" placeholder="https://html.duckduckgo.com/html/?q={query}" type="text"> </div> <div class="tools__websearch-actions"> <button class="tools__save-btn tools__save-websearch-btn" type="button"> 💾 Save Web Search Settings </button> </div> <div class="tools__prompt-hint"> <strong>Proxy URL:</strong> Endpoint for the local proxy service (default: <code>/proxy</code>).<br> <strong>Web Search URL:</strong> Target search engine URL template. Use <code>{query}</code> as placeholder for search terms (default: <code>https://html.duckduckgo.com/html/?q={query}</code>). </div> </div> <div class="tools__section"> <h3 class="tools__section-title">WebMCP</h3> <label class="tools__webmcp-optin" for="toolsWebMcpOptIn"> <input class="tools__webmcp-toggle" id="toolsWebMcpOptIn" type="checkbox"> Enable WebMCP tool registration </label> <div class="tools__webmcp-mode-row"> <label for="toolsWebMcpMode">Mode:</label> <select class="tools__webmcp-mode" id="toolsWebMcpMode"> <option value="polyfill">Polyfill (recommended)</option> <option value="native">Native WebMCP flag (experimental)</option> </select> </div> <div class="tools__prompt-hint"> <strong>Polyfill</strong> uses @mcp-b/webmcp-polyfill — works in all browsers, no flags needed.<br> <strong>Native</strong> uses WebMCP API — requires chrome://flags/#enable-webmcp-testing (may crash in early Canary builds). </div> </div> <div class="tools__section"> <h3 class="tools__section-title">System Prompt Override</h3> <textarea aria-label="System prompt override" class="tools__prompt-area" placeholder="Leave empty to use the default system prompt. Any text here is appended to the default prompt."></textarea> <div class="tools__prompt-hint"> This text is appended to the built-in system prompt for all providers. Leave empty to use defaults only. </div> <div class="tools__prompt-actions"> <button class="tools__save-btn tools__save-prompt-btn" type="button"> 💾 Save Prompt </button> <button class="tools__clear-prompt-btn" type="button">Clear</button> </div> </div> </div> <shadow-claw-dialog dialog-aria-labelledby="toolsDialogTitle" dialog-class="tools__dialog"> <div class="tools__dialog-header"> <h3 class="tools__dialog-title" id="toolsDialogTitle"> Add Custom Tool </h3> <button aria-label="Close dialog" class="tools__dialog-close" type="button"> ✕ </button> </div> <form class="tools__dialog-form"> <div class="tools__dialog-body"> <div class="tools__form-group"> <label class="tools__form-label" for="toolNameInput"> Tool Name </label> <input class="tools__form-input" id="toolNameInput" name="name" pattern="[a-z][a-z0-9_]*" placeholder="my_tool" required type="text"> <div class="tools__form-hint"> Lowercase letters, digits, and underscores only. </div> </div> <div class="tools__form-group"> <label class="tools__form-label" for="toolDescInput"> Description </label> <textarea class="tools__form-textarea" id="toolDescInput" name="description" placeholder="Describe what this tool does..." required></textarea> </div> <div class="tools__form-group"> <label class="tools__form-label" for="toolSchemaInput"> Input Schema (JSON) </label> <textarea class="tools__form-textarea" id="toolSchemaInput" name="input_schema" placeholder='{"type":"object","properties":{}}'></textarea> <div class="tools__form-hint"> JSON Schema for the tool's input parameters. Leave empty for no parameters. </div> </div> </div> <div class="tools__dialog-footer"> <button class="tools__btn-cancel" type="button">Cancel</button> <button class="tools__btn-save" type="submit">Add Tool</button> </div> </form> </shadow-claw-dialog> <shadow-claw-dialog dialog-aria-labelledby="cloneDialogTitle" dialog-class="tools__clone-dialog"> <div class="tools__dialog-header"> <h3 class="tools__dialog-title" id="cloneDialogTitle">Clone Tool</h3> <button aria-label="Close dialog" class="tools__clone-dialog-close tools__dialog-close" type="button"> ✕ </button> </div> <form class="tools__clone-dialog-form"> <div class="tools__dialog-body"> <div class="tools__form-group"> <label class="tools__form-label">Source Tool</label> <input class="tools__form-input" name="source" readonly type="text"> </div> <div class="tools__form-group"> <label class="tools__form-label" for="cloneNameInput"> New Tool Name </label> <input class="tools__form-input" id="cloneNameInput" name="name" pattern="[a-z][a-z0-9_]*" placeholder="my_tool_v2" required type="text"> <div class="tools__form-hint"> Lowercase letters, digits, and underscores only. </div> </div> <div class="tools__form-group"> <label class="tools__form-label" for="cloneDescInput"> Description (optional) </label> <textarea class="tools__form-textarea" id="cloneDescInput" name="description" placeholder="Leave empty to copy the original description."></textarea> </div> </div> <div class="tools__dialog-footer"> <button class="tools__btn-cancel tools__clone-cancel-btn" type="button"> Cancel </button> <button class="tools__btn-save" type="submit">Clone Tool</button> </div> </form> </shadow-claw-dialog> <shadow-claw-dialog dialog-aria-labelledby="profileDialogTitle" dialog-class="tools__profile-dialog"> <div class="tools__dialog-header"> <h3 class="tools__dialog-title" id="profileDialogTitle"> Save Tool Profile </h3> <button aria-label="Close dialog" class="tools__dialog-close tools__profile-dialog-close" type="button"> ✕ </button> </div> <form class="tools__profile-dialog-form"> <div class="tools__dialog-body"> <div class="tools__form-group"> <label class="tools__form-label" for="profileNameInput"> Profile Name </label> <input class="tools__form-input" id="profileNameInput" name="name" placeholder="Claude Haiku Tools" required type="text"> </div> <div class="tools__form-group"> <label class="tools__form-label" for="profileProviderSelect"> Provider (optional) </label> <select class="tools__form-input" id="profileProviderSelect" name="providerId"> <option value>— Any provider —</option> </select> <div class="tools__form-hint"> Associate this profile with a specific provider. </div> </div> <div class="tools__form-group"> <label class="tools__form-label" for="profileModelInput"> Model (optional) </label> <input class="tools__form-input" id="profileModelInput" name="model" placeholder="e.g. anthropic/claude-haiku-4.5" type="text"> <div class="tools__form-hint"> Associate this profile with a specific model name. </div> </div> </div> <div class="tools__dialog-footer"> <button class="tools__btn-cancel tools__profile-cancel-btn" type="button"> Cancel </button> <button class="tools__btn-save" type="submit">Save Profile</button> </div> </form> </shadow-claw-dialog> <shadow-claw-dialog dialog-aria-labelledby="importDialogTitle" dialog-class="tools__import-dialog"> <div class="tools__dialog-header"> <h3 class="tools__dialog-title" id="importDialogTitle"> 🌐 Import Tools, Skills &amp; Scripts </h3> <button aria-label="Close dialog" class="tools__dialog-close tools__import-dialog-close" type="button"> ✕ </button> </div> <form class="tools__import-dialog-form"> <div class="tools__dialog-body"> <div class="tools__form-group"> <label class="tools__form-label" for="importUrlInput"> Site or Discovery URL </label> <div class="tools__import-url-row"> <input class="tools__form-input tools__import-url-input" id="importUrlInput" name="url" placeholder="https://kherrick.github.io/pwgen-knowledge-hub" required type="url"> <button class="tools__import-fetch-btn tools__toolbar-btn" type="button"> 🔍 Fetch </button> </div> <div class="tools__form-hint"> Enter any ShadowClaw site URL (e.g. GitHub Pages) or Discovery Index endpoint. </div> </div> <div class="tools__form-group"> <label class="tools__form-label"> Quick-Pick Presets </label> <div class="tools__import-presets"> <button class="tools__preset-btn" data-url="https://kherrick.github.io/pwgen-knowledge-hub" type="button"> 🔑 pwgen Knowledge Hub </button> <button class="tools__preset-btn" data-url="https://kherrick.github.io/block-garden-knowledge-hub" type="button"> 🌱 Block Garden Knowledge Hub </button> <button class="tools__preset-btn" data-url="https://xt-ml.github.io/shadow-claw-agent-cli-weather" type="button"> 🌦️ Weather Agent (Open-Meteo) </button> </div> </div> <div class="tools__import-manifest-status" hidden> <div class="tools__import-site-info"> <h4 class="tools__import-site-name"></h4> <p class="tools__import-site-desc"></p> </div> <div class="tools__import-catalog"> </div> <div class="tools__form-group tools__import-options"> <label class="tools__checkbox-label"> <input checked class="tools__import-auto-enable" type="checkbox"> Automatically enable imported tools </label> <label class="tools__checkbox-label"> <input class="tools__import-overwrite" type="checkbox"> Overwrite existing tools/skills if names match </label> </div> </div> </div> <div class="tools__dialog-footer"> <button class="tools__btn-cancel tools__import-cancel-btn" type="button"> Cancel </button> <button class="tools__btn-save tools__import-submit-btn" disabled type="submit"> Import Selected </button> </div> </form> </shadow-claw-dialog> </section></template>
@@ -1 +1 @@
1
- import{r as e}from"./config-D4lVqMs8.js";import{Ct as t,ht as n}from"./orchestrator-Baif6kmu.js";import{t as r}from"./setConfig-DdSThoKj.js";async function i(i,a,o,s,c=``,l=0,u=``,d=!0){let f=o.trim(),p=t(s),m=c.trim(),h=Number.isFinite(l)?Math.max(0,Math.floor(l)):0,g=u.trim(),_=!!d;i.peerjsMyPeerId=f,i.peerjsTrustedPeerIds=p,i.peerjsServerHost=m,i.peerjsServerPort=h,i.peerjsServerPath=g,i.peerjsServerSecure=_,await r(a,e.PEERJS_MY_PEER_ID,f),await r(a,e.PEERJS_TRUSTED_PEER_IDS,JSON.stringify(p)),await r(a,e.PEERJS_SERVER_HOST,m),await r(a,e.PEERJS_SERVER_PORT,h?String(h):``),await r(a,e.PEERJS_SERVER_PATH,g),await r(a,e.PEERJS_SERVER_SECURE,_?`true`:`false`);let v=m?{host:m,path:g||void 0,port:h||void 0,secure:_}:{};i.peerjs.stop(),i.peerjs.configure(f,p,v),f&&n(i,`peerjs`)&&i.peerjs.start()}export{i as t};
1
+ import{r as e}from"./config-D4lVqMs8.js";import{Ct as t,ht as n}from"./orchestrator-0BnBtxHO.js";import{t as r}from"./setConfig-DdSThoKj.js";async function i(i,a,o,s,c=``,l=0,u=``,d=!0){let f=o.trim(),p=t(s),m=c.trim(),h=Number.isFinite(l)?Math.max(0,Math.floor(l)):0,g=u.trim(),_=!!d;i.peerjsMyPeerId=f,i.peerjsTrustedPeerIds=p,i.peerjsServerHost=m,i.peerjsServerPort=h,i.peerjsServerPath=g,i.peerjsServerSecure=_,await r(a,e.PEERJS_MY_PEER_ID,f),await r(a,e.PEERJS_TRUSTED_PEER_IDS,JSON.stringify(p)),await r(a,e.PEERJS_SERVER_HOST,m),await r(a,e.PEERJS_SERVER_PORT,h?String(h):``),await r(a,e.PEERJS_SERVER_PATH,g),await r(a,e.PEERJS_SERVER_SECURE,_?`true`:`false`);let v=m?{host:m,path:g||void 0,port:h||void 0,secure:_}:{};i.peerjs.stop(),i.peerjs.configure(f,p,v),f&&n(i,`peerjs`)&&i.peerjs.start()}export{i as t};
@@ -18,7 +18,7 @@ Introduce a **headless runtime target** for the agent loop that is a full partic
18
18
  1. **Dual-Dispatch Storage Backend.** Introduce a `node:sqlite`-backed wrapper (`ShadowClawSqliteDatabase`) behind `src/db/` with dual-dispatch branches across all `src/db/*.ts` operations. `ShadowClawDatabase` is widened to `IDBDatabase | ShadowClawSqliteDatabase | null`. When running in headless mode, `openSqliteDatabase()` creates/opens an SQLite database mirroring the 5 IndexedDB stores (`messages`, `sessions`, `tasks`, `config`, `pendingShares`).
19
19
  2. **Filesystem Storage Backend.** `NodeFsDirectoryHandle` implements the `FileSystemDirectoryHandle` and `FileSystemFileHandle` subset used across storage modules (`getGroupDir`, `readGroupFile`, `writeGroupFile`, `discoverSkills`, `loadDeclarativeTools`). `setStorageRootFromPath()` allows switching the storage root to a real filesystem directory.
20
20
  3. **Tool Capability Matrix & Headless Mode Flag.** `src/config/headless.ts` maintains an explicit `isHeadlessMode()` flag. Tools that require a browser execution surface (`ask_user`, `attach_file_to_chat`, `clear_chat`, `list_components`, `open_file`, `render_component`, `send_file`, `show_toast`, `send_notification`, `spawn_subagent`) return a clean, descriptive error message rather than silently failing. Environment-agnostic tools (`bash`, `read_file`, `write_file`, `git_*`, `fetch_*`, `tasks`, `skills`, `javascript`) execute headlessly. `bash` uses native OS `node:child_process` in headless mode for full command-line power.
21
- 4. **CLI Integration.** `shadow-claw agent` subcommands (`init`, `skills`, `tools`, `skill`, `run`) provide a unified command-line experience for initializing workspaces, discovering and inspecting declarative skills and tools, running skills directly, and composable Unix pipeline integration via stdin piping and `-o`/`-q` flags.
21
+ 4. **CLI Integration.** `shadow-claw agent` subcommands (`init`, `model`, `skills`, `tools`, `tool`, `skill`, `import`, `run`) provide a unified command-line experience for initializing workspaces, managing local ONNX/GGUF models, discovering, inspecting, and importing remote RFC v0.2.0 declarative skills and tools, running skills directly, and composable Unix pipeline integration via stdin piping and `-o`/`-q` flags.
22
22
 
23
23
  ## Trade-offs
24
24
 
@@ -1,4 +1,4 @@
1
- <!doctype html><html lang="en"><head><title>ShadowClaw</title><meta charset="utf-8"><meta name="revision" content="25a61e273c78a4e8d94f73bf714f6584f3c4be4c" /><meta name="version" content="1.34.5" /><meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"><meta name="description" content="ShadowClaw - Browser-native AI agent." /><link rel="canonical" href="https://xt-ml.github.io/shadow-claw/"><meta property="og:title" content="ShadowClaw"><meta property="og:description" content="Browser-native personal AI assistant with local and remote LLM orchestration, Web Workers, OPFS storage, and agentic tool execution."><meta property="og:type" content="website"><meta property="og:url" content="https://xt-ml.github.io/shadow-claw/"><meta property="og:image" content="https://xt-ml.github.io/shadow-claw/assets/icons/512.png"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="ShadowClaw"><meta name="twitter:description" content="Browser-native personal AI assistant with local and remote LLM orchestration, Web Workers, OPFS storage, and agentic tool execution."><meta name="twitter:image" content="https://xt-ml.github.io/shadow-claw/assets/icons/512.png"><script type="application/ld+json">{"@context":"https://schema.org","@graph":[{"@type":"SoftwareApplication","@id":"https://xt-ml.github.io/shadow-claw/#software","name":"ShadowClaw","description":"Browser-native personal AI assistant with local and remote LLM orchestration, Web Workers, OPFS storage, and agentic tool execution.","url":"https://xt-ml.github.io/shadow-claw/","applicationCategory":"DeveloperApplication","operatingSystem":"Web, Browser, Desktop","offers":{"@type":"Offer","price":"0","priceCurrency":"USD"},"author":{"@type":"Organization","name":"xt-ml","url":"https://github.com/xt-ml"},"codeRepository":"https://github.com/xt-ml/shadow-claw","license":"https://github.com/xt-ml/shadow-claw/blob/main/LICENSE"},{"@type":"WebSite","@id":"https://xt-ml.github.io/shadow-claw/#website","url":"https://xt-ml.github.io/shadow-claw/","name":"ShadowClaw","description":"Official website and live deployment of ShadowClaw browser-native AI assistant.","publisher":{"@type":"Organization","name":"xt-ml","url":"https://github.com/xt-ml"}},{"@type":"Organization","@id":"https://xt-ml.github.io/shadow-claw/#organization","name":"xt-ml","url":"https://github.com/xt-ml","sameAs":["https://github.com/xt-ml"]}]}</script><meta name="theme-color" content="#fff" /><meta http-equiv="origin-trial" content="A88uTgESYWcVgREKVReKB6jom41uP8TzW6ei3jNdidf+Xl6ONqKRjKNBphsxhrZmdkakLK3oXDgkGq53rtZYSAMAAAB2eyJvcmlnaW4iOiJodHRwczovL3h0LW1sLmdpdGh1Yi5pbzo0NDMiLCJmZWF0dXJlIjoiV2ViTUNQIiwiZXhwaXJ5IjoxNzk0ODczNjAwLCJpc1N1YmRvbWFpbiI6dHJ1ZSwiaXNUaGlyZFBhcnR5Ijp0cnVlfQ=="><base href="/shadow-claw/" /><script id="shadow-claw-site-config" type="application/json">{"$schema":"https:\u002f\u002fjson-schema.org\u002fdraft\u002f2020-12\u002fschema","site":{"title":"ShadowClaw","description":"ShadowClaw - Browser-native AI agent.","themeColor":"#fff"},"branding":{"faviconPath":"assets\u002ficons\u002ffavicon.ico","appleTouchIconPath":"assets\u002ficons\u002f180.png","notFoundPath":"404.html"},"pwa":{"manifestPath":"manifest.json","themeColor":"#000000"},"sitemapPath":"sitemap.xml","assets":["assets"],"sidebar":{"pagesHidden":false,"chatHidden":false,"tasksHidden":false,"filesHidden":false,"defaultPage":"pages"},"settings":{"defaultToolsProfile":"__builtin_default"},"agent":{"defaultProvider":"transformers_js_local","defaultModel":"onnx-community\u002fgemma-3-1b-it-ONNX-GQA"},"customElements":{"allowedElements":["block-garden","block-garden-option","block-garden-select"],"allowedDomains":["kherrick.github.io","cdn.jsdelivr.net"],"scripts":[{"src":"https:\u002f\u002fkherrick.github.io\u002fblock-garden-knowledge-hub\u002f.agents\u002fscripts\u002fmain\u002fblock-garden-adapter.js","hasInit":true},"https:\u002f\u002fkherrick.github.io\u002fblock-garden\u002fblock-garden-bundle-min.mjs"]},"security":{"connectSrc":["'self'","blob:","data:","https:\u002f\u002fkherrick.github.io","https:\u002f\u002fcdn.jsdelivr.net"]}}</script>
1
+ <!doctype html><html lang="en"><head><title>ShadowClaw</title><meta charset="utf-8"><meta name="revision" content="72143ec88485970231da8c06ecf74089dd580224" /><meta name="version" content="1.35.0" /><meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"><meta name="description" content="ShadowClaw - Browser-native AI agent." /><link rel="canonical" href="https://xt-ml.github.io/shadow-claw/"><meta property="og:title" content="ShadowClaw"><meta property="og:description" content="Browser-native personal AI assistant with local and remote LLM orchestration, Web Workers, OPFS storage, and agentic tool execution."><meta property="og:type" content="website"><meta property="og:url" content="https://xt-ml.github.io/shadow-claw/"><meta property="og:image" content="https://xt-ml.github.io/shadow-claw/assets/icons/512.png"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="ShadowClaw"><meta name="twitter:description" content="Browser-native personal AI assistant with local and remote LLM orchestration, Web Workers, OPFS storage, and agentic tool execution."><meta name="twitter:image" content="https://xt-ml.github.io/shadow-claw/assets/icons/512.png"><script type="application/ld+json">{"@context":"https://schema.org","@graph":[{"@type":"SoftwareApplication","@id":"https://xt-ml.github.io/shadow-claw/#software","name":"ShadowClaw","description":"Browser-native personal AI assistant with local and remote LLM orchestration, Web Workers, OPFS storage, and agentic tool execution.","url":"https://xt-ml.github.io/shadow-claw/","applicationCategory":"DeveloperApplication","operatingSystem":"Web, Browser, Desktop","offers":{"@type":"Offer","price":"0","priceCurrency":"USD"},"author":{"@type":"Organization","name":"xt-ml","url":"https://github.com/xt-ml"},"codeRepository":"https://github.com/xt-ml/shadow-claw","license":"https://github.com/xt-ml/shadow-claw/blob/main/LICENSE"},{"@type":"WebSite","@id":"https://xt-ml.github.io/shadow-claw/#website","url":"https://xt-ml.github.io/shadow-claw/","name":"ShadowClaw","description":"Official website and live deployment of ShadowClaw browser-native AI assistant.","publisher":{"@type":"Organization","name":"xt-ml","url":"https://github.com/xt-ml"}},{"@type":"Organization","@id":"https://xt-ml.github.io/shadow-claw/#organization","name":"xt-ml","url":"https://github.com/xt-ml","sameAs":["https://github.com/xt-ml"]}]}</script><meta name="theme-color" content="#fff" /><meta http-equiv="origin-trial" content="A88uTgESYWcVgREKVReKB6jom41uP8TzW6ei3jNdidf+Xl6ONqKRjKNBphsxhrZmdkakLK3oXDgkGq53rtZYSAMAAAB2eyJvcmlnaW4iOiJodHRwczovL3h0LW1sLmdpdGh1Yi5pbzo0NDMiLCJmZWF0dXJlIjoiV2ViTUNQIiwiZXhwaXJ5IjoxNzk0ODczNjAwLCJpc1N1YmRvbWFpbiI6dHJ1ZSwiaXNUaGlyZFBhcnR5Ijp0cnVlfQ=="><base href="/shadow-claw/" /><script id="shadow-claw-site-config" type="application/json">{"$schema":"https:\u002f\u002fjson-schema.org\u002fdraft\u002f2020-12\u002fschema","site":{"title":"ShadowClaw","description":"ShadowClaw - Browser-native AI agent.","themeColor":"#fff"},"branding":{"faviconPath":"assets\u002ficons\u002ffavicon.ico","appleTouchIconPath":"assets\u002ficons\u002f180.png","notFoundPath":"404.html"},"pwa":{"manifestPath":"manifest.json","themeColor":"#000000"},"sitemapPath":"sitemap.xml","assets":["assets"],"sidebar":{"pagesHidden":false,"chatHidden":false,"tasksHidden":false,"filesHidden":false,"defaultPage":"pages"},"settings":{"defaultToolsProfile":"__builtin_default"},"agent":{"defaultProvider":"transformers_js_local","defaultModel":"onnx-community\u002fgemma-3-1b-it-ONNX-GQA"},"customElements":{"allowedElements":["block-garden","block-garden-option","block-garden-select"],"allowedDomains":["kherrick.github.io","cdn.jsdelivr.net"],"scripts":[{"src":"https:\u002f\u002fkherrick.github.io\u002fblock-garden-knowledge-hub\u002f.agents\u002fscripts\u002fmain\u002fblock-garden-adapter.js","hasInit":true},"https:\u002f\u002fkherrick.github.io\u002fblock-garden\u002fblock-garden-bundle-min.mjs"]},"security":{"connectSrc":["'self'","blob:","data:","https:\u002f\u002fkherrick.github.io","https:\u002f\u002fcdn.jsdelivr.net"]}}</script>
2
2
  <script>var ShadowClawThemeInit=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports);function n(){if(globalThis.trustedTypes)return;let e=new Map;globalThis.trustedTypes={createPolicy:(t,n)=>{if(e.has(t))throw TypeError(`Policy with name "${t}" already exists.`);let r={createHTML:n.createHTML?e=>n.createHTML(e):void 0,createScriptURL:n.createScriptURL?e=>n.createScriptURL(e):void 0,createScript:n.createScript?e=>n.createScript(e):void 0};return e.set(t,r),r},getPolicy:t=>e.get(t)??null,isHTML:e=>typeof e==`string`||e instanceof Object&&`toString`in e,isScriptURL:e=>typeof e==`string`||e instanceof Object&&`toString`in e,isScript:e=>typeof e==`string`||e instanceof Object&&`toString`in e}}let r=`__shadowClawDefaultTrustedTypesPolicyState`,i=`default`,a=`shadowclaw-sandbox`;function o(){let e=globalThis;return e[r]||(e[r]={initialized:!1,policy:null}),e[r]}function s(){let e=o();if(e.initialized)return;let t=Reflect.get(globalThis,`trustedTypes`);if(!(!t||typeof t.createPolicy!=`function`)){e.initialized=!0;try{if(typeof t.getPolicy==`function`){let n=t.getPolicy(i);if(n){e.policy=n;return}}let n=!1;if(typeof t.getPolicyNames==`function`){let e=t.getPolicyNames();Array.isArray(e)&&e.includes(i)&&(n=!0)}if(n){if(typeof t.getPolicy==`function`){let n=t.getPolicy(a);if(n){e.policy=n;return}}e.policy=t.createPolicy(a,{createHTML:e=>e,createScriptURL:e=>e});return}e.policy=t.createPolicy(i,{createHTML:e=>e,createScriptURL:e=>e})}catch{typeof t.getPolicy==`function`&&(e.policy=t.getPolicy(i)??t.getPolicy(a)??null)}}}new class{loading=!1;models=new Map;getModelInfo(e){if(!e||typeof e!=`string`)return null;let t=e.toLowerCase().trim(),n=this.models.get(t);if(n)return n;if(t.includes(`/`)){let e=t.split(`/`).pop();if(e&&this.models.has(e))return this.models.get(e)}else for(let[e,n]of this.models.entries())if(e.endsWith(`/${t}`))return n;if(t.includes(`:`)){let e=t.split(`:`)[0],n=this.getModelInfo(e);if(n)return n}return null}registerModelInfo(e,t){this.models.set(e.toLowerCase(),t)}clear(){this.models.clear()}async fetchModelInfo(e,t,n){if(e.modelsUrl){this.loading=!0;try{let r=new Headers;if(r.set(`Content-Type`,`application/json`),e.headers)for(let[t,n]of Object.entries(e.headers))r.set(t,n);if(n)for(let[e,t]of Object.entries(n))r.set(e,t);if(t&&e.apiKeyHeader){let n=e.apiKeyHeaderFormat;r.set(e.apiKeyHeader,n?n.replace(`{key}`,t):t)}let i=await fetch(e.modelsUrl,{headers:r});if(!i.ok)throw Error(`HTTP ${i.status} ${i.statusText}`);let a=await i.json(),o=Array.isArray(a.data)?a.data:Array.isArray(a.models)?a.models:[];for(let e of o){if(!e.id)continue;let t=e.context_length??e.context_window??0,n=e.max_completion_tokens||e.per_request_limits?.completion_tokens||e.top_provider?.max_completion_tokens||null,r=typeof e.supports_tools==`boolean`?e.supports_tools:typeof e.supportsTools==`boolean`?e.supportsTools:void 0,i=this.extractModalities(e,`input`),a=this.extractModalities(e,`output`),o=this.modalitiesInclude(i,`image`,`vision`),s=this.modalitiesInclude(i,`audio`,`voice`,`hearing`),c=this.modalitiesInclude(i,`video`),l=e.id===`openrouter/free`||this.supportedParametersInclude(e,`image`,`vision`,`audio`,`tool`,`structured`),u=this.extractReasoning(e),d=typeof e.supports_prompt_caching==`boolean`?e.supports_prompt_caching:typeof e.supportsPromptCaching==`boolean`?e.supportsPromptCaching:this.supportedParametersInclude(e,`cache_control`,`prompt_caching`)?!0:void 0;!t&&Array.isArray(e.providers)&&e.providers.length>0&&(t=e.providers[0]?.context_length||0),this.registerModelInfo(e.id,{contextWindow:t,maxOutput:n,...r!==void 0&&{supportsTools:r},...i.length>0&&{inputModalities:i},...a.length>0&&{outputModalities:a},...o!==void 0&&{supportsImageInput:o},...s!==void 0&&{supportsAudioInput:s},...c!==void 0&&{supportsVideoInput:c},...u&&{reasoning:u},...l&&{routesByRequestFeatures:l},...d!==void 0&&{supportsPromptCaching:d}})}o.length>0&&console.log(`[ModelRegistry] Registered ${o.length} models for provider "${e.id}"`)}catch(t){console.error(`[ModelRegistry] Error fetching models for ${e.id}:`,t)}finally{this.loading=!1}}}extractModalities(e,t){let n=t===`input`?e.input_modalities||e.inputModalities:e.output_modalities||e.outputModalities,r=e.architecture||{},i=t===`input`?r.input_modalities||r.inputModalities:r.output_modalities||r.outputModalities,a=typeof r.modality==`string`?r.modality:``,o=this.parseArchitectureModality(a,t),s=[...this.normalizeStringArray(n),...this.normalizeStringArray(i),...o];return Array.from(new Set(s))}extractReasoning(e){let t=e?.reasoning;if(!t||typeof t!=`object`)return;let n=this.normalizeStringArray(t.supported_efforts),r=typeof t.default_effort==`string`?t.default_effort.toLowerCase():void 0,i=typeof t.default_enabled==`boolean`?t.default_enabled:void 0,a=typeof t.supports_max_tokens==`boolean`?t.supports_max_tokens:void 0,o=typeof t.mandatory==`boolean`?t.mandatory:void 0;if(!(n.length===0&&r===void 0&&i===void 0&&a===void 0&&o===void 0))return{...n.length>0&&{supportedEfforts:n},...r!==void 0&&{defaultEffort:r},...i!==void 0&&{defaultEnabled:i},...a!==void 0&&{supportsMaxTokens:a},...o!==void 0&&{mandatory:o}}}modalitiesInclude(e,...t){if(e.length!==0)return t.some(t=>e.some(e=>e.includes(t)))}normalizeStringArray(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`).map(e=>e.toLowerCase()):[]}parseArchitectureModality(e,t){if(!e)return[];let[n=``,r=``]=e.toLowerCase().split(`->`).map(e=>e.trim());return(t===`input`?n:r).split(/[+,/\s]+/).map(e=>e.trim()).filter(Boolean)}supportedParametersInclude(e,...t){let n=this.normalizeStringArray(e.supported_parameters||e.supportedParameters);return t.some(e=>n.some(t=>t.includes(e)))}};function c(e){return e.replace(/^\/+|\/+$/g,``)}function l(e){let t=c(e);return t.endsWith(`/index.html`)?t=t.slice(0,-11):t.endsWith(`index.html`)&&(t=t.slice(0,-10)),c(t)}function u(e){let t=c(e),n=t.split(`/`).filter(Boolean);if(n.length===0)return null;let r=n[0].toLowerCase();if(r===`pages`){if(n.length>=3){let e=n[1];return{page:`pages`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e,path:n.slice(2).join(`/`)}}return{page:`pages`,groupId:`br:main`,path:n.slice(1).join(`/`)}}if(r===`files`){let e=n[1]||`main`;return{page:`files`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e,path:n.slice(2).join(`/`)||void 0}}if(r===`chat`){let e=n[1]||`main`;return{page:`chat`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e}}if(r===`tasks`){let e=n[1]||`main`;return{page:`tasks`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e}}return r===`main`?{page:`pages`,groupId:`br:main`,path:n.slice(1).join(`/`)}:{page:`pages`,groupId:`br:main`,path:t}}function d(){if(typeof document<`u`){let e=document.getElementById(`shadow-claw-static-routing`);if(e&&e.textContent)try{let t=JSON.parse(e.textContent);if(t&&typeof t==`object`&&t.routes)return t}catch(e){console.warn(`Failed to parse embedded static routing manifest:`,e)}}return null}function f(e,t){let n=t||d();if(!n||!n.routes)return null;let r=l(e);if(!r)return null;for(let[e,t]of Object.entries(n.routes))if(!(!t||!t.prettyPath)&&l(t.prettyPath)===r)return u(e);return null}let p=null,ee=new Set([`chat`,`files`,`tasks`,`pages`,`settings`,`tools`,`channels`]);function m(){p=null}typeof globalThis<`u`&&(globalThis.__applyBasePathCacheReset=m);function h(){let e=typeof window<`u`?window:typeof self<`u`?self:globalThis;if(e&&e.__SHADOWCLAW_DEPLOY_ID__){let t=String(e.__SHADOWCLAW_DEPLOY_ID__).trim();if(t)return t.replace(/[^a-zA-Z0-9_-]/g,`-`)}let t=globalThis.process;if(t&&t.env&&t.env.SHADOWCLAW_DEPLOY_ID){let e=String(t.env.SHADOWCLAW_DEPLOY_ID).trim();if(e)return e.replace(/[^a-zA-Z0-9_-]/g,`-`)}let n=te();return!n||n===`/`?``:n.replace(/^\/+|\/+$/g,``).replace(/[^a-zA-Z0-9_-]/g,`-`)}function te(){if(p!==null)return p;if((typeof window>`u`||globalThis.WorkerGlobalScope!==void 0&&typeof self<`u`&&self instanceof globalThis.WorkerGlobalScope)&&typeof self<`u`&&self.location){let e=(self.location.pathname||`/`).split(`/`).filter(Boolean);if(e.length<=1)return p=`/`,p;let t=e[0].toLowerCase(),n=t===`service-worker.js`||t===`sw.js`||t===`manifest.json`||t===`sitemap.xml`||t===`favicon.ico`||t===`index.html`||t===`service-worker`||t===`assets`||t===`dist`||t===`public`;if(n&&e.length===2)return p=`/`,p;if(!n)return p=`/`+e[0]+`/`,p}let e=typeof window<`u`&&window.location?window.location:typeof self<`u`&&self.location?self.location:null;if(!e)return`/`;if(typeof document<`u`&&typeof document.querySelector==`function`){let t=document.querySelector(`base[href]`);if(t){let n=t.getAttribute(`href`);if(n)try{let t=new URL(n,e.origin).pathname;return t.endsWith(`/`)||(t+=`/`),p=t,p}catch{}}}let t=e.pathname||`/`;if(t===`/`)return p=`/`,p;let n=t.split(`/`).filter(Boolean),r=n.findIndex(e=>ee.has(e.toLowerCase()));return r>=0?r===0?(p=`/`,p):(p=`/`+n.slice(0,r).join(`/`)+`/`,p):f(t)?(p=`/`,p):n.length===1&&!t.includes(`.`)?(p=`/`+n[0]+`/`,p):(p=`/`,p)}function ne(e){let t=e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`);return RegExp(`(^|\\s)@${t}\\b`,`i`)}ne(`ShadowClaw`);function g(){let e=h();return e?`shadowclaw-${e}`:`shadowclaw`}g();let _={github:{providerId:`github`,name:`GitHub`,aliases:[`github`,`api.github.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`token `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://github.com/login/oauth/authorize`,tokenUrl:`https://github.com/login/oauth/access_token`,defaultScopes:[`repo`,`read:user`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},gitlab:{providerId:`gitlab`,name:`GitLab`,aliases:[`gitlab`,`gitlab.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`PRIVATE-TOKEN`,headerPrefix:``},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},tokenAuthByServiceType:{git_remote:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}}},oauth:{authorizeUrl:`https://gitlab.com/oauth/authorize`,tokenUrl:`https://gitlab.com/oauth/token`,defaultScopes:[`read_api`,`read_user`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},figma:{providerId:`figma`,name:`Figma`,aliases:[`figma`,`api.figma.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`X-Figma-Token`,headerPrefix:``},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://www.figma.com/oauth`,tokenUrl:`https://api.figma.com/v1/oauth/token`,refreshUrl:`https://api.figma.com/v1/oauth/refresh`,defaultScopes:[],usePkce:!0,clientAuthMethod:`basic_header`,scopeSeparator:`space`}},notion:{providerId:`notion`,name:`Notion`,aliases:[`notion`,`api.notion.com`,`notion.so`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://api.notion.com/v1/oauth/authorize`,tokenUrl:`https://api.notion.com/v1/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`basic_header`,scopeSeparator:`space`}},microsoft_graph:{providerId:`microsoft_graph`,name:`Microsoft Graph`,aliases:[`microsoft`,`graph.microsoft.com`,`microsoft_graph`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://login.microsoftonline.com/common/oauth2/v2.0/authorize`,tokenUrl:`https://login.microsoftonline.com/common/oauth2/v2.0/token`,defaultScopes:[`openid`,`profile`,`offline_access`,`User.Read`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},yahoo_mail:{providerId:`yahoo_mail`,name:`Yahoo Mail`,aliases:[`yahoo`,`yahoo_mail`,`mail.yahoo.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://api.login.yahoo.com/oauth2/request_auth`,tokenUrl:`https://api.login.yahoo.com/oauth2/get_token`,defaultScopes:[`mail-r`,`mail-w`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},google:{providerId:`google`,name:`Google`,aliases:[`google`,`googleapis.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://accounts.google.com/o/oauth2/v2/auth`,tokenUrl:`https://oauth2.googleapis.com/token`,defaultScopes:[`openid`,`profile`,`email`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},atlassian:{providerId:`atlassian`,name:`Atlassian`,aliases:[`atlassian`,`api.atlassian.com`],modes:[`oauth`,`token`,`basic`],defaultMode:`basic`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`,`basic_userpass`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `},basic:{headerName:`Authorization`,headerPrefix:`Basic `}},oauth:{authorizeUrl:`https://auth.atlassian.com/authorize`,tokenUrl:`https://auth.atlassian.com/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},slack:{providerId:`slack`,name:`Slack`,aliases:[`slack`,`slack.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://slack.com/oauth/v2/authorize`,tokenUrl:`https://slack.com/api/oauth.v2.access`,defaultScopes:[],usePkce:!1,clientAuthMethod:`request_body`,scopeSeparator:`comma`}},linear:{providerId:`linear`,name:`Linear`,aliases:[`linear`,`api.linear.app`,`linear.app`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://linear.app/oauth/authorize`,tokenUrl:`https://api.linear.app/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},azure_devops:{providerId:`azure_devops`,name:`Azure DevOps`,aliases:[`azure_devops`,`dev.azure.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`,`basic_userpass`,`ssh_key`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},tokenAuthByServiceType:{git_remote:{token:{headerName:`Authorization`,headerPrefix:`Basic `},oauth:{headerName:`Authorization`,headerPrefix:`Basic `}}},oauth:{authorizeUrl:`https://app.vssps.visualstudio.com/oauth2/authorize`,tokenUrl:`https://app.vssps.visualstudio.com/oauth2/token`,defaultScopes:[`vso.code`,`vso.profile`,`offline_access`],usePkce:!1,clientAuthMethod:`request_body`,scopeSeparator:`space`}},custom_mcp:{providerId:`custom_mcp`,name:`Custom MCP`,aliases:[`custom_mcp`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`mcp_remote`,`webmcp_local`],authTypes:[`none`,`token`,`oauth`,`custom_header`,`ssh_key`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://example.com/oauth/authorize`,tokenUrl:`https://example.com/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}}};Object.fromEntries(Object.values(_).filter(e=>!!e.oauth).map(e=>{let t=e.oauth;return[e.providerId,{id:e.providerId,name:e.name,authorizeUrl:t.authorizeUrl,tokenUrl:t.tokenUrl,redirectUri:t.redirectUri,defaultScopes:t.defaultScopes,usePkce:t.usePkce,clientAuthMethod:t.clientAuthMethod,scopeSeparator:t.scopeSeparator}]})),Object.fromEntries(Object.values(_).map(e=>[e.providerId,{providerId:e.providerId,modes:e.modes,defaultMode:e.defaultMode}])),new Promise(e=>{});
3
3
  /*! @license DOMPurify 3.4.13 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.13/LICENSE */
4
4
  function v(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n<t;n++)r[n]=e[n];return r}function y(e){if(Array.isArray(e))return e}function b(e,t){var n=e==null?null:typeof Symbol<`u`&&e[Symbol.iterator]||e[`@@iterator`];if(n!=null){var r,i,a,o,s=[],c=!0,l=!1;try{if(a=(n=n.call(e)).next,t!==0)for(;!(c=(r=a.call(n)).done)&&(s.push(r.value),s.length!==t);c=!0);}catch(e){l=!0,i=e}finally{try{if(!c&&n.return!=null&&(o=n.return(),Object(o)!==o))return}finally{if(l)throw i}}return s}}function re(){throw TypeError(`Invalid attempt to destructure non-iterable instance.
@@ -1,4 +1,4 @@
1
- <!doctype html><html lang="en"><head><title>ShadowClaw</title><meta charset="utf-8"><meta name="revision" content="25a61e273c78a4e8d94f73bf714f6584f3c4be4c" /><meta name="version" content="1.34.5" /><meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"><meta name="description" content="ShadowClaw - Browser-native AI agent." /><link rel="canonical" href="https://xt-ml.github.io/shadow-claw/"><meta property="og:title" content="ShadowClaw"><meta property="og:description" content="Browser-native personal AI assistant with local and remote LLM orchestration, Web Workers, OPFS storage, and agentic tool execution."><meta property="og:type" content="website"><meta property="og:url" content="https://xt-ml.github.io/shadow-claw/"><meta property="og:image" content="https://xt-ml.github.io/shadow-claw/assets/icons/512.png"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="ShadowClaw"><meta name="twitter:description" content="Browser-native personal AI assistant with local and remote LLM orchestration, Web Workers, OPFS storage, and agentic tool execution."><meta name="twitter:image" content="https://xt-ml.github.io/shadow-claw/assets/icons/512.png"><script type="application/ld+json">{"@context":"https://schema.org","@graph":[{"@type":"SoftwareApplication","@id":"https://xt-ml.github.io/shadow-claw/#software","name":"ShadowClaw","description":"Browser-native personal AI assistant with local and remote LLM orchestration, Web Workers, OPFS storage, and agentic tool execution.","url":"https://xt-ml.github.io/shadow-claw/","applicationCategory":"DeveloperApplication","operatingSystem":"Web, Browser, Desktop","offers":{"@type":"Offer","price":"0","priceCurrency":"USD"},"author":{"@type":"Organization","name":"xt-ml","url":"https://github.com/xt-ml"},"codeRepository":"https://github.com/xt-ml/shadow-claw","license":"https://github.com/xt-ml/shadow-claw/blob/main/LICENSE"},{"@type":"WebSite","@id":"https://xt-ml.github.io/shadow-claw/#website","url":"https://xt-ml.github.io/shadow-claw/","name":"ShadowClaw","description":"Official website and live deployment of ShadowClaw browser-native AI assistant.","publisher":{"@type":"Organization","name":"xt-ml","url":"https://github.com/xt-ml"}},{"@type":"Organization","@id":"https://xt-ml.github.io/shadow-claw/#organization","name":"xt-ml","url":"https://github.com/xt-ml","sameAs":["https://github.com/xt-ml"]}]}</script><meta name="theme-color" content="#fff" /><meta http-equiv="origin-trial" content="A88uTgESYWcVgREKVReKB6jom41uP8TzW6ei3jNdidf+Xl6ONqKRjKNBphsxhrZmdkakLK3oXDgkGq53rtZYSAMAAAB2eyJvcmlnaW4iOiJodHRwczovL3h0LW1sLmdpdGh1Yi5pbzo0NDMiLCJmZWF0dXJlIjoiV2ViTUNQIiwiZXhwaXJ5IjoxNzk0ODczNjAwLCJpc1N1YmRvbWFpbiI6dHJ1ZSwiaXNUaGlyZFBhcnR5Ijp0cnVlfQ=="><base href="/shadow-claw/" /><script id="shadow-claw-site-config" type="application/json">{"$schema":"https:\u002f\u002fjson-schema.org\u002fdraft\u002f2020-12\u002fschema","site":{"title":"ShadowClaw","description":"ShadowClaw - Browser-native AI agent.","themeColor":"#fff"},"branding":{"faviconPath":"assets\u002ficons\u002ffavicon.ico","appleTouchIconPath":"assets\u002ficons\u002f180.png","notFoundPath":"404.html"},"pwa":{"manifestPath":"manifest.json","themeColor":"#000000"},"sitemapPath":"sitemap.xml","assets":["assets"],"sidebar":{"pagesHidden":false,"chatHidden":false,"tasksHidden":false,"filesHidden":false,"defaultPage":"pages"},"settings":{"defaultToolsProfile":"__builtin_default"},"agent":{"defaultProvider":"transformers_js_local","defaultModel":"onnx-community\u002fgemma-3-1b-it-ONNX-GQA"},"customElements":{"allowedElements":["block-garden","block-garden-option","block-garden-select"],"allowedDomains":["kherrick.github.io","cdn.jsdelivr.net"],"scripts":[{"src":"https:\u002f\u002fkherrick.github.io\u002fblock-garden-knowledge-hub\u002f.agents\u002fscripts\u002fmain\u002fblock-garden-adapter.js","hasInit":true},"https:\u002f\u002fkherrick.github.io\u002fblock-garden\u002fblock-garden-bundle-min.mjs"]},"security":{"connectSrc":["'self'","blob:","data:","https:\u002f\u002fkherrick.github.io","https:\u002f\u002fcdn.jsdelivr.net"]}}</script>
1
+ <!doctype html><html lang="en"><head><title>ShadowClaw</title><meta charset="utf-8"><meta name="revision" content="72143ec88485970231da8c06ecf74089dd580224" /><meta name="version" content="1.35.0" /><meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"><meta name="description" content="ShadowClaw - Browser-native AI agent." /><link rel="canonical" href="https://xt-ml.github.io/shadow-claw/"><meta property="og:title" content="ShadowClaw"><meta property="og:description" content="Browser-native personal AI assistant with local and remote LLM orchestration, Web Workers, OPFS storage, and agentic tool execution."><meta property="og:type" content="website"><meta property="og:url" content="https://xt-ml.github.io/shadow-claw/"><meta property="og:image" content="https://xt-ml.github.io/shadow-claw/assets/icons/512.png"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="ShadowClaw"><meta name="twitter:description" content="Browser-native personal AI assistant with local and remote LLM orchestration, Web Workers, OPFS storage, and agentic tool execution."><meta name="twitter:image" content="https://xt-ml.github.io/shadow-claw/assets/icons/512.png"><script type="application/ld+json">{"@context":"https://schema.org","@graph":[{"@type":"SoftwareApplication","@id":"https://xt-ml.github.io/shadow-claw/#software","name":"ShadowClaw","description":"Browser-native personal AI assistant with local and remote LLM orchestration, Web Workers, OPFS storage, and agentic tool execution.","url":"https://xt-ml.github.io/shadow-claw/","applicationCategory":"DeveloperApplication","operatingSystem":"Web, Browser, Desktop","offers":{"@type":"Offer","price":"0","priceCurrency":"USD"},"author":{"@type":"Organization","name":"xt-ml","url":"https://github.com/xt-ml"},"codeRepository":"https://github.com/xt-ml/shadow-claw","license":"https://github.com/xt-ml/shadow-claw/blob/main/LICENSE"},{"@type":"WebSite","@id":"https://xt-ml.github.io/shadow-claw/#website","url":"https://xt-ml.github.io/shadow-claw/","name":"ShadowClaw","description":"Official website and live deployment of ShadowClaw browser-native AI assistant.","publisher":{"@type":"Organization","name":"xt-ml","url":"https://github.com/xt-ml"}},{"@type":"Organization","@id":"https://xt-ml.github.io/shadow-claw/#organization","name":"xt-ml","url":"https://github.com/xt-ml","sameAs":["https://github.com/xt-ml"]}]}</script><meta name="theme-color" content="#fff" /><meta http-equiv="origin-trial" content="A88uTgESYWcVgREKVReKB6jom41uP8TzW6ei3jNdidf+Xl6ONqKRjKNBphsxhrZmdkakLK3oXDgkGq53rtZYSAMAAAB2eyJvcmlnaW4iOiJodHRwczovL3h0LW1sLmdpdGh1Yi5pbzo0NDMiLCJmZWF0dXJlIjoiV2ViTUNQIiwiZXhwaXJ5IjoxNzk0ODczNjAwLCJpc1N1YmRvbWFpbiI6dHJ1ZSwiaXNUaGlyZFBhcnR5Ijp0cnVlfQ=="><base href="/shadow-claw/" /><script id="shadow-claw-site-config" type="application/json">{"$schema":"https:\u002f\u002fjson-schema.org\u002fdraft\u002f2020-12\u002fschema","site":{"title":"ShadowClaw","description":"ShadowClaw - Browser-native AI agent.","themeColor":"#fff"},"branding":{"faviconPath":"assets\u002ficons\u002ffavicon.ico","appleTouchIconPath":"assets\u002ficons\u002f180.png","notFoundPath":"404.html"},"pwa":{"manifestPath":"manifest.json","themeColor":"#000000"},"sitemapPath":"sitemap.xml","assets":["assets"],"sidebar":{"pagesHidden":false,"chatHidden":false,"tasksHidden":false,"filesHidden":false,"defaultPage":"pages"},"settings":{"defaultToolsProfile":"__builtin_default"},"agent":{"defaultProvider":"transformers_js_local","defaultModel":"onnx-community\u002fgemma-3-1b-it-ONNX-GQA"},"customElements":{"allowedElements":["block-garden","block-garden-option","block-garden-select"],"allowedDomains":["kherrick.github.io","cdn.jsdelivr.net"],"scripts":[{"src":"https:\u002f\u002fkherrick.github.io\u002fblock-garden-knowledge-hub\u002f.agents\u002fscripts\u002fmain\u002fblock-garden-adapter.js","hasInit":true},"https:\u002f\u002fkherrick.github.io\u002fblock-garden\u002fblock-garden-bundle-min.mjs"]},"security":{"connectSrc":["'self'","blob:","data:","https:\u002f\u002fkherrick.github.io","https:\u002f\u002fcdn.jsdelivr.net"]}}</script>
2
2
  <script>var ShadowClawThemeInit=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports);function n(){if(globalThis.trustedTypes)return;let e=new Map;globalThis.trustedTypes={createPolicy:(t,n)=>{if(e.has(t))throw TypeError(`Policy with name "${t}" already exists.`);let r={createHTML:n.createHTML?e=>n.createHTML(e):void 0,createScriptURL:n.createScriptURL?e=>n.createScriptURL(e):void 0,createScript:n.createScript?e=>n.createScript(e):void 0};return e.set(t,r),r},getPolicy:t=>e.get(t)??null,isHTML:e=>typeof e==`string`||e instanceof Object&&`toString`in e,isScriptURL:e=>typeof e==`string`||e instanceof Object&&`toString`in e,isScript:e=>typeof e==`string`||e instanceof Object&&`toString`in e}}let r=`__shadowClawDefaultTrustedTypesPolicyState`,i=`default`,a=`shadowclaw-sandbox`;function o(){let e=globalThis;return e[r]||(e[r]={initialized:!1,policy:null}),e[r]}function s(){let e=o();if(e.initialized)return;let t=Reflect.get(globalThis,`trustedTypes`);if(!(!t||typeof t.createPolicy!=`function`)){e.initialized=!0;try{if(typeof t.getPolicy==`function`){let n=t.getPolicy(i);if(n){e.policy=n;return}}let n=!1;if(typeof t.getPolicyNames==`function`){let e=t.getPolicyNames();Array.isArray(e)&&e.includes(i)&&(n=!0)}if(n){if(typeof t.getPolicy==`function`){let n=t.getPolicy(a);if(n){e.policy=n;return}}e.policy=t.createPolicy(a,{createHTML:e=>e,createScriptURL:e=>e});return}e.policy=t.createPolicy(i,{createHTML:e=>e,createScriptURL:e=>e})}catch{typeof t.getPolicy==`function`&&(e.policy=t.getPolicy(i)??t.getPolicy(a)??null)}}}new class{loading=!1;models=new Map;getModelInfo(e){if(!e||typeof e!=`string`)return null;let t=e.toLowerCase().trim(),n=this.models.get(t);if(n)return n;if(t.includes(`/`)){let e=t.split(`/`).pop();if(e&&this.models.has(e))return this.models.get(e)}else for(let[e,n]of this.models.entries())if(e.endsWith(`/${t}`))return n;if(t.includes(`:`)){let e=t.split(`:`)[0],n=this.getModelInfo(e);if(n)return n}return null}registerModelInfo(e,t){this.models.set(e.toLowerCase(),t)}clear(){this.models.clear()}async fetchModelInfo(e,t,n){if(e.modelsUrl){this.loading=!0;try{let r=new Headers;if(r.set(`Content-Type`,`application/json`),e.headers)for(let[t,n]of Object.entries(e.headers))r.set(t,n);if(n)for(let[e,t]of Object.entries(n))r.set(e,t);if(t&&e.apiKeyHeader){let n=e.apiKeyHeaderFormat;r.set(e.apiKeyHeader,n?n.replace(`{key}`,t):t)}let i=await fetch(e.modelsUrl,{headers:r});if(!i.ok)throw Error(`HTTP ${i.status} ${i.statusText}`);let a=await i.json(),o=Array.isArray(a.data)?a.data:Array.isArray(a.models)?a.models:[];for(let e of o){if(!e.id)continue;let t=e.context_length??e.context_window??0,n=e.max_completion_tokens||e.per_request_limits?.completion_tokens||e.top_provider?.max_completion_tokens||null,r=typeof e.supports_tools==`boolean`?e.supports_tools:typeof e.supportsTools==`boolean`?e.supportsTools:void 0,i=this.extractModalities(e,`input`),a=this.extractModalities(e,`output`),o=this.modalitiesInclude(i,`image`,`vision`),s=this.modalitiesInclude(i,`audio`,`voice`,`hearing`),c=this.modalitiesInclude(i,`video`),l=e.id===`openrouter/free`||this.supportedParametersInclude(e,`image`,`vision`,`audio`,`tool`,`structured`),u=this.extractReasoning(e),d=typeof e.supports_prompt_caching==`boolean`?e.supports_prompt_caching:typeof e.supportsPromptCaching==`boolean`?e.supportsPromptCaching:this.supportedParametersInclude(e,`cache_control`,`prompt_caching`)?!0:void 0;!t&&Array.isArray(e.providers)&&e.providers.length>0&&(t=e.providers[0]?.context_length||0),this.registerModelInfo(e.id,{contextWindow:t,maxOutput:n,...r!==void 0&&{supportsTools:r},...i.length>0&&{inputModalities:i},...a.length>0&&{outputModalities:a},...o!==void 0&&{supportsImageInput:o},...s!==void 0&&{supportsAudioInput:s},...c!==void 0&&{supportsVideoInput:c},...u&&{reasoning:u},...l&&{routesByRequestFeatures:l},...d!==void 0&&{supportsPromptCaching:d}})}o.length>0&&console.log(`[ModelRegistry] Registered ${o.length} models for provider "${e.id}"`)}catch(t){console.error(`[ModelRegistry] Error fetching models for ${e.id}:`,t)}finally{this.loading=!1}}}extractModalities(e,t){let n=t===`input`?e.input_modalities||e.inputModalities:e.output_modalities||e.outputModalities,r=e.architecture||{},i=t===`input`?r.input_modalities||r.inputModalities:r.output_modalities||r.outputModalities,a=typeof r.modality==`string`?r.modality:``,o=this.parseArchitectureModality(a,t),s=[...this.normalizeStringArray(n),...this.normalizeStringArray(i),...o];return Array.from(new Set(s))}extractReasoning(e){let t=e?.reasoning;if(!t||typeof t!=`object`)return;let n=this.normalizeStringArray(t.supported_efforts),r=typeof t.default_effort==`string`?t.default_effort.toLowerCase():void 0,i=typeof t.default_enabled==`boolean`?t.default_enabled:void 0,a=typeof t.supports_max_tokens==`boolean`?t.supports_max_tokens:void 0,o=typeof t.mandatory==`boolean`?t.mandatory:void 0;if(!(n.length===0&&r===void 0&&i===void 0&&a===void 0&&o===void 0))return{...n.length>0&&{supportedEfforts:n},...r!==void 0&&{defaultEffort:r},...i!==void 0&&{defaultEnabled:i},...a!==void 0&&{supportsMaxTokens:a},...o!==void 0&&{mandatory:o}}}modalitiesInclude(e,...t){if(e.length!==0)return t.some(t=>e.some(e=>e.includes(t)))}normalizeStringArray(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`).map(e=>e.toLowerCase()):[]}parseArchitectureModality(e,t){if(!e)return[];let[n=``,r=``]=e.toLowerCase().split(`->`).map(e=>e.trim());return(t===`input`?n:r).split(/[+,/\s]+/).map(e=>e.trim()).filter(Boolean)}supportedParametersInclude(e,...t){let n=this.normalizeStringArray(e.supported_parameters||e.supportedParameters);return t.some(e=>n.some(t=>t.includes(e)))}};function c(e){return e.replace(/^\/+|\/+$/g,``)}function l(e){let t=c(e);return t.endsWith(`/index.html`)?t=t.slice(0,-11):t.endsWith(`index.html`)&&(t=t.slice(0,-10)),c(t)}function u(e){let t=c(e),n=t.split(`/`).filter(Boolean);if(n.length===0)return null;let r=n[0].toLowerCase();if(r===`pages`){if(n.length>=3){let e=n[1];return{page:`pages`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e,path:n.slice(2).join(`/`)}}return{page:`pages`,groupId:`br:main`,path:n.slice(1).join(`/`)}}if(r===`files`){let e=n[1]||`main`;return{page:`files`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e,path:n.slice(2).join(`/`)||void 0}}if(r===`chat`){let e=n[1]||`main`;return{page:`chat`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e}}if(r===`tasks`){let e=n[1]||`main`;return{page:`tasks`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e}}return r===`main`?{page:`pages`,groupId:`br:main`,path:n.slice(1).join(`/`)}:{page:`pages`,groupId:`br:main`,path:t}}function d(){if(typeof document<`u`){let e=document.getElementById(`shadow-claw-static-routing`);if(e&&e.textContent)try{let t=JSON.parse(e.textContent);if(t&&typeof t==`object`&&t.routes)return t}catch(e){console.warn(`Failed to parse embedded static routing manifest:`,e)}}return null}function f(e,t){let n=t||d();if(!n||!n.routes)return null;let r=l(e);if(!r)return null;for(let[e,t]of Object.entries(n.routes))if(!(!t||!t.prettyPath)&&l(t.prettyPath)===r)return u(e);return null}let p=null,ee=new Set([`chat`,`files`,`tasks`,`pages`,`settings`,`tools`,`channels`]);function m(){p=null}typeof globalThis<`u`&&(globalThis.__applyBasePathCacheReset=m);function h(){let e=typeof window<`u`?window:typeof self<`u`?self:globalThis;if(e&&e.__SHADOWCLAW_DEPLOY_ID__){let t=String(e.__SHADOWCLAW_DEPLOY_ID__).trim();if(t)return t.replace(/[^a-zA-Z0-9_-]/g,`-`)}let t=globalThis.process;if(t&&t.env&&t.env.SHADOWCLAW_DEPLOY_ID){let e=String(t.env.SHADOWCLAW_DEPLOY_ID).trim();if(e)return e.replace(/[^a-zA-Z0-9_-]/g,`-`)}let n=te();return!n||n===`/`?``:n.replace(/^\/+|\/+$/g,``).replace(/[^a-zA-Z0-9_-]/g,`-`)}function te(){if(p!==null)return p;if((typeof window>`u`||globalThis.WorkerGlobalScope!==void 0&&typeof self<`u`&&self instanceof globalThis.WorkerGlobalScope)&&typeof self<`u`&&self.location){let e=(self.location.pathname||`/`).split(`/`).filter(Boolean);if(e.length<=1)return p=`/`,p;let t=e[0].toLowerCase(),n=t===`service-worker.js`||t===`sw.js`||t===`manifest.json`||t===`sitemap.xml`||t===`favicon.ico`||t===`index.html`||t===`service-worker`||t===`assets`||t===`dist`||t===`public`;if(n&&e.length===2)return p=`/`,p;if(!n)return p=`/`+e[0]+`/`,p}let e=typeof window<`u`&&window.location?window.location:typeof self<`u`&&self.location?self.location:null;if(!e)return`/`;if(typeof document<`u`&&typeof document.querySelector==`function`){let t=document.querySelector(`base[href]`);if(t){let n=t.getAttribute(`href`);if(n)try{let t=new URL(n,e.origin).pathname;return t.endsWith(`/`)||(t+=`/`),p=t,p}catch{}}}let t=e.pathname||`/`;if(t===`/`)return p=`/`,p;let n=t.split(`/`).filter(Boolean),r=n.findIndex(e=>ee.has(e.toLowerCase()));return r>=0?r===0?(p=`/`,p):(p=`/`+n.slice(0,r).join(`/`)+`/`,p):f(t)?(p=`/`,p):n.length===1&&!t.includes(`.`)?(p=`/`+n[0]+`/`,p):(p=`/`,p)}function ne(e){let t=e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`);return RegExp(`(^|\\s)@${t}\\b`,`i`)}ne(`ShadowClaw`);function g(){let e=h();return e?`shadowclaw-${e}`:`shadowclaw`}g();let _={github:{providerId:`github`,name:`GitHub`,aliases:[`github`,`api.github.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`token `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://github.com/login/oauth/authorize`,tokenUrl:`https://github.com/login/oauth/access_token`,defaultScopes:[`repo`,`read:user`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},gitlab:{providerId:`gitlab`,name:`GitLab`,aliases:[`gitlab`,`gitlab.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`PRIVATE-TOKEN`,headerPrefix:``},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},tokenAuthByServiceType:{git_remote:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}}},oauth:{authorizeUrl:`https://gitlab.com/oauth/authorize`,tokenUrl:`https://gitlab.com/oauth/token`,defaultScopes:[`read_api`,`read_user`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},figma:{providerId:`figma`,name:`Figma`,aliases:[`figma`,`api.figma.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`X-Figma-Token`,headerPrefix:``},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://www.figma.com/oauth`,tokenUrl:`https://api.figma.com/v1/oauth/token`,refreshUrl:`https://api.figma.com/v1/oauth/refresh`,defaultScopes:[],usePkce:!0,clientAuthMethod:`basic_header`,scopeSeparator:`space`}},notion:{providerId:`notion`,name:`Notion`,aliases:[`notion`,`api.notion.com`,`notion.so`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://api.notion.com/v1/oauth/authorize`,tokenUrl:`https://api.notion.com/v1/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`basic_header`,scopeSeparator:`space`}},microsoft_graph:{providerId:`microsoft_graph`,name:`Microsoft Graph`,aliases:[`microsoft`,`graph.microsoft.com`,`microsoft_graph`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://login.microsoftonline.com/common/oauth2/v2.0/authorize`,tokenUrl:`https://login.microsoftonline.com/common/oauth2/v2.0/token`,defaultScopes:[`openid`,`profile`,`offline_access`,`User.Read`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},yahoo_mail:{providerId:`yahoo_mail`,name:`Yahoo Mail`,aliases:[`yahoo`,`yahoo_mail`,`mail.yahoo.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://api.login.yahoo.com/oauth2/request_auth`,tokenUrl:`https://api.login.yahoo.com/oauth2/get_token`,defaultScopes:[`mail-r`,`mail-w`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},google:{providerId:`google`,name:`Google`,aliases:[`google`,`googleapis.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://accounts.google.com/o/oauth2/v2/auth`,tokenUrl:`https://oauth2.googleapis.com/token`,defaultScopes:[`openid`,`profile`,`email`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},atlassian:{providerId:`atlassian`,name:`Atlassian`,aliases:[`atlassian`,`api.atlassian.com`],modes:[`oauth`,`token`,`basic`],defaultMode:`basic`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`,`basic_userpass`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `},basic:{headerName:`Authorization`,headerPrefix:`Basic `}},oauth:{authorizeUrl:`https://auth.atlassian.com/authorize`,tokenUrl:`https://auth.atlassian.com/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},slack:{providerId:`slack`,name:`Slack`,aliases:[`slack`,`slack.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://slack.com/oauth/v2/authorize`,tokenUrl:`https://slack.com/api/oauth.v2.access`,defaultScopes:[],usePkce:!1,clientAuthMethod:`request_body`,scopeSeparator:`comma`}},linear:{providerId:`linear`,name:`Linear`,aliases:[`linear`,`api.linear.app`,`linear.app`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://linear.app/oauth/authorize`,tokenUrl:`https://api.linear.app/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},azure_devops:{providerId:`azure_devops`,name:`Azure DevOps`,aliases:[`azure_devops`,`dev.azure.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`,`basic_userpass`,`ssh_key`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},tokenAuthByServiceType:{git_remote:{token:{headerName:`Authorization`,headerPrefix:`Basic `},oauth:{headerName:`Authorization`,headerPrefix:`Basic `}}},oauth:{authorizeUrl:`https://app.vssps.visualstudio.com/oauth2/authorize`,tokenUrl:`https://app.vssps.visualstudio.com/oauth2/token`,defaultScopes:[`vso.code`,`vso.profile`,`offline_access`],usePkce:!1,clientAuthMethod:`request_body`,scopeSeparator:`space`}},custom_mcp:{providerId:`custom_mcp`,name:`Custom MCP`,aliases:[`custom_mcp`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`mcp_remote`,`webmcp_local`],authTypes:[`none`,`token`,`oauth`,`custom_header`,`ssh_key`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://example.com/oauth/authorize`,tokenUrl:`https://example.com/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}}};Object.fromEntries(Object.values(_).filter(e=>!!e.oauth).map(e=>{let t=e.oauth;return[e.providerId,{id:e.providerId,name:e.name,authorizeUrl:t.authorizeUrl,tokenUrl:t.tokenUrl,redirectUri:t.redirectUri,defaultScopes:t.defaultScopes,usePkce:t.usePkce,clientAuthMethod:t.clientAuthMethod,scopeSeparator:t.scopeSeparator}]})),Object.fromEntries(Object.values(_).map(e=>[e.providerId,{providerId:e.providerId,modes:e.modes,defaultMode:e.defaultMode}])),new Promise(e=>{});
3
3
  /*! @license DOMPurify 3.4.13 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.13/LICENSE */
4
4
  function v(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n<t;n++)r[n]=e[n];return r}function y(e){if(Array.isArray(e))return e}function b(e,t){var n=e==null?null:typeof Symbol<`u`&&e[Symbol.iterator]||e[`@@iterator`];if(n!=null){var r,i,a,o,s=[],c=!0,l=!1;try{if(a=(n=n.call(e)).next,t!==0)for(;!(c=(r=a.call(n)).done)&&(s.push(r.value),s.length!==t);c=!0);}catch(e){l=!0,i=e}finally{try{if(!c&&n.return!=null&&(o=n.return(),Object(o)!==o))return}finally{if(l)throw i}}return s}}function re(){throw TypeError(`Invalid attempt to destructure non-iterable instance.
@@ -1,4 +1,4 @@
1
- <!doctype html><html lang="en"><head><title>ShadowClaw</title><meta charset="utf-8"><meta name="revision" content="25a61e273c78a4e8d94f73bf714f6584f3c4be4c" /><meta name="version" content="1.34.5" /><meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"><meta name="description" content="ShadowClaw - Browser-native AI agent." /><link rel="canonical" href="https://xt-ml.github.io/shadow-claw/"><meta property="og:title" content="ShadowClaw"><meta property="og:description" content="Browser-native personal AI assistant with local and remote LLM orchestration, Web Workers, OPFS storage, and agentic tool execution."><meta property="og:type" content="website"><meta property="og:url" content="https://xt-ml.github.io/shadow-claw/"><meta property="og:image" content="https://xt-ml.github.io/shadow-claw/assets/icons/512.png"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="ShadowClaw"><meta name="twitter:description" content="Browser-native personal AI assistant with local and remote LLM orchestration, Web Workers, OPFS storage, and agentic tool execution."><meta name="twitter:image" content="https://xt-ml.github.io/shadow-claw/assets/icons/512.png"><script type="application/ld+json">{"@context":"https://schema.org","@graph":[{"@type":"SoftwareApplication","@id":"https://xt-ml.github.io/shadow-claw/#software","name":"ShadowClaw","description":"Browser-native personal AI assistant with local and remote LLM orchestration, Web Workers, OPFS storage, and agentic tool execution.","url":"https://xt-ml.github.io/shadow-claw/","applicationCategory":"DeveloperApplication","operatingSystem":"Web, Browser, Desktop","offers":{"@type":"Offer","price":"0","priceCurrency":"USD"},"author":{"@type":"Organization","name":"xt-ml","url":"https://github.com/xt-ml"},"codeRepository":"https://github.com/xt-ml/shadow-claw","license":"https://github.com/xt-ml/shadow-claw/blob/main/LICENSE"},{"@type":"WebSite","@id":"https://xt-ml.github.io/shadow-claw/#website","url":"https://xt-ml.github.io/shadow-claw/","name":"ShadowClaw","description":"Official website and live deployment of ShadowClaw browser-native AI assistant.","publisher":{"@type":"Organization","name":"xt-ml","url":"https://github.com/xt-ml"}},{"@type":"Organization","@id":"https://xt-ml.github.io/shadow-claw/#organization","name":"xt-ml","url":"https://github.com/xt-ml","sameAs":["https://github.com/xt-ml"]}]}</script><meta name="theme-color" content="#fff" /><meta http-equiv="origin-trial" content="A88uTgESYWcVgREKVReKB6jom41uP8TzW6ei3jNdidf+Xl6ONqKRjKNBphsxhrZmdkakLK3oXDgkGq53rtZYSAMAAAB2eyJvcmlnaW4iOiJodHRwczovL3h0LW1sLmdpdGh1Yi5pbzo0NDMiLCJmZWF0dXJlIjoiV2ViTUNQIiwiZXhwaXJ5IjoxNzk0ODczNjAwLCJpc1N1YmRvbWFpbiI6dHJ1ZSwiaXNUaGlyZFBhcnR5Ijp0cnVlfQ=="><base href="/shadow-claw/" /><script id="shadow-claw-site-config" type="application/json">{"$schema":"https:\u002f\u002fjson-schema.org\u002fdraft\u002f2020-12\u002fschema","site":{"title":"ShadowClaw","description":"ShadowClaw - Browser-native AI agent.","themeColor":"#fff"},"branding":{"faviconPath":"assets\u002ficons\u002ffavicon.ico","appleTouchIconPath":"assets\u002ficons\u002f180.png","notFoundPath":"404.html"},"pwa":{"manifestPath":"manifest.json","themeColor":"#000000"},"sitemapPath":"sitemap.xml","assets":["assets"],"sidebar":{"pagesHidden":false,"chatHidden":false,"tasksHidden":false,"filesHidden":false,"defaultPage":"pages"},"settings":{"defaultToolsProfile":"__builtin_default"},"agent":{"defaultProvider":"transformers_js_local","defaultModel":"onnx-community\u002fgemma-3-1b-it-ONNX-GQA"},"customElements":{"allowedElements":["block-garden","block-garden-option","block-garden-select"],"allowedDomains":["kherrick.github.io","cdn.jsdelivr.net"],"scripts":[{"src":"https:\u002f\u002fkherrick.github.io\u002fblock-garden-knowledge-hub\u002f.agents\u002fscripts\u002fmain\u002fblock-garden-adapter.js","hasInit":true},"https:\u002f\u002fkherrick.github.io\u002fblock-garden\u002fblock-garden-bundle-min.mjs"]},"security":{"connectSrc":["'self'","blob:","data:","https:\u002f\u002fkherrick.github.io","https:\u002f\u002fcdn.jsdelivr.net"]}}</script>
1
+ <!doctype html><html lang="en"><head><title>ShadowClaw</title><meta charset="utf-8"><meta name="revision" content="72143ec88485970231da8c06ecf74089dd580224" /><meta name="version" content="1.35.0" /><meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"><meta name="description" content="ShadowClaw - Browser-native AI agent." /><link rel="canonical" href="https://xt-ml.github.io/shadow-claw/"><meta property="og:title" content="ShadowClaw"><meta property="og:description" content="Browser-native personal AI assistant with local and remote LLM orchestration, Web Workers, OPFS storage, and agentic tool execution."><meta property="og:type" content="website"><meta property="og:url" content="https://xt-ml.github.io/shadow-claw/"><meta property="og:image" content="https://xt-ml.github.io/shadow-claw/assets/icons/512.png"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="ShadowClaw"><meta name="twitter:description" content="Browser-native personal AI assistant with local and remote LLM orchestration, Web Workers, OPFS storage, and agentic tool execution."><meta name="twitter:image" content="https://xt-ml.github.io/shadow-claw/assets/icons/512.png"><script type="application/ld+json">{"@context":"https://schema.org","@graph":[{"@type":"SoftwareApplication","@id":"https://xt-ml.github.io/shadow-claw/#software","name":"ShadowClaw","description":"Browser-native personal AI assistant with local and remote LLM orchestration, Web Workers, OPFS storage, and agentic tool execution.","url":"https://xt-ml.github.io/shadow-claw/","applicationCategory":"DeveloperApplication","operatingSystem":"Web, Browser, Desktop","offers":{"@type":"Offer","price":"0","priceCurrency":"USD"},"author":{"@type":"Organization","name":"xt-ml","url":"https://github.com/xt-ml"},"codeRepository":"https://github.com/xt-ml/shadow-claw","license":"https://github.com/xt-ml/shadow-claw/blob/main/LICENSE"},{"@type":"WebSite","@id":"https://xt-ml.github.io/shadow-claw/#website","url":"https://xt-ml.github.io/shadow-claw/","name":"ShadowClaw","description":"Official website and live deployment of ShadowClaw browser-native AI assistant.","publisher":{"@type":"Organization","name":"xt-ml","url":"https://github.com/xt-ml"}},{"@type":"Organization","@id":"https://xt-ml.github.io/shadow-claw/#organization","name":"xt-ml","url":"https://github.com/xt-ml","sameAs":["https://github.com/xt-ml"]}]}</script><meta name="theme-color" content="#fff" /><meta http-equiv="origin-trial" content="A88uTgESYWcVgREKVReKB6jom41uP8TzW6ei3jNdidf+Xl6ONqKRjKNBphsxhrZmdkakLK3oXDgkGq53rtZYSAMAAAB2eyJvcmlnaW4iOiJodHRwczovL3h0LW1sLmdpdGh1Yi5pbzo0NDMiLCJmZWF0dXJlIjoiV2ViTUNQIiwiZXhwaXJ5IjoxNzk0ODczNjAwLCJpc1N1YmRvbWFpbiI6dHJ1ZSwiaXNUaGlyZFBhcnR5Ijp0cnVlfQ=="><base href="/shadow-claw/" /><script id="shadow-claw-site-config" type="application/json">{"$schema":"https:\u002f\u002fjson-schema.org\u002fdraft\u002f2020-12\u002fschema","site":{"title":"ShadowClaw","description":"ShadowClaw - Browser-native AI agent.","themeColor":"#fff"},"branding":{"faviconPath":"assets\u002ficons\u002ffavicon.ico","appleTouchIconPath":"assets\u002ficons\u002f180.png","notFoundPath":"404.html"},"pwa":{"manifestPath":"manifest.json","themeColor":"#000000"},"sitemapPath":"sitemap.xml","assets":["assets"],"sidebar":{"pagesHidden":false,"chatHidden":false,"tasksHidden":false,"filesHidden":false,"defaultPage":"pages"},"settings":{"defaultToolsProfile":"__builtin_default"},"agent":{"defaultProvider":"transformers_js_local","defaultModel":"onnx-community\u002fgemma-3-1b-it-ONNX-GQA"},"customElements":{"allowedElements":["block-garden","block-garden-option","block-garden-select"],"allowedDomains":["kherrick.github.io","cdn.jsdelivr.net"],"scripts":[{"src":"https:\u002f\u002fkherrick.github.io\u002fblock-garden-knowledge-hub\u002f.agents\u002fscripts\u002fmain\u002fblock-garden-adapter.js","hasInit":true},"https:\u002f\u002fkherrick.github.io\u002fblock-garden\u002fblock-garden-bundle-min.mjs"]},"security":{"connectSrc":["'self'","blob:","data:","https:\u002f\u002fkherrick.github.io","https:\u002f\u002fcdn.jsdelivr.net"]}}</script>
2
2
  <script>var ShadowClawThemeInit=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports);function n(){if(globalThis.trustedTypes)return;let e=new Map;globalThis.trustedTypes={createPolicy:(t,n)=>{if(e.has(t))throw TypeError(`Policy with name "${t}" already exists.`);let r={createHTML:n.createHTML?e=>n.createHTML(e):void 0,createScriptURL:n.createScriptURL?e=>n.createScriptURL(e):void 0,createScript:n.createScript?e=>n.createScript(e):void 0};return e.set(t,r),r},getPolicy:t=>e.get(t)??null,isHTML:e=>typeof e==`string`||e instanceof Object&&`toString`in e,isScriptURL:e=>typeof e==`string`||e instanceof Object&&`toString`in e,isScript:e=>typeof e==`string`||e instanceof Object&&`toString`in e}}let r=`__shadowClawDefaultTrustedTypesPolicyState`,i=`default`,a=`shadowclaw-sandbox`;function o(){let e=globalThis;return e[r]||(e[r]={initialized:!1,policy:null}),e[r]}function s(){let e=o();if(e.initialized)return;let t=Reflect.get(globalThis,`trustedTypes`);if(!(!t||typeof t.createPolicy!=`function`)){e.initialized=!0;try{if(typeof t.getPolicy==`function`){let n=t.getPolicy(i);if(n){e.policy=n;return}}let n=!1;if(typeof t.getPolicyNames==`function`){let e=t.getPolicyNames();Array.isArray(e)&&e.includes(i)&&(n=!0)}if(n){if(typeof t.getPolicy==`function`){let n=t.getPolicy(a);if(n){e.policy=n;return}}e.policy=t.createPolicy(a,{createHTML:e=>e,createScriptURL:e=>e});return}e.policy=t.createPolicy(i,{createHTML:e=>e,createScriptURL:e=>e})}catch{typeof t.getPolicy==`function`&&(e.policy=t.getPolicy(i)??t.getPolicy(a)??null)}}}new class{loading=!1;models=new Map;getModelInfo(e){if(!e||typeof e!=`string`)return null;let t=e.toLowerCase().trim(),n=this.models.get(t);if(n)return n;if(t.includes(`/`)){let e=t.split(`/`).pop();if(e&&this.models.has(e))return this.models.get(e)}else for(let[e,n]of this.models.entries())if(e.endsWith(`/${t}`))return n;if(t.includes(`:`)){let e=t.split(`:`)[0],n=this.getModelInfo(e);if(n)return n}return null}registerModelInfo(e,t){this.models.set(e.toLowerCase(),t)}clear(){this.models.clear()}async fetchModelInfo(e,t,n){if(e.modelsUrl){this.loading=!0;try{let r=new Headers;if(r.set(`Content-Type`,`application/json`),e.headers)for(let[t,n]of Object.entries(e.headers))r.set(t,n);if(n)for(let[e,t]of Object.entries(n))r.set(e,t);if(t&&e.apiKeyHeader){let n=e.apiKeyHeaderFormat;r.set(e.apiKeyHeader,n?n.replace(`{key}`,t):t)}let i=await fetch(e.modelsUrl,{headers:r});if(!i.ok)throw Error(`HTTP ${i.status} ${i.statusText}`);let a=await i.json(),o=Array.isArray(a.data)?a.data:Array.isArray(a.models)?a.models:[];for(let e of o){if(!e.id)continue;let t=e.context_length??e.context_window??0,n=e.max_completion_tokens||e.per_request_limits?.completion_tokens||e.top_provider?.max_completion_tokens||null,r=typeof e.supports_tools==`boolean`?e.supports_tools:typeof e.supportsTools==`boolean`?e.supportsTools:void 0,i=this.extractModalities(e,`input`),a=this.extractModalities(e,`output`),o=this.modalitiesInclude(i,`image`,`vision`),s=this.modalitiesInclude(i,`audio`,`voice`,`hearing`),c=this.modalitiesInclude(i,`video`),l=e.id===`openrouter/free`||this.supportedParametersInclude(e,`image`,`vision`,`audio`,`tool`,`structured`),u=this.extractReasoning(e),d=typeof e.supports_prompt_caching==`boolean`?e.supports_prompt_caching:typeof e.supportsPromptCaching==`boolean`?e.supportsPromptCaching:this.supportedParametersInclude(e,`cache_control`,`prompt_caching`)?!0:void 0;!t&&Array.isArray(e.providers)&&e.providers.length>0&&(t=e.providers[0]?.context_length||0),this.registerModelInfo(e.id,{contextWindow:t,maxOutput:n,...r!==void 0&&{supportsTools:r},...i.length>0&&{inputModalities:i},...a.length>0&&{outputModalities:a},...o!==void 0&&{supportsImageInput:o},...s!==void 0&&{supportsAudioInput:s},...c!==void 0&&{supportsVideoInput:c},...u&&{reasoning:u},...l&&{routesByRequestFeatures:l},...d!==void 0&&{supportsPromptCaching:d}})}o.length>0&&console.log(`[ModelRegistry] Registered ${o.length} models for provider "${e.id}"`)}catch(t){console.error(`[ModelRegistry] Error fetching models for ${e.id}:`,t)}finally{this.loading=!1}}}extractModalities(e,t){let n=t===`input`?e.input_modalities||e.inputModalities:e.output_modalities||e.outputModalities,r=e.architecture||{},i=t===`input`?r.input_modalities||r.inputModalities:r.output_modalities||r.outputModalities,a=typeof r.modality==`string`?r.modality:``,o=this.parseArchitectureModality(a,t),s=[...this.normalizeStringArray(n),...this.normalizeStringArray(i),...o];return Array.from(new Set(s))}extractReasoning(e){let t=e?.reasoning;if(!t||typeof t!=`object`)return;let n=this.normalizeStringArray(t.supported_efforts),r=typeof t.default_effort==`string`?t.default_effort.toLowerCase():void 0,i=typeof t.default_enabled==`boolean`?t.default_enabled:void 0,a=typeof t.supports_max_tokens==`boolean`?t.supports_max_tokens:void 0,o=typeof t.mandatory==`boolean`?t.mandatory:void 0;if(!(n.length===0&&r===void 0&&i===void 0&&a===void 0&&o===void 0))return{...n.length>0&&{supportedEfforts:n},...r!==void 0&&{defaultEffort:r},...i!==void 0&&{defaultEnabled:i},...a!==void 0&&{supportsMaxTokens:a},...o!==void 0&&{mandatory:o}}}modalitiesInclude(e,...t){if(e.length!==0)return t.some(t=>e.some(e=>e.includes(t)))}normalizeStringArray(e){return Array.isArray(e)?e.filter(e=>typeof e==`string`).map(e=>e.toLowerCase()):[]}parseArchitectureModality(e,t){if(!e)return[];let[n=``,r=``]=e.toLowerCase().split(`->`).map(e=>e.trim());return(t===`input`?n:r).split(/[+,/\s]+/).map(e=>e.trim()).filter(Boolean)}supportedParametersInclude(e,...t){let n=this.normalizeStringArray(e.supported_parameters||e.supportedParameters);return t.some(e=>n.some(t=>t.includes(e)))}};function c(e){return e.replace(/^\/+|\/+$/g,``)}function l(e){let t=c(e);return t.endsWith(`/index.html`)?t=t.slice(0,-11):t.endsWith(`index.html`)&&(t=t.slice(0,-10)),c(t)}function u(e){let t=c(e),n=t.split(`/`).filter(Boolean);if(n.length===0)return null;let r=n[0].toLowerCase();if(r===`pages`){if(n.length>=3){let e=n[1];return{page:`pages`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e,path:n.slice(2).join(`/`)}}return{page:`pages`,groupId:`br:main`,path:n.slice(1).join(`/`)}}if(r===`files`){let e=n[1]||`main`;return{page:`files`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e,path:n.slice(2).join(`/`)||void 0}}if(r===`chat`){let e=n[1]||`main`;return{page:`chat`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e}}if(r===`tasks`){let e=n[1]||`main`;return{page:`tasks`,groupId:e===`main`?`br:main`:e.startsWith(`br-`)?`br:${e.slice(3)}`:e}}return r===`main`?{page:`pages`,groupId:`br:main`,path:n.slice(1).join(`/`)}:{page:`pages`,groupId:`br:main`,path:t}}function d(){if(typeof document<`u`){let e=document.getElementById(`shadow-claw-static-routing`);if(e&&e.textContent)try{let t=JSON.parse(e.textContent);if(t&&typeof t==`object`&&t.routes)return t}catch(e){console.warn(`Failed to parse embedded static routing manifest:`,e)}}return null}function f(e,t){let n=t||d();if(!n||!n.routes)return null;let r=l(e);if(!r)return null;for(let[e,t]of Object.entries(n.routes))if(!(!t||!t.prettyPath)&&l(t.prettyPath)===r)return u(e);return null}let p=null,ee=new Set([`chat`,`files`,`tasks`,`pages`,`settings`,`tools`,`channels`]);function m(){p=null}typeof globalThis<`u`&&(globalThis.__applyBasePathCacheReset=m);function h(){let e=typeof window<`u`?window:typeof self<`u`?self:globalThis;if(e&&e.__SHADOWCLAW_DEPLOY_ID__){let t=String(e.__SHADOWCLAW_DEPLOY_ID__).trim();if(t)return t.replace(/[^a-zA-Z0-9_-]/g,`-`)}let t=globalThis.process;if(t&&t.env&&t.env.SHADOWCLAW_DEPLOY_ID){let e=String(t.env.SHADOWCLAW_DEPLOY_ID).trim();if(e)return e.replace(/[^a-zA-Z0-9_-]/g,`-`)}let n=te();return!n||n===`/`?``:n.replace(/^\/+|\/+$/g,``).replace(/[^a-zA-Z0-9_-]/g,`-`)}function te(){if(p!==null)return p;if((typeof window>`u`||globalThis.WorkerGlobalScope!==void 0&&typeof self<`u`&&self instanceof globalThis.WorkerGlobalScope)&&typeof self<`u`&&self.location){let e=(self.location.pathname||`/`).split(`/`).filter(Boolean);if(e.length<=1)return p=`/`,p;let t=e[0].toLowerCase(),n=t===`service-worker.js`||t===`sw.js`||t===`manifest.json`||t===`sitemap.xml`||t===`favicon.ico`||t===`index.html`||t===`service-worker`||t===`assets`||t===`dist`||t===`public`;if(n&&e.length===2)return p=`/`,p;if(!n)return p=`/`+e[0]+`/`,p}let e=typeof window<`u`&&window.location?window.location:typeof self<`u`&&self.location?self.location:null;if(!e)return`/`;if(typeof document<`u`&&typeof document.querySelector==`function`){let t=document.querySelector(`base[href]`);if(t){let n=t.getAttribute(`href`);if(n)try{let t=new URL(n,e.origin).pathname;return t.endsWith(`/`)||(t+=`/`),p=t,p}catch{}}}let t=e.pathname||`/`;if(t===`/`)return p=`/`,p;let n=t.split(`/`).filter(Boolean),r=n.findIndex(e=>ee.has(e.toLowerCase()));return r>=0?r===0?(p=`/`,p):(p=`/`+n.slice(0,r).join(`/`)+`/`,p):f(t)?(p=`/`,p):n.length===1&&!t.includes(`.`)?(p=`/`+n[0]+`/`,p):(p=`/`,p)}function ne(e){let t=e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`);return RegExp(`(^|\\s)@${t}\\b`,`i`)}ne(`ShadowClaw`);function g(){let e=h();return e?`shadowclaw-${e}`:`shadowclaw`}g();let _={github:{providerId:`github`,name:`GitHub`,aliases:[`github`,`api.github.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`token `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://github.com/login/oauth/authorize`,tokenUrl:`https://github.com/login/oauth/access_token`,defaultScopes:[`repo`,`read:user`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},gitlab:{providerId:`gitlab`,name:`GitLab`,aliases:[`gitlab`,`gitlab.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`PRIVATE-TOKEN`,headerPrefix:``},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},tokenAuthByServiceType:{git_remote:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}}},oauth:{authorizeUrl:`https://gitlab.com/oauth/authorize`,tokenUrl:`https://gitlab.com/oauth/token`,defaultScopes:[`read_api`,`read_user`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},figma:{providerId:`figma`,name:`Figma`,aliases:[`figma`,`api.figma.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`X-Figma-Token`,headerPrefix:``},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://www.figma.com/oauth`,tokenUrl:`https://api.figma.com/v1/oauth/token`,refreshUrl:`https://api.figma.com/v1/oauth/refresh`,defaultScopes:[],usePkce:!0,clientAuthMethod:`basic_header`,scopeSeparator:`space`}},notion:{providerId:`notion`,name:`Notion`,aliases:[`notion`,`api.notion.com`,`notion.so`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://api.notion.com/v1/oauth/authorize`,tokenUrl:`https://api.notion.com/v1/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`basic_header`,scopeSeparator:`space`}},microsoft_graph:{providerId:`microsoft_graph`,name:`Microsoft Graph`,aliases:[`microsoft`,`graph.microsoft.com`,`microsoft_graph`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://login.microsoftonline.com/common/oauth2/v2.0/authorize`,tokenUrl:`https://login.microsoftonline.com/common/oauth2/v2.0/token`,defaultScopes:[`openid`,`profile`,`offline_access`,`User.Read`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},yahoo_mail:{providerId:`yahoo_mail`,name:`Yahoo Mail`,aliases:[`yahoo`,`yahoo_mail`,`mail.yahoo.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://api.login.yahoo.com/oauth2/request_auth`,tokenUrl:`https://api.login.yahoo.com/oauth2/get_token`,defaultScopes:[`mail-r`,`mail-w`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},google:{providerId:`google`,name:`Google`,aliases:[`google`,`googleapis.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://accounts.google.com/o/oauth2/v2/auth`,tokenUrl:`https://oauth2.googleapis.com/token`,defaultScopes:[`openid`,`profile`,`email`],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},atlassian:{providerId:`atlassian`,name:`Atlassian`,aliases:[`atlassian`,`api.atlassian.com`],modes:[`oauth`,`token`,`basic`],defaultMode:`basic`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`,`basic_userpass`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `},basic:{headerName:`Authorization`,headerPrefix:`Basic `}},oauth:{authorizeUrl:`https://auth.atlassian.com/authorize`,tokenUrl:`https://auth.atlassian.com/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},slack:{providerId:`slack`,name:`Slack`,aliases:[`slack`,`slack.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://slack.com/oauth/v2/authorize`,tokenUrl:`https://slack.com/api/oauth.v2.access`,defaultScopes:[],usePkce:!1,clientAuthMethod:`request_body`,scopeSeparator:`comma`}},linear:{providerId:`linear`,name:`Linear`,aliases:[`linear`,`api.linear.app`,`linear.app`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`],authTypes:[`token`,`oauth`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://linear.app/oauth/authorize`,tokenUrl:`https://api.linear.app/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}},azure_devops:{providerId:`azure_devops`,name:`Azure DevOps`,aliases:[`azure_devops`,`dev.azure.com`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`http_api`,`git_remote`],authTypes:[`token`,`oauth`,`basic_userpass`,`ssh_key`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},tokenAuthByServiceType:{git_remote:{token:{headerName:`Authorization`,headerPrefix:`Basic `},oauth:{headerName:`Authorization`,headerPrefix:`Basic `}}},oauth:{authorizeUrl:`https://app.vssps.visualstudio.com/oauth2/authorize`,tokenUrl:`https://app.vssps.visualstudio.com/oauth2/token`,defaultScopes:[`vso.code`,`vso.profile`,`offline_access`],usePkce:!1,clientAuthMethod:`request_body`,scopeSeparator:`space`}},custom_mcp:{providerId:`custom_mcp`,name:`Custom MCP`,aliases:[`custom_mcp`],modes:[`oauth`,`token`],defaultMode:`oauth`,serviceTypes:[`mcp_remote`,`webmcp_local`],authTypes:[`none`,`token`,`oauth`,`custom_header`,`ssh_key`],tokenAuth:{token:{headerName:`Authorization`,headerPrefix:`Bearer `},oauth:{headerName:`Authorization`,headerPrefix:`Bearer `}},oauth:{authorizeUrl:`https://example.com/oauth/authorize`,tokenUrl:`https://example.com/oauth/token`,defaultScopes:[],usePkce:!0,clientAuthMethod:`request_body`,scopeSeparator:`space`}}};Object.fromEntries(Object.values(_).filter(e=>!!e.oauth).map(e=>{let t=e.oauth;return[e.providerId,{id:e.providerId,name:e.name,authorizeUrl:t.authorizeUrl,tokenUrl:t.tokenUrl,redirectUri:t.redirectUri,defaultScopes:t.defaultScopes,usePkce:t.usePkce,clientAuthMethod:t.clientAuthMethod,scopeSeparator:t.scopeSeparator}]})),Object.fromEntries(Object.values(_).map(e=>[e.providerId,{providerId:e.providerId,modes:e.modes,defaultMode:e.defaultMode}])),new Promise(e=>{});
3
3
  /*! @license DOMPurify 3.4.13 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.13/LICENSE */
4
4
  function v(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n<t;n++)r[n]=e[n];return r}function y(e){if(Array.isArray(e))return e}function b(e,t){var n=e==null?null:typeof Symbol<`u`&&e[Symbol.iterator]||e[`@@iterator`];if(n!=null){var r,i,a,o,s=[],c=!0,l=!1;try{if(a=(n=n.call(e)).next,t!==0)for(;!(c=(r=a.call(n)).done)&&(s.push(r.value),s.length!==t);c=!0);}catch(e){l=!0,i=e}finally{try{if(!c&&n.return!=null&&(o=n.return(),Object(o)!==o))return}finally{if(l)throw i}}return s}}function re(){throw TypeError(`Invalid attempt to destructure non-iterable instance.
@@ -168,6 +168,10 @@ npx shadow-claw agent tool read_file '{"path": "package.json"}'
168
168
 
169
169
  # Execute a skill's tool chain pipeline directly
170
170
  npx shadow-claw agent skill <name>
171
+
172
+ # Import published tools, skills, and companion scripts from an RFC v0.2.0 discovery manifest
173
+ npx shadow-claw agent import https://xt-ml.github.io/shadow-claw-agent-cli-weather/.well-known/agent-skills/index.json
174
+ npx shadow-claw agent import https://xt-ml.github.io/shadow-claw-agent-cli-weather/ --tools "get_current_weather,get_weather_forecast"
171
175
  ```
172
176
 
173
177
  #### Subcommands
@@ -181,32 +185,38 @@ npx shadow-claw agent skill <name>
181
185
  | `tools` | none | Lists all registered tools (built-in and declarative) annotated with `[headless-safe]` or `[browser-only]` capability tags. |
182
186
  | `tool` | `<name> [jsonArgs]` | When called with only `<name>`, prints the tool description and JSON Schema. When called with `[jsonArgs]`, executes the tool headlessly and prints the result. Accepts JSON arguments or plain text from stdin (auto-mapped to tool schema). |
183
187
  | `skill` | `<name>` | Executes a skill's declarative tool pipeline (`execution.type: "tools"`) or runs the skill body as an LLM prompt. |
188
+ | `import` | `<url>` | Imports tools, skills, and companion scripts from a remote RFC v0.2.0 discovery endpoint into the agent workspace. Verifies SHA-256 integrity and auto-enables imported declarative tools by default. |
184
189
 
185
190
  #### Options
186
191
 
187
- | Option | Type | Description | Default |
188
- | :----------------------------- | :------ | :----------------------------------------------------------------------------------- | :---------------------------------------- |
189
- | `--workspace <dir>` | string | Workspace directory for file I/O and configuration | `".cache"` |
190
- | `--database-dir <dir>` | string | Directory where SQLite databases (`shadow-claw.db`) are stored | `<workspace>/database` |
191
- | `--cache-dir <dir>` | string | Custom cache directory for databases, models, and logs | `undefined` |
192
- | `--tmp, --temp` | boolean | Store cache, token, and databases in OS temporary directory (`tmpdir()`) | `false` |
193
- | `-y, --yes` | boolean | Skip interactive prompts and accept defaults | `false` |
194
- | `--group <groupId>` | string | Conversation group identifier | `"server:main"` |
195
- | `--provider <provider>` | string | LLM provider ID (`transformers_js_local`, `openrouter`, `llamafile`, `gemini`, etc.) | auto-resolved (`"transformers_js_local"`) |
196
- | `--model <model>` | string | Model identifier (e.g. `onnx-community/gemma-3-1b-it-ONNX-GQA`) | auto-resolved (curated local default) |
197
- | `--download` | boolean | Prewarm and download the model during agent init or before execution | `false` |
198
- | `--api-key <key>` | string | API key for cloud providers | auto-resolved from env |
199
- | `--stream` / `--no-stream` | boolean | Stream response tokens to stdout as they arrive | `true` |
200
- | `--progress` / `--no-progress` | boolean | Show terminal progress bar during model downloads | `true` |
201
- | `--system-prompt <text>` | string | Override system prompt with inline text | `undefined` |
202
- | `--system-prompt-file <file>` | string | Load system prompt from a text or markdown file | `undefined` |
203
- | `--tools <tools>` | string | Comma-separated list of tools to enable (e.g. `bash,read_file`) | `undefined` |
204
- | `--tools-profile <name>` | string | Tools profile to activate (e.g. `__builtin_default`) | `undefined` |
205
- | `-r, --remote`, `--hf` | boolean | List or search available models from Hugging Face `onnx-community` repository | `false` |
206
- | `--query <query>` | string | Filter remote or local models by search query | `undefined` |
207
- | `-v, --verbose` | boolean | Enable verbose tool activity and diagnostic logging | `false` |
208
- | `-o, --output <file>` | string | Write command output to a file instead of stdout | `undefined` |
209
- | `-q, --quiet` | boolean | Suppress all non-error output | `false` |
192
+ | Option | Type | Description | Default |
193
+ | :----------------------------------- | :------ | :----------------------------------------------------------------------------------- | :---------------------------------------- |
194
+ | `--workspace <dir>` | string | Workspace directory for file I/O and configuration | `".cache"` |
195
+ | `--database-dir <dir>` | string | Directory where SQLite databases (`shadow-claw.db`) are stored | `<workspace>/database` |
196
+ | `--cache-dir <dir>` | string | Custom cache directory for databases, models, and logs | `undefined` |
197
+ | `--tmp, --temp` | boolean | Store cache, token, and databases in OS temporary directory (`tmpdir()`) | `false` |
198
+ | `-y, --yes` | boolean | Skip interactive prompts and accept defaults | `false` |
199
+ | `--group <groupId>` | string | Conversation group identifier | `"server:main"` |
200
+ | `--provider <provider>` | string | LLM provider ID (`transformers_js_local`, `openrouter`, `llamafile`, `gemini`, etc.) | auto-resolved (`"transformers_js_local"`) |
201
+ | `--model <model>` | string | Model identifier (e.g. `onnx-community/gemma-3-1b-it-ONNX-GQA`) | auto-resolved (curated local default) |
202
+ | `--download` | boolean | Prewarm and download the model during agent init or before execution | `false` |
203
+ | `--api-key <key>` | string | API key for cloud providers | auto-resolved from env |
204
+ | `--stream` / `--no-stream` | boolean | Stream response tokens to stdout as they arrive | `true` |
205
+ | `--progress` / `--no-progress` | boolean | Show terminal progress bar during model downloads | `true` |
206
+ | `--system-prompt <text>` | string | Override system prompt with inline text | `undefined` |
207
+ | `--system-prompt-file <file>` | string | Load system prompt from a text or markdown file | `undefined` |
208
+ | `--tools <tools>` | string | Comma-separated list of tools to enable or import (e.g. `bash,read_file`) | `undefined` |
209
+ | `--skills <skills>` | string | Comma-separated list of skills to filter or import | `undefined` |
210
+ | `--scripts <scripts>` | string | Comma-separated list of companion scripts to import | `undefined` |
211
+ | `--all` | boolean | Import all tools, skills, and scripts from discovery manifest | `false` |
212
+ | `--overwrite` | boolean | Overwrite existing skills, tools, or scripts during import | `false` |
213
+ | `--auto-enable` / `--no-auto-enable` | boolean | Automatically enable imported declarative tools in database | `true` |
214
+ | `--tools-profile <name>` | string | Tools profile to activate (e.g. `__builtin_default`) | `undefined` |
215
+ | `-r, --remote`, `--hf` | boolean | List or search available models from Hugging Face `onnx-community` repository | `false` |
216
+ | `--query <query>` | string | Filter remote or local models by search query | `undefined` |
217
+ | `-v, --verbose` | boolean | Enable verbose tool activity and diagnostic logging | `false` |
218
+ | `-o, --output <file>` | string | Write command output to a file instead of stdout | `undefined` |
219
+ | `-q, --quiet` | boolean | Suppress all non-error output | `false` |
210
220
 
211
221
  > **First-Run Cache Directory Wizard:** When running the agent without an explicit `--workspace`, `--cache-dir`, or `--tmp` in a directory where no `.cache` or database exists, ShadowClaw prompts interactively (identical to `dev` and `serve`) to let you choose between the current directory (`.cache`), system temporary storage (`tmpdir()`), or a custom path. Pass `-y`, `--yes`, `--tmp`, or `--workspace <dir>` to skip the prompt.
212
222
 
@@ -111,6 +111,23 @@ During `shadow-claw build` (`bin/build.mjs`), the build runner automatically inv
111
111
 
112
112
  For content-only sites, files under `.agents/skills/main/` are copied into the build manifest and seeded into the Main conversation's workspace. They are not copied into other conversation workspaces. A skills purge marker can clear the previously seeded `.agents/skills/main/` directory before the current published skills are seeded; see the template README for the marker format.
113
113
 
114
+ ## Remote Skill Discovery and Importing
115
+
116
+ Skills can be discovered and imported across compatible templates and peer sites adhering to the Agent Skills Discovery RFC v0.2.0 specification:
117
+
118
+ - **Browser UI**: Accessible via the "Import" modal in `<shadow-claw-tools>` (`/settings/tool-configuration`), providing quick-pick presets (pwgen Knowledge Hub, Block Garden Knowledge Hub, Weather Agent) or custom site URLs.
119
+ - **Headless CLI**: Discover and import skills directly into an agent workspace via:
120
+
121
+ ```bash
122
+ # Import all skills, tools, and scripts
123
+ npx shadow-claw agent import https://xt-ml.github.io/shadow-claw-agent-cli-weather/
124
+
125
+ # Selectively import specific skills
126
+ npx shadow-claw agent import https://example.com/ --skills "my-skill"
127
+ ```
128
+
129
+ - **Verification & Storage**: Artifacts are validated against SHA-256 digests and saved into `.agents/skills/main/<name>/SKILL.md` (and accompanying resources) in the workspace directory.
130
+
114
131
  ## Source locations
115
132
 
116
133
  - `src/subsystems/skills/parseSkill.ts` — frontmatter parsing, validation, and suppression cascading
@@ -368,7 +368,7 @@ ShadowClaw supports discovering and importing tools, skills, and companion scrip
368
368
  - **Discovery Standard**: Adheres to the Agent Skills Discovery RFC v0.2.0 (`/.well-known/agent-skills/index.json`), automatically produced by `skills:index` and the static build pipeline. Relative URLs (`../../.agents/...`) resolve to the published static assets on GitHub Pages or custom origins.
369
369
  - **Integrity Validation**: Verifies SHA-256 digests (`sha256:...`) against downloaded artifacts to ensure code integrity before persisting to storage.
370
370
  - **OPFS Persistence**: Imported tools (`.agents/tools/`), skills (`.agents/skills/`), and companion scripts (`.agents/scripts/`) are persisted directly into the user's OPFS workspace and automatically indexed by `loadDeclarativeTools` and `discoverSkills`.
371
- - **UI Importer (`<shadow-claw-tools>`)**: Accessible via the "Import" action in the Tool Configuration header. Provides source URL entry, quick-pick presets (e.g. pwgen Knowledge Hub, Block Garden Knowledge Hub), catalog inspection with schema/code preview, and selective batch import with auto-enablement options.
371
+ - **UI Importer (`<shadow-claw-tools>`)**: Accessible via the "Import" action in the Tool Configuration header. Provides source URL entry, quick-pick presets (e.g. pwgen Knowledge Hub, Block Garden Knowledge Hub, Weather Agent), catalog inspection with schema/code preview, and selective batch import with auto-enablement options.
372
372
 
373
373
  ### Runtime vs. Build-Time Security Boundaries
374
374
 
@@ -7,7 +7,7 @@ Comprehensive testing guide for ShadowClaw covering the **browser-native E2E tes
7
7
  ShadowClaw features a **dual test architecture** tailored to its two runtime modes:
8
8
 
9
9
  1. **Browser E2E Test Suite (`e2e/`)**: Powered by Playwright using the Page Object Model (POM). Validates frontend user interfaces, Web Components, reactive signals, Web Worker orchestrator loops, sandboxed OPFS/IndexedDB storage, WebVM/just-bash shell emulation, and PWA/Electron workflows.
10
- 2. **Server-Side Headless Agent & CLI Integration Tests (`src/cli/` & `src/`)**: Powered by Jest. Validates the host-native CLI agent participant (`shadow-claw agent run`), direct tool inspection and execution (`shadow-claw agent tools`, `shadow-claw agent tool`), SQLite database storage (`src/db/sqlite/`), Node filesystem handle abstraction (`src/storage/node-fs-handle.ts`), and headless tool capability execution (`src/worker/utils/executeTool.headless.test.ts`).
10
+ 2. **Server-Side Headless Agent & CLI Integration Tests (`src/cli/` & `src/`)**: Powered by Jest. Validates the host-native CLI agent participant (`shadow-claw agent run`), direct tool inspection and execution (`shadow-claw agent tools`, `shadow-claw agent tool`), remote skill and tool discovery and importing (`shadow-claw agent import`), SQLite database storage (`src/db/sqlite/`), Node filesystem handle abstraction (`src/storage/node-fs-handle.ts`), and headless tool capability execution (`src/worker/utils/executeTool.headless.test.ts`).
11
11
 
12
12
  ### Browser E2E Directory Structure
13
13
 
@@ -465,7 +465,7 @@ npm run e2e -- --reporter=html
465
465
 
466
466
  The build, CLI, prerender, and headless agent test suites live under `src/cli/` and `src/` and run through the project Jest configuration. Coverage includes:
467
467
 
468
- - **CLI Commands & Utilities**: `build`, `dev`, `run`, `serve`, `server`, `init`, `clients`, `send`, `backup`, `tasks`, `mcp`, `skills:index`, `webrtc`, `peer-id`, `--version`, `agent` (`src/cli/commands/agent.test.ts`), and `agent model` (`src/cli/commands/agent-models.test.ts`).
468
+ - **CLI Commands & Utilities**: `build`, `dev`, `run`, `serve`, `server`, `init`, `clients`, `send`, `backup`, `tasks`, `mcp`, `skills:index`, `webrtc`, `peer-id`, `--version`, `agent` (`src/cli/commands/agent.test.ts` covering init, models, tools, skills, tool execution, and remote RFC v0.2.0 skill/tool/script import workflows), and `agent model` (`src/cli/commands/agent-models.test.ts`).
469
469
  - **Model Download & Progress Bar Utilities**: Hugging Face ONNX model querying, downloading, and local caching (`src/cli/utils/local-models.test.ts`), terminal progress bar (`src/cli/utils/progress-bar.test.ts`), and cache directory resolution (`src/cli/utils/resolve-cache-dir.test.ts`).
470
470
  - **Headless Agent Subsystems**: SQLite database implementation (`src/db/sqlite/*.test.ts`), Node.js filesystem directory handle polyfill (`src/storage/node-fs-handle.test.ts`), headless tool execution and capability matrix (`src/worker/utils/executeTool.headless.test.ts`, `src/worker/tools/builtin-ai/builtin-ai.headless.test.ts`), and default provider/model configuration (`src/config/headless.test.ts`).
471
471
  - **Host-Native Offline Model Executors**: In-process Node.js Transformers.js execution (`src/worker/tools/node-transformers-executor.test.ts`, `src/server/services/transformers-runtime.test.ts`), Llamafile manager & executor (`src/worker/tools/node-llamafile-executor.test.ts`, `src/server/services/llamafile-manager.test.ts`), and agent invocation loop (`src/worker/utils/handleInvoke.test.ts`).
@@ -1 +1 @@
1
- import{W as e,it as t,tt as n,v as r}from"./orchestrator-Baif6kmu.js";function i(i,a){globalThis.__SHADOWCLAW_E2E__={isReady(){return i.ready},getDb(){return a.db},getActiveGroupId(){return i.activeGroupId},async createConversation(e){let t=a.db;if(!t)throw Error(`[E2E Bridge] DB not ready`);return i.createConversation(t,e)},async switchConversation(e){let t=a.db;if(!t)throw Error(`[E2E Bridge] DB not ready`);await i.switchConversation(t,e)},async loadTasks(){let e=a.db;if(!e)throw Error(`[E2E Bridge] DB not ready`);await i.loadTasks(e)},async configureProvider(o,s,c,l){let u=i.orchestrator,d=a.db;if(!u||!d)throw Error(`[E2E Bridge] Orchestrator or DB not ready`);await t(u,d,o,{loadApiKeyForProvider:u.loadApiKeyForProvider.bind(u),getApiKeyForHeaders:()=>e(u)}),await u.setApiKey(d,s),await n(u,d,c),await r(u,d,l)}}}function a(){return globalThis.__SHADOWCLAW_E2E_ENABLE__===!0}export{a as n,i as t};
1
+ import{W as e,it as t,tt as n,v as r}from"./orchestrator-0BnBtxHO.js";function i(i,a){globalThis.__SHADOWCLAW_E2E__={isReady(){return i.ready},getDb(){return a.db},getActiveGroupId(){return i.activeGroupId},async createConversation(e){let t=a.db;if(!t)throw Error(`[E2E Bridge] DB not ready`);return i.createConversation(t,e)},async switchConversation(e){let t=a.db;if(!t)throw Error(`[E2E Bridge] DB not ready`);await i.switchConversation(t,e)},async loadTasks(){let e=a.db;if(!e)throw Error(`[E2E Bridge] DB not ready`);await i.loadTasks(e)},async configureProvider(o,s,c,l){let u=i.orchestrator,d=a.db;if(!u||!d)throw Error(`[E2E Bridge] Orchestrator or DB not ready`);await t(u,d,o,{loadApiKeyForProvider:u.loadApiKeyForProvider.bind(u),getApiKeyForHeaders:()=>e(u)}),await u.setApiKey(d,s),await n(u,d,c),await r(u,d,l)}}}function a(){return globalThis.__SHADOWCLAW_E2E_ENABLE__===!0}export{a as n,i as t};
@@ -2071,7 +2071,7 @@ Important: Use write_file (not bash/sed) to write resolved files. Ensure NO conf
2071
2071
  ---
2072
2072
 
2073
2073
  `)}return tX(rX(e,n),n,{parentGroupId:t,workspaceMode:r})}function aX(e,t){if(!e.schedule||typeof e.schedule!=`string`)return`Error: Missing or invalid 'schedule' (cron expression) for create_task.`;let n=e.type===`tools`?`tools`:`prompt`;if(n===`prompt`&&(!e.prompt||typeof e.prompt!=`string`))return`Error: Missing or invalid 'prompt' for create_task with type 'prompt'.`;let r={createdAt:Date.now(),enabled:!0,groupId:t,id:fe(),lastRun:null,prompt:e.prompt?e.prompt.trim():``,schedule:e.schedule.trim(),tools:Array.isArray(e.tools)?e.tools:[],type:n};return typeof e.name==`string`&&e.name.trim()&&(r.name=e.name.trim()),ci({type:`task-created`,payload:{task:r}}),`Task created successfully.\nID: ${r.id}\nSchedule: ${r.schedule}\nType: ${r.type}`}function oX(e,t){return e.id?(ci({type:`delete-task`,payload:{id:e.id,groupId:t}}),`Task ${e.id} deleted successfully.`):`Error: Missing required task ID for deletion.`}async function sX(e,t){return(await pe(e)).filter(e=>e.groupId===t)}async function cX(e,t,n){let r=(await sX(e,n)).find(e=>e.id===t.id);return r?(r.enabled=!1,ci({type:`update-task`,payload:{task:r}}),`Task ${t.id} disabled successfully.`):`Error: Task with ID ${t.id} not found.`}async function lX(e,t,n){let r=(await sX(e,n)).find(e=>e.id===t.id);return r?(r.enabled=!0,ci({type:`update-task`,payload:{task:r}}),`Task ${t.id} enabled successfully.`):`Error: Task with ID ${t.id} not found.`}async function uX(e,t){let n=await sX(e,t);return n.length===0?`No tasks found for this group.`:(n.sort((e,t)=>{let n=e.order??0,r=t.order??0;return n===r?e.createdAt-t.createdAt:n-r}),n.map(e=>`[ID: ${e.id}] Name: ${e.name||`(none)`}, Schedule: ${e.schedule}, Type: ${e.type||`prompt`}, Enabled: ${e.enabled}`).join(`
2074
- `))}async function dX(e,t,n){let r=(await sX(e,n)).find(e=>e.id===t.id);return r?(ci({type:`run-task`,payload:{task:r}}),`Task ${t.id} triggered successfully.`):`Error: Task with ID ${t.id} not found.`}async function fX(e,t,n){let r=(await sX(e,n)).find(e=>e.id===t.id);return r?(t.schedule&&(r.schedule=t.schedule),(t.type===`prompt`||t.type===`tools`)&&(r.type=t.type),t.prompt&&(r.prompt=t.prompt),Array.isArray(t.tools)&&(r.tools=t.tools),t.enabled!==void 0&&(r.enabled=!!t.enabled),t.name!==void 0&&(r.name=typeof t.name==`string`?t.name.trim():void 0),ci({type:`update-task`,payload:{task:r}}),`Task ${t.id} updated successfully.`):`Error: Task with ID ${t.id} not found.`}async function pX(e,t){let{question:n,options:r}=e;if(!n)return`Error: question is required.`;let i=fe();return ci({type:`ask-user`,payload:{id:i,groupId:t,question:n,options:r}}),await new Promise(e=>{globalThis.pendingAskUserResolvers=globalThis.pendingAskUserResolvers||{},globalThis.pendingAskUserResolvers[i]=e})}function mX(e){return ci({type:`clear-chat`,payload:{groupId:e}}),`Chat history cleared successfully. New session started.`}function hX(e){let t=new Date,n=e.timezone;if(n)try{return new Intl.DateTimeFormat(`en-US`,{timeZone:n,timeStyle:`long`,dateStyle:`long`}).format(t)}catch(e){return`Error: Invalid timezone ${n} - ${e.message}`}return t.toISOString()}let gX;function _X(){if(gX!==void 0)return gX;let e=Reflect.get(globalThis,`trustedTypes`);if(!e||typeof e.createPolicy!=`function`)return gX=null,gX;try{gX=e.createPolicy(`shadowclaw-sandbox`,{createScriptURL:e=>e})}catch{gX=null}return gX}const vX=[`self`,`postMessage`,`importScripts`,`close`,`onmessage`,`onmessageerror`,`onerror`,`setInterval`,`XMLHttpRequest`,`WebSocket`,`indexedDB`,`caches`,`navigator`,`location`,`eval`,`Function`];function yX(e){return e?vX:[...vX,`fetch`]}async function bX(e,t=3e4,n=!1,r){let i=xX(e,n,r),a=new Blob([i],{type:`application/javascript`}),o=URL.createObjectURL(a),s=null;try{return await new Promise(e=>{let n=setTimeout(()=>{s&&=(s.terminate(),null),e({ok:!1,error:`Execution timed out after ${t/1e3}s`})},t),r=_X(),i=r&&typeof r.createScriptURL==`function`?r.createScriptURL(o):o,a=new Worker(i);s=a,a.onmessage=t=>{clearTimeout(n),a.terminate(),s=null,e(t.data)},a.onerror=t=>{clearTimeout(n),a.terminate(),s=null,e({ok:!1,error:t.message||`Unknown worker error`})}})}finally{URL.revokeObjectURL(o),s&&s.terminate()}}function xX(e=``,t=!1,n){return`
2074
+ `))}async function dX(e,t,n){let r=(await sX(e,n)).find(e=>e.id===t.id);return r?(ci({type:`run-task`,payload:{task:r}}),`Task ${t.id} triggered successfully.`):`Error: Task with ID ${t.id} not found.`}async function fX(e,t,n){let r=(await sX(e,n)).find(e=>e.id===t.id);return r?(t.schedule&&(r.schedule=t.schedule),(t.type===`prompt`||t.type===`tools`)&&(r.type=t.type),t.prompt&&(r.prompt=t.prompt),Array.isArray(t.tools)&&(r.tools=t.tools),t.enabled!==void 0&&(r.enabled=!!t.enabled),t.name!==void 0&&(r.name=typeof t.name==`string`?t.name.trim():void 0),ci({type:`update-task`,payload:{task:r}}),`Task ${t.id} updated successfully.`):`Error: Task with ID ${t.id} not found.`}async function pX(e,t){let{question:n,options:r}=e;if(!n)return`Error: question is required.`;let i=fe();return ci({type:`ask-user`,payload:{id:i,groupId:t,question:n,options:r}}),await new Promise(e=>{globalThis.pendingAskUserResolvers=globalThis.pendingAskUserResolvers||{},globalThis.pendingAskUserResolvers[i]=e})}function mX(e){return ci({type:`clear-chat`,payload:{groupId:e}}),`Chat history cleared successfully. New session started.`}function hX(e){let t=new Date,n=e.timezone;if(n)try{return new Intl.DateTimeFormat(`en-US`,{timeZone:n,timeStyle:`long`,dateStyle:`long`}).format(t)}catch(e){return`Error: Invalid timezone ${n} - ${e.message}`}return t.toISOString()}let gX;function _X(){if(gX!==void 0)return gX;let e=Reflect.get(globalThis,`trustedTypes`);if(!e||typeof e.createPolicy!=`function`)return gX=null,gX;try{gX=e.createPolicy(`shadowclaw-sandbox`,{createScriptURL:e=>e})}catch{gX=null}return gX}const vX=[`self`,`postMessage`,`importScripts`,`close`,`onmessage`,`onmessageerror`,`onerror`,`setInterval`,`XMLHttpRequest`,`WebSocket`,`indexedDB`,`caches`,`navigator`,`location`,`eval`,`Function`];function yX(e){return e?vX:[...vX,`fetch`]}async function bX(e,t=3e4,n=!1,r){if(typeof Worker>`u`){let i=globalThis.__headlessEvalExecutor;return typeof i==`function`?i(e,t,n,r):{ok:!1,error:`Web Workers are not supported in this environment.`}}let i=xX(e,n,r),a=new Blob([i],{type:`application/javascript`}),o=URL.createObjectURL(a),s=null;try{return await new Promise(e=>{let n=setTimeout(()=>{s&&=(s.terminate(),null),e({ok:!1,error:`Execution timed out after ${t/1e3}s`})},t),r=_X(),i=r&&typeof r.createScriptURL==`function`?r.createScriptURL(o):o,a=new Worker(i);s=a,a.onmessage=t=>{clearTimeout(n),a.terminate(),s=null,e(t.data)},a.onerror=t=>{clearTimeout(n),a.terminate(),s=null,e({ok:!1,error:t.message||`Unknown worker error`})}})}finally{URL.revokeObjectURL(o),s&&s.terminate()}}function xX(e=``,t=!1,n){return`
2075
2075
  "use strict";
2076
2076
  const blocked = ${JSON.stringify(yX(t))};
2077
2077
  const __hostPostMessage =
@@ -2176,6 +2176,7 @@ const restoreGlobals = applyGlobalShadows();
2176
2176
  const Function = undefined;
2177
2177
  ${t?``:`const fetch = undefined;`}
2178
2178
  const $PIPE_DATA = ${JSON.stringify(n??``)};
2179
+ const data = $PIPE_DATA;
2179
2180
 
2180
2181
  ${e}
2181
2182
  })();