@desplega.ai/agent-swarm 1.173.0 → 2.1.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 (511) hide show
  1. package/README.md +1 -1
  2. package/dist/{acp-adapter-ccm9xrep.js → acp-adapter-kr4abrsk.js} +17 -16
  3. package/dist/{actions-sgeyv08k.js → actions-eh4g6w2r.js} +39 -34
  4. package/dist/{amp-adapter-gpxp3kc9.js → amp-adapter-7h7wdt8q.js} +8 -7
  5. package/dist/{app-km9qnv2f.js → app-7k8s9w5p.js} +18 -14
  6. package/dist/{assistant-5r8tcvvd.js → assistant-wswt2y7p.js} +40 -35
  7. package/dist/{boot-reembed-16rv9843.js → boot-reembed-9apm9qw6.js} +10 -9
  8. package/dist/{boot-reembed-swwzdgd7.js → boot-reembed-whzbdthj.js} +10 -9
  9. package/dist/{boot-scrub-sweep-zkr6vkqb.js → boot-scrub-sweep-ndzmvmgt.js} +11 -10
  10. package/dist/{boot-seeds-d8sp49t2.js → boot-seeds-e9sta0yt.js} +16 -13
  11. package/dist/{claude-adapter-yzzzh8jp.js → claude-adapter-4mqd3r6r.js} +19 -18
  12. package/dist/{claude-managed-adapter-xgxhqrt2.js → claude-managed-adapter-9wswb52m.js} +7 -5
  13. package/dist/{claude-managed-setup-bnmjarjc.js → claude-managed-setup-tpe1effx.js} +6 -4
  14. package/dist/{claude-sdk-session-4whxere4.js → claude-sdk-session-q49ghmh9.js} +12402 -11850
  15. package/dist/{cli-ne7smj1b.js → cli-01xvj1rq.js} +10 -10
  16. package/dist/{cli-nwrdx840.js → cli-035vekcw.js} +3 -3
  17. package/dist/{cli-9xf7fdtp.js → cli-0pd8hzhc.js} +29 -6
  18. package/dist/{cli-tmhsxhb8.js → cli-0veb92a3.js} +28 -6
  19. package/dist/{cli-c3rk1v0x.js → cli-10jesr25.js} +3 -3
  20. package/dist/{cli-6thk4jz3.js → cli-1y7yywmj.js} +6 -6
  21. package/dist/{cli-6r2f7edk.js → cli-20krdhz5.js} +47 -19
  22. package/dist/{cli-rfs4vddv.js → cli-29rja109.js} +39 -20
  23. package/dist/cli-2m1ktfzz.js +17795 -0
  24. package/dist/{cli-na2pxe4f.js → cli-2pwa0q4d.js} +15 -4
  25. package/dist/{cli-7a93cv8c.js → cli-4mengw46.js} +441 -43
  26. package/dist/{cli-94b48jhk.js → cli-5twdke5x.js} +16 -14
  27. package/dist/{cli-7a0sde2x.js → cli-6cagzc96.js} +9 -0
  28. package/dist/cli-6cvfxs8r.js +47 -0
  29. package/dist/{cli-w1apjecb.js → cli-711vwbxn.js} +2 -2
  30. package/dist/{cli-r4fqfpwf.js → cli-78vv7446.js} +50 -27
  31. package/dist/{cli-srqq3gc5.js → cli-7n6s5man.js} +5 -5
  32. package/dist/{cli-m89t9jdf.js → cli-7x0g4sn1.js} +15416 -16024
  33. package/dist/{cli-2bw3w47j.js → cli-8wwxw8a3.js} +1 -1
  34. package/dist/{cli-yatxqynm.js → cli-9ha6hs2d.js} +1 -290
  35. package/dist/{cli-ywm1azg9.js → cli-9jzdj90z.js} +8 -4
  36. package/dist/cli-9traj3dn.js +638 -0
  37. package/dist/{cli-smcrrmhy.js → cli-agzebbd5.js} +9 -9
  38. package/dist/{cli-c5km5znr.js → cli-c8r3kzzb.js} +1 -1
  39. package/dist/cli-cvv9k0hh.js +10143 -0
  40. package/dist/{cli-anzdk0z4.js → cli-d9286m9h.js} +2 -2
  41. package/dist/cli-e3g5mbkz.js +776 -0
  42. package/dist/cli-f527ce74.js +781 -0
  43. package/dist/{cli-gmdajfw5.js → cli-gxzkg1de.js} +5020 -1729
  44. package/dist/{cli-451ks6aw.js → cli-h1arqn31.js} +1 -1
  45. package/dist/cli-hjwmhcg1.js +10622 -0
  46. package/dist/{cli-jpc4r224.js → cli-hqr88ew6.js} +19 -3
  47. package/dist/{cli-h7k0xtry.js → cli-hvqbtna4.js} +4 -1256
  48. package/dist/{cli-cd0hv9nh.js → cli-j67mh2vg.js} +2 -2
  49. package/dist/cli-jta2eyzc.js +619 -0
  50. package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
  51. package/dist/cli-kceah1eh.js +92 -0
  52. package/dist/{cli-d603qf86.js → cli-ksphgh48.js} +10 -8
  53. package/dist/{cli-3nxdmjrc.js → cli-mgz4nbgg.js} +4 -4
  54. package/dist/{cli-saqyage0.js → cli-mz5as5ha.js} +179 -27
  55. package/dist/cli-n2wq4j7m.js +6985 -0
  56. package/dist/{cli-ppy4nnnh.js → cli-n8ndw8j7.js} +2 -2
  57. package/dist/{cli-1w0vj3gw.js → cli-naqzr17j.js} +7 -7
  58. package/dist/{cli-0a7mm3zp.js → cli-nm1p8jfr.js} +25 -21
  59. package/dist/{cli-wrh08zjd.js → cli-p3zbc1rw.js} +5 -18
  60. package/dist/{cli-hsem16hb.js → cli-pe05f8yk.js} +29 -6
  61. package/dist/{cli-5wpw4rab.js → cli-qmzsbkvy.js} +5 -5
  62. package/dist/{cli-bv2etw2y.js → cli-qnm4e9e4.js} +99 -590
  63. package/dist/{cli-1x7da0de.js → cli-qqy4q48b.js} +8 -8
  64. package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
  65. package/dist/{cli-5er5gjbm.js → cli-r35rw5de.js} +15 -15
  66. package/dist/{cli-v3zhnxsb.js → cli-r610p9bs.js} +2 -2
  67. package/dist/{cli-7jykv7ef.js → cli-rc6pf5kv.js} +1 -1
  68. package/dist/{cli-x119kdba.js → cli-rdxm77k3.js} +1 -1
  69. package/dist/cli-rhs9pz33.js +294 -0
  70. package/dist/{cli-0p3enk35.js → cli-s69nfyeh.js} +9 -1
  71. package/dist/{cli-0kxf81jj.js → cli-sawv63e8.js} +2 -2
  72. package/dist/{cli-e3pmyt39.js → cli-sg19gbf0.js} +1 -1
  73. package/dist/{cli-d161g69q.js → cli-shk7r5cs.js} +57 -6
  74. package/dist/{cli-7pz3b7x5.js → cli-sksetb2q.js} +36 -1
  75. package/dist/{cli-014vns2y.js → cli-stfs0k5s.js} +208 -525
  76. package/dist/{cli-db3m6zdt.js → cli-t02z59f2.js} +1 -1
  77. package/dist/{cli-b07hr3bt.js → cli-txqb9s38.js} +2 -2
  78. package/dist/{cli-s8z6x4b4.js → cli-v7g551te.js} +225 -113
  79. package/dist/{cli-rfz2ve55.js → cli-vgqzp4em.js} +3341 -955
  80. package/dist/{cli-7avy4v8w.js → cli-w1q7h2r2.js} +5 -3
  81. package/dist/{cli-rcp5aqdw.js → cli-w7m81tb7.js} +1 -1
  82. package/dist/cli-wfnemr1t.js +86 -0
  83. package/dist/cli-wmgrkscj.js +23254 -0
  84. package/dist/{cli-qm1448f9.js → cli-wms6x9db.js} +8 -8
  85. package/dist/{cli-tadfgd0t.js → cli-x1r545x8.js} +33 -4
  86. package/dist/{cli-xc7p7s3g.js → cli-xczsgshn.js} +6 -6
  87. package/dist/{cli-fmhj1ehj.js → cli-y3167wt1.js} +882 -271
  88. package/dist/{cli-r0byd61h.js → cli-yjmqpxt3.js} +17 -16
  89. package/dist/{cli-81r1wjyv.js → cli-z780ec7c.js} +1 -1
  90. package/dist/{cli-phcqp1b9.js → cli-zm9t2vde.js} +2 -2
  91. package/dist/cli.js +34 -49
  92. package/dist/{codex-adapter-r3dn4fp5.js → codex-adapter-4z16v9n0.js} +15 -14
  93. package/dist/{codex-login-841kv5fk.js → codex-login-pkepy2j1.js} +3 -2
  94. package/dist/{codex-session-runner-fwt793h4.js → codex-session-runner-rvms9y2x.js} +15 -14
  95. package/dist/{commands-z8fq9qna.js → commands-2a9rtyat.js} +11 -10
  96. package/dist/{controller-hq5ggpcp.js → controller-3k3madbn.js} +5 -4
  97. package/dist/{controllers-connect-3mf2btyj.js → controllers-connect-v9yn8zb9.js} +42 -37
  98. package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-98gbhyzn.js} +7 -6
  99. package/dist/{db-qfpq4bt7.js → db-eywwsnjw.js} +30 -9
  100. package/dist/{db-yyhqqrbf.js → db-ncypk9np.js} +12 -11
  101. package/dist/{devin-adapter-xgtj680j.js → devin-adapter-zeq8b9gz.js} +3 -2
  102. package/dist/{dispatcher-x0x7qfcq.js → dispatcher-w94kc575.js} +38 -33
  103. package/dist/{docker-f503k9p0.js → docker-b1e4cqab.js} +3 -2
  104. package/dist/{dsh-adapter-34etvb5m.js → dsh-adapter-47ztej36.js} +7 -6
  105. package/dist/{e2b-66xra4az.js → e2b-bbt8ey45.js} +4 -3
  106. package/dist/{e2b-xwhyj8cw.js → e2b-dp804sen.js} +4 -3
  107. package/dist/{event-bus-vjtv82gm.js → event-bus-j2jdg3qb.js} +3 -2
  108. package/dist/{external-channel-h1fjecwe.js → external-channel-zrkg8ar9.js} +11 -10
  109. package/dist/{google-generative-ai-szdxbpqg.js → google-generative-ai-g8db7x0f.js} +4 -2
  110. package/dist/{google-vertex-qzw3p36d.js → google-vertex-r2692q0r.js} +4 -2
  111. package/dist/{grok-adapter-e7mr287e.js → grok-adapter-s9ncp9vy.js} +18 -17
  112. package/dist/{handlers-znb9n7d4.js → handlers-kqftvjgw.js} +41 -36
  113. package/dist/{heartbeat-bm4qkn8d.js → heartbeat-9q96p8v9.js} +43 -38
  114. package/dist/{hook-254r4mza.js → hook-56qe10dp.js} +11 -23
  115. package/dist/{hook-xaa3q80s.js → hook-yy6rw42k.js} +12 -11
  116. package/dist/{http-3dgrgp8y.js → http-r2dnb8f8.js} +2411 -275
  117. package/dist/{index-fx3jqq1e.js → index-3gtmkhc6.js} +38 -33
  118. package/dist/{index-bdsqa799.js → index-3k5hkz7r.js} +43 -38
  119. package/dist/index-52j1aja4.js +3465 -0
  120. package/dist/{index-g2byveez.js → index-5e101240.js} +38 -33
  121. package/dist/{index-2ybk7khy.js → index-5wyfjc2c.js} +845 -683
  122. package/dist/index-9gw58n4k.js +193 -0
  123. package/dist/index-9j87mcm8.js +56305 -0
  124. package/dist/index-bsmcresk.js +79424 -0
  125. package/dist/index-mh85qhec.js +475 -0
  126. package/dist/{index-jd7sejm2.js → index-vxvhxxbn.js} +10 -9
  127. package/dist/index-we27kd31.js +12810 -0
  128. package/dist/{keepalive-6ywhpja1.js → keepalive-psj6j54c.js} +12 -11
  129. package/dist/{lead-cw45n21s.js → lead-hajz4688.js} +61 -56
  130. package/dist/{maintenance-ss1a1ge9.js → maintenance-ps02sb5m.js} +10 -9
  131. package/dist/{node-fkajaq96.js → node-mwb5r697.js} +2 -1
  132. package/dist/{oauth-refresh-sweep-3c6f8vyy.js → oauth-refresh-sweep-0284h93b.js} +11 -10
  133. package/dist/{onboard-8hzdhc1k.js → onboard-rbf6bg01.js} +11 -3
  134. package/dist/{opencode-adapter-qbchbcfz.js → opencode-adapter-13dcatgq.js} +18 -13
  135. package/dist/{otel-impl-e6mnvc4v.js → otel-impl-zvqcbhp7.js} +242 -868
  136. package/dist/{pi-mono-adapter-3c9g9snh.js → pi-mono-adapter-xxy47q4v.js} +244 -106
  137. package/dist/{pricing-refresh-gedkjdw0.js → pricing-refresh-kefx9v3t.js} +12 -11
  138. package/dist/{rbac-roles-dt4vh1yy.js → rbac-roles-3m36rxm2.js} +11 -10
  139. package/dist/{rbac-roles-eszdg6pm.js → rbac-roles-z76xv0hq.js} +12 -11
  140. package/dist/{render-v2-907cy6sa.js → render-v2-wz73bs25.js} +18 -14
  141. package/dist/{resume-42stcaz0.js → resume-k4wzge8w.js} +25 -21
  142. package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
  143. package/dist/{setup-adbc1c10.js → setup-2fp2pypp.js} +2 -2
  144. package/dist/{virtual-modules-rtmt494q.js → virtual-modules-9gkxqrap.js} +6 -5
  145. package/dist/{work-objects-wpj8ck95.js → work-objects-6k0ywz3m.js} +41 -36
  146. package/dist/{worker-bzdra9rf.js → worker-5v3rxsdh.js} +61 -56
  147. package/dist/{x-qfmygqqv.js → x-4mtdeym1.js} +3 -2
  148. package/openapi.json +1279 -329
  149. package/package.json +15 -15
  150. package/src/agentmail/handlers.ts +21 -11
  151. package/src/apps/definition.ts +16 -0
  152. package/src/azure-devops/api.ts +52 -2
  153. package/src/azure-devops/boards-sync.ts +50 -20
  154. package/src/azure-devops/index.ts +1 -0
  155. package/src/azure-devops/outbound.ts +95 -0
  156. package/src/be/boot-seeds.ts +11 -0
  157. package/src/be/config-refs.ts +60 -0
  158. package/src/be/db/model-profiles.ts +315 -0
  159. package/src/be/db/page-versions.ts +15 -0
  160. package/src/be/db/runtime.ts +35 -8
  161. package/src/be/db/skill-name-resolution.ts +36 -0
  162. package/src/be/db/tasks/read.ts +237 -0
  163. package/src/be/db/tasks/write.ts +7 -0
  164. package/src/be/db.ts +87 -36
  165. package/src/be/migrations/215_swarm_config_export_to_env.sql +34 -0
  166. package/src/be/migrations/216_task_resolved_effort.sql +5 -0
  167. package/src/be/migrations/217_chat_sdk_state.sql +104 -0
  168. package/src/be/migrations/219_relabel_pi_context_compactions.sql +22 -0
  169. package/src/be/migrations/220_model_profiles.sql +137 -0
  170. package/src/be/model-catalog-store.ts +5 -1
  171. package/src/be/model-tier-resolution.ts +155 -10
  172. package/src/be/model-validation.ts +12 -2
  173. package/src/be/models-catalog.ts +26 -1
  174. package/src/be/modelsdev-cache.json +198 -63
  175. package/src/be/modelsdev-cache.ts +9 -1
  176. package/src/be/script-credential-broker.ts +39 -2
  177. package/src/be/scripts/run-saved.ts +1 -0
  178. package/src/be/scripts/typecheck.ts +22 -8
  179. package/src/be/secret-registry.ts +6 -2
  180. package/src/be/seed/runner.ts +13 -1
  181. package/src/be/seed/state-db.ts +5 -0
  182. package/src/be/seed/types.ts +6 -0
  183. package/src/be/seed-model-profiles.ts +41 -0
  184. package/src/be/seed-pricing.ts +19 -2
  185. package/src/be/seed-scripts/catalog/heartbeat-jev-gate.ts +368 -0
  186. package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
  187. package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
  188. package/src/be/seed-scripts/index.ts +9 -0
  189. package/src/be/seed-skills/bundled-files.generated.json +0 -18
  190. package/src/be/seed-skills/index.ts +104 -3
  191. package/src/be/swarm-config-guard.ts +53 -0
  192. package/src/cli.tsx +0 -16
  193. package/src/commands/onboard/non-interactive.ts +4 -2
  194. package/src/commands/provider-credentials.ts +52 -6
  195. package/src/commands/runner.ts +193 -71
  196. package/src/e2b/env.ts +5 -0
  197. package/src/extensions/catalog.generated.json +17 -0
  198. package/src/extensions/contract-types.generated.ts +1 -1
  199. package/src/extensions/contract.ts +45 -0
  200. package/src/extensions/dispatcher.ts +7 -0
  201. package/src/extensions/loader.ts +10 -5
  202. package/src/extensions/post-bridge.ts +40 -0
  203. package/src/github/handlers.ts +11 -4
  204. package/src/heartbeat/heartbeat.ts +2 -1
  205. package/src/heartbeat/templates.ts +8 -5
  206. package/src/hooks/hook.ts +0 -26
  207. package/src/http/agents.ts +49 -2
  208. package/src/http/all-routes.ts +1 -1
  209. package/src/http/apps.ts +30 -3
  210. package/src/http/config.ts +62 -19
  211. package/src/http/core.ts +8 -0
  212. package/src/http/index.ts +25 -3
  213. package/src/http/integrations.ts +88 -1
  214. package/src/http/mcp.ts +15 -5
  215. package/src/http/model-profiles.ts +303 -0
  216. package/src/http/page-proxy.ts +2 -4
  217. package/src/http/pages-public.ts +13 -7
  218. package/src/http/pages.ts +3 -13
  219. package/src/http/poll.ts +42 -71
  220. package/src/http/requested-by.ts +54 -0
  221. package/src/http/script-runs.ts +1 -1
  222. package/src/http/scripts.ts +26 -2
  223. package/src/http/stats.ts +1 -30
  224. package/src/http/tasks.ts +79 -5
  225. package/src/http/utils.ts +14 -0
  226. package/src/http/webhooks.ts +160 -3
  227. package/src/http/x.ts +1 -0
  228. package/src/integrations/chat-sdk/adapters.ts +129 -0
  229. package/src/integrations/chat-sdk/attachments.ts +232 -0
  230. package/src/integrations/chat-sdk/background.ts +36 -0
  231. package/src/integrations/chat-sdk/commands.ts +194 -0
  232. package/src/integrations/chat-sdk/config.ts +198 -0
  233. package/src/integrations/chat-sdk/discord-api.ts +63 -0
  234. package/src/integrations/chat-sdk/discord-gateway.ts +185 -0
  235. package/src/integrations/chat-sdk/handlers.ts +28 -0
  236. package/src/integrations/chat-sdk/inbound.ts +426 -0
  237. package/src/integrations/chat-sdk/limits.ts +48 -0
  238. package/src/integrations/chat-sdk/logger.ts +81 -0
  239. package/src/integrations/chat-sdk/outbound.ts +123 -0
  240. package/src/integrations/chat-sdk/relay.ts +172 -0
  241. package/src/integrations/chat-sdk/router.ts +169 -0
  242. package/src/integrations/chat-sdk/runtime.ts +351 -0
  243. package/src/integrations/chat-sdk/schema.ts +245 -0
  244. package/src/integrations/chat-sdk/state.ts +243 -0
  245. package/src/integrations/chat-sdk/status.ts +244 -0
  246. package/src/integrations/chat-sdk/steering.ts +58 -0
  247. package/src/integrations/chat-sdk/teams-origin.ts +77 -0
  248. package/src/integrations/chat-sdk/telegram-api.ts +56 -0
  249. package/src/integrations/chat-sdk/templates.ts +16 -0
  250. package/src/integrations/chat-sdk/test-hooks.ts +37 -0
  251. package/src/integrations/chat-sdk/thread-buffer.ts +100 -0
  252. package/src/integrations/chat-sdk/typing.ts +65 -0
  253. package/src/{artifact-sdk → pages}/feedback-overlay.ts +65 -6
  254. package/src/pages/version.ts +4 -5
  255. package/src/prompts/base-prompt.ts +111 -45
  256. package/src/providers/acp-target-catalog.ts +6 -0
  257. package/src/providers/acp-targets.ts +56 -3
  258. package/src/providers/amp-adapter.ts +1 -1
  259. package/src/providers/claude-adapter.ts +1 -1
  260. package/src/providers/claude-flag-settings.ts +17 -5
  261. package/src/providers/claude-managed-adapter.ts +1 -1
  262. package/src/providers/claude-session-events.ts +6 -1
  263. package/src/providers/dsh-adapter.ts +1 -1
  264. package/src/providers/opencode-adapter.ts +243 -26
  265. package/src/providers/opencode-swarm-plugin.ts +31 -0
  266. package/src/providers/pi-mono-adapter.ts +398 -153
  267. package/src/providers/pi-mono-extension.ts +17 -3
  268. package/src/providers/pricing-sources.md +33 -2
  269. package/src/providers/reasoning-effort.ts +37 -0
  270. package/src/providers/types.ts +6 -2
  271. package/src/rbac/legacy-policy.ts +16 -2
  272. package/src/rbac/permissions.ts +26 -0
  273. package/src/scheduler/deferred-task-waits.ts +18 -5
  274. package/src/scheduler/scheduler.ts +11 -3
  275. package/src/scripts-runtime/executors/quickjs-prelude.ts +75 -6
  276. package/src/scripts-runtime/executors/quickjs-runner.ts +85 -8
  277. package/src/scripts-runtime/executors/quickjs-web-shims.ts +1041 -0
  278. package/src/scripts-runtime/extract-args-schema.ts +9 -8
  279. package/src/scripts-runtime/loader.ts +19 -7
  280. package/src/scripts-runtime/quickjs-compat.ts +464 -0
  281. package/src/scripts-runtime/sdk-allowlist.ts +12 -6
  282. package/src/scripts-runtime/swarm-sdk.ts +0 -11
  283. package/src/scripts-runtime/types/stdlib.d.ts +39 -15
  284. package/src/scripts-runtime/types/swarm-extension.d.ts +66 -1
  285. package/src/scripts-runtime/types/swarm-sdk.d.ts +39 -15
  286. package/src/server.ts +62 -19
  287. package/src/slack/blocks.ts +4 -2
  288. package/src/slack/event-dedup.ts +1 -1
  289. package/src/slack/reaction-shortcode.ts +14 -0
  290. package/src/slack/render-v2.ts +12 -7
  291. package/src/slack/responses.ts +5 -4
  292. package/src/slack/thread-buffer.ts +41 -22
  293. package/src/tasks/additive-buffer.ts +4 -2
  294. package/src/tasks/context-key.ts +60 -1
  295. package/src/tasks/model-pin.ts +67 -0
  296. package/src/tasks/worker-follow-up.ts +15 -33
  297. package/src/tests/acp-adapter.test.ts +42 -0
  298. package/src/tests/agentmail-filters.test.ts +47 -0
  299. package/src/tests/api-drain-worker-e2e.test.ts +1 -6
  300. package/src/tests/apps-spike2.test.ts +35 -0
  301. package/src/tests/azure-devops-outbound.test.ts +420 -0
  302. package/src/tests/base-prompt.test.ts +58 -54
  303. package/src/tests/chat-sdk-commands.test.ts +191 -0
  304. package/src/tests/chat-sdk-config.test.ts +334 -0
  305. package/src/tests/chat-sdk-discord-api.test.ts +68 -0
  306. package/src/tests/chat-sdk-discord-gateway.test.ts +252 -0
  307. package/src/tests/chat-sdk-discord-webhook.test.ts +126 -0
  308. package/src/tests/chat-sdk-fields-sync.test.ts +50 -0
  309. package/src/tests/chat-sdk-harness.ts +146 -0
  310. package/src/tests/chat-sdk-inbound.test.ts +363 -0
  311. package/src/tests/chat-sdk-relay.test.ts +470 -0
  312. package/src/tests/chat-sdk-reload.test.ts +128 -0
  313. package/src/tests/chat-sdk-shutdown.test.ts +220 -0
  314. package/src/tests/chat-sdk-slack.test.ts +268 -0
  315. package/src/tests/chat-sdk-state.test.ts +163 -0
  316. package/src/tests/chat-sdk-status-route.test.ts +382 -0
  317. package/src/tests/chat-sdk-teams.test.ts +277 -0
  318. package/src/tests/chat-sdk-tools.test.ts +520 -0
  319. package/src/tests/chat-sdk-typing.test.ts +120 -0
  320. package/src/tests/chat-sdk-webhook-telegram.test.ts +200 -0
  321. package/src/tests/claude-adapter-binary.test.ts +9 -4
  322. package/src/tests/claude-flag-settings.test.ts +11 -3
  323. package/src/tests/claude-managed-adapter.test.ts +1 -1
  324. package/src/tests/claude-sdk-lifecycle.test.ts +9 -3
  325. package/src/tests/claude-thinking-tokens.test.ts +57 -0
  326. package/src/tests/claude-worker-parity.test.ts +2 -7
  327. package/src/tests/config-env-export-chat-sdk.test.ts +11 -0
  328. package/src/tests/config-refs.test.ts +66 -0
  329. package/src/tests/context-key.test.ts +19 -0
  330. package/src/tests/cors-allowlist.test.ts +25 -0
  331. package/src/tests/create-page-tool.test.ts +18 -0
  332. package/src/tests/credential-check.test.ts +139 -0
  333. package/src/tests/credentials.test.ts +21 -0
  334. package/src/tests/db-runtime.test.ts +61 -0
  335. package/src/tests/db-task-read-contract.test.ts +22 -0
  336. package/src/tests/db-task-write-contract.test.ts +11 -0
  337. package/src/tests/deferred-task-wake.test.ts +78 -1
  338. package/src/tests/docs-index-footprint.test.js +3 -3
  339. package/src/tests/dsh-adapter.test.ts +1 -1
  340. package/src/tests/enabled-capabilities.test.ts +20 -0
  341. package/src/tests/entrypoint-api-readiness.test.ts +14 -8
  342. package/src/tests/entrypoint-config-env-export.test.ts +5 -2
  343. package/src/tests/entrypoint-export-to-env.test.ts +78 -0
  344. package/src/tests/env-flag.test.ts +1 -0
  345. package/src/tests/explicit-model-validation.test.ts +105 -1
  346. package/src/tests/extensions-catalog.test.ts +1 -0
  347. package/src/tests/extensions-example-comms-adherence-gate.test.ts +413 -0
  348. package/src/tests/extensions-example-route-chat-channel.test.ts +154 -0
  349. package/src/tests/extensions-loader.test.ts +15 -0
  350. package/src/tests/extensions-post-bridge.test.ts +51 -0
  351. package/src/tests/fetch-resolved-env.test.ts +128 -0
  352. package/src/tests/fixtures/chat-sdk/telegram-private-text.json +10 -0
  353. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/hooks.ts +27 -0
  354. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/manifest.json +9 -0
  355. package/src/tests/fixtures/openai-compatible-registry-extension.child.ts +146 -0
  356. package/src/tests/fixtures/runner-legacy-resume-prompt.ts +122 -0
  357. package/src/tests/github-handlers.test.ts +35 -0
  358. package/src/tests/harness-provider-resolution.test.ts +12 -0
  359. package/src/tests/heartbeat-checklist.test.ts +3 -2
  360. package/src/tests/heartbeat-jev-gate-credentials.test.ts +168 -0
  361. package/src/tests/heartbeat-jev-gate.test.ts +385 -0
  362. package/src/tests/hook-runtime-close.test.ts +0 -2
  363. package/src/tests/http-api-integration.test.ts +0 -25
  364. package/src/tests/migration-046-budgets.test.ts +17 -16
  365. package/src/tests/migration-217-chat-sdk-state.test.ts +113 -0
  366. package/src/tests/model-profile-backcompat.test.ts +327 -0
  367. package/src/tests/model-profile-providers.test.ts +302 -0
  368. package/src/tests/model-profile-runtime-secrets.test.ts +223 -0
  369. package/src/tests/model-profiles-api.test.ts +614 -0
  370. package/src/tests/model-tier-resolution.test.ts +93 -0
  371. package/src/tests/models-catalog.test.ts +42 -1
  372. package/src/tests/openai-compatible-providers.test.ts +134 -0
  373. package/src/tests/openai-compatible-registry-extension.test.ts +42 -0
  374. package/src/tests/opencode-adapter.test.ts +216 -2
  375. package/src/tests/opencode-helmcode-sdk.test.ts +201 -0
  376. package/src/tests/opencode-shell-env.test.ts +145 -0
  377. package/src/tests/openrouter-base-url.test.ts +101 -151
  378. package/src/tests/page-proxy.test.ts +1 -1
  379. package/src/tests/pages-browser-sdk.test.ts +76 -0
  380. package/src/tests/pages-feedback-keys.test.ts +121 -0
  381. package/src/tests/pages-feedback.test.ts +4 -1
  382. package/src/tests/paused-tasks-requested-by.test.ts +102 -0
  383. package/src/tests/pi-context-compaction.test.ts +193 -0
  384. package/src/tests/pi-custom-provider-fake-server.test.ts +442 -0
  385. package/src/tests/pi-mono-adapter.test.ts +385 -5
  386. package/src/tests/pi-shell-env.test.ts +170 -0
  387. package/src/tests/profile-claim.test.ts +269 -0
  388. package/src/tests/prompt-template-remaining.test.ts +35 -0
  389. package/src/tests/quickjs-compat.test.ts +213 -0
  390. package/src/tests/rbac-engine.test.ts +32 -3
  391. package/src/tests/runner-exit-after-tasks.test.ts +0 -4
  392. package/src/tests/runner-legacy-resume-prompt.test.ts +64 -0
  393. package/src/tests/script-connections.test.ts +6 -0
  394. package/src/tests/script-executor-conformance.test.ts +252 -1
  395. package/src/tests/script-executor-quickjs.test.ts +96 -0
  396. package/src/tests/script-runs-http.test.ts +89 -0
  397. package/src/tests/scripts-http.test.ts +217 -1
  398. package/src/tests/scripts-only-gating.test.ts +2 -3
  399. package/src/tests/scripts-runtime.test.ts +58 -40
  400. package/src/tests/secret-scrubber.test.ts +96 -0
  401. package/src/tests/seed-model-profiles.test.ts +174 -0
  402. package/src/tests/seed-scripts.test.ts +2 -2
  403. package/src/tests/seed-skills-bundled-files.test.ts +45 -53
  404. package/src/tests/seed-skills-retire.test.ts +182 -0
  405. package/src/tests/send-task-requester-gate.test.ts +58 -0
  406. package/src/tests/session-cost-recompute-unit.test.ts +46 -0
  407. package/src/tests/skill-get-name-resolution.test.ts +137 -0
  408. package/src/tests/skill-invocations.test.ts +4 -2
  409. package/src/tests/slack-blocks.test.ts +36 -0
  410. package/src/tests/slack-inline-output.test.ts +14 -0
  411. package/src/tests/slack-reaction-config.test.ts +20 -0
  412. package/src/tests/slack-render-v2.test.ts +37 -0
  413. package/src/tests/slack-thread-buffer.test.ts +98 -1
  414. package/src/tests/sonnet-5-5-registry.test.ts +9 -1
  415. package/src/tests/swarm-config-export-to-env.test.ts +267 -0
  416. package/src/tests/swarm-config-reserved-keys.test.ts +16 -0
  417. package/src/tests/swarm-diff.test.ts +1 -1
  418. package/src/tests/task-model-effort-rerun.test.ts +164 -0
  419. package/src/tests/task-session-prompts.test.ts +132 -0
  420. package/src/tests/task-tool-manifest.test.ts +14 -0
  421. package/src/tests/task-tools-ctx.test.ts +2 -0
  422. package/src/tests/tool-annotations.test.ts +6 -6
  423. package/src/tests/ui-logs-parser.test.ts +43 -0
  424. package/src/tests/used-by-route.test.ts +6 -0
  425. package/src/tests/vercel-ai-gateway.test.ts +149 -0
  426. package/src/tests/workflow-llm.test.ts +61 -0
  427. package/src/tests/workflow-swarm-script.test.ts +38 -0
  428. package/src/tests/workflow-wait-filter.test.ts +5 -3
  429. package/src/tools/chat-access.ts +41 -0
  430. package/src/tools/chat-open-dm.ts +59 -0
  431. package/src/tools/chat-post.ts +74 -0
  432. package/src/tools/chat-read-thread.ts +100 -0
  433. package/src/tools/chat-reply.ts +75 -0
  434. package/src/tools/create-page.ts +3 -13
  435. package/src/tools/model-catalog/index.ts +2 -1
  436. package/src/tools/model-profiles/list-model-profiles.ts +54 -0
  437. package/src/tools/register-chat-channel.ts +139 -0
  438. package/src/tools/request-human-input.ts +11 -3
  439. package/src/tools/script-apis.ts +5 -1
  440. package/src/tools/script-upsert.ts +4 -1
  441. package/src/tools/send-task.ts +48 -1
  442. package/src/tools/skills/skill-get.ts +17 -9
  443. package/src/tools/swarm-config/get-config.ts +2 -1
  444. package/src/tools/swarm-config/list-config.ts +2 -1
  445. package/src/tools/swarm-config/set-config.ts +16 -3
  446. package/src/tools/system-one-decide.ts +104 -0
  447. package/src/tools/task-action.ts +22 -1
  448. package/src/tools/tool-config.ts +14 -6
  449. package/src/types.ts +48 -3
  450. package/src/utils/config-env-export.ts +74 -0
  451. package/src/utils/credentials.test.ts +92 -0
  452. package/src/utils/credentials.ts +52 -4
  453. package/src/utils/harness-provider.ts +15 -6
  454. package/src/utils/runtime-model-catalog.ts +11 -1
  455. package/src/utils/secret-scrubber.ts +86 -10
  456. package/src/utils/task-tool-manifest.ts +19 -2
  457. package/src/utils/vcs-repo-match.ts +29 -0
  458. package/src/workflows/executors/raw-llm.ts +8 -3
  459. package/src/workflows/executors/swarm-script.ts +1 -0
  460. package/src/workflows/executors/system-one-decision.ts +21 -11
  461. package/src/workflows/executors/workflow-llm.ts +22 -0
  462. package/src/workflows/index.ts +21 -12
  463. package/templates/extensions/README.md +1 -0
  464. package/templates/extensions/comms-adherence-gate/README.md +88 -0
  465. package/templates/extensions/comms-adherence-gate/hooks.ts +489 -0
  466. package/templates/extensions/comms-adherence-gate/manifest.json +8 -0
  467. package/templates/skills/agent-browser/SKILL.md +1 -1
  468. package/templates/skills/agent-browser/content.md +1 -1
  469. package/templates/skills/apps/SKILL.md +8 -1
  470. package/templates/skills/apps/content.md +8 -1
  471. package/templates/skills/chat-sdk/config.json +14 -0
  472. package/templates/skills/chat-sdk/content.md +84 -0
  473. package/templates/skills/composio-gmail/content.md +7 -0
  474. package/templates/skills/composio-google-docs/content.md +1 -1
  475. package/templates/skills/email-domain-health/config.json +14 -0
  476. package/templates/skills/email-domain-health/content.md +131 -0
  477. package/templates/skills/kv-storage/content.md +2 -2
  478. package/templates/skills/pages/content.md +8 -10
  479. package/templates/skills/swarm-extensions/content.md +2 -0
  480. package/templates/skills/swarm-scripts/SKILL.md +4 -2
  481. package/templates/skills/swarm-scripts/config.json +1 -1
  482. package/templates/skills/swarm-scripts/content.md +4 -2
  483. package/templates/skills/workflow-iterate/content.md +38 -0
  484. package/dist/artifact-eczsknc3.js +0 -3843
  485. package/dist/cli-42qhyvwx.js +0 -33
  486. package/dist/cli-bd957dgb.js +0 -153
  487. package/dist/cli-q51yprfq.js +0 -1104
  488. package/dist/cli-t1ze64h3.js +0 -811
  489. package/dist/cli-tcc4wd5d.js +0 -56471
  490. package/dist/index-9ch1n761.js +0 -1102
  491. package/src/artifact-sdk/index.ts +0 -2
  492. package/src/artifact-sdk/localtunnel.d.ts +0 -20
  493. package/src/artifact-sdk/port.ts +0 -12
  494. package/src/artifact-sdk/server.ts +0 -183
  495. package/src/artifact-sdk/tunnel.ts +0 -19
  496. package/src/commands/artifact.ts +0 -249
  497. package/src/http/ecosystem.ts +0 -77
  498. package/src/tests/artifact-commands.test.ts +0 -92
  499. package/src/tests/artifact-sdk.test.ts +0 -793
  500. package/src/tests/register-service-security.test.ts +0 -194
  501. package/src/tools/list-services.ts +0 -107
  502. package/src/tools/register-service.ts +0 -177
  503. package/src/tools/unregister-service.ts +0 -73
  504. package/src/tools/update-service-status.ts +0 -96
  505. package/templates/skills/artifacts/config.json +0 -14
  506. package/templates/skills/artifacts/content.md +0 -344
  507. package/templates/skills/artifacts/files/examples/approval-flow.ts +0 -34
  508. package/templates/skills/artifacts/files/examples/hono-dashboard.ts +0 -31
  509. package/templates/skills/artifacts/files/examples/multi-artifact.ts +0 -20
  510. package/templates/skills/artifacts/files/examples/static-report.sh +0 -17
  511. /package/src/{artifact-sdk → pages}/browser-sdk.ts +0 -0
