@desplega.ai/agent-swarm 2.6.0 → 2.7.1

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 (324) hide show
  1. package/README.md +2 -0
  2. package/dist/{acp-adapter-dgfakfpw.js → acp-adapter-8dn5tq56.js} +6 -6
  3. package/dist/{actions-fbqma8aa.js → actions-3r6gw7dy.js} +18 -18
  4. package/dist/{amp-adapter-en0jy6hs.js → amp-adapter-wjtq413p.js} +3 -3
  5. package/dist/{anthropic-messages-txs190dd.js → anthropic-messages-n4cby1rz.js} +4 -4
  6. package/dist/{app-k8a5cgpp.js → app-d53yhykk.js} +3 -3
  7. package/dist/{assistant-a16433gr.js → assistant-ksrawj3k.js} +20 -20
  8. package/dist/{boot-reembed-6c1sjfnr.js → boot-reembed-fxfw17m2.js} +11 -3
  9. package/dist/{boot-reembed-f4nn561n.js → boot-reembed-k5y5xbj0.js} +15 -8
  10. package/dist/{boot-scrub-sweep-d4w9gajf.js → boot-scrub-sweep-se41j9fd.js} +3 -3
  11. package/dist/{boot-seeds-9bmkspyw.js → boot-seeds-r3kwwv8b.js} +4 -4
  12. package/dist/{claude-adapter-nys1kjxa.js → claude-adapter-ahmyf231.js} +8 -8
  13. package/dist/{claude-managed-adapter-55207973.js → claude-managed-adapter-5knamegc.js} +3 -3
  14. package/dist/{claude-managed-setup-3cm35gey.js → claude-managed-setup-mdc7dy6b.js} +3 -3
  15. package/dist/{claude-sdk-session-nw5yd8cp.js → claude-sdk-session-n5wen9zk.js} +8729 -8600
  16. package/dist/{cli-683y0b3a.js → cli-0dqtv2jg.js} +2 -2
  17. package/dist/{cli-a34pr3w1.js → cli-0tww7dnz.js} +2 -2
  18. package/dist/{cli-w2656ka1.js → cli-0w4e4v58.js} +7 -7
  19. package/dist/{cli-5sckpqcx.js → cli-2sjm3t4r.js} +1 -1
  20. package/dist/{cli-gf2850q7.js → cli-3cywb44q.js} +18 -1
  21. package/dist/{cli-8wkb09q5.js → cli-3k9jej0c.js} +2 -2
  22. package/dist/{cli-3aqd4jaa.js → cli-3v2cyqb8.js} +3 -3
  23. package/dist/{cli-emhjcj2y.js → cli-5qt9vzmq.js} +89 -20
  24. package/dist/{cli-abkm71vr.js → cli-5rp1w4cr.js} +1 -1
  25. package/dist/{cli-gf95q3bm.js → cli-6vwjsetx.js} +4 -4
  26. package/dist/{cli-k21gx5ht.js → cli-6w2yrrc0.js} +10 -10
  27. package/dist/{cli-m70bajzr.js → cli-75tdydgv.js} +1 -1
  28. package/dist/{cli-5rj83bam.js → cli-8feswf2v.js} +47 -36
  29. package/dist/{cli-sxgtf5fw.js → cli-8wd3mbyj.js} +1 -1
  30. package/dist/{cli-ca6banwz.js → cli-9csfthbw.js} +10 -8
  31. package/dist/{cli-dqygeeh5.js → cli-b41p764a.js} +31 -3
  32. package/dist/{cli-m5a9m0ak.js → cli-c1t533hh.js} +3 -3
  33. package/dist/{cli-2sjhpsns.js → cli-c28jxpcv.js} +1 -1
  34. package/dist/{cli-bqztfyve.js → cli-cb50g9cd.js} +1 -1
  35. package/dist/{cli-ag2yrnt5.js → cli-d8gaphtr.js} +2 -2
  36. package/dist/{cli-9abdgn5t.js → cli-dgtfysne.js} +3 -3
  37. package/dist/{cli-nq677csy.js → cli-dscnhn18.js} +1 -1
  38. package/dist/{cli-3fwqy71j.js → cli-dv11pgpv.js} +39 -32
  39. package/dist/{cli-v49fkghp.js → cli-ekptgk2m.js} +6 -6
  40. package/dist/{cli-c2habvsf.js → cli-f3mzy662.js} +17 -17
  41. package/dist/{cli-zqne1qcj.js → cli-fk7k8s9x.js} +2 -2
  42. package/dist/{cli-1tem6ns9.js → cli-gt4h8cxw.js} +3 -2
  43. package/dist/{cli-vbhgkw9h.js → cli-h3e7bhvn.js} +3 -3
  44. package/dist/{cli-ffvsj2qw.js → cli-hkxqdmnb.js} +3 -3
  45. package/dist/{cli-a2rr0fmg.js → cli-hpkvgdad.js} +7 -4
  46. package/dist/{cli-art7c1zh.js → cli-hq2v4rr5.js} +37 -9
  47. package/dist/{cli-sqma6vw1.js → cli-k1zb7raw.js} +5 -5
  48. package/dist/{cli-4rvr7zfx.js → cli-k4awgvzm.js} +5 -0
  49. package/dist/{cli-3ndmpxxv.js → cli-k7d3hvya.js} +2 -2
  50. package/dist/{cli-3gfvmzht.js → cli-kbtdftbw.js} +74 -26
  51. package/dist/{cli-hfeb68z4.js → cli-kj69b9tw.js} +34 -6
  52. package/dist/{cli-az25fn5f.js → cli-kk9jy4nm.js} +2 -2
  53. package/dist/{cli-39aet655.js → cli-m52t4cta.js} +1 -1
  54. package/dist/{cli-cz37jqss.js → cli-mdadewqp.js} +5 -5
  55. package/dist/{cli-esakngh8.js → cli-nf6k60sn.js} +1 -1
  56. package/dist/{cli-j6rktc91.js → cli-nzdf09js.js} +1 -1
  57. package/dist/{cli-rr7zx19j.js → cli-prcyzecg.js} +12 -12
  58. package/dist/{cli-4hxrf0p4.js → cli-pzafyqtk.js} +393 -62
  59. package/dist/{cli-g8ka5j4j.js → cli-qvaa1gjp.js} +173 -52
  60. package/dist/{cli-d2dpf95n.js → cli-rg4b2g6j.js} +1 -1
  61. package/dist/{cli-kf4jree9.js → cli-rm66vqdz.js} +1 -1
  62. package/dist/{cli-4rf0f5vg.js → cli-s504c3w7.js} +5 -3
  63. package/dist/{cli-nn6vx2mq.js → cli-swkx9jm2.js} +7 -5
  64. package/dist/{cli-g20jjwt4.js → cli-thsaq4pd.js} +16 -16
  65. package/dist/{cli-aj8djyqt.js → cli-twjnyvs8.js} +2 -2
  66. package/dist/{cli-03gg9pd1.js → cli-typdpe8g.js} +4 -4
  67. package/dist/{cli-mtnj4fwa.js → cli-vg8pwmsd.js} +6 -6
  68. package/dist/{cli-gnsb1zxa.js → cli-vhpckzvp.js} +1 -1
  69. package/dist/{cli-7enjwya9.js → cli-vz78s5wm.js} +1 -1
  70. package/dist/{cli-dv97kdzv.js → cli-wdxgsdz5.js} +2 -2
  71. package/dist/{cli-7a6p4e65.js → cli-wtjg6je3.js} +1 -1
  72. package/dist/{cli-6y51ryva.js → cli-x3cngb1h.js} +44 -12
  73. package/dist/{cli-n7asmce6.js → cli-xbz6mcg4.js} +3 -3
  74. package/dist/{cli-24j1jeeb.js → cli-y4tdxsqb.js} +3 -3
  75. package/dist/{cli-mvjz5an3.js → cli-yqrsp3ny.js} +7 -7
  76. package/dist/cli.js +31 -21
  77. package/dist/{codex-adapter-d95q4qsn.js → codex-adapter-7wepzq15.js} +6 -6
  78. package/dist/{codex-hook-brpnycn5.js → codex-hook-pbdh4xhx.js} +2 -2
  79. package/dist/{codex-session-runner-gdwd8h0c.js → codex-session-runner-02sb49e4.js} +6 -6
  80. package/dist/{commands-jqmgk54b.js → commands-3rcspxct.js} +2 -2
  81. package/dist/{controller-z3jq589f.js → controller-8ar4avd6.js} +3 -3
  82. package/dist/{controllers-connect-r2za39ef.js → controllers-connect-ajvh9h0v.js} +19 -19
  83. package/dist/{cursor-adapter-4mr4jgkw.js → cursor-adapter-gd0sa7bh.js} +1 -1
  84. package/dist/{db-w36wwb5g.js → db-dv9j7ts5.js} +2 -2
  85. package/dist/{db-ppv2tze7.js → db-z26vgdvj.js} +3 -3
  86. package/dist/{dispatcher-pv5h77vf.js → dispatcher-8m0htjq8.js} +17 -17
  87. package/dist/{dsh-adapter-d4pdpb3r.js → dsh-adapter-y8gyk130.js} +1 -1
  88. package/dist/{e2b-fn3zacvt.js → e2b-74vg9b7e.js} +1 -1
  89. package/dist/{e2b-222hsajc.js → e2b-jyay8wzn.js} +1 -1
  90. package/dist/{easter-egg-3d-4sp4dvmc.js → easter-egg-3d-28f880xx.js} +1 -1
  91. package/dist/{execute-jev839y1.js → execute-9seb79vh.js} +1 -1
  92. package/dist/{external-channel-y6nkzhnn.js → external-channel-smz7jyth.js} +2 -2
  93. package/dist/{getMachineId-bsd-brn0xym3.js → getMachineId-bsd-whfre6gr.js} +3 -3
  94. package/dist/{getMachineId-darwin-t339sfxm.js → getMachineId-darwin-5zjynb19.js} +3 -3
  95. package/dist/{getMachineId-linux-5eq95d8b.js → getMachineId-linux-8mksrrsq.js} +3 -3
  96. package/dist/{getMachineId-unsupported-qavrtc7j.js → getMachineId-unsupported-fxtqsdt2.js} +3 -3
  97. package/dist/{getMachineId-win-q7fe1wx7.js → getMachineId-win-nzfmz857.js} +3 -3
  98. package/dist/{grok-adapter-t08b9ntm.js → grok-adapter-z0nnvtbn.js} +7 -7
  99. package/dist/handlers-pspryh6g.js +41 -0
  100. package/dist/{heartbeat-vn31xwdk.js → heartbeat-f928aqyp.js} +20 -20
  101. package/dist/{hook-cpjs8q51.js → hook-d7c605r4.js} +7 -7
  102. package/dist/{hook-6d0zbvvt.js → hook-q9hv5xf9.js} +5 -5
  103. package/dist/{http-9c6medda.js → http-q6j4k989.js} +1000 -251
  104. package/dist/{index-k0yt1tvq.js → index-5nb266jr.js} +17 -17
  105. package/dist/{index-ysd45kgc.js → index-8fxt5eah.js} +10 -10
  106. package/dist/{index-zgvqzjsw.js → index-cekzwhez.js} +5 -5
  107. package/dist/{index-38t35h0n.js → index-ch0c1xff.js} +17 -17
  108. package/dist/{index-pkma1yf8.js → index-ebnjhxh1.js} +1 -1
  109. package/dist/{index-yqgbfgaj.js → index-ed8dy8rn.js} +20 -20
  110. package/dist/{index-qhjf2htm.js → index-ejqdkkqx.js} +262 -96
  111. package/dist/{index-dr94zbnh.js → index-farm1zsn.js} +32661 -29443
  112. package/dist/{index-g6k9tctj.js → index-yn1yxg85.js} +1 -1
  113. package/dist/{keepalive-em0117bp.js → keepalive-zzc3hks1.js} +3 -3
  114. package/dist/lead-c0pjcaw4.js +79 -0
  115. package/dist/{maintenance-kbme3qy9.js → maintenance-kqsxzpzx.js} +1 -1
  116. package/dist/{oauth-refresh-sweep-xysj4yse.js → oauth-refresh-sweep-7qt0g55z.js} +8 -3
  117. package/dist/{onboard-p6q0vz97.js → onboard-k7x13fqg.js} +121 -8
  118. package/dist/{opencode-adapter-esv3k9yn.js → opencode-adapter-tqvrtter.js} +5 -5
  119. package/dist/{otel-impl-54qhqh1z.js → otel-impl-sqkf6k3b.js} +97 -97
  120. package/dist/{pi-mono-adapter-mymbwhj0.js → pi-mono-adapter-m2qnafxm.js} +89 -14
  121. package/dist/{pricing-refresh-yb75wv33.js → pricing-refresh-wkpwhgvs.js} +3 -3
  122. package/dist/{rbac-roles-dexhc9x8.js → rbac-roles-dpn0c0wj.js} +7 -4
  123. package/dist/{rbac-roles-1vp2mw4p.js → rbac-roles-wk7xd6a6.js} +2 -2
  124. package/dist/{render-v2-tjay5hhf.js → render-v2-ex2m4g2k.js} +3 -3
  125. package/dist/{resume-8ayf37g5.js → resume-725g9b0b.js} +7 -7
  126. package/dist/{runtime-hqe5cqcd.js → runtime-kajas15g.js} +1 -1
  127. package/dist/{session-access-fdhp987s.js → session-access-0j2ezjje.js} +8 -4
  128. package/dist/{session-source-backfill-qgtc86z1.js → session-source-backfill-62202awe.js} +2 -2
  129. package/dist/{setup-4rrmqcq3.js → setup-9sj11ax6.js} +1 -1
  130. package/dist/{virtual-modules-52km00d6.js → virtual-modules-c6fcq6zr.js} +2 -2
  131. package/dist/{work-objects-p28fgd70.js → work-objects-0y07495y.js} +20 -20
  132. package/dist/worker-d4q9sqbc.js +79 -0
  133. package/openapi.json +1536 -407
  134. package/package.json +7 -5
  135. package/src/be/automation-preflight.ts +2 -0
  136. package/src/be/connector-codes.ts +65 -22
  137. package/src/be/db/api-keys.ts +9 -0
  138. package/src/be/db/workflow-runs.ts +36 -5
  139. package/src/be/db.ts +80 -22
  140. package/src/be/memory/boot-reembed.ts +4 -1
  141. package/src/be/memory/constants.ts +6 -0
  142. package/src/be/memory/embedding-recovery.ts +48 -0
  143. package/src/be/memory/index-content.ts +5 -2
  144. package/src/be/memory/providers/openai-embedding.ts +65 -14
  145. package/src/be/memory/task-learning.ts +257 -0
  146. package/src/be/memory/types.ts +13 -2
  147. package/src/be/migrations/245_script_executor.sql +3 -0
  148. package/src/be/migrations/246_connector_code_kind.sql +11 -0
  149. package/src/be/oauth-refresh-sweep.ts +2 -1
  150. package/src/be/onboarding.ts +125 -15
  151. package/src/be/rbac-roles.ts +4 -1
  152. package/src/be/scripts/boot-reembed.ts +2 -0
  153. package/src/be/scripts/db.ts +14 -4
  154. package/src/be/scripts/embeddings.ts +4 -2
  155. package/src/be/scripts/run-saved.ts +1 -0
  156. package/src/be/scripts/typecheck.ts +9 -3
  157. package/src/be/seed/agent-fs-provision.ts +75 -14
  158. package/src/be/seed-skills/index.ts +7 -0
  159. package/src/be/swarm-config-guard.ts +3 -0
  160. package/src/cli.tsx +9 -0
  161. package/src/comb/config.ts +6 -1
  162. package/src/commands/onboard/agent-fs-version.ts +5 -0
  163. package/src/commands/onboard/compose-generator.ts +117 -4
  164. package/src/commands/onboard/env-generator.ts +9 -0
  165. package/src/commands/onboard/tei-image.ts +12 -0
  166. package/src/commands/onboard/types.ts +7 -0
  167. package/src/commands/onboard.tsx +11 -1
  168. package/src/commands/provider-credentials.ts +2 -1
  169. package/src/commands/runner.ts +14 -1
  170. package/src/heartbeat/heartbeat.ts +14 -1
  171. package/src/heartbeat/script-runner-probe.ts +47 -0
  172. package/src/http/agentfs-proxy.ts +369 -0
  173. package/src/http/all-routes.ts +1 -0
  174. package/src/http/api-keys.ts +24 -9
  175. package/src/http/codex-oauth-device.ts +8 -1
  176. package/src/http/fs.ts +64 -10
  177. package/src/http/idempotency-key.ts +1 -1
  178. package/src/http/inbox-state.ts +27 -1
  179. package/src/http/index.ts +7 -4
  180. package/src/http/memory.ts +7 -4
  181. package/src/http/onboarding.ts +139 -11
  182. package/src/http/pages-public.ts +38 -5
  183. package/src/http/scripts.ts +27 -0
  184. package/src/http/session-access.ts +24 -0
  185. package/src/http/sessions.ts +10 -6
  186. package/src/http/tasks.ts +85 -11
  187. package/src/http/telemetry-ui.ts +1 -1
  188. package/src/http/user-roles.ts +4 -1
  189. package/src/http/users.ts +272 -3
  190. package/src/http/utils.ts +22 -3
  191. package/src/http/workflows.ts +10 -0
  192. package/src/http/x.ts +5 -0
  193. package/src/prompts/base-prompt.ts +45 -10
  194. package/src/prompts/session-templates.ts +43 -5
  195. package/src/providers/amp-adapter.ts +1 -1
  196. package/src/providers/codex-adapter.ts +6 -1
  197. package/src/providers/cursor-adapter.ts +1 -1
  198. package/src/providers/dsh-adapter.ts +1 -1
  199. package/src/providers/pi-mono-adapter.ts +99 -14
  200. package/src/rbac/legacy-policy.ts +1 -0
  201. package/src/rbac/permissions.ts +4 -0
  202. package/src/scheduler/scheduler.ts +2 -1
  203. package/src/scripts-runtime/eval-harness.ts +8 -1
  204. package/src/scripts-runtime/executors/native.ts +22 -16
  205. package/src/scripts-runtime/executors/quickjs-prelude.ts +103 -1
  206. package/src/scripts-runtime/executors/quickjs-runner.ts +2 -1
  207. package/src/scripts-runtime/executors/quickjs.ts +7 -0
  208. package/src/scripts-runtime/executors/registry.ts +32 -0
  209. package/src/scripts-runtime/executors/types.ts +7 -0
  210. package/src/scripts-runtime/host-validated-args.ts +133 -0
  211. package/src/scripts-runtime/loader.ts +30 -3
  212. package/src/scripts-runtime/swarm-sdk.ts +9 -2
  213. package/src/scripts-runtime/types/stdlib.d.ts +9 -1
  214. package/src/scripts-runtime/types/swarm-sdk.d.ts +9 -1
  215. package/src/tests/agent-fs-provision-seeder.test.ts +261 -92
  216. package/src/tests/agentfs-proxy.test.ts +1106 -0
  217. package/src/tests/api-key-status-by-suffix.test.ts +242 -0
  218. package/src/tests/base-prompt.test.ts +139 -4
  219. package/src/tests/codex-adapter.test.ts +46 -8
  220. package/src/tests/codex-oauth-device.test.ts +12 -1
  221. package/src/tests/comb-config-guard.test.ts +20 -0
  222. package/src/tests/comb-links.test.ts +79 -1
  223. package/src/tests/comb-status.test.ts +44 -0
  224. package/src/tests/community-linear-delivery-loop.test.ts +2099 -0
  225. package/src/tests/credential-check.test.ts +4 -2
  226. package/src/tests/docs-index-footprint.test.js +3 -3
  227. package/src/tests/docs-index.test.ts +135 -0
  228. package/src/tests/dsh-adapter.test.ts +1 -1
  229. package/src/tests/fetch-resolved-env.test.ts +16 -2
  230. package/src/tests/fixtures/onboard/baseline-states.ts +68 -0
  231. package/src/tests/fixtures/onboard/content.compose.yml.txt +190 -0
  232. package/src/tests/fixtures/onboard/content.env.txt +29 -0
  233. package/src/tests/fixtures/onboard/dev.compose.yml.txt +155 -0
  234. package/src/tests/fixtures/onboard/dev.env.txt +28 -0
  235. package/src/tests/fixtures/onboard/full-all.compose.yml.txt +582 -0
  236. package/src/tests/fixtures/onboard/full-all.env.txt +59 -0
  237. package/src/tests/fixtures/onboard/full.compose.yml.txt +435 -0
  238. package/src/tests/fixtures/onboard/full.env.txt +36 -0
  239. package/src/tests/fixtures/onboard/research.compose.yml.txt +155 -0
  240. package/src/tests/fixtures/onboard/research.env.txt +28 -0
  241. package/src/tests/fixtures/onboard/solo.compose.yml.txt +85 -0
  242. package/src/tests/fixtures/onboard/solo.env.txt +26 -0
  243. package/src/tests/heartbeat-checklist.test.ts +89 -0
  244. package/src/tests/heartbeat-script-runner-probe.test.ts +83 -0
  245. package/src/tests/http-log-scrubbing.test.ts +34 -1
  246. package/src/tests/http-task-write-ownership.test.ts +5 -1
  247. package/src/tests/invites-http.test.ts +464 -0
  248. package/src/tests/memory-embedding-input-cap.test.ts +122 -1
  249. package/src/tests/memory-embedding-recovery.test.ts +137 -0
  250. package/src/tests/oauth-refresh-sweep.test.ts +34 -1
  251. package/src/tests/onboard-compose.test.ts +162 -0
  252. package/src/tests/onboard-env.test.ts +24 -0
  253. package/src/tests/onboard-non-interactive.test.ts +29 -0
  254. package/src/tests/onboard-tei-image.test.ts +36 -0
  255. package/src/tests/onboarding.test.ts +451 -4
  256. package/src/tests/openai-embedding-provider.test.ts +105 -2
  257. package/src/tests/opencode-adapter.test.ts +2 -1
  258. package/src/tests/pages-frame-ancestors.test.ts +162 -0
  259. package/src/tests/pi-codemode.test.ts +2 -1
  260. package/src/tests/pi-mono-adapter.test.ts +54 -0
  261. package/src/tests/pi-mono-session-cost-on-abort.test.ts +270 -0
  262. package/src/tests/pi-native-mcp.test.ts +13 -1
  263. package/src/tests/pi-tool-exposure.test.ts +8 -8
  264. package/src/tests/preload-env.test.ts +4 -0
  265. package/src/tests/preload.ts +4 -0
  266. package/src/tests/probe-tool-schema-tokens.test.ts +144 -0
  267. package/src/tests/prompt-template-session.test.ts +20 -3
  268. package/src/tests/rbac-admission-e2e.test.ts +22 -6
  269. package/src/tests/rbac-admission.test.ts +342 -4
  270. package/src/tests/rbac-engine.test.ts +1 -0
  271. package/src/tests/rbac-roles.test.ts +9 -1
  272. package/src/tests/request-composition-derive.test.ts +329 -0
  273. package/src/tests/script-connections.test.ts +2 -0
  274. package/src/tests/script-executor-conformance.test.ts +36 -1
  275. package/src/tests/script-executor-quickjs-pool.test.ts +50 -0
  276. package/src/tests/script-executor-quickjs.test.ts +140 -0
  277. package/src/tests/script-host-validated-args-native.test.ts +196 -0
  278. package/src/tests/script-per-script-executor.test.ts +415 -0
  279. package/src/tests/scripts-external-api.test.ts +45 -0
  280. package/src/tests/sdk-allowlist.test.ts +3 -2
  281. package/src/tests/self-improvement.test.ts +4 -4
  282. package/src/tests/send-task-requester-gate.test.ts +64 -3
  283. package/src/tests/sessions-move.test.ts +39 -1
  284. package/src/tests/store-progress-learning.test.ts +381 -0
  285. package/src/tests/ui-logs-parser.test.ts +180 -0
  286. package/src/tests/whoami-route.test.ts +41 -0
  287. package/src/tests/workflow-engine-v2.test.ts +116 -1
  288. package/src/tests/workflow-mcp-trigger-schema.test.ts +209 -0
  289. package/src/tests/workflow-run-finished-event.test.ts +263 -0
  290. package/src/tests/workflow-swarm-script.test.ts +34 -0
  291. package/src/tools/inject-learning.ts +1 -1
  292. package/src/tools/memory-edit.ts +1 -1
  293. package/src/tools/memory-search.ts +1 -1
  294. package/src/tools/script-common.ts +1 -0
  295. package/src/tools/script-run.ts +7 -0
  296. package/src/tools/script-upsert.ts +7 -0
  297. package/src/tools/store-progress.ts +71 -1
  298. package/src/tools/utils.ts +12 -1
  299. package/src/tools/workflows/trigger-workflow.ts +104 -10
  300. package/src/types.ts +12 -1
  301. package/src/utils/constants.ts +39 -0
  302. package/src/utils/credentials.ts +5 -2
  303. package/src/utils/task-attachment-links.ts +15 -3
  304. package/src/workflows/engine.ts +18 -16
  305. package/src/workflows/executors/swarm-script.ts +5 -0
  306. package/src/workflows/foreach-driver.ts +5 -11
  307. package/src/workflows/node-timeout.ts +30 -0
  308. package/templates/community/linear-delivery-loop/PLAYBOOK.md +323 -0
  309. package/templates/community/linear-delivery-loop/README.md +38 -0
  310. package/templates/community/linear-delivery-loop/config.example.json +31 -0
  311. package/templates/community/linear-delivery-loop/lead-prompt.md +34 -0
  312. package/templates/community/linear-delivery-loop/schedule.json +10 -0
  313. package/templates/community/linear-delivery-loop/scripts/linear-delivery-setup.ts +148 -0
  314. package/templates/community/linear-delivery-loop/scripts/linear-delivery-tick.ts +1781 -0
  315. package/templates/skills/swarm-docs/config.json +14 -0
  316. package/templates/skills/swarm-docs/content.md +144 -0
  317. package/dist/handlers-eqsnmv95.js +0 -41
  318. package/dist/lead-23e7z59t.js +0 -79
  319. package/dist/worker-e3w7vye1.js +0 -79
  320. package/dist/{azure-openai-responses-9kfwhmn7.js → azure-openai-responses-43asa23s.js} +0 -0
  321. package/dist/{node-vxx896fa.js → node-t28cc56b.js} +0 -0
  322. package/dist/{openai-codex-responses-79s8xnbh.js → openai-codex-responses-jhr3sjtm.js} +3 -3
  323. package/dist/{openai-completions-tdbyts0x.js → openai-completions-gs6yn37k.js} +3 -3
  324. /package/dist/{openai-responses-dm3t5vqp.js → openai-responses-jqm9vwwk.js} +0 -0
