shadow-claw 1.34.5 → 1.36.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 (208) 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/build/build.js +1 -1
  5. package/dist/cli/{chunk-B4a6SyOL.js → chunk-6dsqmo6-.js} +3 -3
  6. package/dist/cli/chunk-BCLxzLO92.js +5 -0
  7. package/dist/cli/chunk-BHNCi3ui2.js +1 -0
  8. package/dist/cli/chunk-BMPhLSn8.js +1 -0
  9. package/dist/cli/chunk-BMW6QB5-2.js +27 -0
  10. package/dist/cli/chunk-BQCWtrzh.js +1 -0
  11. package/dist/cli/chunk-BQCWtrzh2.js +1 -0
  12. package/dist/cli/chunk-BT2f5jvi.js +1 -0
  13. package/dist/cli/chunk-BdJUjMuz.js +1 -0
  14. package/dist/cli/{chunk-DoHbMaWn.js → chunk-BwtEsB9s.js} +6 -6
  15. package/dist/cli/chunk-C47WdxKx2.js +1 -0
  16. package/dist/cli/{chunk-BRiGkTIi.js → chunk-CAqvQuGr.js} +1 -1
  17. package/dist/cli/{chunk-DoVxObGa2.js → chunk-CHzhUS1N2.js} +1 -1
  18. package/dist/cli/chunk-CUMlq7Jw2.js +263 -0
  19. package/dist/cli/chunk-CXi9LU9e2.js +110 -0
  20. package/dist/cli/chunk-ChefnX-r.js +110 -0
  21. package/dist/cli/{chunk-zvi71-77.js → chunk-D3JxbzjI2.js} +15 -15
  22. package/dist/cli/chunk-D7VYMl092.js +1 -0
  23. package/dist/cli/chunk-DMRbWmO2.js +1 -0
  24. package/dist/cli/chunk-DNIduiJ82.js +91 -0
  25. package/dist/cli/{chunk-C4UfzPK4.js → chunk-DTbrldk_.js} +1 -1
  26. package/dist/cli/chunk-DZ8YBXax.js +969 -0
  27. package/dist/cli/chunk-DduRvw8B2.js +1 -0
  28. package/dist/cli/chunk-DnYxGjdu2.js +1 -0
  29. package/dist/cli/chunk-Du5qiUIk.js +2 -0
  30. package/dist/cli/chunk-Ea7nXmgb2.js +1 -0
  31. package/dist/cli/{chunk-D9dku_3t.js → chunk-EohNY8vW.js} +1 -1
  32. package/dist/cli/chunk-LIIpZlwP2.js +1 -0
  33. package/dist/cli/chunk-LIJZHJml2.js +1 -0
  34. package/dist/cli/chunk-MHBh2BgF2.js +91 -0
  35. package/dist/cli/{chunk-BQ7lHA7c.js → chunk-SKgLGyTY.js} +1 -1
  36. package/dist/cli/chunk-kcJSXcbq2.js +1 -0
  37. package/dist/cli/chunk-rJtMBaNB.js +2 -0
  38. package/dist/cli/cli.js +12 -11
  39. package/dist/headless-agent.js +191 -187
  40. package/dist/lib/components/index.js +21 -21
  41. package/dist/lib/components/settings/shadow-claw-channel-config.js +1 -1
  42. package/dist/lib/components/settings/shadow-claw-control-plane.js +1 -1
  43. package/dist/lib/components/settings/shadow-claw-git.js +1 -1
  44. package/dist/lib/components/settings/shadow-claw-llm.js +1 -1
  45. package/dist/lib/components/settings/shadow-claw-networking.js +1 -1
  46. package/dist/lib/components/settings/shadow-claw-notifications.js +1 -1
  47. package/dist/lib/components/settings/shadow-claw-peerjs.js +1 -1
  48. package/dist/lib/components/settings/shadow-claw-storage.js +1 -1
  49. package/dist/lib/components/settings/shadow-claw-task-server.js +1 -1
  50. package/dist/lib/components/settings/shadow-claw-webvm.js +1 -1
  51. package/dist/lib/components/shadow-claw-channel-config.js +1 -1
  52. package/dist/lib/components/shadow-claw-channels.js +1 -1
  53. package/dist/lib/components/shadow-claw-chat.js +1 -1
  54. package/dist/lib/components/shadow-claw-control-plane.js +1 -1
  55. package/dist/lib/components/shadow-claw-conversations.js +1 -1
  56. package/dist/lib/components/shadow-claw-file-viewer.js +1 -1
  57. package/dist/lib/components/shadow-claw-files.js +1 -1
  58. package/dist/lib/components/shadow-claw-git.js +1 -1
  59. package/dist/lib/components/shadow-claw-llm.js +1 -1
  60. package/dist/lib/components/shadow-claw-networking.js +1 -1
  61. package/dist/lib/components/shadow-claw-notifications.js +1 -1
  62. package/dist/lib/components/shadow-claw-pages.js +1 -1
  63. package/dist/lib/components/shadow-claw-peerjs.js +1 -1
  64. package/dist/lib/components/shadow-claw-settings.js +1 -1
  65. package/dist/lib/components/shadow-claw-storage.js +1 -1
  66. package/dist/lib/components/shadow-claw-task-server.js +1 -1
  67. package/dist/lib/components/shadow-claw-tasks.js +1 -1
  68. package/dist/lib/components/shadow-claw-terminal.js +1 -1
  69. package/dist/lib/components/shadow-claw-tools.js +1 -1
  70. package/dist/lib/components/shadow-claw-webvm.js +1 -1
  71. package/dist/lib/components/shadow-claw.js +1 -1
  72. package/dist/lib/{configurePeerJs-DSRcNp3v.js → configurePeerJs-CdPzmpyI.js} +2 -2
  73. package/dist/lib/{configurePeerJs-DSRcNp3v.js.map → configurePeerJs-CdPzmpyI.js.map} +1 -1
  74. package/dist/lib/index.js +21 -21
  75. package/dist/lib/{initControlPlane-CWfE_Oan.js → initControlPlane-BLymRW34.js} +7 -7
  76. package/dist/lib/{initControlPlane-CWfE_Oan.js.map → initControlPlane-BLymRW34.js.map} +1 -1
  77. package/dist/lib/{orchestrator-BEUug1TW.js → orchestrator-DZkhJj7f.js} +13 -2
  78. package/dist/lib/{orchestrator-BEUug1TW.js.map → orchestrator-DZkhJj7f.js.map} +1 -1
  79. package/dist/lib/{orchestrator-CGUzfGbE.js → orchestrator-ZsQTRknI.js} +5 -5
  80. package/dist/lib/{orchestrator-CGUzfGbE.js.map → orchestrator-ZsQTRknI.js.map} +1 -1
  81. package/dist/lib/{peerjs-BSD88rT3.js → peerjs-CJYfnR5V.js} +3 -3
  82. package/dist/lib/{peerjs-BSD88rT3.js.map → peerjs-CJYfnR5V.js.map} +1 -1
  83. package/dist/lib/{push-client-CC3qifgc.js → push-client-C9hIPqLZ.js} +2 -2
  84. package/dist/lib/{push-client-CC3qifgc.js.map → push-client-C9hIPqLZ.js.map} +1 -1
  85. package/dist/lib/{shadow-claw-BWS-UVnE.js → shadow-claw-8qL2-QoK.js} +4 -4
  86. package/dist/lib/{shadow-claw-BWS-UVnE.js.map → shadow-claw-8qL2-QoK.js.map} +1 -1
  87. package/dist/lib/{shadow-claw-channel-config-DwMn3nFJ.js → shadow-claw-channel-config-CvnbqGBy.js} +3 -3
  88. package/dist/lib/{shadow-claw-channel-config-DwMn3nFJ.js.map → shadow-claw-channel-config-CvnbqGBy.js.map} +1 -1
  89. package/dist/lib/{shadow-claw-channels-BdE4cUlx.js → shadow-claw-channels-CkCvjWn1.js} +2 -2
  90. package/dist/lib/{shadow-claw-channels-BdE4cUlx.js.map → shadow-claw-channels-CkCvjWn1.js.map} +1 -1
  91. package/dist/lib/{shadow-claw-chat-DqaMoE9C.js → shadow-claw-chat-B1YEHKN3.js} +3 -3
  92. package/dist/lib/{shadow-claw-chat-DqaMoE9C.js.map → shadow-claw-chat-B1YEHKN3.js.map} +1 -1
  93. package/dist/lib/{shadow-claw-control-plane-D3DUDO29.js → shadow-claw-control-plane-jjo789IE.js} +6 -6
  94. package/dist/lib/{shadow-claw-control-plane-D3DUDO29.js.map → shadow-claw-control-plane-jjo789IE.js.map} +1 -1
  95. package/dist/lib/{shadow-claw-conversations-XT-OXogj.js → shadow-claw-conversations-MMT7wYH3.js} +2 -2
  96. package/dist/lib/{shadow-claw-conversations-XT-OXogj.js.map → shadow-claw-conversations-MMT7wYH3.js.map} +1 -1
  97. package/dist/lib/{shadow-claw-file-viewer-DJqxyd97.js → shadow-claw-file-viewer-qSkD1RQK.js} +2 -2
  98. package/dist/lib/{shadow-claw-file-viewer-DJqxyd97.js.map → shadow-claw-file-viewer-qSkD1RQK.js.map} +1 -1
  99. package/dist/lib/{shadow-claw-files-CUEJlA1o.js → shadow-claw-files-Cg8ycQ_N.js} +2 -2
  100. package/dist/lib/{shadow-claw-files-CUEJlA1o.js.map → shadow-claw-files-Cg8ycQ_N.js.map} +1 -1
  101. package/dist/lib/{shadow-claw-git-D_iVcb86.js → shadow-claw-git-gaClz-ZC.js} +3 -3
  102. package/dist/lib/{shadow-claw-git-D_iVcb86.js.map → shadow-claw-git-gaClz-ZC.js.map} +1 -1
  103. package/dist/lib/{shadow-claw-llm-C2msAsyU.js → shadow-claw-llm-DyOz0qzT.js} +2 -2
  104. package/dist/lib/{shadow-claw-llm-C2msAsyU.js.map → shadow-claw-llm-DyOz0qzT.js.map} +1 -1
  105. package/dist/lib/{shadow-claw-networking-D2NvodC-.js → shadow-claw-networking-B2xx0UUR.js} +2 -2
  106. package/dist/lib/{shadow-claw-networking-D2NvodC-.js.map → shadow-claw-networking-B2xx0UUR.js.map} +1 -1
  107. package/dist/lib/{shadow-claw-notifications-BcvVn_vu.js → shadow-claw-notifications-BjKfZy0k.js} +2 -2
  108. package/dist/lib/{shadow-claw-notifications-BcvVn_vu.js.map → shadow-claw-notifications-BjKfZy0k.js.map} +1 -1
  109. package/dist/lib/{shadow-claw-pages-CTMXRhEX.js → shadow-claw-pages-CJ2tsMtL.js} +2 -2
  110. package/dist/lib/{shadow-claw-pages-CTMXRhEX.js.map → shadow-claw-pages-CJ2tsMtL.js.map} +1 -1
  111. package/dist/lib/{shadow-claw-peerjs-C2YvypqM.js → shadow-claw-peerjs-CazCkXQ1.js} +3 -3
  112. package/dist/lib/{shadow-claw-peerjs-C2YvypqM.js.map → shadow-claw-peerjs-CazCkXQ1.js.map} +1 -1
  113. package/dist/lib/{shadow-claw-settings-B0hiDwEt.js → shadow-claw-settings-qro6pO9I.js} +2 -2
  114. package/dist/lib/{shadow-claw-settings-B0hiDwEt.js.map → shadow-claw-settings-qro6pO9I.js.map} +1 -1
  115. package/dist/lib/{shadow-claw-storage-Dt4yg4Q4.js → shadow-claw-storage-ewDdvCGa.js} +2 -2
  116. package/dist/lib/{shadow-claw-storage-Dt4yg4Q4.js.map → shadow-claw-storage-ewDdvCGa.js.map} +1 -1
  117. package/dist/lib/{shadow-claw-task-server-B2gbZaz4.js → shadow-claw-task-server-DpqQcNIs.js} +2 -2
  118. package/dist/lib/{shadow-claw-task-server-B2gbZaz4.js.map → shadow-claw-task-server-DpqQcNIs.js.map} +1 -1
  119. package/dist/lib/{shadow-claw-tasks-Bxq-niuM.js → shadow-claw-tasks-B2fMF4qE.js} +2 -2
  120. package/dist/lib/{shadow-claw-tasks-Bxq-niuM.js.map → shadow-claw-tasks-B2fMF4qE.js.map} +1 -1
  121. package/dist/lib/{shadow-claw-terminal-BfbW3xtg.js → shadow-claw-terminal-B25kQJC3.js} +2 -2
  122. package/dist/lib/{shadow-claw-terminal-BfbW3xtg.js.map → shadow-claw-terminal-B25kQJC3.js.map} +1 -1
  123. package/dist/lib/{shadow-claw-tools-BNSAaK0Y.js → shadow-claw-tools-B0y8JzoA.js} +10 -3
  124. package/dist/lib/{shadow-claw-tools-BNSAaK0Y.js.map → shadow-claw-tools-B0y8JzoA.js.map} +1 -1
  125. package/dist/lib/{shadow-claw-webvm-Dc2KmEXq.js → shadow-claw-webvm-CoCedIza.js} +2 -2
  126. package/dist/lib/{shadow-claw-webvm-Dc2KmEXq.js.map → shadow-claw-webvm-CoCedIza.js.map} +1 -1
  127. package/dist/lib/{syncWebMcpRegistration-BIL83P0Q.js → syncWebMcpRegistration-JKLALq4e.js} +2 -2
  128. package/dist/lib/{syncWebMcpRegistration-BIL83P0Q.js.map → syncWebMcpRegistration-JKLALq4e.js.map} +1 -1
  129. package/dist/lib/worker/utils/sandboxedEval.d.ts +8 -0
  130. package/dist/public/.well-known/mcp/server-card.json +1 -1
  131. package/dist/public/.well-known/mcp.json +1 -1
  132. package/dist/public/AGENTS.md +1 -1
  133. package/dist/public/README.md +5 -1
  134. package/dist/public/agent.worker.js +1 -1
  135. package/dist/public/components/shadow-claw-tools/shadow-claw-tools.html +1 -1
  136. package/dist/public/{configurePeerJs-B925t9C7.js → configurePeerJs-BrHeJGw1.js} +1 -1
  137. package/dist/public/docs/decisions/headless-cli-agent-participant.md +1 -1
  138. package/dist/public/docs/example/article/index.html +1 -1
  139. package/dist/public/docs/publishing/index.html +1 -1
  140. package/dist/public/docs/skill-creator/index.html +1 -1
  141. package/dist/public/docs/subsystems/cli.md +43 -32
  142. package/dist/public/docs/subsystems/skills.md +17 -0
  143. package/dist/public/docs/subsystems/tools.md +1 -1
  144. package/dist/public/e2e/README.md +2 -2
  145. package/dist/public/{e2e-bridge-CE1xleGn.js → e2e-bridge-D_lyH__p.js} +1 -1
  146. package/dist/public/{executeTool-DtTrsZHV.js → executeTool-b6DnRhcv.js} +2 -1
  147. package/dist/public/{file-viewer-LEEStSq-.js → file-viewer-CKrQoxZi.js} +1 -1
  148. package/dist/public/index.html +1 -1
  149. package/dist/public/index.js +1 -1
  150. package/dist/public/{initControlPlane-e2JgxXEb.js → initControlPlane-Dn-XFuxg.js} +1 -1
  151. package/dist/public/llms.txt +2 -0
  152. package/dist/public/main/index.html +1 -1
  153. package/dist/public/main/memory/index.html +1 -1
  154. package/dist/public/{orchestrator-Baif6kmu.js → orchestrator-0BnBtxHO.js} +3 -2
  155. package/dist/public/{orchestrator-TndREZPL.js → orchestrator-C3GKZriH.js} +1 -1
  156. package/dist/public/{peerjs-DE6WF3h2.js → peerjs-CgfLEp2N.js} +1 -1
  157. package/dist/public/{push-client-B_IkTpNm.js → push-client-D6DLiY1s.js} +1 -1
  158. package/dist/public/service-worker.js +1 -1
  159. package/dist/public/{shadow-claw-channels-BCGz36Zl.js → shadow-claw-channels-DSijinZ9.js} +1 -1
  160. package/dist/public/{shadow-claw-chat-H9U9jbqD.js → shadow-claw-chat-BeHlAjnA.js} +1 -1
  161. package/dist/public/{shadow-claw-control-plane-CeKh64s2.js → shadow-claw-control-plane-BfF_AZSg.js} +2 -2
  162. package/dist/public/{shadow-claw-conversations-C26t1HXN.js → shadow-claw-conversations-Bb_2bVLJ.js} +1 -1
  163. package/dist/public/{shadow-claw-DBeXILmk.js → shadow-claw-f71DlRdV.js} +2 -2
  164. package/dist/public/{shadow-claw-file-viewer-DWUoTjkr.js → shadow-claw-file-viewer-B67000Mm.js} +1 -1
  165. package/dist/public/{shadow-claw-files-BQ8DSX-z.js → shadow-claw-files-DeZZYt01.js} +1 -1
  166. package/dist/public/{shadow-claw-git-CIXyISw-.js → shadow-claw-git-Ccy25LuW.js} +2 -2
  167. package/dist/public/{shadow-claw-llm-D_GpOSZE.js → shadow-claw-llm-XjYcTlXT.js} +1 -1
  168. package/dist/public/{shadow-claw-networking-BYp5K3B7.js → shadow-claw-networking-efXQtydQ.js} +1 -1
  169. package/dist/public/{shadow-claw-notifications-ZiXmPweY.js → shadow-claw-notifications-Cq2ZSeJa.js} +1 -1
  170. package/dist/public/{shadow-claw-pages-BHXwf3BD.js → shadow-claw-pages-BVFNSSb1.js} +1 -1
  171. package/dist/public/{shadow-claw-settings-BfvDvt9c.js → shadow-claw-settings-B3sHxPh9.js} +2 -2
  172. package/dist/public/{shadow-claw-storage-Bvn85DII.js → shadow-claw-storage-DNdlzCgC.js} +1 -1
  173. package/dist/public/{shadow-claw-task-server-DZL_d8FC.js → shadow-claw-task-server-BJq0rpju.js} +1 -1
  174. package/dist/public/{shadow-claw-tasks-Cy8jzd8q.js → shadow-claw-tasks-CkF1uMeZ.js} +1 -1
  175. package/dist/public/{shadow-claw-terminal-BgEXr82T.js → shadow-claw-terminal-CnHhcpA-.js} +1 -1
  176. package/dist/public/{shadow-claw-tools-BjnssRSi.js → shadow-claw-tools-iw0fljud.js} +8 -1
  177. package/dist/public/{shadow-claw-webvm-tZ1VDoVA.js → shadow-claw-webvm-Bliu4V__.js} +1 -1
  178. package/dist/public/{syncWebMcpRegistration-CLMvJ4kV.js → syncWebMcpRegistration-D91usEMu.js} +1 -1
  179. package/package.json +1 -1
  180. package/src/cli/build/build.test.ts +11 -0
  181. package/src/cli/build/build.ts +10 -9
  182. package/src/cli/cli.ts +31 -2
  183. package/src/cli/commands/agent.test.ts +421 -0
  184. package/src/cli/commands/agent.ts +270 -20
  185. package/src/cli/commands/skills-index.test.ts +84 -2
  186. package/src/cli/commands/skills-index.ts +20 -2
  187. package/src/components/shadow-claw-tools/shadow-claw-tools.html +7 -0
  188. package/src/worker/headless-agent.ts +14 -0
  189. package/src/worker/headless-types.ts +39 -0
  190. package/src/worker/utils/native-eval-executor.test.ts +36 -0
  191. package/src/worker/utils/native-eval-executor.ts +58 -0
  192. package/src/worker/utils/sandboxedEval.ts +36 -0
  193. package/dist/cli/chunk-1BmpU7jO2.js +0 -1
  194. package/dist/cli/chunk-54Euq8Ji2.js +0 -1
  195. package/dist/cli/chunk-BTitwCgT2.js +0 -88
  196. package/dist/cli/chunk-Bjh76Dai.js +0 -1081
  197. package/dist/cli/chunk-C2HeZQ6K.js +0 -1
  198. package/dist/cli/chunk-Cdq9DIrc.js +0 -1
  199. package/dist/cli/chunk-CxdpafWg.js +0 -1
  200. package/dist/cli/chunk-DBk6e4N-.js +0 -1
  201. package/dist/cli/chunk-DEy72ZNV2.js +0 -88
  202. package/dist/cli/chunk-Da1BZyqf.js +0 -1
  203. package/dist/cli/chunk-FHsJD-Sf2.js +0 -27
  204. package/dist/cli/chunk-JBAAPRpe.js +0 -2
  205. package/dist/cli/chunk-aWgoVB7x2.js +0 -1
  206. package/dist/cli/chunk-hOceZVB92.js +0 -371
  207. package/dist/cli/chunk-pwNc6VtC2.js +0 -1
  208. package/dist/cli/chunk-uOnw3tuc.js +0 -2
