@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
@@ -0,0 +1,64 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { mkdtempSync, readFileSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { CHILD_PROCESS_TEST_BUDGET_MS, expectChildOk, runChild } from "./test-proc";
6
+
7
+ // Must match src/tests/fixtures/runner-legacy-resume-prompt.ts.
8
+ const REQUESTER_SENTINEL = "RESUME_REQUESTER_SENTINEL_7f3a";
9
+ const MISSING_DIR = "/nonexistent/legacy-resume-dir-7f3a";
10
+
11
+ describe("runner legacy paused-task resume prompt", () => {
12
+ test(
13
+ "resumed session gets the requester profile and missing-dir warning in its task prompt",
14
+ async () => {
15
+ const directory = mkdtempSync(join(tmpdir(), "runner-legacy-resume-"));
16
+ try {
17
+ expectChildOk(
18
+ await runChild(
19
+ [
20
+ process.execPath,
21
+ join(import.meta.dir, "fixtures/runner-legacy-resume-prompt.ts"),
22
+ directory,
23
+ ],
24
+ {
25
+ cwd: directory,
26
+ env: {
27
+ PATH: process.env.PATH,
28
+ HOME: directory,
29
+ AGENT_ID: "11111111-1111-4111-8111-111111111111",
30
+ API_KEY: "example-fixture-key",
31
+ MCP_BASE_URL: "http://runner.invalid",
32
+ HARNESS_PROVIDER: "pi",
33
+ CRED_CHECK_DISABLE: "1",
34
+ DATABASE_PATH: ":memory:",
35
+ ANONYMIZED_TELEMETRY: "false",
36
+ STEERING_ENABLED: "false",
37
+ },
38
+ },
39
+ ),
40
+ "Runner legacy resume fixture",
41
+ );
42
+ const result = JSON.parse(readFileSync(join(directory, "result.json"), "utf8")) as {
43
+ taskId: string;
44
+ prompt: string;
45
+ systemPrompt: string;
46
+ };
47
+
48
+ expect(result.taskId).toBe("44444444-4444-4444-8444-444444444444");
49
+ expect(result.prompt).toContain(REQUESTER_SENTINEL);
50
+ expect(result.prompt).toContain("Fixture Requester");
51
+ // The same text the fresh task path sends (missingTaskDirWarning).
52
+ expect(result.prompt).toContain(
53
+ `Note: The task requested working directory "${MISSING_DIR}" but it does not exist. Falling back to default directory.`,
54
+ );
55
+ // Per-task context stays out of the cacheable system prompt.
56
+ expect(result.systemPrompt).not.toContain(REQUESTER_SENTINEL);
57
+ expect(result.systemPrompt).not.toContain(MISSING_DIR);
58
+ } finally {
59
+ rmSync(directory, { recursive: true, force: true });
60
+ }
61
+ },
62
+ CHILD_PROCESS_TEST_BUDGET_MS,
63
+ );
64
+ });
@@ -626,6 +626,12 @@ describe("script connections", () => {
626
626
  markMigrationApplied(database, "207_runtime_session_tokens.sql");
627
627
  // 213 rebuilds script_connections with the post-117 columns this fixture skips.
628
628
  markMigrationApplied(database, "213_script_connections_sql.sql");
629
+ // 215 alters swarm_config, which this migration-112-only fixture does not create.
630
+ markMigrationApplied(database, "215_swarm_config_export_to_env.sql");
631
+ // 219 rewrites task_context_snapshots, which this fixture does not create.
632
+ markMigrationApplied(database, "219_relabel_pi_context_compactions.sql");
633
+ // 220 reads agents and swarm_config, which this migration-112-only fixture does not create.
634
+ markMigrationApplied(database, "220_model_profiles.sql");
629
635
 
630
636
  const id = crypto.randomUUID();
631
637
  const now = new Date().toISOString();
@@ -1,4 +1,7 @@
1
- import { describe, expect, test } from "bun:test";
1
+ import { afterAll, describe, expect, test } from "bun:test";
2
+ import { mkdtemp, rm } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
2
5
  import { classifyExit, NativeScriptExecutor } from "../scripts-runtime/executors/native";
3
6
  import { QuickJSScriptExecutor } from "../scripts-runtime/executors/quickjs";
4
7
  import type {
@@ -8,6 +11,7 @@ import type {
8
11
  } from "../scripts-runtime/executors/types";
9
12
  import { DEFAULT_SCRIPT_RESOURCES } from "../scripts-runtime/executors/types";
10
13
  import { SKIP_SANDBOX_SPAWN_TESTS } from "./sandbox-spawn-test-helpers";
14
+ import { CHILD_PROCESS_TEST_BUDGET_MS } from "./test-proc";
11
15
 
12
16
  const payload = {
13
17
  system: {
@@ -132,6 +136,253 @@ conformance("fake", () => new FakeScriptExecutor(), false);
132
136
  // Worker threads and WASM, no Bun.spawn: runs even when the spawn probe fails.
133
137
  conformance("quickjs", () => new QuickJSScriptExecutor(), false);
134
138
 
139
+ // Each probe is plain JavaScript, so the test process (Bun) can evaluate it too:
140
+ // both executors must return exactly what Bun's own web globals return.
141
+ const echoServer = Bun.serve({
142
+ port: 0,
143
+ async fetch(req) {
144
+ const bytes = new Uint8Array(await req.arrayBuffer());
145
+ return Response.json({
146
+ contentType: req.headers.get("content-type"),
147
+ bytes: Array.from(bytes),
148
+ });
149
+ },
150
+ });
151
+ afterAll(() => echoServer.stop(true));
152
+
153
+ const PARITY_PROBES: Record<string, string> = {
154
+ URL: `async () => {
155
+ const cases = [
156
+ ["https://user:pa ss@Example.COM:443/a/./b/../c d?q=1 2&x='y'#frag ment"],
157
+ ["http://example.com:8080"], ["../x?y#z", "https://a.b/c/d/e"], ["//other.host/p", "https://a.b/c"],
158
+ ["?new", "https://a.b/c/d?q#h"], ["#h2", "https://a.b/c/d?q#h"], ["mailto:someone@example.com"],
159
+ ["foo://EXAMPLE.com/a/../b"], ["http://0x7f.1/"], ["http://[0:0:0:0:0:0:0:1]:80/"],
160
+ ["https://münchen.de/straße"], ["http://a.b/c\\\\d"], ["file:///etc/hosts"], ["http:foo", "http://a.b/c/d"],
161
+ ["not a url"], ["https://a.b:99999/"], ["http://exa mple.com"], ["http://256.1.1.1/"],
162
+ ];
163
+ const keys = ["href", "origin", "protocol", "username", "password", "host", "hostname", "port", "pathname", "search", "hash"];
164
+ const parsed = cases.map(([url, base]) => {
165
+ try {
166
+ const u = new URL(url, base);
167
+ return Object.fromEntries(keys.map((key) => [key, u[key]]).concat([["params", [...u.searchParams]]]));
168
+ } catch (error) {
169
+ return { error: error.name };
170
+ }
171
+ });
172
+ const u = new URL("https://a.b/p?x=1#h");
173
+ u.searchParams.append("y", "two words");
174
+ const linked = u.href;
175
+ u.search = "?z=3";
176
+ u.pathname = "/new path";
177
+ u.port = "8443";
178
+ u.hostname = "C.D";
179
+ u.hash = "";
180
+ return { parsed, linked, mutated: u.href, json: JSON.stringify({ u }), canParse: [URL.canParse("x"), URL.canParse("x", "https://a/")] };
181
+ }`,
182
+ "URL relative state": `async () => {
183
+ const base = "http://a/b?q#f";
184
+ const cases = [
185
+ "http:/x", "http:/", "http:", "http:?y", "http:#g", "http:x/../y", "http:\\\\x", "http://x", "http:///x",
186
+ "http:/\\\\x", "https:/x", "ws:/x", "foo:/x", "file:/x",
187
+ ];
188
+ return cases.map((url) => {
189
+ try { return new URL(url, base).href; } catch (error) { return error.name; }
190
+ });
191
+ }`,
192
+ "URL setters": `async () => {
193
+ const run = (start, key, values) => values.map((value) => {
194
+ const u = new URL(start);
195
+ u[key] = value;
196
+ return u.href;
197
+ });
198
+ return {
199
+ hostname: run("http://a.b:81/p", "hostname", [
200
+ "[::1]", "[::1]:90", "example.net:90", "c.d/e", "c.d?x", "user@c.d", "", "exa mple", "C.D", "[::1", "0x7f.1",
201
+ ]),
202
+ host: run("http://a.b:81/p", "host", [
203
+ "[::1]", "[::1]:90", "c.d:90", "c.d:", "c.d:80", "c.d:99999", "c.d:9x", "user@c.d", "user:pw@c.d:9", "", "c.d/e:1", ":90",
204
+ "user:pw@c.d", "c.d:1:2", "c.d:9/x:1", "c.d:080", "c.d:65536", "c.d:x", "[::1]:80",
205
+ ]),
206
+ hostNoPort: run("http://a.b/p", "host", ["c.d:80", "c.d:99999"]),
207
+ nonSpecialHost: run("foo://a:5/p", "host", ["", "b:1", "[::1]", "b@c", "b:", "b:99999"]),
208
+ nonSpecialEmptyHost: run("foo://a/p", "host", [""]),
209
+ nonSpecialHostname: run("foo://u@a:5/p", "hostname", ["", "b"]),
210
+ protocol: run("http://a.b/p", "protocol", [
211
+ "https", "https:", "https:foo", "https://x", "ht tps", "https!", "1http", "foo", "ws:", "HTTPS", "\\thttps",
212
+ ]),
213
+ fileProtocol: run("file:///p", "protocol", ["http"]),
214
+ opaqueHost: run("mailto:x@y", "host", ["z"]),
215
+ };
216
+ }`,
217
+ URLSearchParams: `async () => {
218
+ const p = new URLSearchParams("?b=2&a=1&a=3&sp=a+b%20c&uni=%C3%A9&bad=%E0%A4%A");
219
+ p.append("x y", "ä&=+");
220
+ p.sort();
221
+ const sorted = p.toString();
222
+ const all = p.getAll("a");
223
+ p.set("a", "9");
224
+ p.delete("b");
225
+ return {
226
+ sorted, all, size: p.size, after: p.toString(), entries: [...p],
227
+ has: [p.has("a"), p.has("a", "1"), p.has("zz")],
228
+ fromObject: new URLSearchParams({ q: "hello world", n: 5 }).toString(),
229
+ fromPairs: new URLSearchParams([["k", "v"], ["k", "w"]]).toString(),
230
+ };
231
+ }`,
232
+ "URLSearchParams live traversal": `async () => {
233
+ const appended = new URLSearchParams("a=1&b=2");
234
+ const keys = appended.keys();
235
+ appended.append("c", "3");
236
+ const keysAfterAppend = [...keys];
237
+
238
+ const deleted = new URLSearchParams("a=1&b=2&c=3");
239
+ const visited = [];
240
+ deleted.forEach((value, key) => {
241
+ visited.push(key + value);
242
+ if (key === "a") deleted.delete("c");
243
+ });
244
+
245
+ const grown = new URLSearchParams("x=1");
246
+ const grownVisits = [];
247
+ grown.forEach((value, key) => {
248
+ grownVisits.push(key);
249
+ if (grownVisits.length < 3) grown.append("y" + grownVisits.length, value);
250
+ });
251
+
252
+ const shifted = new URLSearchParams("a=1&b=2&c=3");
253
+ const entries = shifted.entries();
254
+ const first = entries.next().value;
255
+ shifted.delete("a");
256
+ const rest = [...entries];
257
+
258
+ const reset = new URLSearchParams("a=1&b=2");
259
+ const values = reset.values();
260
+ values.next();
261
+ reset.set("a", "9");
262
+ reset.sort();
263
+ const drained = values.next();
264
+ reset.append("z", "0");
265
+ const afterDone = values.next();
266
+
267
+ const linked = new URL("https://h/?a=1&b=2");
268
+ const linkedKeys = linked.searchParams.keys();
269
+ linked.search = "?c=3&d=4&e=5";
270
+ const linkedRest = [...linkedKeys];
271
+
272
+ return { keysAfterAppend, visited, grownVisits, first, rest, drained, afterDone, linkedRest };
273
+ }`,
274
+ base64: `async () => {
275
+ const attempt = (run) => { try { return run(); } catch (error) { return error.name; } };
276
+ return {
277
+ encode: ["", "f", "fo", "foo", "foob", "fooba", "foobar", "\\u00ff\\u0000\\u0080", 123, null, undefined].map((value) => btoa(value)),
278
+ decode: ["", "Zg", "Zg==", "Zm8=", "Zm9v", " Zm 9v\\n", "Zm9vYg", "/+8=", "AP+A"].map((value) => atob(value)),
279
+ decodeBad: ["Z", "Zg=", "Zg===", "Z=g=", "Zm9v!", "Zm9v=", "===="].map((value) => attempt(() => atob(value))),
280
+ encodeBad: attempt(() => btoa("é👋")),
281
+ roundTrip: atob(btoa(String.fromCharCode(...Array.from({ length: 256 }, (_, i) => i)))).length,
282
+ };
283
+ }`,
284
+ TextEncoder: `async () => {
285
+ const encoder = new TextEncoder();
286
+ const bytes = encoder.encode("héllo 👋 \\uD800x");
287
+ const decoder = new TextDecoder();
288
+ const wave = encoder.encode("👋é");
289
+ const streamed = [decoder.decode(wave.subarray(0, 2), { stream: true }), decoder.decode(wave.subarray(2, 5), { stream: true }), decoder.decode(wave.subarray(5))];
290
+ let fatal = "accepted";
291
+ try { new TextDecoder("utf-8", { fatal: true }).decode(new Uint8Array([0xc3])); } catch (error) { fatal = error.name; }
292
+ const target = new Uint8Array(5);
293
+ return {
294
+ bytes: Array.from(bytes),
295
+ roundTrip: decoder.decode(bytes),
296
+ replaced: decoder.decode(new Uint8Array([0x61, 0xff, 0xe2, 0x82, 0x62, 0xf0, 0x9f, 0x91])),
297
+ streamed, fatal,
298
+ bom: decoder.decode(new Uint8Array([0xef, 0xbb, 0xbf, 0x41])),
299
+ fromBuffer: decoder.decode(new Uint8Array([104, 105]).buffer),
300
+ encodeInto: [encoder.encodeInto("aé👋", target), Array.from(target)],
301
+ };
302
+ }`,
303
+ setInterval: `async () => {
304
+ let ticks = 0;
305
+ await new Promise((resolve) => {
306
+ const id = setInterval((step) => {
307
+ ticks += step;
308
+ if (ticks === 3) { clearInterval(id); resolve(); }
309
+ }, 5, 1);
310
+ });
311
+ let crossCleared = 0;
312
+ const other = setInterval(() => { crossCleared++; }, 5);
313
+ clearTimeout(other);
314
+ await new Promise((resolve) => setTimeout(resolve, 40));
315
+ return { ticks, crossCleared };
316
+ }`,
317
+ crypto: `async () => {
318
+ const uuids = [crypto.randomUUID(), crypto.randomUUID()];
319
+ const words = new Uint32Array(8);
320
+ const same = crypto.getRandomValues(words) === words;
321
+ let quota = "accepted";
322
+ try { crypto.getRandomValues(new Uint8Array(65537)); } catch (error) { quota = error.name; }
323
+ return {
324
+ v4: uuids.every((id) => /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(id)),
325
+ distinct: uuids[0] !== uuids[1],
326
+ same,
327
+ filled: words.some((word) => word !== 0),
328
+ quota,
329
+ };
330
+ }`,
331
+ "fetch bodies": `async ({ base }) => {
332
+ const send = async (body, headers) => (await fetch(base, { method: "POST", body, headers })).json();
333
+ return {
334
+ uint8: await send(new Uint8Array([0, 1, 254, 255])),
335
+ view: await send(new Uint16Array([1, 2]).subarray(1)),
336
+ buffer: await send(new Uint8Array([7, 8, 9]).buffer, { "content-type": "application/octet-stream" }),
337
+ form: await send(new URLSearchParams({ a: "1 2", b: "é" })),
338
+ formTyped: await send(new URLSearchParams("x=1"), { "Content-Type": "text/plain" }),
339
+ };
340
+ }`,
341
+ };
342
+
343
+ // Imports the probe as a module, so Bun's own globals compute the expectation.
344
+ async function runInBun(probe: string, args: unknown): Promise<unknown> {
345
+ const dir = await mkdtemp(join(tmpdir(), "parity-probe-"));
346
+ try {
347
+ const file = join(dir, "probe.js");
348
+ await Bun.write(file, `export default ${probe};`);
349
+ const mod = (await import(file)) as { default: (args: unknown) => Promise<unknown> };
350
+ return await mod.default(args);
351
+ } finally {
352
+ await rm(dir, { recursive: true, force: true });
353
+ }
354
+ }
355
+
356
+ describe("quickjs parity shims match native and Bun", () => {
357
+ const args = { base: `http://127.0.0.1:${echoServer.port}/echo` };
358
+ const executors: Array<[string, () => ScriptExecutor, boolean]> = [
359
+ ["native", () => new NativeScriptExecutor(), true],
360
+ ["quickjs", () => new QuickJSScriptExecutor(), false],
361
+ ];
362
+ for (const [name, probe] of Object.entries(PARITY_PROBES)) {
363
+ for (const [executorName, makeExecutor, spawns] of executors) {
364
+ // The native case spawns a bun subprocess with a 10s script budget, so
365
+ // the test timeout must outlast it (runbooks/testing.md hard rule 5).
366
+ test.skipIf(spawns && SKIP_SANDBOX_SPAWN_TESTS)(
367
+ `${name} (${executorName})`,
368
+ async () => {
369
+ const expected = await runInBun(probe, args);
370
+ const output = await makeExecutor().run(
371
+ input({
372
+ source: `export default ${probe};`,
373
+ args,
374
+ resources: { ...DEFAULT_SCRIPT_RESOURCES, memoryMb: 2048, wallClockMs: 10_000 },
375
+ }),
376
+ );
377
+ expect(output.error).toBeUndefined();
378
+ expect(output.result).toEqual(expected);
379
+ },
380
+ CHILD_PROCESS_TEST_BUDGET_MS,
381
+ );
382
+ }
383
+ }
384
+ });
385
+
135
386
  describe("native-only executor behavior", () => {
136
387
  test.skipIf(SKIP_SANDBOX_SPAWN_TESTS)("timeout maps to timeout", async () => {
137
388
  const output = await new NativeScriptExecutor().run(
@@ -69,6 +69,9 @@ const server = Bun.serve({
69
69
  afterAll(() => server.stop(true));
70
70
 
71
71
  const baseUrl = `http://127.0.0.1:${server.port}`;
72
+ // The documented binary body caps (scripts-runtime guide).
73
+ const MAX_BINARY_BODY_BYTES = 16 * 1024 * 1024;
74
+ const MAX_PENDING_BINARY_BODY_BYTES = 64 * 1024 * 1024;
72
75
  const executor = new QuickJSScriptExecutor();
73
76
 
74
77
  function input(source: string, overrides: Partial<ExecutorInput> = {}): ExecutorInput {
@@ -294,6 +297,35 @@ export default async (args: T) => explode(args);
294
297
  expect(output.runtimeError?.message).toContain("fetch body must be a string");
295
298
  });
296
299
 
300
+ // Parity with native lives in script-executor-conformance.test.ts. These
301
+ // are the documented limits of the shims.
302
+ test("shims refuse what they do not implement, with a clear error", async () => {
303
+ const output = await executor.run(
304
+ input(`export default async () => {
305
+ const attempt = (run) => { try { run(); return "ok"; } catch (e) { return e.name + ": " + e.message; } };
306
+ return {
307
+ latin1: attempt(() => new TextDecoder("latin1")),
308
+ floatRandom: attempt(() => crypto.getRandomValues(new Float64Array(2))),
309
+ subtle: typeof crypto.subtle,
310
+ };
311
+ };`),
312
+ );
313
+ expect(output.result).toEqual({
314
+ latin1: 'RangeError: quickjs executor: TextDecoder supports utf-8 only, not "latin1"',
315
+ floatRandom: "TypeError: crypto.getRandomValues: the argument must be an integer TypedArray",
316
+ subtle: "undefined",
317
+ });
318
+ });
319
+
320
+ test("binary bodies reach the host through ctx.stdlib.fetch too", async () => {
321
+ const output = await executor.run(
322
+ input(
323
+ `export default async (_args, ctx) => (await ctx.stdlib.fetch("${baseUrl}/echo", { method: "POST", body: new TextEncoder().encode("é!") })).json();`,
324
+ ),
325
+ );
326
+ expect(output.result).toMatchObject({ method: "POST", body: "é!" });
327
+ });
328
+
297
329
  test("user config values stay redacted until unwrapped", async () => {
298
330
  const output = await executor.run(
299
331
  input(`export default async (_args, ctx) => {
@@ -436,6 +468,70 @@ export default async (args: T) => explode(args);
436
468
  expect(output.result).toContain("Too many pending host calls");
437
469
  });
438
470
 
471
+ // The sandbox controls bodyBase64, so these call the bridge directly, past
472
+ // the prelude's own encoding, and check that no request reaches the server.
473
+ const bridgeSend = `const send = async (bodyBase64: unknown) =>
474
+ JSON.parse(await (globalThis as any).__host_call("fetch", JSON.stringify([{ url: "${baseUrl}/sink", method: "POST", bodyBase64 }]))).error?.message ?? "sent";`;
475
+
476
+ test("rejects a malformed binary body before any fetch starts", async () => {
477
+ const before = requests.length;
478
+ const output = await executor.run(
479
+ input(`export default async () => {
480
+ ${bridgeSend}
481
+ return [await send(123), await send({ length: 4 }), await send("!!!!"), await send("AAA"), await send("AA=A")];
482
+ };`),
483
+ );
484
+ expect(output.result).toEqual([
485
+ "fetch bodyBase64 must be a string",
486
+ "fetch bodyBase64 must be a string",
487
+ "fetch bodyBase64 is not valid base64",
488
+ "fetch bodyBase64 is not valid base64",
489
+ "fetch bodyBase64 is not valid base64",
490
+ ]);
491
+ expect(requests.length).toBe(before);
492
+ });
493
+
494
+ test("rejects an oversized binary body before any fetch starts", async () => {
495
+ const before = requests.length;
496
+ const encodedLimit = Math.ceil(MAX_BINARY_BODY_BYTES / 3) * 4;
497
+ const output = await executor.run(
498
+ input(`export default async () => {
499
+ ${bridgeSend}
500
+ return {
501
+ encoded: await send("A".repeat(${encodedLimit + 4})),
502
+ decoded: await send("A".repeat(${encodedLimit})),
503
+ atLimit: await send("A".repeat(${encodedLimit - 4}) + "AA=="),
504
+ };
505
+ };`),
506
+ );
507
+ const tooLarge = `fetch binary body exceeds the ${MAX_BINARY_BODY_BYTES}-byte limit of the quickjs executor`;
508
+ expect(output.result).toEqual({ encoded: tooLarge, decoded: tooLarge, atLimit: "sent" });
509
+ // Only the body at the limit reached the server.
510
+ expect(requests.length).toBe(before + 1);
511
+ });
512
+
513
+ test("caps the binary bodies one run can have pending", async () => {
514
+ const before = { ...hang };
515
+ const encodedLimit = Math.ceil(MAX_BINARY_BODY_BYTES / 3) * 4;
516
+ const output = await executor.run(
517
+ input(
518
+ `export default async () => {
519
+ // Decodes to exactly MAX_BINARY_BODY_BYTES; four fill the per-run budget.
520
+ const args = JSON.stringify([{ url: "${baseUrl}/hang", method: "POST", bodyBase64: "A".repeat(${encodedLimit - 4}) + "AA==" }]);
521
+ const call = async () => JSON.parse(await (globalThis as any).__host_call("fetch", args)).error?.message ?? "sent";
522
+ const held = [call(), call(), call(), call()];
523
+ return call();
524
+ };`,
525
+ { resources: { ...DEFAULT_SCRIPT_RESOURCES, memoryMb: 1024, wallClockMs: 10_000 } },
526
+ ),
527
+ );
528
+ expect(output.result).toBe(
529
+ `Pending fetch binary bodies in one script run exceed ${MAX_PENDING_BINARY_BODY_BYTES} bytes`,
530
+ );
531
+ expect(await waitFor(() => hang.started === before.started + 4)).toBe(true);
532
+ expect(await waitFor(() => hang.aborted === before.aborted + 4)).toBe(true);
533
+ }, 30_000);
534
+
439
535
  test("queues runs beyond the pool size", async () => {
440
536
  const outputs = await Promise.all(
441
537
  Array.from({ length: 12 }, (_, i) =>
@@ -24,6 +24,7 @@ import {
24
24
  clearVolatileSecretsForTesting,
25
25
  refreshSecretScrubberCache,
26
26
  } from "../utils/secret-scrubber";
27
+ import { executeRawLlm } from "../workflows/executors/raw-llm";
27
28
  import { randomToken } from "./synthetic-secret-helpers";
28
29
 
29
30
  const TEST_DB_PATH = "./test-script-runs-http.sqlite";
@@ -661,3 +662,91 @@ describe("durable launch source with an embedded secret", () => {
661
662
  }
662
663
  });
663
664
  });
665
+
666
+ describe("POST /api/internal/raw-llm failure", () => {
667
+ const KEY_VARS = ["NAN_API_KEY", "HELMCODE_API_NAN"] as const;
668
+
669
+ // The provider echoes the key it received; `selectCredential` sends it
670
+ // trimmed, so a padded config value must still be redacted.
671
+ async function withNanKey(
672
+ keyVar: (typeof KEY_VARS)[number],
673
+ pad: string,
674
+ fn: (dummy: string, calls: () => number) => Promise<void>,
675
+ dummy = `nan-${randomToken(32)}`,
676
+ ) {
677
+ const previous = Object.fromEntries(KEY_VARS.map((name) => [name, process.env[name]]));
678
+ const previousFetch = globalThis.fetch;
679
+ for (const name of KEY_VARS) delete process.env[name];
680
+ process.env[keyVar] = `${pad}${dummy}${pad}`;
681
+ refreshSecretScrubberCache();
682
+ let calls = 0;
683
+ globalThis.fetch = (async () => {
684
+ calls++;
685
+ return new Response(
686
+ JSON.stringify({ error: { message: `Invalid API key: ${dummy}`, type: "auth" } }),
687
+ { status: 401, headers: { "Content-Type": "application/json" } },
688
+ );
689
+ }) as unknown as typeof fetch;
690
+ try {
691
+ await fn(dummy, () => calls);
692
+ } finally {
693
+ globalThis.fetch = previousFetch;
694
+ for (const name of KEY_VARS) {
695
+ if (previous[name] === undefined) delete process.env[name];
696
+ else process.env[name] = previous[name];
697
+ }
698
+ refreshSecretScrubberCache();
699
+ }
700
+ }
701
+
702
+ for (const keyVar of KEY_VARS) {
703
+ for (const pad of ["", " "]) {
704
+ const label = pad ? "padded" : "unpadded";
705
+ test(`does not echo a provider error that carries the ${label} ${keyVar} key`, async () => {
706
+ await withNanKey(keyVar, pad, async (dummy, calls) => {
707
+ const response = await dispatch("/api/internal/raw-llm", {
708
+ method: "POST",
709
+ agentId,
710
+ body: JSON.stringify({ prompt: "hi", model: "nan/qwen3.8-flash" }),
711
+ });
712
+ expect(calls()).toBe(1);
713
+ expect(response.status).toBe(500);
714
+ expect(response.text).not.toContain(dummy);
715
+ const body = (await response.json()) as { error: string };
716
+ expect(body.error).toContain("LLM call failed");
717
+ expect(body.error).toContain(`[REDACTED:${keyVar}]`);
718
+ });
719
+ });
720
+ }
721
+
722
+ // A random key can contain "529"; that must not read as an overload.
723
+ test(`a ${keyVar} key containing 529 is not mistaken for a rate limit`, async () => {
724
+ await withNanKey(
725
+ keyVar,
726
+ "",
727
+ async (dummy, calls) => {
728
+ const result = await executeRawLlm({ prompt: "hi", model: "nan/qwen3.8-flash" });
729
+ expect(calls()).toBe(1);
730
+ expect(result.status).toBe("failed");
731
+ if (result.status === "failed") expect(result.error).not.toContain(dummy);
732
+ },
733
+ `nan-5L76Mxo2D529A9S0GrqOZWIpYdfO1aIM`,
734
+ );
735
+ });
736
+
737
+ test(`scrubs the fallback-port error for a padded ${keyVar} key`, async () => {
738
+ await withNanKey(keyVar, " ", async (dummy, calls) => {
739
+ const result = await executeRawLlm({
740
+ prompt: "hi",
741
+ model: "nan/qwen3.8-flash",
742
+ fallbackPort: "fallback",
743
+ });
744
+ expect(calls()).toBe(1);
745
+ expect(result.status).toBe("success");
746
+ expect(result.error).toContain("using fallback port");
747
+ expect(result.error).not.toContain(dummy);
748
+ expect(result.error).toContain(`[REDACTED:${keyVar}]`);
749
+ });
750
+ });
751
+ }
752
+ });