@@ -0,0 +1,329 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import {
3
+ type CompositionCall,
4
+ type CompositionManifest,
5
+ compositionCallsOf,
6
+ deriveCallCompositions,
7
+ staticTokens,
8
+ } from "../../apps/ui/src/lib/request-composition";
9
+ import type { CallUsage, NormalizedItem } from "../../apps/ui/src/logs-parser/types";
10
+
11
+ const T0 = Date.parse("2026-10-26T10:00:00.000Z");
12
+
13
+ function manifest(overrides: Partial<CompositionManifest> = {}): CompositionManifest {
14
+ return {
15
+ id: "m-0",
16
+ taskId: "task-1",
17
+ agentId: "agent-1",
18
+ sessionId: "s-1",
19
+ seq: 0,
20
+ provider: "claude",
21
+ model: null,
22
+ source: "worker",
23
+ parts: [
24
+ {
25
+ kind: "system_section",
26
+ id: "system.agent.role",
27
+ label: "role",
28
+ chars: 4000,
29
+ estTokens: 1000,
30
+ fidelity: "exact",
31
+ },
32
+ {
33
+ kind: "persona",
34
+ id: "persona",
35
+ label: "persona",
36
+ chars: 8000,
37
+ estTokens: 2000,
38
+ fidelity: "exact",
39
+ },
40
+ {
41
+ kind: "tool_schemas",
42
+ id: "mcp:agent-swarm",
43
+ label: "tools",
44
+ chars: 40000,
45
+ estTokens: 10000,
46
+ fidelity: "estimate",
47
+ },
48
+ {
49
+ kind: "skills_listing",
50
+ id: "skills_listing",
51
+ label: "skills",
52
+ chars: 4000,
53
+ estTokens: 1000,
54
+ fidelity: "estimate",
55
+ },
56
+ {
57
+ kind: "task_prompt",
58
+ id: "task_prompt",
59
+ label: "task",
60
+ chars: 2000,
61
+ estTokens: 500,
62
+ fidelity: "exact",
63
+ },
64
+ {
65
+ kind: "memories",
66
+ id: "memories",
67
+ label: "memories",
68
+ chars: 2000,
69
+ estTokens: 500,
70
+ fidelity: "exact",
71
+ },
72
+ ],
73
+ staticEstTokens: 15000,
74
+ tokenRatio: 1,
75
+ createdAt: new Date(T0 - 1000).toISOString(),
76
+ ...overrides,
77
+ };
78
+ }
79
+
80
+ function call(
81
+ callId: string,
82
+ promptTokens: number,
83
+ offsetMs: number,
84
+ extra: Partial<CallUsage> = {},
85
+ sessionId = "s-1",
86
+ ): CompositionCall {
87
+ return {
88
+ sessionId,
89
+ t: T0 + offsetMs,
90
+ usage: {
91
+ callId,
92
+ promptTokens,
93
+ cacheReadTokens: 0,
94
+ cacheWriteTokens: 0,
95
+ uncachedInputTokens: promptTokens,
96
+ outputTokens: 10,
97
+ sidechain: false,
98
+ harness: "claude",
99
+ reportedAt: "start",
100
+ ...extra,
101
+ },
102
+ };
103
+ }
104
+
105
+ function sum(tokens: { tokens: number }[]): number {
106
+ return tokens.reduce((acc, s) => acc + s.tokens, 0);
107
+ }
108
+
109
+ function tokensOf(segments: { group: string; tokens: number }[], group: string): number {
110
+ return segments.find((s) => s.group === group)?.tokens ?? 0;
111
+ }
112
+
113
+ describe("deriveCallCompositions", () => {
114
+ test("segments sum to each call's prompt tokens", () => {
115
+ const { byCall } = deriveCallCompositions(
116
+ [call("c1", 40_000, 0), call("c2", 52_000, 1000), call("c3", 90_000, 2000)],
117
+ [manifest()],
118
+ );
119
+ for (const [id, prompt] of [
120
+ ["c1", 40_000],
121
+ ["c2", 52_000],
122
+ ["c3", 90_000],
123
+ ] as const) {
124
+ const comp = byCall.get(id);
125
+ expect(comp).toBeDefined();
126
+ expect(sum(comp!.segments)).toBeCloseTo(prompt, 6);
127
+ expect(comp!.scaled).toBe(false);
128
+ }
129
+ });
130
+
131
+ test("the first main call anchors the harness base; later calls add history", () => {
132
+ const { byCall, anchors } = deriveCallCompositions(
133
+ [call("c1", 40_000, 0), call("c2", 52_000, 1000)],
134
+ [manifest()],
135
+ );
136
+ const first = byCall.get("c1")!;
137
+ expect(tokensOf(first.segments, "harness")).toBe(25_000);
138
+ expect(tokensOf(first.segments, "history")).toBe(0);
139
+ expect(tokensOf(first.segments, "task")).toBe(1000);
140
+ const second = byCall.get("c2")!;
141
+ expect(tokensOf(second.segments, "harness")).toBe(25_000);
142
+ expect(tokensOf(second.segments, "history")).toBe(12_000);
143
+ expect(anchors.get("m-0")).toEqual({
144
+ callId: "c1",
145
+ promptTokens: 40_000,
146
+ residualTokens: 25_000,
147
+ });
148
+ });
149
+
150
+ test("sub-agent calls are never the anchor and get no composition", () => {
151
+ const { byCall, anchors } = deriveCallCompositions(
152
+ [call("sub", 5_000, 0, { sidechain: true }), call("c1", 40_000, 1000)],
153
+ [manifest()],
154
+ );
155
+ expect(byCall.has("sub")).toBe(false);
156
+ expect(anchors.get("m-0")?.callId).toBe("c1");
157
+ expect(tokensOf(byCall.get("c1")!.segments, "harness")).toBe(25_000);
158
+ });
159
+
160
+ test("a compaction starts a new anchor whose harness base never grows", () => {
161
+ const compactions = [{ sessionId: "s-1", t: T0 + 1500 }];
162
+ // Post-compaction prompt carries a 6k summary: it is history, not harness.
163
+ const grown = deriveCallCompositions(
164
+ [call("c1", 40_000, 0), call("c2", 120_000, 1000), call("c3", 46_000, 2000)],
165
+ [manifest()],
166
+ compactions,
167
+ );
168
+ const post = grown.byCall.get("c3")!;
169
+ expect(tokensOf(post.segments, "harness")).toBe(25_000);
170
+ expect(tokensOf(post.segments, "history")).toBe(6_000);
171
+ // The rail's "first call" stays the session's first call.
172
+ expect(grown.anchors.get("m-0")?.callId).toBe("c1");
173
+
174
+ // The harness dropped part of its base after compacting: the base shrinks.
175
+ const shrunk = deriveCallCompositions(
176
+ [call("c1", 40_000, 0), call("c3", 30_000, 2000), call("c4", 33_000, 3000)],
177
+ [manifest()],
178
+ compactions,
179
+ );
180
+ expect(tokensOf(shrunk.byCall.get("c3")!.segments, "harness")).toBe(15_000);
181
+ expect(tokensOf(shrunk.byCall.get("c4")!.segments, "history")).toBe(3_000);
182
+ // Control: without the compaction, the base stays at the first call's.
183
+ const control = deriveCallCompositions(
184
+ [call("c1", 40_000, 0), call("c3", 30_000, 2000)],
185
+ [manifest()],
186
+ );
187
+ expect(tokensOf(control.byCall.get("c3")!.segments, "harness")).toBe(
188
+ 25_000 * (30_000 / 40_000),
189
+ );
190
+ });
191
+
192
+ test("nothing goes negative when the estimates overshoot the prompt", () => {
193
+ const { byCall } = deriveCallCompositions(
194
+ [call("c1", 9_000, 0), call("c2", 7_500, 1000)],
195
+ [manifest()],
196
+ );
197
+ for (const id of ["c1", "c2"]) {
198
+ const comp = byCall.get(id)!;
199
+ expect(comp.scaled).toBe(true);
200
+ for (const seg of comp.segments) expect(seg.tokens).toBeGreaterThan(0);
201
+ expect(tokensOf(comp.segments, "harness")).toBe(0);
202
+ expect(tokensOf(comp.segments, "history")).toBe(0);
203
+ }
204
+ expect(sum(byCall.get("c1")!.segments)).toBeCloseTo(9_000, 6);
205
+ expect(sum(byCall.get("c2")!.segments)).toBeCloseTo(7_500, 6);
206
+ });
207
+
208
+ test("the token ratio scales the static parts", () => {
209
+ const m = manifest({ tokenRatio: 1.2 });
210
+ expect(staticTokens(m)).toBeCloseTo(18_000, 6);
211
+ const { byCall } = deriveCallCompositions([call("c1", 40_000, 0)], [m]);
212
+ expect(tokensOf(byCall.get("c1")!.segments, "tools")).toBeCloseTo(12_000, 6);
213
+ expect(tokensOf(byCall.get("c1")!.segments, "harness")).toBeCloseTo(22_000, 6);
214
+ });
215
+
216
+ test("each session uses its own manifest; a later seq applies from its write time", () => {
217
+ const s2 = manifest({ id: "m-s2", sessionId: "s-2", parts: manifest().parts.slice(0, 1) });
218
+ const seq1 = manifest({
219
+ id: "m-1",
220
+ seq: 1,
221
+ createdAt: new Date(T0 + 1500).toISOString(),
222
+ parts: manifest().parts.slice(0, 2),
223
+ });
224
+ const { byCall } = deriveCallCompositions(
225
+ [
226
+ call("c1", 40_000, 0),
227
+ call("c2", 41_000, 2000),
228
+ call("d1", 20_000, 0, {}, "s-2"),
229
+ call("x1", 20_000, 0, {}, "s-unknown"),
230
+ ],
231
+ [manifest(), seq1, s2],
232
+ );
233
+ expect(byCall.get("c1")!.manifest.id).toBe("m-0");
234
+ expect(byCall.get("c2")!.manifest.id).toBe("m-1");
235
+ expect(byCall.get("d1")!.manifest.id).toBe("m-s2");
236
+ expect(tokensOf(byCall.get("d1")!.segments, "harness")).toBe(19_000);
237
+ expect(byCall.has("x1")).toBe(false);
238
+ for (const comp of byCall.values())
239
+ expect(sum(comp.segments)).toBeCloseTo(comp.promptTokens, 6);
240
+ });
241
+
242
+ test("a hook manifest after the first call keeps later growth as history", () => {
243
+ // The worker manifest (15k static) anchors c1. The harness hook then posts
244
+ // exact parts worth 40k, the whole first prompt. c2's extra 12k is history.
245
+ const hook = manifest({
246
+ id: "m-hook",
247
+ seq: 1,
248
+ source: "harness-hook",
249
+ createdAt: new Date(T0 + 500).toISOString(),
250
+ parts: [{ ...manifest().parts[0]!, estTokens: 40_000, fidelity: "exact" }],
251
+ });
252
+ const { byCall, anchors } = deriveCallCompositions(
253
+ [call("c1", 40_000, 0), call("c2", 52_000, 1000)],
254
+ [manifest(), hook],
255
+ );
256
+ const second = byCall.get("c2")!;
257
+ expect(second.manifest.id).toBe("m-hook");
258
+ expect(tokensOf(second.segments, "harness")).toBe(0);
259
+ expect(tokensOf(second.segments, "history")).toBe(12_000);
260
+ expect(sum(second.segments)).toBeCloseTo(52_000, 6);
261
+ expect(anchors.get("m-hook")).toEqual({
262
+ callId: "c1",
263
+ promptTokens: 40_000,
264
+ residualTokens: 0,
265
+ });
266
+ // After a compaction the re-split base still caps the harness base: the
267
+ // 6k summary is history.
268
+ const compacted = deriveCallCompositions(
269
+ [call("c1", 40_000, 0), call("c2", 52_000, 1000), call("c3", 46_000, 2000)],
270
+ [manifest(), hook],
271
+ [{ sessionId: "s-1", t: T0 + 1500 }],
272
+ );
273
+ expect(tokensOf(compacted.byCall.get("c3")!.segments, "harness")).toBe(0);
274
+ expect(tokensOf(compacted.byCall.get("c3")!.segments, "history")).toBe(6_000);
275
+ // A hook manifest smaller than the first prompt leaves the rest as harness.
276
+ const partial = deriveCallCompositions(
277
+ [call("c1", 40_000, 0), call("c2", 52_000, 1000)],
278
+ [manifest(), { ...hook, parts: [{ ...hook.parts[0]!, estTokens: 30_000 }] }],
279
+ );
280
+ expect(tokensOf(partial.byCall.get("c2")!.segments, "harness")).toBe(10_000);
281
+ expect(tokensOf(partial.byCall.get("c2")!.segments, "history")).toBe(12_000);
282
+ });
283
+
284
+ test("a later worker manifest keeps the harness base and swaps only the static parts", () => {
285
+ // m-0 has 15k static, so c1 (40k) measures a 25k harness base. A worker
286
+ // manifest then re-estimates the static parts as 16k. c2's history is
287
+ // 55k − 16k − 25k = 14k.
288
+ const part = manifest().parts[0]!;
289
+ const first = manifest({ parts: [{ ...part, estTokens: 15_000 }] });
290
+ const second = manifest({
291
+ id: "m-1",
292
+ seq: 1,
293
+ createdAt: new Date(T0 + 500).toISOString(),
294
+ parts: [{ ...part, estTokens: 16_000 }],
295
+ });
296
+ const { byCall, anchors } = deriveCallCompositions(
297
+ [call("c1", 40_000, 0), call("c2", 55_000, 1000)],
298
+ [first, second],
299
+ );
300
+ const c2 = byCall.get("c2")!;
301
+ expect(c2.manifest.id).toBe("m-1");
302
+ expect(tokensOf(c2.segments, "harness")).toBe(25_000);
303
+ expect(tokensOf(c2.segments, "history")).toBe(14_000);
304
+ expect(sum(c2.segments)).toBeCloseTo(55_000, 6);
305
+ expect(anchors.get("m-1")).toEqual({
306
+ callId: "c1",
307
+ promptTokens: 40_000,
308
+ residualTokens: 25_000,
309
+ });
310
+ });
311
+
312
+ test("no manifests means no compositions", () => {
313
+ expect(deriveCallCompositions([call("c1", 40_000, 0)], []).byCall.size).toBe(0);
314
+ });
315
+ });
316
+
317
+ describe("compositionCallsOf", () => {
318
+ test("keeps call_usage items whose record has a session", () => {
319
+ const base = { fileIndex: 0, iteration: 1, lineNumber: 1, createdAt: "", t: T0, cli: "claude" };
320
+ const usage = call("c1", 100, 0).usage;
321
+ const items: NormalizedItem[] = [
322
+ { ...base, recId: "r1", kind: "call_usage", usage },
323
+ { ...base, recId: "r2", kind: "call_usage", usage: { ...usage, callId: "c2" } },
324
+ { ...base, recId: "r1", kind: "text", text: "hi" },
325
+ ];
326
+ const calls = compositionCallsOf(items, (recId) => (recId === "r1" ? "s-1" : undefined));
327
+ expect(calls).toEqual([{ usage, sessionId: "s-1", t: T0 }]);
328
+ });
329
+ });
@@ -653,6 +653,8 @@ describe("script connections", () => {
653
653
  markMigrationApplied(database, "241_skills_meta_fts.sql");
654
654
  // 244 alters api_key_status, which this migration-112-only fixture does not create.
655
655
  markMigrationApplied(database, "244_api_key_validation.sql");
656
+ // 245 alters scripts, which this migration-112-only fixture does not create.
657
+ markMigrationApplied(database, "245_script_executor.sql");
656
658
 
657
659
  const id = crypto.randomUUID();
658
660
  const now = new Date().toISOString();
@@ -1,4 +1,4 @@
1
- import { afterAll, describe, expect, test } from "bun:test";
1
+ import { afterAll, describe, expect, spyOn, test } from "bun:test";
2
2
  import { mkdtemp, rm } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
@@ -402,6 +402,41 @@ describe("native-only executor behavior", () => {
402
402
  expect(output.error).toBe("killed");
403
403
  });
404
404
 
405
+ // Before the fix the abort listener was added after the file writes, so an
406
+ // abort that landed during them was missed and the script ran to the end.
407
+ test.skipIf(SKIP_SANDBOX_SPAWN_TESTS)(
408
+ "an abort while args.json is written kills the run before the script starts",
409
+ async () => {
410
+ const dir = await mkdtemp(join(tmpdir(), "native-abort-"));
411
+ const marker = join(dir, "side-effect.marker");
412
+ const controller = new AbortController();
413
+ const write = Bun.write;
414
+ const spy = spyOn(Bun, "write").mockImplementation((async (
415
+ ...writeArgs: Parameters<typeof Bun.write>
416
+ ) => {
417
+ const written = await write(...writeArgs);
418
+ if (String(writeArgs[0]).endsWith("/args.json")) controller.abort();
419
+ return written;
420
+ }) as typeof Bun.write);
421
+ try {
422
+ const output = await new NativeScriptExecutor().run(
423
+ input({
424
+ source: `export default async () => { await Bun.write(${JSON.stringify(marker)}, "ran"); return "ran"; };`,
425
+ signal: controller.signal,
426
+ resources: { ...DEFAULT_SCRIPT_RESOURCES, memoryMb: 2048, wallClockMs: 10_000 },
427
+ }),
428
+ );
429
+ expect(output.error).toBe("killed");
430
+ expect(output.result).toBeUndefined();
431
+ expect(await Bun.file(marker).exists()).toBe(false);
432
+ } finally {
433
+ spy.mockRestore();
434
+ await rm(dir, { recursive: true, force: true });
435
+ }
436
+ },
437
+ CHILD_PROCESS_TEST_BUDGET_MS,
438
+ );
439
+
405
440
  // PR #1326 review finding: exit 134 (SIGABRT) was classified `capacity_exceeded`
406
441
  // — and therefore retried by `runScript` — purely from the exit code, with no
407
442
  // evidence about *when* the abort happened. A script that causes a side effect
@@ -104,3 +104,53 @@ describe("QuickJS pool configuration", () => {
104
104
  );
105
105
  }
106
106
  });