@@ -21,6 +21,8 @@ import type {
21
21
  AgentRunResult,
22
22
  AgentModelOptions,
23
23
  AgentModelResult,
24
+ AgentImportOptions,
25
+ AgentImportResult,
24
26
  } from "../../worker/headless-types.js";
25
27
 
26
28
  import { bootstrapHeadlessAgent } from "./agent-bootstrap.js";
@@ -254,6 +256,8 @@ export async function runAgentTools(
254
256
  options: AgentToolsOptions & Record<string, any> = {},
255
257
  ): Promise<any> {
256
258
  const { db, core } = await bootstrapHeadlessAgent(options);
259
+ const groupId =
260
+ options.group || core.DEFAULT_SERVER_GROUP_ID || DEFAULT_SERVER_GROUP_ID;
257
261
 
258
262
  let profileToolNames: Set<string> | null = null;
259
263
  if (options.toolsProfile || options.profile) {
@@ -283,7 +287,21 @@ export async function runAgentTools(
283
287
  }
284
288
  }
285
289
 
286
- const tools = (core.TOOL_DEFINITIONS || []).map((def) => ({
290
+ const allTools = [...(core.TOOL_DEFINITIONS || [])];
291
+ if (typeof core.loadDeclarativeTools === "function") {
292
+ try {
293
+ const decl = await core.loadDeclarativeTools(db, groupId);
294
+ if (decl && Array.isArray(decl.tools)) {
295
+ for (const dt of decl.tools) {
296
+ if (!allTools.some((t) => t.name === dt.name)) {
297
+ allTools.push(dt);
298
+ }
299
+ }
300
+ }
301
+ } catch {}
302
+ }
303
+
304
+ const tools = allTools.map((def) => ({
287
305
  name: def.name,
288
306
  description: def.description,
289
307
  headlessSafe: !BROWSER_ONLY_TOOLS.has(def.name),
@@ -310,11 +328,14 @@ export async function runAgentTool(
310
328
  options.group || core.DEFAULT_SERVER_GROUP_ID || DEFAULT_SERVER_GROUP_ID;
311
329
 
312
330
  // Find tool in built-in TOOL_DEFINITIONS or declarative tools
313
- let toolDef = (core.TOOL_DEFINITIONS || []).find((t) => t.name === toolName);
331
+ let toolDef = (core.TOOL_DEFINITIONS || []).find(
332
+ (t: any) => t.name === toolName,
333
+ );
334
+
314
335
  if (!toolDef && typeof core.loadDeclarativeTools === "function") {
315
336
  try {
316
337
  const decl = await core.loadDeclarativeTools(db, groupId);
317
- toolDef = decl.tools.find((t) => t.name === toolName);
338
+ toolDef = decl.tools.find((t: any) => t.name === toolName);
318
339
  } catch {}
319
340
  }
320
341
 
@@ -370,12 +391,10 @@ export async function runAgentTool(
370
391
 
371
392
  let invokeContext: any = undefined;
372
393
  try {
373
- const resolved = await resolveAgentProvider(
374
- db,
375
- core,
376
- workspaceDir,
377
- options,
378
- );
394
+ const resolved = await resolveAgentProvider(db, core, workspaceDir, {
395
+ ...options,
396
+ interactive: false,
397
+ });
379
398
  invokeContext = {
380
399
  db,
381
400
  provider: resolved.providerId,
@@ -471,7 +490,7 @@ export async function runAgentSkill(
471
490
  skill.execution?.type === "tools" &&
472
491
  Array.isArray(skill.execution.tools)
473
492
  ) {
474
- const normalizedTools = skill.execution.tools.map((t) => ({
493
+ const normalizedTools = skill.execution.tools.map((t: any) => ({
475
494
  name: t.name || t.tool,
476
495
  input: t.input || t.arguments || {},
477
496
  suppressOutput: t.suppressOutput,
@@ -565,7 +584,8 @@ export async function resolveAgentProvider(
565
584
  process.env.CI &&
566
585
  !["0", "false"].includes(process.env.CI.toLowerCase()),
567
586
  );
568
- const isInteractive = isTTY && !isCI && !options.quiet;
587
+ const isInteractive =
588
+ isTTY && !isCI && !options.quiet && options.interactive !== false;
569
589
 
570
590
  if (!providerId) {
571
591
  if (isInteractive) {
@@ -726,7 +746,7 @@ export async function resolveAgentProvider(
726
746
  const processSigintListener = () => handleSigint();
727
747
  process.on("SIGINT", processSigintListener);
728
748
 
729
- const dataListener = (chunk) => {
749
+ const dataListener = (chunk: any) => {
730
750
  const str =
731
751
  typeof chunk === "string" ? chunk : chunk.toString("binary");
732
752
  if (str.includes("\x03") || str.includes("\x04")) {
@@ -883,11 +903,13 @@ async function resolveToolsAndProfile(
883
903
  ? options.tools
884
904
  : String(options.tools).split(",")
885
905
  )
886
- .map((t) => String(t).trim())
906
+ .map((t: unknown) => String(t).trim())
887
907
  .filter(Boolean);
888
908
 
889
909
  enabledTools = requestedNames
890
- .map((name) => allAvailableTools.find((t) => t.name === name))
910
+ .map((name: unknown) =>
911
+ allAvailableTools.find((t) => t.name === String(name)),
912
+ )
891
913
  .filter(Boolean);
892
914
  return { enabledTools, profileSystemPromptOverride };
893
915
  }
@@ -1098,7 +1120,7 @@ export async function runAgentRun(
1098
1120
  let hasStreamedChunks = false;
1099
1121
  const pendingWrites: Promise<void>[] = [];
1100
1122
 
1101
- core.setPostHandler(async (message) => {
1123
+ core.setPostHandler(async (message: any) => {
1102
1124
  switch (message.type) {
1103
1125
  case "streaming-start":
1104
1126
  hasStreamedChunks = true;
@@ -1197,15 +1219,15 @@ export async function runAgentRun(
1197
1219
  groupId: taskGroupId,
1198
1220
  db,
1199
1221
  })
1200
- .then((res) => {
1201
- const resolvers = globalThis.pendingNativeAiResolvers;
1222
+ .then((res: unknown) => {
1223
+ const resolvers = (globalThis as any).pendingNativeAiResolvers;
1202
1224
  if (resolvers && resolvers[id]) {
1203
1225
  resolvers[id].resolve(res);
1204
1226
  delete resolvers[id];
1205
1227
  }
1206
1228
  })
1207
- .catch((err) => {
1208
- const resolvers = globalThis.pendingNativeAiResolvers;
1229
+ .catch((err: unknown) => {
1230
+ const resolvers = (globalThis as any).pendingNativeAiResolvers;
1209
1231
  if (resolvers && resolvers[id]) {
1210
1232
  resolvers[id].reject(err);
1211
1233
  delete resolvers[id];
@@ -1601,6 +1623,218 @@ export async function runAgentModel(
1601
1623
  return { success: false, error: errorMsg };
1602
1624
  }
1603
1625
 
1626
+ /**
1627
+ * Import tools, skills, and companion scripts from a remote discovery manifest or site URL.
1628
+ * @param {string} urlOrSite
1629
+ * @param {import("../../src/worker/headless-types.js").AgentImportOptions} [options]
1630
+ * @returns {Promise<import("../../src/worker/headless-types.js").AgentImportResult>}
1631
+ */
1632
+ export async function runAgentImport(
1633
+ urlOrSite: string,
1634
+ options: AgentImportOptions & Record<string, any> = {},
1635
+ ): Promise<AgentImportResult> {
1636
+ const trimmedUrl = (urlOrSite || "").trim();
1637
+ if (!trimmedUrl) {
1638
+ const errorMsg =
1639
+ "Error: Discovery URL or site URL required. Usage: shadow-claw agent import <url> [options]";
1640
+ if (!options.quiet) {
1641
+ console.error(errorMsg);
1642
+ }
1643
+ process.exitCode = 1;
1644
+ return { success: false, error: errorMsg };
1645
+ }
1646
+
1647
+ const { db, core } = await bootstrapHeadlessAgent(options);
1648
+ const groupId =
1649
+ options.group || core.DEFAULT_SERVER_GROUP_ID || DEFAULT_SERVER_GROUP_ID;
1650
+
1651
+ const fetchDiscoveryManifest =
1652
+ core.fetchDiscoveryManifest ||
1653
+ (await import("../../subsystems/tools/remote/discovery.js"))
1654
+ .fetchDiscoveryManifest;
1655
+ const importRemoteArtifacts =
1656
+ core.importRemoteArtifacts ||
1657
+ (await import("../../subsystems/tools/remote/importArtifacts.js"))
1658
+ .importRemoteArtifacts;
1659
+
1660
+ const fetchFn = options.fetchFn || globalThis.fetch;
1661
+
1662
+ if (!options.quiet) {
1663
+ console.log(`Fetching discovery manifest from ${trimmedUrl}...`);
1664
+ }
1665
+
1666
+ let resolvedManifest: any;
1667
+ try {
1668
+ resolvedManifest = await fetchDiscoveryManifest(trimmedUrl, fetchFn);
1669
+ } catch (err: any) {
1670
+ const errorMsg = `Failed to fetch discovery manifest: ${err instanceof Error ? err.message : String(err)}`;
1671
+ if (!options.quiet) {
1672
+ console.error(`Error: ${errorMsg}`);
1673
+ }
1674
+ process.exitCode = 1;
1675
+ return { success: false, error: errorMsg };
1676
+ }
1677
+
1678
+ const { manifest, siteUrl, manifestUrl } = resolvedManifest;
1679
+
1680
+ const manifestTools = manifest.tools || [];
1681
+ const manifestSkills = manifest.skills || [];
1682
+ const manifestScripts = manifest.scripts || [];
1683
+
1684
+ if (!options.quiet) {
1685
+ console.log(
1686
+ `Discovered: ${manifest.name || "Remote Site"} (${manifestSkills.length} skill${manifestSkills.length === 1 ? "" : "s"}, ${manifestTools.length} tool${manifestTools.length === 1 ? "" : "s"}, ${manifestScripts.length} script${manifestScripts.length === 1 ? "" : "s"})`,
1687
+ );
1688
+ }
1689
+
1690
+ const allToolNames = manifestTools.map((t: any) => t.name);
1691
+ const allSkillNames = manifestSkills.map((s: any) => s.name);
1692
+ const allScriptNames = manifestScripts.map((s: any) => s.name);
1693
+
1694
+ let toolNames: string[];
1695
+ let skillNames: string[];
1696
+ let scriptNames: string[];
1697
+
1698
+ const hasExplicitFilter = Boolean(
1699
+ options.tools || options.skills || options.scripts,
1700
+ );
1701
+
1702
+ if (hasExplicitFilter && !options.all) {
1703
+ const parseList = (val: any) => {
1704
+ if (!val) return [];
1705
+ if (Array.isArray(val))
1706
+ return val.map((v) => String(v).trim()).filter(Boolean);
1707
+ return String(val)
1708
+ .split(",")
1709
+ .map((v) => v.trim())
1710
+ .filter(Boolean);
1711
+ };
1712
+ toolNames = parseList(options.tools);
1713
+ skillNames = parseList(options.skills);
1714
+ scriptNames = parseList(options.scripts);
1715
+ } else {
1716
+ toolNames = allToolNames;
1717
+ skillNames = allSkillNames;
1718
+ scriptNames = allScriptNames;
1719
+ }
1720
+
1721
+ const autoEnable = options.autoEnable !== false;
1722
+ const overwrite = Boolean(options.overwrite);
1723
+
1724
+ let importResult: any;
1725
+ try {
1726
+ importResult = await importRemoteArtifacts(
1727
+ db,
1728
+ groupId,
1729
+ manifest,
1730
+ { toolNames, skillNames, scriptNames },
1731
+ {
1732
+ autoEnable,
1733
+ overwrite,
1734
+ fetchFn,
1735
+ siteSlug: options.siteSlug,
1736
+ targetSubdir: options.targetSubdir,
1737
+ },
1738
+ );
1739
+ } catch (err: any) {
1740
+ const errorMsg = `Import failed: ${err instanceof Error ? err.message : String(err)}`;
1741
+ if (!options.quiet) {
1742
+ console.error(`Error: ${errorMsg}`);
1743
+ }
1744
+ process.exitCode = 1;
1745
+ return { success: false, error: errorMsg };
1746
+ }
1747
+
1748
+ const importedTools = importResult.tools.filter(
1749
+ (t: any) => t.status === "imported",
1750
+ );
1751
+ const importedSkills = importResult.skills.filter(
1752
+ (s: any) => s.status === "imported",
1753
+ );
1754
+ const importedScripts = importResult.scripts.filter(
1755
+ (sc: any) => sc.status === "imported",
1756
+ );
1757
+
1758
+ const skippedTools = importResult.tools.filter(
1759
+ (t: any) => t.status === "skipped",
1760
+ );
1761
+ const skippedSkills = importResult.skills.filter(
1762
+ (s: any) => s.status === "skipped",
1763
+ );
1764
+ const skippedScripts = importResult.scripts.filter(
1765
+ (sc: any) => sc.status === "skipped",
1766
+ );
1767
+
1768
+ const failedTools = importResult.tools.filter(
1769
+ (t: any) => t.status === "failed",
1770
+ );
1771
+ const failedSkills = importResult.skills.filter(
1772
+ (s: any) => s.status === "failed",
1773
+ );
1774
+ const failedScripts = importResult.scripts.filter(
1775
+ (sc: any) => sc.status === "failed",
1776
+ );
1777
+
1778
+ const importedCount =
1779
+ importedTools.length + importedSkills.length + importedScripts.length;
1780
+ const skippedCount =
1781
+ skippedTools.length + skippedSkills.length + skippedScripts.length;
1782
+ const failedCount =
1783
+ failedTools.length + failedSkills.length + failedScripts.length;
1784
+
1785
+ if (!options.quiet) {
1786
+ console.log("\nImporting artifacts:");
1787
+ for (const item of [
1788
+ ...importResult.tools,
1789
+ ...importResult.skills,
1790
+ ...importResult.scripts,
1791
+ ]) {
1792
+ const type = importResult.tools.includes(item)
1793
+ ? "tool"
1794
+ : importResult.skills.includes(item)
1795
+ ? "skill"
1796
+ : "script";
1797
+ if (item.status === "imported") {
1798
+ console.log(` [${type}] ${item.name} -> ${item.path} (imported)`);
1799
+ } else if (item.status === "skipped") {
1800
+ console.log(
1801
+ ` [${type}] ${item.name} -> ${item.path} (skipped, already exists - use --overwrite to replace)`,
1802
+ );
1803
+ } else {
1804
+ console.log(
1805
+ ` [${type}] ${item.name} (failed: ${item.error || "unknown error"})`,
1806
+ );
1807
+ }
1808
+ }
1809
+
1810
+ if (failedCount > 0) {
1811
+ console.log(
1812
+ `\nCompleted with ${failedCount} error${failedCount === 1 ? "" : "s"}. Successfully imported ${importedCount} items, ${skippedCount} skipped.`,
1813
+ );
1814
+ } else {
1815
+ console.log(
1816
+ `\nSuccessfully imported ${importedCount} item${importedCount === 1 ? "" : "s"} (${importedTools.length} tool${importedTools.length === 1 ? "" : "s"}, ${importedSkills.length} skill${importedSkills.length === 1 ? "" : "s"}, ${importedScripts.length} script${importedScripts.length === 1 ? "" : "s"}).`,
1817
+ );
1818
+ }
1819
+ }
1820
+
1821
+ const success = failedCount === 0 || importedCount > 0;
1822
+ if (!success) {
1823
+ process.exitCode = 1;
1824
+ }
1825
+
1826
+ return {
1827
+ success,
1828
+ manifest,
1829
+ siteUrl,
1830
+ manifestUrl,
1831
+ result: importResult,
1832
+ importedCount,
1833
+ skippedCount,
1834
+ failedCount,
1835
+ };
1836
+ }
1837
+
1604
1838
  /**
1605
1839
  * Main dispatcher for the `agent` command group.
1606
1840
  */
@@ -1714,9 +1948,25 @@ export async function runAgentCommand(
1714
1948
  return await runAgentModel(subaction, modelId, actualOptions);
1715
1949
  }
1716
1950
 
1951
+ case "import": {
1952
+ const url = actualArgs[0];
1953
+ if (!url) {
1954
+ console.error(
1955
+ "Error: Discovery URL or site URL required. Usage: shadow-claw agent import <url> [options]",
1956
+ );
1957
+ process.exitCode = 1;
1958
+ return {
1959
+ success: false,
1960
+ error:
1961
+ "Discovery URL or site URL required. Usage: shadow-claw agent import <url> [options]",
1962
+ };
1963
+ }
1964
+ return await runAgentImport(url, actualOptions);
1965
+ }
1966
+
1717
1967
  default: {
1718
1968
  console.log(
1719
- "Usage: shadow-claw agent <init|model|skills|tools|tool|skill|run> [args...]",
1969
+ "Usage: shadow-claw agent <init|model|skills|tools|tool|skill|import|run> [args...]",
1720
1970
  );
1721
1971
  }
1722
1972
  }
@@ -2,8 +2,11 @@ import fs from "node:fs";
2
2
  import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
- import { describe, it, expect, beforeEach, afterEach } from "@jest/globals";
6
- import { computeSha256, generateSkillsIndex } from "./skills-index.js";
5
+ import {
6
+ computeSha256,
7
+ generateSkillsIndex,
8
+ runSkillsIndexCommand,
9
+ } from "./skills-index.js";
7
10
 
8
11
  describe("skills-index command and generator", () => {
9
12
  let tempDir: string;
@@ -231,4 +234,83 @@ metadata:
231
234
  "../../.agents/skills/main/skill-creator/SKILL.md",
232
235
  );
233
236
  });
237
+
238
+ it("does not include bundled skills by default in runSkillsIndexCommand for an external directory", async () => {
239
+ const mockProject = path.join(tempDir, "external-project");
240
+ const toolDir = path.join(mockProject, ".agents", "tools", "main");
241
+ await mkdir(toolDir, { recursive: true });
242
+ await writeFile(
243
+ path.join(toolDir, "get_weather.json"),
244
+ JSON.stringify({ name: "get_weather", description: "Get weather" }),
245
+ "utf8",
246
+ );
247
+
248
+ const res = await runSkillsIndexCommand(mockProject, { write: true });
249
+ expect(res.tools).toHaveLength(1);
250
+ expect(res.tools[0].name).toBe("get_weather");
251
+ expect(res.skills).toHaveLength(0);
252
+
253
+ // Verify .agents/skills was not created with phantom skill-creator
254
+ const phantomSkillPath = path.join(
255
+ mockProject,
256
+ ".agents",
257
+ "skills",
258
+ "main",
259
+ "skill-creator",
260
+ );
261
+ expect(fs.existsSync(phantomSkillPath)).toBe(false);
262
+
263
+ // Verify written index.json matches
264
+ const written = JSON.parse(
265
+ await fs.promises.readFile(
266
+ path.join(mockProject, ".well-known", "agent-skills", "index.json"),
267
+ "utf8",
268
+ ),
269
+ );
270
+ expect(written.skills).toHaveLength(0);
271
+ expect(written.tools).toHaveLength(1);
272
+ });
273
+
274
+ it("materializes bundled skill files onto disk when bundled: true is passed to runSkillsIndexCommand", async () => {
275
+ const mockToolchain = path.join(tempDir, "custom-toolchain");
276
+ const bundledSkillDir = path.join(
277
+ mockToolchain,
278
+ ".agents",
279
+ "skills",
280
+ "main",
281
+ "skill-creator",
282
+ );
283
+ await mkdir(bundledSkillDir, { recursive: true });
284
+ await writeFile(
285
+ path.join(bundledSkillDir, "SKILL.md"),
286
+ "---\nname: skill-creator\ndescription: Bundled skill creator.\n---\n",
287
+ "utf8",
288
+ );
289
+
290
+ const mockProject = path.join(tempDir, "bundled-project");
291
+ const res = await runSkillsIndexCommand(mockProject, {
292
+ bundled: true,
293
+ toolchainRoot: mockToolchain,
294
+ write: true,
295
+ });
296
+
297
+ expect(res.skills).toHaveLength(1);
298
+ expect(res.skills[0].name).toBe("skill-creator");
299
+ expect(res.skills[0].url).toBe(
300
+ "../../.agents/skills/main/skill-creator/SKILL.md",
301
+ );
302
+
303
+ // Verify the file was actually materialized onto disk so the relative URL is not a 404
304
+ const materializedSkill = path.join(
305
+ mockProject,
306
+ ".agents",
307
+ "skills",
308
+ "main",
309
+ "skill-creator",
310
+ "SKILL.md",
311
+ );
312
+ expect(fs.existsSync(materializedSkill)).toBe(true);
313
+ const content = await fs.promises.readFile(materializedSkill, "utf8");
314
+ expect(content).toContain("skill-creator");
315
+ });
234
316
  });
@@ -6,7 +6,14 @@ import { getProjectRoot } from "../utils/resolve-project-root.js";
6
6
  */
7
7
 
8
8
  import crypto from "node:crypto";
9
- import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
9
+ import {
10
+ cp,
11
+ mkdir,
12
+ readdir,
13
+ readFile,
14
+ stat,
15
+ writeFile,
16
+ } from "node:fs/promises";
10
17
  import path from "node:path";
11
18
  import process from "node:process";
12
19
  import matter from "gray-matter";
@@ -381,6 +388,17 @@ export async function generateSkillsIndex(
381
388
  continue;
382
389
  }
383
390
 
391
+ // Materialize bundled skill files onto disk if writing is enabled and file does not yet exist
392
+ if (options.write !== false) {
393
+ const targetSkillFile = path.join(resolvedRoot, relFromRoot);
394
+ if (!(await pathExists(targetSkillFile))) {
395
+ const sourceSkillDir = path.dirname(fullSkillPath);
396
+ const targetSkillDir = path.dirname(targetSkillFile);
397
+ await mkdir(targetSkillDir, { recursive: true });
398
+ await cp(sourceSkillDir, targetSkillDir, { recursive: true });
399
+ }
400
+ }
401
+
384
402
  discoveredSkills.push({
385
403
  name: skillName,
386
404
  type: "skill-md",
@@ -465,7 +483,7 @@ export async function runSkillsIndexCommand(
465
483
  console.log(`Generating Agent Skills Discovery index for ${targetDir}...`);
466
484
 
467
485
  const result = await generateSkillsIndex(targetDir, {
468
- includeBundled: options.bundled !== false,
486
+ includeBundled: Boolean(options.bundled),
469
487
  ...options,
470
488
  configPath: options.config || options.configPath,
471
489
  });
@@ -544,6 +544,13 @@
544
544
  >
545
545
  🌱 Block Garden Knowledge Hub
546
546
  </button>
547
+ <button
548
+ class="tools__preset-btn"
549
+ data-url="https://xt-ml.github.io/shadow-claw-agent-cli-weather"
550
+ type="button"
551
+ >
552
+ 🌦️ Weather Agent (Open-Meteo)
553
+ </button>
547
554
  </div>
548
555
  </div>
549
556
 
@@ -12,11 +12,14 @@ import {
12
12
  } from "./utils/handleInvoke.js";
13
13
  import { executeNodeTransformersCompletion } from "./tools/node-transformers-executor.js";
14
14
  import { executeNodeLlamafileCompletion } from "./tools/node-llamafile-executor.js";
15
+ import { setHeadlessEvalExecutor } from "./utils/sandboxedEval.js";
16
+ import { nativeEvalExecutor } from "./utils/native-eval-executor.js";
15
17
 
16
18
  // Automatically wire native OS process execution for headless CLI agent
17
19
  setHeadlessBashExecutor(nativeBashExecutor);
18
20
  setNodeTransformersCompletionExecutor(executeNodeTransformersCompletion);
19
21
  setNodeLlamafileCompletionExecutor(executeNodeLlamafileCompletion);
22
+ setHeadlessEvalExecutor(nativeEvalExecutor);
20
23
 
21
24
  export { executeTool } from "./utils/executeTool.js";
22
25
  export { executeToolChain } from "./utils/toolChain.js";
@@ -52,6 +55,16 @@ export {
52
55
  export { TOOL_DEFINITIONS } from "../subsystems/tools/index.js";
53
56
  export { DEFAULT_BUILTIN_PROFILE } from "../subsystems/tools/builtin-profiles.js";
54
57
  export { loadDeclarativeTools } from "../subsystems/tools/declarative.js";
58
+ export {
59
+ resolveDiscoveryUrl,
60
+ fetchDiscoveryManifest,
61
+ } from "../subsystems/tools/remote/discovery.js";
62
+ export {
63
+ importRemoteArtifacts,
64
+ importRemoteTool,
65
+ importRemoteSkill,
66
+ importRemoteScript,
67
+ } from "../subsystems/tools/remote/importArtifacts.js";
55
68
  export {
56
69
  getProvider,
57
70
  getDefaultProvider,
@@ -81,4 +94,5 @@ export { executeNodeTransformersCompletion } from "./tools/node-transformers-exe
81
94
  export { createTransformersRuntimeService } from "../server/services/transformers-runtime.js";
82
95
  export { cleanupAllLlamafileProcesses } from "../server/services/llamafile-manager.js";
83
96
  export { nativeBashExecutor, setHeadlessBashExecutor };
97
+ export { nativeEvalExecutor, setHeadlessEvalExecutor };
84
98
  export type * from "./headless-types.js";
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import type { ShadowClawDatabase } from "../db/types.js";
7
+ import type { ImportResult } from "../subsystems/tools/remote/types.js";
7
8
 
8
9
  /**
9
10
  * Dedicated agent configuration section in shadow-claw.config.json.
@@ -235,3 +236,41 @@ export interface AgentModelResult {
235
236
  error?: string;
236
237
  [key: string]: unknown;
237
238
  }
239
+
240
+ /**
241
+ * Options for runAgentImport.
242
+ */
243
+ export interface AgentImportOptions {
244
+ workspace?: string;
245
+ group?: string;
246
+ databaseDir?: string;
247
+ cacheDir?: string;
248
+ tools?: string | string[];
249
+ skills?: string | string[];
250
+ scripts?: string | string[];
251
+ all?: boolean;
252
+ overwrite?: boolean;
253
+ autoEnable?: boolean;
254
+ targetSubdir?: string;
255
+ siteSlug?: string;
256
+ quiet?: boolean;
257
+ verbose?: boolean;
258
+ fetchFn?: typeof fetch;
259
+ [key: string]: unknown;
260
+ }
261
+
262
+ /**
263
+ * Result returned by runAgentImport.
264
+ */
265
+ export interface AgentImportResult {
266
+ success: boolean;
267
+ manifest?: unknown;
268
+ siteUrl?: string;
269
+ manifestUrl?: string;
270
+ result?: ImportResult;
271
+ importedCount?: number;
272
+ skippedCount?: number;
273
+ failedCount?: number;
274
+ error?: string;
275
+ [key: string]: unknown;
276
+ }
@@ -0,0 +1,36 @@
1
+ import { nativeEvalExecutor } from "./native-eval-executor.js";
2
+ import { sandboxedEval, setHeadlessEvalExecutor } from "./sandboxedEval.js";
3
+
4
+ describe("nativeEvalExecutor", () => {
5
+ it("evaluates javascript expressions in node:vm", async () => {
6
+ const res = await nativeEvalExecutor("return 2 + 2;", 5000, false);
7
+ expect(res).toEqual({ ok: true, value: 4 });
8
+ });
9
+
10
+ it("handles injected $PIPE_DATA and data parameter", async () => {
11
+ const res = await nativeEvalExecutor(
12
+ "const d = JSON.parse(data); return d.val * 3;",
13
+ 5000,
14
+ false,
15
+ JSON.stringify({ val: 10 }),
16
+ );
17
+ expect(res).toEqual({ ok: true, value: 30 });
18
+ });
19
+
20
+ it("integrates with sandboxedEval when Worker is undefined", async () => {
21
+ setHeadlessEvalExecutor(nativeEvalExecutor);
22
+ const origWorker = (globalThis as any).Worker;
23
+ try {
24
+ delete (globalThis as any).Worker;
25
+ const res = await sandboxedEval("return 100 * 2;");
26
+ expect(res).toEqual({ ok: true, value: 200 });
27
+ } finally {
28
+ (globalThis as any).Worker = origWorker;
29
+ }
30
+ });
31
+
32
+ it("handles evaluation timeout gracefully", async () => {
33
+ const res = await nativeEvalExecutor("while(true) {}", 50, false);
34
+ expect(res.ok).toBe(false);
35
+ });
36
+ });