@@ -653,9 +653,22 @@ export function createSwarmHooksExtension(config: SwarmHooksConfig): ExtensionFa
653
653
  // === session_compact → PreCompact ===
654
654
  // Re-anchor the model on its task once compaction has summarized the
655
655
  // history. `context` fires before every LLM call, so it cannot carry this.
656
- pi.on("session_compact", async () => {
656
+ pi.on("session_compact", async (event, ctx) => {
657
657
  if (!config.taskId) return;
658
658
 
659
+ // The one real compaction signal: shaped like the claude runner's rows.
660
+ fireAndForget(`${config.apiUrl}/api/tasks/${config.taskId}/context`, {
661
+ method: "POST",
662
+ headers: apiHeaders(config),
663
+ body: JSON.stringify({
664
+ eventType: "compaction",
665
+ sessionId: `pi-${config.taskId}`,
666
+ compactTrigger: event.reason === "manual" ? "manual" : "auto",
667
+ preCompactTokens: event.compactionEntry?.tokensBefore,
668
+ contextTotalTokens: ctx.getContextUsage?.()?.contextWindow,
669
+ }),
670
+ });
671
+
659
672
  const taskDetails = await fetchTaskDetails(config);
660
673
  if (taskDetails) {
661
674
  const reminder = await renderGoalReminder(taskDetails);
@@ -667,14 +680,15 @@ export function createSwarmHooksExtension(config: SwarmHooksConfig): ExtensionFa
667
680
  pi.on("context", async (_event, ctx) => {
668
681
  if (!config.taskId) return undefined;
669
682
 
670
- // Report context usage as a compaction event
683
+ // Per-call usage snapshot: "progress", as the claude/codex runner posts
684
+ // per-turn usage. Only session_compact reports a compaction.
671
685
  const usage = ctx.getContextUsage?.();
672
686
  if (usage) {
673
687
  fireAndForget(`${config.apiUrl}/api/tasks/${config.taskId}/context`, {
674
688
  method: "POST",
675
689
  headers: apiHeaders(config),
676
690
  body: JSON.stringify({
677
- eventType: "compaction",
691
+ eventType: "progress",
678
692
  sessionId: `pi-${config.taskId}`,
679
693
  contextUsedTokens: usage.tokens ?? undefined,
680
694
  contextTotalTokens: usage.contextWindow,
@@ -22,8 +22,8 @@ rate by hand should also update this file.
22
22
  - **Runtime module**: `src/be/models-catalog.ts`; route in
23
23
  `src/http/models-catalog.ts`.
24
24
  - Every successful models.dev fetch in the runtime refresh above also updates
25
- an in-memory slim catalog (openrouter / anthropic / openai / amazon-bedrock
26
- only, picker-relevant fields only). The UI model picker
25
+ an in-memory slim catalog (openrouter / anthropic / openai / amazon-bedrock /
26
+ nan / helmcode only, picker-relevant fields only). The UI model picker
27
27
  (`apps/ui/src/lib/agent-runtime-models.ts` via `useModelsCatalog()`) prefers
28
28
  this over its build-time snapshot, so new models appear without a deploy.
29
29
  - Pinned limited-availability entries (`PINNED_MODELSDEV_ENTRIES`) are
@@ -84,6 +84,25 @@ rate by hand should also update this file.
84
84
  `inputTokens`, clamped to zero, so fresh input is priced separately.
85
85
  Cursor's own models are not in models.dev: see
86
86
  `CURSOR_FIRST_PARTY_PRICING` below.
87
+ - OpenAI-compatible registry providers: NaN models (models.dev `nan`
88
+ section) and Helmcode models (the registry-synthesized `helmcode`
89
+ section, below) → rows under `provider='pi'` and
90
+ `provider='opencode'`, keyed `nan/<id>` and `helmcode/<id>`, at $0 input
91
+ and $0 output. Both providers bill a flat rate per key, so a session's
92
+ marginal cost is $0. `buildModelsDevSeedRows()` projects them, so the rows
93
+ land at boot seed and on refresh, and sessions record $0 with
94
+ `costSource: "pricing-table"` rather than `unpriced`.
95
+
96
+ - **Registry-synthesized `helmcode` section**: Helmcode is not on models.dev.
97
+ Its five agent models (`deepseek-v4-flash`, `glm5.3`, `glm5.3-flash`,
98
+ `qwen3.6`, `gemma4`) live in the provider registry
99
+ (`packages/model-catalog/src/openai-compatible-providers.ts`,
100
+ `catalog: "registry"`). `withRegistryCatalogSections` adds the section,
101
+ at $0, to the snapshot loader (`loadModelsDevCache`), the worker runtime
102
+ catalog and the dashboard bundle, and the live catalog refills it
103
+ (`setLiveModelsCatalog`). Model limits are copied from Helmcode's own
104
+ opencode and pi config pages. Helmcode's resold Claude, GPT and Gemini
105
+ models are not included.
87
106
 
88
107
  - **Snapshot refresh procedure**:
89
108
  - Run `bun run scripts/refresh-modelsdev-pricing.ts` (Phase 2 — adds the
@@ -167,6 +186,18 @@ and heartbeat-classified roots are omitted, and the metrics remain separate.
167
186
  scheduled increase to $3/M input and $15/M output on 2026-09-01. Claude
168
187
  Code's bundled local rate table may be stale-high, so treat its reported USD
169
188
  as advisory and use the server-recomputed row for the canonical total.
189
+ - **Claude 5.5 cache reads:** Opus 5.5 and Sonnet 5.5 cache hits cost 0.05x
190
+ input ($0.20/M and $0.10/M), not the usual 0.1x. Claude Code builds in use
191
+ around 2026-10-09 still priced Sonnet 5.5 hits at $0.20/M, so their
192
+ `harnessCostUsd` runs high by `cacheReadTokens × $0.10/M`.
193
+ - **Claude Haiku 5.5 context tier:** a request whose prompt (input + cache
194
+ reads + cache writes) is over 100k tokens bills every class at 5x: $0.50/M
195
+ input, $0.05/M cache hit, $0.625/M 5m write, $1/M 1h write, $2.50/M output.
196
+ It is the only Claude 5.x model with a long-context tier; the others bill the
197
+ full 1M window at standard rates. The recompute prices Haiku 5.5 at the base
198
+ tier, so sessions with over-100k requests under-count, and the harness total
199
+ is the closer figure for them. Same per-request accounting gap as GPT-5.6
200
+ below.
170
201
  - **GPT-5.6 context tier:** above 272k context, the GPT-5.6 family bills
171
202
  2× input/cache rates and 1.5× output rates. The current recompute receives
172
203
  aggregate session token counts, not enough per-request context information
@@ -245,3 +245,40 @@ export function applyReasoningEffort(
245
245
  }
246
246
  }
247
247
  }
248
+
249
+ const REASONING_HARNESSES: ReadonlySet<string> = new Set<ReasoningHarness>([
250
+ "claude",
251
+ "codex",
252
+ "pi",
253
+ "opencode",
254
+ "dsh",
255
+ "cursor",
256
+ "amp",
257
+ "grok",
258
+ ]);
259
+
260
+ /**
261
+ * The levels a session on this harness and model accepts; empty for a harness
262
+ * this feature does not cover. Unlike `effectiveReasoningEffort`, a supported
263
+ * level with no transport effect (Opencode `off`) is still listed.
264
+ */
265
+ export function supportedReasoningLevels(harness: string, model: string): ReasoningEffort[] {
266
+ if (!REASONING_HARNESSES.has(harness)) return [];
267
+ return reasoningCapability(harness as ReasoningHarness, model).levels;
268
+ }
269
+
270
+ /**
271
+ * The effort a session on this harness and model actually runs at: `level`
272
+ * when the adapter applies it, null when it is unset or dropped (the harness
273
+ * default applies). Same decision the adapters make in `applyReasoningEffort`.
274
+ */
275
+ export function effectiveReasoningEffort(
276
+ harness: string,
277
+ model: string,
278
+ level: ReasoningEffort | undefined,
279
+ ): ReasoningEffort | null {
280
+ if (!level || !REASONING_HARNESSES.has(harness)) return null;
281
+ return applyReasoningEffort(harness as ReasoningHarness, model, level).kind === "noop"
282
+ ? null
283
+ : level;
284
+ }
@@ -33,7 +33,11 @@ export interface CostData {
33
33
  cacheWrite1hTokens?: number;
34
34
  /** Migration 063: codex reasoning_output_tokens (and similar) for reasoning models. */
35
35
  reasoningOutputTokens?: number;
36
- /** Migration 063: claude extended-thinking tokens from CLI's `usage.thinking_input_tokens`. */
36
+ /**
37
+ * Migration 063: claude extended-thinking tokens from the result line's
38
+ * `usage.output_tokens_details.thinking_tokens`. Informational: they are a
39
+ * subset of `outputTokens` and already priced there.
40
+ */
37
41
  thinkingTokens?: number;
38
42
  /** Complete per-model usage, including sidechain/subagent tokens when available. */
39
43
  models?: CostModelUsage[];
@@ -263,7 +267,7 @@ export interface ProviderTraits {
263
267
  * agent never learns they exist. Defaults to true when omitted.
264
268
  */
265
269
  nativeSkillDiscovery?: boolean;
266
- /** Provider runs in the local Docker container with /workspace, identity files, agent-fs, PM2, etc. */
270
+ /** Provider runs in the local Docker container with /workspace, identity files, agent-fs, etc. */
267
271
  hasLocalEnvironment: boolean;
268
272
  /** Live steering modes supported by this provider. Absent means none. */
269
273
  steerModes?: SteerMode[];
@@ -49,6 +49,13 @@ const operatorOrUser: LegacyRule = {
49
49
  evaluate: (principal) => principal.kind === "operator" || principal.kind === "user",
50
50
  };
51
51
 
52
+ /** The shared API key only: swarm-wide settings that no session user or agent may change. */
53
+ const operatorOnly: LegacyRule = {
54
+ name: "operator-only",
55
+ denyReason: "requires operator authentication",
56
+ evaluate: (principal) => principal.kind === "operator",
57
+ };
58
+
52
59
  const leadOrOperatorOrUser: LegacyRule = {
53
60
  name: "lead-or-operator-or-user",
54
61
  denyReason: "requires lead agent, operator, or user authentication",
@@ -206,8 +213,9 @@ const memoryDelete: LegacyRule = {
206
213
  };
207
214
 
208
215
  /**
209
- * task.fs.mutate — operator OR user OR lead OR task-assignee OR task-creator
210
- * (src/http/fs.ts canMutateTask). Order-independent OR: today's early
216
+ * task.fs.mutate and task.parent.attach: operator OR user OR lead OR
217
+ * task-assignee OR task-creator (src/http/fs.ts canMutateTask; send-task and
218
+ * POST /api/tasks for a parentTaskId). Order-independent OR: today's early
211
219
  * operator/user returns short-circuit before agent identity, but no input
212
220
  * satisfies one branch while failing another, so a flat OR is equivalent
213
221
  * (plan Appendix A row 36 note).
@@ -276,6 +284,7 @@ export const LEGACY_POLICY = {
276
284
  "task.steer.own": requesterOwnsTask,
277
285
  "task.action.own": requesterOwnsTask,
278
286
  "task.fs.mutate": taskFsMutate,
287
+ "task.parent.attach": taskFsMutate,
279
288
  "task.progress.write": taskProgressWrite,
280
289
  "favorite.write.own": anyAuthenticated,
281
290
  "memory.learning.inject": leadOnly,
@@ -286,6 +295,10 @@ export const LEGACY_POLICY = {
286
295
  "channel.delete": leadOnly,
287
296
  "repo.merge-policy.write": leadOrOperatorOrUser,
288
297
  "integration.kapso.manage": leadOnly,
298
+ "integration.chat.manage": leadOnly,
299
+ "integration.chat.post": leadOnly,
300
+ "integration.chat.read": leadOnly,
301
+ "integration.chat.dm": leadOnly,
289
302
  "integration.slack.post": leadOnly,
290
303
  "integration.slack.read": leadOnly,
291
304
  "integration.slack.thread.start": leadOnly,
@@ -307,6 +320,7 @@ export const LEGACY_POLICY = {
307
320
  // Lead tokens retain config.read.secrets. This exception does not grant config administration.
308
321
  "models.catalog.write": leadOrOperator,
309
322
  "models.harness-support.write": agentOrOperator,
323
+ "models.profiles.write": operatorOnly,
310
324
  "skill.create.swarm": leadOnly,
311
325
  "skill.install.any": leadOnly,
312
326
  "skill.install.global": leadOnly,
@@ -79,6 +79,11 @@ export const PERMISSIONS = {
79
79
  description: "Mutate a task's filesystem artifacts and attachments.",
80
80
  namespace: "task",
81
81
  },
82
+ "task.parent.attach": {
83
+ description:
84
+ "Create a task under an existing parent task. The child inherits the parent's thread context (contextKey, Slack thread, requester).",
85
+ namespace: "task",
86
+ },
82
87
  "task.progress.write": {
83
88
  description:
84
89
  "Write a task's progress, status, or output (its assignee, a lead, a human, or anyone while unassigned).",
@@ -124,6 +129,22 @@ export const PERMISSIONS = {
124
129
  description: "Register or unregister a Kapso inbound number.",
125
130
  namespace: "integration",
126
131
  },
132
+ "integration.chat.manage": {
133
+ description: "Register or unregister a Chat SDK channel route.",
134
+ namespace: "integration",
135
+ },
136
+ "integration.chat.post": {
137
+ description: "Post to a Chat SDK thread or channel other than the thread of the current task.",
138
+ namespace: "integration",
139
+ },
140
+ "integration.chat.read": {
141
+ description: "Read a Chat SDK thread other than the thread of the current task.",
142
+ namespace: "integration",
143
+ },
144
+ "integration.chat.dm": {
145
+ description: "Open a direct-message thread with a user on a Chat SDK adapter.",
146
+ namespace: "integration",
147
+ },
127
148
  "integration.slack.post": {
128
149
  description: "Post to a direct Slack channel.",
129
150
  namespace: "integration",
@@ -198,6 +219,11 @@ export const PERMISSIONS = {
198
219
  "Record whether a pinned harness CLI version accepts a model (a worker reports its own probe results; the operator may correct any row).",
199
220
  namespace: "models",
200
221
  },
222
+ "models.profiles.write": {
223
+ description:
224
+ "Create, update, or delete model profiles (named harness + model + effort). Operator (shared API key) only.",
225
+ namespace: "models",
226
+ },
201
227
  "config.read.secrets": {
202
228
  description: "Read unmasked secret config values.",
203
229
  namespace: "config",
@@ -98,13 +98,12 @@ async function prepareDeferredWakeTask(
98
98
  */
99
99
  export const DEFERRED_WAIT_WOKE_EVENT = "task_deferred_wait_woke";
100
100
 
101
- /** Undefined means a normal schedule; an empty result means another caller won. */
102
- export async function dispatchDeferredTaskWait(
101
+ /** Undefined means a normal schedule; an empty preparation means the wait is not ready. */
102
+ export async function prepareDeferredTaskWait(
103
103
  scheduleId: string,
104
104
  reason: WakeReason,
105
105
  extraTags: string[] = [],
106
- triggerTaskId?: string,
107
- ): Promise<{ task?: AgentTask } | undefined> {
106
+ ): Promise<{ task?: PreparedTaskCreate } | undefined> {
108
107
  const client = getDbClient();
109
108
  // Keep ordinary schedule dispatch off the write-lock path.
110
109
  if (
@@ -117,8 +116,22 @@ export async function dispatchDeferredTaskWait(
117
116
  // first; the transaction below re-reads the wait and its member set before claiming.
118
117
  // The prepared description carries the cause seen here; for an `any` wait a member
119
118
  // that settles between this read and the claim is missing from that text only.
120
- const preparedForWait = await prepareDeferredWakeTask(scheduleId, reason, extraTags);
119
+ return { task: await prepareDeferredWakeTask(scheduleId, reason, extraTags) };
120
+ }
121
+
122
+ /** Undefined means a normal schedule; an empty result means another caller won. */
123
+ export async function dispatchDeferredTaskWait(
124
+ scheduleId: string,
125
+ reason: WakeReason,
126
+ extraTags: string[] = [],
127
+ triggerTaskId?: string,
128
+ prepared?: { task?: PreparedTaskCreate },
129
+ ): Promise<{ task?: AgentTask } | undefined> {
130
+ const preparation = prepared ?? (await prepareDeferredTaskWait(scheduleId, reason, extraTags));
131
+ if (!preparation) return undefined;
132
+ const preparedForWait = preparation.task;
121
133
  if (!preparedForWait) return {};
134
+ const client = getDbClient();
122
135
  return client.transaction(async () => {
123
136
  const resolved = await resolveWaitCause(scheduleId, reason);
124
137
  if (!resolved) return {};
@@ -23,7 +23,7 @@ import {
23
23
  getScriptApiConnectionDescriptors,
24
24
  getScriptMcpConnectionDescriptors,
25
25
  } from "@/be/script-connections";
26
- import { buildScriptCredentialBindings } from "@/be/script-credential-broker";
26
+ import { buildScriptCredentialBindingsWithFailures } from "@/be/script-credential-broker";
27
27
  import { getScript } from "@/be/scripts/db";
28
28
  import { runScript } from "@/scripts-runtime/loader";
29
29
  import { telemetry } from "@/telemetry";
@@ -38,6 +38,7 @@ import { scrubObject, scrubSecrets } from "../utils/secret-scrubber";
38
38
  import {
39
39
  dispatchDeferredTaskWait,
40
40
  initDeferredTaskWaits,
41
+ prepareDeferredTaskWait,
41
42
  reapSpentDeferredSchedules,
42
43
  reconcileDeferredTaskWaits,
43
44
  stopDeferredTaskWaits,
@@ -99,12 +100,14 @@ async function executeScheduleScript(schedule: ScheduledTask): Promise<void> {
99
100
 
100
101
  let output: Awaited<ReturnType<typeof runScript>>;
101
102
  try {
103
+ const credentials = await buildScriptCredentialBindingsWithFailures({ agentId });
102
104
  output = await runScript({
103
105
  source: script.source,
104
106
  args,
105
107
  fsMode: "none",
106
108
  agentId,
107
- egressSecrets: await buildScriptCredentialBindings({ agentId }),
109
+ egressSecrets: credentials.egressSecrets,
110
+ userConfig: credentials.userConfig,
108
111
  apiConnections: getScriptApiConnectionDescriptors({ agentId }),
109
112
  mcpConnections: getScriptMcpConnectionDescriptors({ agentId }),
110
113
  timeoutMs: 60_000,
@@ -301,6 +304,9 @@ async function throwIfWorkflowNeedsSetup(scheduleId: string, runIds: string[]):
301
304
  * recovery pass, or schedule edit invalidates it before any target is dispatched.
302
305
  */
303
306
  async function claimScheduleOccurrence(snapshot: ScheduledTask, extraTags: string[]) {
307
+ // Run extension pre-hooks before opening the claim transaction. The snapshot
308
+ // guard below still prevents a moved ceiling from consuming this preparation.
309
+ const preparedWait = await prepareDeferredTaskWait(snapshot.id, "ceiling", extraTags);
304
310
  return getDbClient().transaction(async () => {
305
311
  const current = await getScheduledTaskById(snapshot.id);
306
312
  if (!current?.enabled || !current.nextRunAt || current.nextRunAt !== snapshot.nextRunAt) {
@@ -311,7 +317,9 @@ async function claimScheduleOccurrence(snapshot: ScheduledTask, extraTags: strin
311
317
  // Event-driven waits already atomically create their child and consume the
312
318
  // ceiling. Keep that transaction (and its afterCommit emits) intact: disabling
313
319
  // a one-time schedule first would prevent its wake-up task from being created.
314
- const deferred = await dispatchDeferredTaskWait(schedule.id, "ceiling", extraTags);
320
+ const deferred = preparedWait
321
+ ? await dispatchDeferredTaskWait(schedule.id, "ceiling", extraTags, undefined, preparedWait)
322
+ : undefined;
315
323
  if (deferred) {
316
324
  return {
317
325
  schedule,
@@ -5,11 +5,14 @@
5
5
  *
6
6
  * - `__host_call(path, argsJson)`: async host call, resolves to a JSON envelope.
7
7
  * - `__host_sync(op, json)`: sync host call, returns a JSON envelope.
8
+ * `crypto.random` returns that many random bytes.
8
9
  * - `__host_log(stream, text)`: append to the captured stdout or stderr.
9
10
  * - `__host_sleep(ms)`: resolves after `ms` on the host clock.
10
11
  * - `__host_done(json)`: reports the final outcome of the run.
11
12
  *
12
13
  * Every value crosses the boundary as JSON. The sandbox has no other way out.
14
+ * `SANDBOX_WEB_SHIMS` (quickjs-web-shims.ts) runs first and adds URL,
15
+ * URLSearchParams, TextEncoder and TextDecoder.
13
16
  * `__swarm_shape` (injected before this file) lists the configured api/mcp
14
17
  * connections so `ctx.api.<slug>` matches the native executor exactly.
15
18
  */
@@ -64,6 +67,39 @@ const setTimeout = (fn, ms, ...args) => {
64
67
  const clearTimeout = (id) => {
65
68
  timers.delete(id);
66
69
  };
70
+ // Shares the timer ids with setTimeout, so either clear function stops either.
71
+ const setInterval = (fn, ms, ...args) => {
72
+ const id = ++timerSeq;
73
+ const delay = Math.max(1, Number(ms) || 0);
74
+ timers.add(id);
75
+ const tick = () =>
76
+ __host_sleep(delay).then(() => {
77
+ if (!timers.has(id)) return;
78
+ tick();
79
+ fn(...args);
80
+ });
81
+ tick();
82
+ return id;
83
+ };
84
+ const clearInterval = clearTimeout;
85
+
86
+ // ── crypto (bytes come from the host CSPRNG) ──
87
+ const INTEGER_ARRAYS = [Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, BigInt64Array, BigUint64Array];
88
+ const getRandomValues = (array) => {
89
+ if (!INTEGER_ARRAYS.some((type) => array instanceof type)) {
90
+ throw new TypeError("crypto.getRandomValues: the argument must be an integer TypedArray");
91
+ }
92
+ new Uint8Array(array.buffer, array.byteOffset, array.byteLength).set(hostSync("crypto.random", array.byteLength));
93
+ return array;
94
+ };
95
+ const randomUUID = () => {
96
+ const bytes = hostSync("crypto.random", 16);
97
+ bytes[6] = (bytes[6] & 0x0f) | 0x40;
98
+ bytes[8] = (bytes[8] & 0x3f) | 0x80;
99
+ const hex = bytes.map((b) => (b + 256).toString(16).slice(1)).join("");
100
+ return hex.slice(0, 8) + "-" + hex.slice(8, 12) + "-" + hex.slice(12, 16) + "-" + hex.slice(16, 20) + "-" + hex.slice(20);
101
+ };
102
+ const crypto = Object.freeze({ getRandomValues, randomUUID });
67
103
 
68
104
  // ── Redacted ──
69
105
  const redactedEntries = new WeakMap();
@@ -115,7 +151,7 @@ const config = Object.freeze({
115
151
  },
116
152
  });
117
153
 
118
- // ── fetch (text bodies only) ──
154
+ // ── fetch ──
119
155
  class Headers {
120
156
  constructor(init) {
121
157
  this._map = new Map();
@@ -165,14 +201,44 @@ class Response {
165
201
  return JSON.parse(this._body);
166
202
  }
167
203
  }
204
+ const BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
205
+ const toBase64 = (bytes) => {
206
+ let out = "";
207
+ let i = 0;
208
+ for (; i + 2 < bytes.length; i += 3) {
209
+ const n = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
210
+ out += BASE64[n >> 18] + BASE64[(n >> 12) & 63] + BASE64[(n >> 6) & 63] + BASE64[n & 63];
211
+ }
212
+ if (i < bytes.length) {
213
+ const n = (bytes[i] << 16) | ((bytes[i + 1] || 0) << 8);
214
+ out += BASE64[n >> 18] + BASE64[(n >> 12) & 63] + (i + 1 < bytes.length ? BASE64[(n >> 6) & 63] : "=") + "=";
215
+ }
216
+ return out;
217
+ };
218
+ // Binary bodies cross the host boundary as base64; the host decodes them.
219
+ const encodeBody = (body, headers) => {
220
+ if (body == null) return {};
221
+ if (typeof body === "string") return { body };
222
+ if (body instanceof globalThis.URLSearchParams) {
223
+ if (!headers.has("content-type")) headers.set("content-type", "application/x-www-form-urlencoded;charset=UTF-8");
224
+ return { body: body.toString() };
225
+ }
226
+ if (body instanceof ArrayBuffer) return { bodyBase64: toBase64(new Uint8Array(body)) };
227
+ if (ArrayBuffer.isView(body)) {
228
+ return { bodyBase64: toBase64(new Uint8Array(body.buffer, body.byteOffset, body.byteLength)) };
229
+ }
230
+ throw new TypeError(
231
+ "quickjs executor: fetch body must be a string, Uint8Array, ArrayBuffer or URLSearchParams (JSON.stringify objects first)",
232
+ );
233
+ };
168
234
  const toRequest = (input, init) => {
169
235
  const options = init || {};
170
236
  const url = typeof input === "string" ? input : input && (input.href || input.url) ? input.href || input.url : String(input);
171
- if (options.body != null && typeof options.body !== "string") {
172
- throw new TypeError("quickjs executor: fetch body must be a string (JSON.stringify it first)");
173
- }
174
- const headers = options.headers == null ? undefined : Array.from(new Headers(options.headers).entries());
175
- const request = { url, method: options.method, headers, body: options.body == null ? undefined : options.body };
237
+ const headerMap = new Headers(options.headers);
238
+ const body = encodeBody(options.body, headerMap);
239
+ const headerList = Array.from(headerMap.entries());
240
+ const headers = options.headers == null && headerList.length === 0 ? undefined : headerList;
241
+ const request = { url, method: options.method, headers, ...body };
176
242
  if (options.retries !== undefined) request.retries = options.retries;
177
243
  if (options.timeoutMs !== undefined) request.timeoutMs = options.timeoutMs;
178
244
  return request;
@@ -223,6 +289,9 @@ const define = (name, value) =>
223
289
  define("console", console);
224
290
  define("setTimeout", setTimeout);
225
291
  define("clearTimeout", clearTimeout);
292
+ define("setInterval", setInterval);
293
+ define("clearInterval", clearInterval);
294
+ define("crypto", crypto);
226
295
  define("fetch", fetch);
227
296
  define("Headers", Headers);
228
297
  define("Response", Response);
@@ -13,6 +13,7 @@ import { table } from "../stdlib/table";
13
13
  import { SwarmConfig } from "../swarm-config";
14
14
  import { bundleForQuickJS, USER_SCRIPT_FILE } from "./quickjs-bundle";
15
15
  import { SANDBOX_EPILOGUE, SANDBOX_PRELUDE } from "./quickjs-prelude";
16
+ import { SANDBOX_WEB_SHIMS } from "./quickjs-web-shims";
16
17
  import type { SourceMapReader } from "./sourcemap";
17
18
  import type {
18
19
  ExecutorOutput,
@@ -37,6 +38,13 @@ const BUNDLE_FILE = USER_SCRIPT_FILE.replace(/\.ts$/, ".js");
37
38
  const MAX_CONCURRENT_HOST_CALLS = 16;
38
39
  // Host calls and timers that one run can have in flight or queued.
39
40
  const MAX_PENDING_HOST_CALLS = 1024;
41
+ // A binary fetch body crosses the bridge as base64 and is decoded on the host.
42
+ // One body decodes to at most this many bytes, and the binary bodies a run has
43
+ // queued or in flight total at most MAX_PENDING_BINARY_BODY_BYTES. Both are
44
+ // checked before decoding, so a rejected request never reaches fetch.
45
+ const MAX_BINARY_BODY_BYTES = 16 * 1024 * 1024;
46
+ const MAX_BINARY_BODY_BASE64_LENGTH = Math.ceil(MAX_BINARY_BODY_BYTES / 3) * 4;
47
+ const MAX_PENDING_BINARY_BODY_BYTES = 64 * 1024 * 1024;
40
48
  // After a run ends, its host calls are aborted. Wait this long for them to settle.
41
49
  const HOST_CALL_SETTLE_MS = 1_000;
42
50
 
@@ -46,6 +54,9 @@ export type QuickJSJobResult = {
46
54
  leakedHostCalls: number;
47
55
  };
48
56
  const MAX_STACK_BYTES = 1024 * 1024;
57
+ // Bun's getRandomValues has no 64 KiB quota, so neither does the sandbox. This
58
+ // bound only keeps one sync host call's JSON reply small.
59
+ const MAX_RANDOM_BYTES = 1024 * 1024;
49
60
  const TIMEOUT_EXIT_CODE = 124;
50
61
 
51
62
  type ErrorPayload = { name: string; message: string; stack: string };
@@ -126,12 +137,51 @@ type SandboxRequest = {
126
137
  method?: string;
127
138
  headers?: [string, string][];
128
139
  body?: string;
140
+ /** Binary body, base64-encoded by the sandbox. */
141
+ bodyBase64?: string;
129
142
  retries?: number;
130
143
  timeoutMs?: number;
131
144
  };
132
145
 
146
+ const FETCH_PATHS = new Set(["fetch", "stdlib.fetch", "stdlib.fetchJson"]);
147
+ const BASE64_RE = /^[A-Za-z0-9+/]*={0,2}$/;
148
+
149
+ /**
150
+ * Checks a sandbox fetch request's base64 body without decoding it and returns
151
+ * its decoded size (0 when there is none). The sandbox controls the value, so
152
+ * the type, alphabet and size are checked before the host allocates anything.
153
+ */
154
+ function binaryBodySize(request: unknown): number {
155
+ if (!request || typeof request !== "object") return 0;
156
+ const encoded = (request as { bodyBase64?: unknown }).bodyBase64;
157
+ if (encoded === undefined) return 0;
158
+ if (typeof encoded !== "string") throw new TypeError("fetch bodyBase64 must be a string");
159
+ if (encoded.length > MAX_BINARY_BODY_BASE64_LENGTH) {
160
+ throw new RangeError(
161
+ `fetch binary body exceeds the ${MAX_BINARY_BODY_BYTES}-byte limit of the quickjs executor`,
162
+ );
163
+ }
164
+ if (encoded.length % 4 !== 0 || !BASE64_RE.test(encoded)) {
165
+ throw new TypeError("fetch bodyBase64 is not valid base64");
166
+ }
167
+ const padding = encoded.endsWith("==") ? 2 : encoded.endsWith("=") ? 1 : 0;
168
+ const size = (encoded.length / 4) * 3 - padding;
169
+ if (size > MAX_BINARY_BODY_BYTES) {
170
+ throw new RangeError(
171
+ `fetch binary body exceeds the ${MAX_BINARY_BODY_BYTES}-byte limit of the quickjs executor`,
172
+ );
173
+ }
174
+ return size;
175
+ }
176
+
133
177
  function requestInit(request: SandboxRequest): RequestInit {
134
- return { method: request.method, headers: request.headers, body: request.body };
178
+ if (request.bodyBase64 === undefined) {
179
+ return { method: request.method, headers: request.headers, body: request.body };
180
+ }
181
+ // Already checked by binaryBodySize when the call was accepted.
182
+ const bytes = Buffer.from(request.bodyBase64, "base64");
183
+ const body = new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength);
184
+ return { method: request.method, headers: request.headers, body };
135
185
  }
136
186
 
137
187
  async function serializeResponse(response: Response, operation: string) {
@@ -223,6 +273,16 @@ function dispatchSync(config: SwarmConfig, op: string, value: unknown): unknown
223
273
  return Redacted.meta(redactedFor(config, value));
224
274
  case "config.has":
225
275
  return typeof value === "string" && config.get(value) !== undefined;
276
+ case "crypto.random": {
277
+ if (
278
+ !Number.isInteger(value) ||
279
+ (value as number) < 0 ||
280
+ (value as number) > MAX_RANDOM_BYTES
281
+ ) {
282
+ throw new Error(`crypto.random needs a byte count between 0 and ${MAX_RANDOM_BYTES}`);
283
+ }
284
+ return Array.from(crypto.getRandomValues(new Uint8Array(value as number)));
285
+ }
226
286
  case "table":
227
287
  return table(value as Array<Record<string, unknown>>);
228
288
  default:
@@ -287,6 +347,7 @@ class HostCallScope {
287
347
  private readonly waiters: Array<() => void> = [];
288
348
  private readonly outerFetch = globalThis.fetch;
289
349
  private active = 0;
350
+ private bodyBytes = 0;
290
351
 
291
352
  constructor() {
292
353
  const outer = this.outerFetch;
@@ -306,8 +367,11 @@ class HostCallScope {
306
367
  return this.controller.signal;
307
368
  }
308
369
 
309
- /** Track a host call. `limited` calls share the per-run concurrency budget. */
310
- call<T>(run: () => Promise<T>, limited: boolean): Promise<T> {
370
+ /**
371
+ * Track a host call. `limited` calls share the per-run concurrency budget.
372
+ * `bodyBytes` counts against the per-run binary body budget until it settles.
373
+ */
374
+ call<T>(run: () => Promise<T>, limited: boolean, bodyBytes = 0): Promise<T> {
311
375
  if (this.ops.size >= MAX_PENDING_HOST_CALLS) {
312
376
  return Promise.reject(
313
377
  new Error(
@@ -315,12 +379,21 @@ class HostCallScope {
315
379
  ),
316
380
  );
317
381
  }
382
+ if (this.bodyBytes + bodyBytes > MAX_PENDING_BINARY_BODY_BYTES) {
383
+ return Promise.reject(
384
+ new RangeError(
385
+ `Pending fetch binary bodies in one script run exceed ${MAX_PENDING_BINARY_BODY_BYTES} bytes`,
386
+ ),
387
+ );
388
+ }
389
+ this.bodyBytes += bodyBytes;
318
390
  const op = limited ? this.limited(run) : run();
319
391
  this.ops.add(op);
320
- void op.then(
321
- () => this.ops.delete(op),
322
- () => this.ops.delete(op),
323
- );
392
+ const settle = () => {
393
+ this.ops.delete(op);
394
+ this.bodyBytes -= bodyBytes;
395
+ };
396
+ void op.then(settle, settle);
324
397
  return op;
325
398
  }
326
399
 
@@ -491,7 +564,10 @@ async function evaluateJob(
491
564
  define("__host_call", (pathHandle, argsHandle) => {
492
565
  const path = ctx.getString(pathHandle);
493
566
  const callArgs = JSON.parse(ctx.getString(argsHandle)) as unknown[];
494
- return bridge(() => host.call(() => dispatchCall(hostCtx, path, callArgs, host.signal), true));
567
+ return bridge(async () => {
568
+ const bodyBytes = FETCH_PATHS.has(path) ? binaryBodySize(callArgs?.[0]) : 0;
569
+ return host.call(() => dispatchCall(hostCtx, path, callArgs, host.signal), true, bodyBytes);
570
+ });
495
571
  });
496
572
  define("__host_sync", (opHandle, valueHandle) => {
497
573
  let envelope: unknown;
@@ -538,6 +614,7 @@ async function evaluateJob(
538
614
  const init = `globalThis.__swarm_shape = ${JSON.stringify(shape)};\nglobalThis.__swarm_args = ${JSON.stringify(args)};`;
539
615
  const ok =
540
616
  evaluate(init, "swarm-init.js") &&
617
+ evaluate(SANDBOX_WEB_SHIMS, "swarm-web-shims.js") &&
541
618
  evaluate(SANDBOX_PRELUDE, "swarm-prelude.js") &&
542
619
  evaluate(bundle.code, BUNDLE_FILE) &&
543
620
  evaluate(SANDBOX_EPILOGUE, "swarm-epilogue.js");