107
+
108
+ // A fresh pool spawns its first worker, so the run waits on the worker's boot
109
+ // (`slot.ready`) when the abort lands. The server counts the script's fetch.
110
+ const abortDuringBootProbe = `
111
+ import { QuickJSScriptExecutor } from "./src/scripts-runtime/executors/quickjs";
112
+ import { DEFAULT_SCRIPT_RESOURCES } from "./src/scripts-runtime/executors/types";
113
+ let hits = 0;
114
+ const server = Bun.serve({ port: 0, fetch() { hits++; return new Response("ran"); } });
115
+ try {
116
+ const controller = new AbortController();
117
+ const run = new QuickJSScriptExecutor().run({
118
+ source: 'export default async (args) => (await fetch(args.url)).text();',
119
+ args: { url: "http://127.0.0.1:" + server.port },
120
+ configPayload: { system: {
121
+ apiKey: { value: "pool-test", isSecret: true },
122
+ agentId: { value: crypto.randomUUID(), isSecret: false },
123
+ mcpBaseUrl: { value: "http://127.0.0.1:" + server.port, isSecret: false },
124
+ }, user: {} },
125
+ resources: DEFAULT_SCRIPT_RESOURCES,
126
+ fsMode: "none", network: "open", signal: controller.signal,
127
+ });
128
+ // One macrotask: the run has its slot and awaits the worker's boot.
129
+ await Bun.sleep(0);
130
+ controller.abort();
131
+ const output = await run;
132
+ await Bun.sleep(200);
133
+ console.log(JSON.stringify({ error: output.error ?? null, result: output.result ?? null, hits }));
134
+ } finally {
135
+ server.stop(true);
136
+ }
137
+ `;
138
+
139
+ describe("QuickJS abort while the worker boots", () => {
140
+ test(
141
+ "an abort before the worker is ready kills the run and the script never runs",
142
+ async () => {
143
+ const result = expectChildOk(
144
+ await runChild([process.execPath, "--eval", abortDuringBootProbe], { env: process.env }),
145
+ "QuickJS abort-during-boot probe",
146
+ );
147
+ const out = JSON.parse(result.stdout.trim()) as {
148
+ error: string | null;
149
+ result: string | null;
150
+ hits: number;
151
+ };
152
+ expect(out).toEqual({ error: "killed", result: null, hits: 0 });
153
+ },
154
+ CHILD_PROCESS_TEST_BUDGET_MS,
155
+ );
156
+ });
@@ -640,3 +640,143 @@ describe("quickjs egress policy", () => {
640
640
  expect(JSON.stringify(output)).not.toContain("quickjs-test-secret");
641
641
  });
642
642
  });
643
+
644
+ describe("quickjs hostValidatedArgs (#2011)", () => {
645
+ const countedStructuralScript = `
646
+ import { z } from "zod";
647
+ export const argsSchema = z.object({
648
+ value: z.number(),
649
+ label: z.string().optional(),
650
+ });
651
+ let parseCalls = 0;
652
+ const origParse = argsSchema.parse.bind(argsSchema);
653
+ argsSchema.parse = ((v: unknown) => {
654
+ parseCalls++;
655
+ return origParse(v);
656
+ }) as typeof argsSchema.parse;
657
+
658
+ export default async function (args: z.infer<typeof argsSchema>) {
659
+ return { doubled: args.value * 2, label: args.label ?? null, parseCalls };
660
+ }
661
+ `;
662
+
663
+ test("skips duplicate argsSchema.parse when hostValidatedArgs is true and schema is structural", async () => {
664
+ const out = await executor.run(
665
+ input(countedStructuralScript, {
666
+ args: { value: 21, label: "ok" },
667
+ hostValidatedArgs: true,
668
+ }),
669
+ );
670
+ expect(out.exitCode).toBe(0);
671
+ expect(out.error).toBeUndefined();
672
+ expect(out.result).toEqual({ doubled: 42, label: "ok", parseCalls: 0 });
673
+ });
674
+
675
+ test("still runs argsSchema.parse when hostValidatedArgs is omitted or false", async () => {
676
+ const out = await executor.run(input(countedStructuralScript, { args: { value: 21 } }));
677
+ expect(out.exitCode).toBe(0);
678
+ expect(out.result).toEqual({ doubled: 42, label: null, parseCalls: 1 });
679
+ });
680
+
681
+ test("still runs argsSchema.parse when args contain extra keys that Zod strips", async () => {
682
+ const out = await executor.run(
683
+ input(countedStructuralScript, {
684
+ args: { value: 21, extra: "stripped-by-zod" },
685
+ hostValidatedArgs: true,
686
+ }),
687
+ );
688
+ expect(out.exitCode).toBe(0);
689
+ expect(out.result).toEqual({ doubled: 42, label: null, parseCalls: 1 });
690
+ });
691
+
692
+ test("still runs argsSchema.parse when schema uses defaults, transforms, or refinements", async () => {
693
+ const scriptWithDefaultsAndTransforms = `
694
+ import { z } from "zod";
695
+ export const argsSchema = z.object({
696
+ value: z.number().default(10),
697
+ tag: z.string().transform((s) => s.toUpperCase()),
698
+ });
699
+ let parseCalls = 0;
700
+ const origParse = argsSchema.parse.bind(argsSchema);
701
+ argsSchema.parse = ((v: unknown) => {
702
+ parseCalls++;
703
+ return origParse(v);
704
+ }) as typeof argsSchema.parse;
705
+
706
+ export default async function (args: z.infer<typeof argsSchema>) {
707
+ return { value: args.value, tag: args.tag, parseCalls };
708
+ }
709
+ `;
710
+ const out = await executor.run(
711
+ input(scriptWithDefaultsAndTransforms, {
712
+ args: { tag: "hello" },
713
+ hostValidatedArgs: true,
714
+ }),
715
+ );
716
+ expect(out.exitCode).toBe(0);
717
+ expect(out.result).toEqual({ value: 10, tag: "HELLO", parseCalls: 1 });
718
+ });
719
+
720
+ test("caller-supplied args payload cannot spoof the trusted host validation marker", async () => {
721
+ const out = await executor.run(
722
+ input(countedStructuralScript, {
723
+ args: {
724
+ value: 7,
725
+ hostValidatedArgs: true,
726
+ __swarm_host_validated_args: true,
727
+ },
728
+ }),
729
+ );
730
+ expect(out.exitCode).toBe(0);
731
+ expect(out.result).toEqual({ doubled: 14, label: null, parseCalls: 1 });
732
+ });
733
+
734
+ test("retains Zod property semantics on inherited Object.prototype keys (constructor, toString) and reverse numeric enum keys", async () => {
735
+ for (const propName of ["constructor", "toString"] as const) {
736
+ const protoScript = `
737
+ import { z } from "zod";
738
+ export const argsSchema = z.object({
739
+ ${propName}: z.string().nullable().optional(),
740
+ });
741
+ export default async function (args: z.infer<typeof argsSchema>) {
742
+ return { val: args.${propName} ?? null };
743
+ }
744
+ `;
745
+ const out = await executor.run(
746
+ input(protoScript, {
747
+ args: {},
748
+ hostValidatedArgs: true,
749
+ }),
750
+ );
751
+ expect(out.exitCode).toBe(1);
752
+ expect(out.stderr).toContain("argsSchema validation failed");
753
+ }
754
+
755
+ const numericEnumScript = `
756
+ import { z } from "zod";
757
+ export const argsSchema = z.object({
758
+ mode: z.enum({ 0: "Read", Read: 0 }).nullable(),
759
+ });
760
+ export default async function (args: z.infer<typeof argsSchema>) {
761
+ return { mode: args.mode };
762
+ }
763
+ `;
764
+ const rejected = await executor.run(
765
+ input(numericEnumScript, {
766
+ args: { mode: "Read" },
767
+ hostValidatedArgs: true,
768
+ }),
769
+ );
770
+ expect(rejected.exitCode).toBe(1);
771
+ expect(rejected.stderr).toContain("argsSchema validation failed");
772
+
773
+ const accepted = await executor.run(
774
+ input(numericEnumScript, {
775
+ args: { mode: 0 },
776
+ hostValidatedArgs: true,
777
+ }),
778
+ );
779
+ expect(accepted.exitCode).toBe(0);
780
+ expect(accepted.result).toEqual({ mode: 0 });
781
+ });
782
+ });