@desplega.ai/agent-swarm 1.131.1 → 1.134.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 (360) hide show
  1. package/README.md +3 -3
  2. package/dist/{actions-jgcy477k.js → actions-7kast243.js} +20 -18
  3. package/dist/{anthropic-messages-rjmd2e7n.js → anthropic-messages-706yad38.js} +33 -17
  4. package/dist/app-0g84t8wf.js +28 -0
  5. package/dist/{artifact-jysj3wrg.js → artifact-dje5ke4h.js} +37 -131
  6. package/dist/{assistant-f2vkk1hj.js → assistant-ac4htcky.js} +25 -22
  7. package/dist/{azure-openai-responses-c0yjmbfd.js → azure-openai-responses-be59fbsz.js} +13 -13
  8. package/dist/{boot-reembed-q7mp8hcr.js → boot-reembed-b1rcqn85.js} +13 -11
  9. package/dist/{boot-reembed-xmw8wcx6.js → boot-reembed-kw2541c4.js} +14 -12
  10. package/dist/{boot-scrub-logs-dsqrv79n.js → boot-scrub-logs-ja29t92g.js} +11 -9
  11. package/dist/{claude-adapter-q6x7yspx.js → claude-adapter-5cg1fv8s.js} +33 -29
  12. package/dist/{claude-managed-adapter-wsg37bt8.js → claude-managed-adapter-9z9sdytx.js} +9 -9
  13. package/dist/{claude-managed-setup-52z4mq9w.js → claude-managed-setup-3zqp51cr.js} +5 -5
  14. package/dist/{claude-1ex232fc.js → claude-wqn3dtca.js} +1 -1
  15. package/dist/{cli-4wgngmwv.js → cli-00w4hbam.js} +1 -1
  16. package/dist/{cli-er3a4bb0.js → cli-15429qdw.js} +6 -1
  17. package/dist/{cli-e5dabkx0.js → cli-1cwhyvta.js} +4 -4
  18. package/dist/{cli-zsspvvm2.js → cli-1ybr6vw2.js} +237 -237
  19. package/dist/{cli-xpxde7rm.js → cli-2wv4kj0e.js} +60 -31
  20. package/dist/cli-34w2584r.js +530 -0
  21. package/dist/{cli-se2p6rkb.js → cli-38taxznr.js} +6 -6
  22. package/dist/{cli-9sbhdpvb.js → cli-3cgfyfzm.js} +14 -14
  23. package/dist/{cli-5n6e5yfn.js → cli-3zmdzfjf.js} +79 -74
  24. package/dist/{cli-3ty0t082.js → cli-43x36mty.js} +197 -8
  25. package/dist/{cli-ycrnynpw.js → cli-480nehnh.js} +29 -22
  26. package/dist/{cli-0258203w.js → cli-492e7ap5.js} +140 -20
  27. package/dist/{cli-3fj60etf.js → cli-497b685t.js} +3 -3
  28. package/dist/{cli-pxfxkx5m.js → cli-4bap00k0.js} +1 -1
  29. package/dist/{cli-w2wxd56v.js → cli-4bc8vmhm.js} +848 -770
  30. package/dist/{cli-aca5pz9f.js → cli-589599p9.js} +1 -1
  31. package/dist/{cli-t930g0xh.js → cli-5tnr64pm.js} +1 -1
  32. package/dist/{cli-jxg3s9ey.js → cli-65d93ves.js} +7 -7
  33. package/dist/{cli-6dpytdr2.js → cli-6x363v1z.js} +2 -2
  34. package/dist/{cli-dxak0fqd.js → cli-787ph852.js} +11 -11
  35. package/dist/{cli-793jvbsh.js → cli-7bv8nckk.js} +16 -11
  36. package/dist/{cli-zyxs93y9.js → cli-7jcc9y1r.js} +3 -3
  37. package/dist/{cli-f1am9szv.js → cli-87t2gstf.js} +1 -1
  38. package/dist/{cli-9aryf9tv.js → cli-95m51ztz.js} +1 -1
  39. package/dist/{cli-4w4fmq9x.js → cli-9w49enmh.js} +2 -2
  40. package/dist/{cli-v69vk5h0.js → cli-9y5b02vr.js} +13 -13
  41. package/dist/{cli-x23me2mg.js → cli-a1rrkrb4.js} +1 -1
  42. package/dist/{cli-psrqtw2m.js → cli-a3pkfpqs.js} +2 -2
  43. package/dist/{cli-h0rgxd24.js → cli-a4ajmfvp.js} +6 -6
  44. package/dist/{cli-ygrbmjja.js → cli-aw8bb6sf.js} +6 -6
  45. package/dist/{cli-g74vpjp2.js → cli-brm3et47.js} +55 -22
  46. package/dist/{cli-czkae1fp.js → cli-c5ddbdfj.js} +9 -9
  47. package/dist/{cli-114fvtmj.js → cli-c79a9zrc.js} +1 -1
  48. package/dist/{cli-49n9f49x.js → cli-dx3dz0ms.js} +3 -3
  49. package/dist/{cli-6dp3ydxa.js → cli-e07exmsb.js} +18 -14
  50. package/dist/cli-et1bbwqf.js +13 -0
  51. package/dist/{cli-yazx6hap.js → cli-f3s9dry0.js} +14 -14
  52. package/dist/{cli-t8d349cy.js → cli-f4wj0s3m.js} +3 -3
  53. package/dist/{cli-d6n0thxw.js → cli-f64gdfxc.js} +1 -1
  54. package/dist/{cli-pwfsqc2x.js → cli-fyd58xgb.js} +55 -55
  55. package/dist/{cli-7dm2zqy2.js → cli-g0j14czv.js} +2 -2
  56. package/dist/{cli-b0p7rfnd.js → cli-g2p9bd1z.js} +14 -14
  57. package/dist/{cli-z905tvmm.js → cli-g814jbst.js} +2 -2
  58. package/dist/{cli-5dsbcjjw.js → cli-ghexzd2d.js} +970 -970
  59. package/dist/{cli-jvv0g8bh.js → cli-gpt9kf01.js} +15 -15
  60. package/dist/{cli-rgrfzg8z.js → cli-grt6qv91.js} +380 -380
  61. package/dist/{cli-wz9115x0.js → cli-gtafvdy0.js} +8 -8
  62. package/dist/{cli-q5dzzak0.js → cli-gy07xk4s.js} +2 -2
  63. package/dist/{cli-sy7jk3tx.js → cli-h0djenzm.js} +2 -2
  64. package/dist/{cli-nrkkknat.js → cli-h37gbjbf.js} +2 -281
  65. package/dist/{cli-jevgrb4w.js → cli-h39xxnq6.js} +1200 -491
  66. package/dist/{cli-gz9sbyk3.js → cli-jepmgn7r.js} +1 -1
  67. package/dist/{cli-ccf9vy7q.js → cli-jmvk6et8.js} +7 -7
  68. package/dist/{cli-2rk0cx7s.js → cli-jxj92gkt.js} +51 -41
  69. package/dist/{cli-bqw0mt1n.js → cli-k46t4664.js} +18 -3
  70. package/dist/{cli-50vbt00w.js → cli-m07313tr.js} +1153 -696
  71. package/dist/{cli-4j2mrzag.js → cli-mkq1rk7b.js} +1 -1
  72. package/dist/{cli-1z7048dy.js → cli-mnm7w46e.js} +1 -1
  73. package/dist/{cli-kd1ck4rx.js → cli-mp9sg4cg.js} +14 -8
  74. package/dist/{cli-jbcva0xj.js → cli-mrammd8c.js} +60 -35
  75. package/dist/cli-nanaxrz6.js +27 -0
  76. package/dist/{cli-p9swy5t3.js → cli-pq2ee5ja.js} +8 -6
  77. package/dist/{cli-2wadx7p2.js → cli-pt43pdp1.js} +2 -2
  78. package/dist/{cli-7sgrxg9a.js → cli-q6d0vvey.js} +4 -4
  79. package/dist/{cli-k9qkf1hw.js → cli-qf9kpsve.js} +9 -9
  80. package/dist/cli-qh4sqfws.js +15 -0
  81. package/dist/{cli-e15jhafb.js → cli-qntkxysp.js} +14 -14
  82. package/dist/{cli-sajgpkqg.js → cli-qs9hhsvb.js} +1723 -325
  83. package/dist/{cli-z2zcxes1.js → cli-qt5bt3vk.js} +1 -1
  84. package/dist/{cli-wx82tw1k.js → cli-qtekrty1.js} +3 -3
  85. package/dist/{cli-g1re8r05.js → cli-rh6f3s9n.js} +3 -3
  86. package/dist/{cli-n0krnc6d.js → cli-rzhcrw2p.js} +2 -2
  87. package/dist/{cli-gecy3z2s.js → cli-snw62ze5.js} +4 -4
  88. package/dist/{cli-n6h8qt8n.js → cli-snz3p4yf.js} +15 -10
  89. package/dist/{cli-r6kqqbj0.js → cli-tkf8n6cw.js} +11 -11
  90. package/dist/{cli-4rkfg1wx.js → cli-tkrr24mz.js} +14 -9
  91. package/dist/{cli-9dr5qzc0.js → cli-tv9f1kyb.js} +54 -43
  92. package/dist/{cli-gm9cbhbq.js → cli-vtz6yj3v.js} +28 -9
  93. package/dist/{cli-2f07m0xr.js → cli-wf86d5t7.js} +1 -1
  94. package/dist/{cli-7zd05xb4.js → cli-whrvct19.js} +45 -45
  95. package/dist/{cli-ap1czx8f.js → cli-wrrea8xm.js} +3 -3
  96. package/dist/{cli-57khth04.js → cli-xmyqv00z.js} +120 -4
  97. package/dist/{cli-cwy5hqbq.js → cli-yjmk7tzc.js} +1 -1
  98. package/dist/{cli-6pcz9h15.js → cli-zykjrwcx.js} +180 -394
  99. package/dist/cli.js +45 -45
  100. package/dist/{codex-adapter-htjkn22b.js → codex-adapter-hsq6mz3m.js} +21 -19
  101. package/dist/{codex-hook-hq54t9ft.js → codex-hook-8c500gq8.js} +8 -7
  102. package/dist/{codex-login-z6yhbkmk.js → codex-login-x7603481.js} +3 -3
  103. package/dist/{codex-session-runner-wjfc7smz.js → codex-session-runner-c1x3x143.js} +15 -13
  104. package/dist/{commands-xhrfwqsk.js → commands-rwtddtst.js} +11 -9
  105. package/dist/{db-anvwb22f.js → db-j2b355vz.js} +458 -444
  106. package/dist/{devin-adapter-v1sarqtw.js → devin-adapter-8qmhqpy3.js} +3 -3
  107. package/dist/{devtools-2y1r7yqk.js → devtools-j2rnj8w7.js} +4 -4
  108. package/dist/{e2b-jdkwemv2.js → e2b-znpx78eg.js} +16 -16
  109. package/dist/{es5-vjgtkgp6.js → es5-fmqrjzy3.js} +2 -2
  110. package/dist/{event-bus-axn823e9.js → event-bus-n8sb35pw.js} +4 -4
  111. package/dist/{getMachineId-bsd-6tc9wxdj.js → getMachineId-bsd-wpzbhkm6.js} +4 -4
  112. package/dist/{getMachineId-darwin-sjt9sgnc.js → getMachineId-darwin-45zabjy0.js} +4 -4
  113. package/dist/{getMachineId-linux-v5n18y9p.js → getMachineId-linux-warnp5pd.js} +3 -3
  114. package/dist/{getMachineId-unsupported-yrjyqw3d.js → getMachineId-unsupported-5spent2a.js} +3 -3
  115. package/dist/{getMachineId-win-w3r8063c.js → getMachineId-win-v4bvcw7a.js} +4 -4
  116. package/dist/{google-generative-ai-hysrd8qx.js → google-generative-ai-b0wzss55.js} +17 -14
  117. package/dist/{google-vertex-p5arrga0.js → google-vertex-e8q4dpya.js} +18 -15
  118. package/dist/{handlers-b9kbc621.js → handlers-hchgrxkh.js} +32 -29
  119. package/dist/{hook-xkvn6c5h.js → hook-p49egqc0.js} +66 -52
  120. package/dist/hook-spb00w76.js +34 -0
  121. package/dist/{http-9xh6ta4r.js → http-nh14kkms.js} +663 -181
  122. package/dist/index-1qthq7vc.js +6 -0
  123. package/dist/{index-0g68f0p6.js → index-20xqmn3v.js} +3 -3
  124. package/dist/{index-vdkr5z2w.js → index-3e03k6e4.js} +114 -114
  125. package/dist/{index-ywds4812.js → index-4cbyhq2g.js} +43 -41
  126. package/dist/index-67pzqfrb.js +36 -0
  127. package/dist/{index-bccrkdkn.js → index-7m8yxkw5.js} +16 -16
  128. package/dist/{index-eaxyqcmj.js → index-dvsk64sm.js} +163 -83
  129. package/dist/{index-dqkpnmqm.js → index-e19s2m8k.js} +18 -18
  130. package/dist/{index-j30k5zxn.js → index-emvxcy98.js} +16 -16
  131. package/dist/{index-7ndw6ecy.js → index-gqnmr60d.js} +9 -9
  132. package/dist/{index-9znhar3x.js → index-j02xvjtd.js} +184 -185
  133. package/dist/{index-1s2442yw.js → index-ns5kksc1.js} +7 -7
  134. package/dist/{index-dpy2hth8.js → index-pdxc8kfh.js} +71 -71
  135. package/dist/{index-z1m7pw0v.js → index-ptcqm8bc.js} +107 -107
  136. package/dist/{index-070v4eyb.js → index-pyp7rexm.js} +5 -6
  137. package/dist/{index-86znjz1p.js → index-shne41sd.js} +13 -13
  138. package/dist/{index-2qn2qndv.js → index-t36q2v4t.js} +183 -183
  139. package/dist/index-tcnvv8w6.js +7 -0
  140. package/dist/index-w91qbfv1.js +7 -0
  141. package/dist/{index-fzc0mwvj.js → index-yqweff5n.js} +32 -32
  142. package/dist/index-yrfphye1.js +39 -0
  143. package/dist/{keepalive-vc7s2aa6.js → keepalive-9k51nqhc.js} +21 -15
  144. package/dist/lead-2xq3dxbz.js +88 -0
  145. package/dist/maintenance-0c0g6nym.js +28 -0
  146. package/dist/mistral-conversations-2njwdwhj.js +717 -0
  147. package/dist/{multipart-parser-qaktc7p8.js → multipart-parser-6gftesw0.js} +2 -2
  148. package/dist/{oauth-refresh-sweep-yecgjpk4.js → oauth-refresh-sweep-273a64r2.js} +14 -12
  149. package/dist/{onboard-xj0f73f9.js → onboard-tsv9wwhz.js} +12 -12
  150. package/dist/{openai-codex-responses-5g27pd6f.js → openai-codex-responses-kt84eq86.js} +26 -26
  151. package/dist/{openai-completions-3vkmfz2a.js → openai-completions-mrypq7qp.js} +17 -16
  152. package/dist/{openai-responses-dg4t7j7a.js → openai-responses-qhrk21ez.js} +20 -17
  153. package/dist/{opencode-adapter-1gm38aba.js → opencode-adapter-rar9676j.js} +13 -11
  154. package/dist/{openrouter-images-g3gm8931.js → openrouter-images-684fysx9.js} +5 -5
  155. package/dist/{otel-impl-nxg0n0b3.js → otel-impl-wp1mfzd2.js} +752 -537
  156. package/dist/{photon_rs-ky7dj5qw.js → photon_rs-8dwt2pzy.js} +581 -581
  157. package/dist/{pi-messages-tqj8q6b9.js → pi-messages-txp9f97r.js} +7 -7
  158. package/dist/{pi-mono-adapter-4g5kby31.js → pi-mono-adapter-yz47kk8e.js} +1910 -1180
  159. package/dist/pricing-refresh-jw1yw2fn.js +22 -0
  160. package/dist/rbac-roles-dg964c8p.js +32 -0
  161. package/dist/{rbac-roles-1hcxdtg7.js → rbac-roles-p53cg5cv.js} +19 -17
  162. package/dist/{registry-02cy9z5z.js → registry-mh12y17v.js} +4 -4
  163. package/dist/render-v2-56vn2rk9.js +36 -0
  164. package/dist/seed-pricing-52xx6z63.js +19 -0
  165. package/dist/{setup-46xtgq3c.js → setup-e8gp79sy.js} +7 -7
  166. package/dist/token-util-zy908s1a.js +6 -0
  167. package/dist/{token-fxkn2zf2.js → token-xg5711x5.js} +4 -4
  168. package/dist/worker-s17f1g46.js +88 -0
  169. package/dist/{x-6mg4kfpe.js → x-xkfh0w0v.js} +5 -5
  170. package/openapi.json +566 -22
  171. package/package.json +6 -6
  172. package/src/be/budget-refusal-notify.ts +14 -7
  173. package/src/be/db-queries/task-artifacts.ts +182 -0
  174. package/src/be/db.ts +834 -171
  175. package/src/be/events.ts +5 -0
  176. package/src/be/migrations/131_session_logs_created_at_index.sql +2 -0
  177. package/src/be/migrations/132_multi_runtime_instances.sql +44 -0
  178. package/src/be/migrations/133_workflow_run_trigger_type.sql +12 -0
  179. package/src/be/migrations/135_backfill_task_pull_request_attachments.sql +228 -0
  180. package/src/be/migrations/136_task_requester_provenance.sql +37 -0
  181. package/src/be/migrations/137_memory_retrieval_composite_index.sql +1 -0
  182. package/src/be/migrations/runner.ts +17 -11
  183. package/src/be/multi-runtime.ts +430 -0
  184. package/src/be/scripts/extract-schema.ts +3 -1
  185. package/src/be/scripts/typecheck.ts +6 -2
  186. package/src/be/seed-prompt-templates.ts +25 -2
  187. package/src/be/seed-skills/index.ts +7 -0
  188. package/src/be/steering.ts +1 -1
  189. package/src/be/swarm-config-guard.ts +18 -0
  190. package/src/commands/credential-wait.ts +54 -0
  191. package/src/commands/onboard/steps/core-credentials.tsx +1 -1
  192. package/src/commands/onboard/steps/custom-templates.tsx +1 -1
  193. package/src/commands/onboard/steps/harness-credentials.tsx +1 -1
  194. package/src/commands/onboard/steps/health-check.tsx +1 -1
  195. package/src/commands/onboard/steps/start.tsx +1 -1
  196. package/src/commands/profile-sync-audit.ts +56 -0
  197. package/src/commands/profile-sync.ts +287 -15
  198. package/src/commands/provider-credentials.ts +46 -19
  199. package/src/commands/runner.ts +199 -33
  200. package/src/commands/templates.ts +22 -0
  201. package/src/github/handlers.ts +55 -9
  202. package/src/heartbeat/heartbeat.ts +109 -17
  203. package/src/hooks/hook.ts +49 -33
  204. package/src/http/active-sessions.ts +11 -1
  205. package/src/http/agents.ts +261 -30
  206. package/src/http/all-routes.ts +1 -0
  207. package/src/http/config.ts +13 -2
  208. package/src/http/core.ts +102 -4
  209. package/src/http/db-query-bounded.ts +313 -0
  210. package/src/http/db-query-shared.ts +249 -0
  211. package/src/http/db-query.ts +54 -42
  212. package/src/http/events.ts +2 -0
  213. package/src/http/index.ts +23 -2
  214. package/src/http/mcp-bridge.ts +37 -3
  215. package/src/http/mcp.ts +12 -0
  216. package/src/http/memory.ts +13 -2
  217. package/src/http/openapi.ts +1 -0
  218. package/src/http/poll.ts +70 -27
  219. package/src/http/prompt-templates.ts +36 -13
  220. package/src/http/route-def.ts +43 -9
  221. package/src/http/schedules.ts +2 -2
  222. package/src/http/scripts.ts +10 -1
  223. package/src/http/session-data.ts +44 -0
  224. package/src/http/tasks.ts +2 -2
  225. package/src/http/users.ts +74 -52
  226. package/src/http/webhooks.ts +5 -2
  227. package/src/http/workflows.ts +1 -4
  228. package/src/integrations/kapso/inbound.ts +1 -1
  229. package/src/jira/outbound.ts +39 -8
  230. package/src/jira/webhook-lifecycle.ts +13 -2
  231. package/src/linear/outbound.ts +43 -10
  232. package/src/oauth/keepalive.ts +13 -2
  233. package/src/prompts/base-prompt.ts +1 -1
  234. package/src/prompts/default-drift.ts +21 -0
  235. package/src/prompts/session-templates.ts +56 -7
  236. package/src/providers/claude-adapter.ts +6 -0
  237. package/src/providers/codex-adapter.ts +6 -0
  238. package/src/providers/opencode-adapter.ts +6 -0
  239. package/src/providers/pi-mono-adapter.ts +8 -0
  240. package/src/providers/pricing-sources.md +24 -0
  241. package/src/queue-stall-alarm.ts +216 -0
  242. package/src/rbac/legacy-policy.ts +3 -0
  243. package/src/rbac/permissions.ts +12 -0
  244. package/src/scheduler/scheduler.ts +7 -0
  245. package/src/script-workflows/executor.ts +2 -1
  246. package/src/scripts-runtime/executors/native.ts +2 -1
  247. package/src/scripts-runtime/executors/types.ts +5 -0
  248. package/src/scripts-runtime/loader.ts +5 -0
  249. package/src/scripts-runtime/sdk-allowlist.ts +3 -0
  250. package/src/scripts-runtime/swarm-config.ts +8 -0
  251. package/src/scripts-runtime/swarm-sdk.ts +7 -0
  252. package/src/scripts-runtime/types/stdlib.d.ts +4 -0
  253. package/src/scripts-runtime/types/swarm-sdk.d.ts +4 -0
  254. package/src/server.ts +6 -0
  255. package/src/slack/channel-join.ts +22 -3
  256. package/src/slack/channel-lifecycle.ts +114 -0
  257. package/src/tests/budget-claim-gate.test.ts +20 -0
  258. package/src/tests/claude-adapter.test.ts +32 -0
  259. package/src/tests/codex-adapter.test.ts +30 -0
  260. package/src/tests/create-task-validation.test.ts +128 -0
  261. package/src/tests/credential-wait.test.ts +67 -1
  262. package/src/tests/db-query.test.ts +711 -2
  263. package/src/tests/heartbeat-checklist.test.ts +28 -0
  264. package/src/tests/http-api-integration.test.ts +81 -0
  265. package/src/tests/http-users.test.ts +166 -1
  266. package/src/tests/identity-field-budget.test.ts +176 -0
  267. package/src/tests/mcp-tools-user.test.ts +34 -0
  268. package/src/tests/mcp-user-route.test.ts +17 -0
  269. package/src/tests/metrics-http.test.ts +44 -0
  270. package/src/tests/migration-failure.test.ts +86 -0
  271. package/src/tests/multi-runtime-registration.test.ts +2810 -0
  272. package/src/tests/openapi-response-contract.test.ts +24 -0
  273. package/src/tests/openapi-route-headers.test.ts +83 -0
  274. package/src/tests/opencode-adapter.test.ts +40 -0
  275. package/src/tests/outbound-listener-rejection.test.ts +138 -0
  276. package/src/tests/pi-mono-adapter.test.ts +98 -0
  277. package/src/tests/preload.ts +2 -0
  278. package/src/tests/profile-sync-warning.test.ts +277 -0
  279. package/src/tests/profile-sync.test.ts +232 -6
  280. package/src/tests/prompt-template-resolver.test.ts +177 -6
  281. package/src/tests/prompt-template-session.test.ts +39 -3
  282. package/src/tests/queue-stall-alarm.test.ts +226 -0
  283. package/src/tests/rbac-charact-slack.test.ts +29 -0
  284. package/src/tests/rbac-engine.test.ts +3 -0
  285. package/src/tests/requester-comms.test.ts +32 -0
  286. package/src/tests/runner-requester-profile.test.ts +38 -0
  287. package/src/tests/schedule-target-type.test.ts +38 -0
  288. package/src/tests/script-connections.test.ts +13 -1
  289. package/src/tests/scripts-runtime-identity.test.ts +375 -0
  290. package/src/tests/scripts-runtime.test.ts +4 -3
  291. package/src/tests/sdk-allowlist.test.ts +47 -0
  292. package/src/tests/send-task-requested-by.test.ts +9 -0
  293. package/src/tests/session-costs.test.ts +506 -1
  294. package/src/tests/shutdown-error-scrubbing.test.ts +92 -0
  295. package/src/tests/slack-channel-lifecycle.test.ts +241 -0
  296. package/src/tests/slack-render-v2.test.ts +43 -0
  297. package/src/tests/steering-core.test.ts +28 -0
  298. package/src/tests/swarm-config-reserved-keys.test.ts +22 -0
  299. package/src/tests/task-artifacts.test.ts +386 -0
  300. package/src/tests/task-attachments-schema.test.ts +17 -1
  301. package/src/tests/task-completion-idempotency.test.ts +57 -0
  302. package/src/tests/task-pull-request-backfill-migration.test.ts +321 -0
  303. package/src/tests/tool-annotations.test.ts +4 -0
  304. package/src/tests/update-profile-api.test.ts +67 -39
  305. package/src/tests/update-profile-auth.test.ts +48 -4
  306. package/src/tests/user-attribution-migration.test.ts +54 -0
  307. package/src/tests/user-identity.test.ts +72 -0
  308. package/src/tests/workflow-async-v2.test.ts +39 -1
  309. package/src/tests/workflow-http-v2.test.ts +3 -2
  310. package/src/tests/workflow-mcp-trigger-schema.test.ts +52 -3
  311. package/src/tests/workflow-retry-v2.test.ts +45 -1
  312. package/src/tools/db-query.ts +18 -8
  313. package/src/tools/get-task-details.ts +24 -3
  314. package/src/tools/manage-user.ts +36 -3
  315. package/src/tools/poll-task.ts +93 -14
  316. package/src/tools/script-common.ts +5 -0
  317. package/src/tools/send-task.ts +1 -4
  318. package/src/tools/slack-archive-channel.ts +70 -0
  319. package/src/tools/slack-create-channel.ts +74 -0
  320. package/src/tools/slack-invite-to-channel.ts +78 -0
  321. package/src/tools/store-progress.ts +13 -2
  322. package/src/tools/swarm-config/delete-config.ts +9 -0
  323. package/src/tools/swarm-config/set-config.ts +3 -2
  324. package/src/tools/task-action.ts +30 -0
  325. package/src/tools/task-tool-ctx.ts +8 -1
  326. package/src/tools/tool-config.ts +4 -1
  327. package/src/tools/update-profile.ts +16 -21
  328. package/src/tools/utils.ts +5 -0
  329. package/src/tools/workflows/trigger-workflow.ts +7 -1
  330. package/src/types.ts +165 -1
  331. package/src/utils/github-pull-request.ts +76 -0
  332. package/src/utils/identity-field-budget.ts +73 -0
  333. package/src/utils/multi-runtime.ts +20 -0
  334. package/src/utils/requester-comms.ts +24 -0
  335. package/src/workflows/engine.ts +22 -2
  336. package/src/workflows/resume.ts +19 -11
  337. package/src/workflows/retry-poller.ts +1 -0
  338. package/src/workflows/triggers.ts +4 -0
  339. package/templates/skills/db-query-guidance/config.json +14 -0
  340. package/templates/skills/db-query-guidance/content.md +57 -0
  341. package/dist/app-5da78bd4.js +0 -26
  342. package/dist/cli-3te4jdkm.js +0 -225
  343. package/dist/cli-bmze986d.js +0 -7
  344. package/dist/cli-kh3ba2zf.js +0 -607
  345. package/dist/hook-cgk4wxmx.js +0 -31
  346. package/dist/index-25wjsdv2.js +0 -34
  347. package/dist/index-76at2ymp.js +0 -7
  348. package/dist/index-hase6zbe.js +0 -6
  349. package/dist/index-vg4snay4.js +0 -36
  350. package/dist/index-vy8edt27.js +0 -7
  351. package/dist/lead-r2yqfmx7.js +0 -85
  352. package/dist/maintenance-mw70k468.js +0 -26
  353. package/dist/mistral-conversations-81fwav0j.js +0 -30578
  354. package/dist/otel-nqa12wf3.js +0 -3149
  355. package/dist/pricing-refresh-2cn6effr.js +0 -20
  356. package/dist/rbac-roles-vynday5g.js +0 -30
  357. package/dist/render-v2-tc0bh8kk.js +0 -34
  358. package/dist/seed-pricing-8kceb43f.js +0 -17
  359. package/dist/token-util-pqyc8kng.js +0 -6
  360. package/dist/worker-qrbz6hmv.js +0 -85
@@ -22,4 +22,42 @@ describe("runner requester profile prompt", () => {
22
22
  expect(prompt).toContain("Lead with the answer; keep updates terse.");
23
23
  expect(prompt).toContain("where it doesn't conflict with correctness or your operating rules");
24
24
  });
25
+
26
+ test("renders structured comms preferences alongside role and notes", async () => {
27
+ const prompt = await buildRequesterProfilePrompt({
28
+ name: "Taras",
29
+ role: "CEO",
30
+ notes: "Keep updates terse.",
31
+ comms: { tone: "casual", language: "Ukrainian", verbosity: "terse" },
32
+ });
33
+
34
+ expect(prompt).toContain(
35
+ "Their communication preferences: tone: casual, language: Ukrainian, verbosity: terse.",
36
+ );
37
+ expect(prompt).toContain("Their stated notes for how you should respond and act:");
38
+ });
39
+
40
+ test("fires when only comms preferences are set", async () => {
41
+ const prompt = await buildRequesterProfilePrompt({
42
+ name: "Taras",
43
+ comms: { language: "Ukrainian" },
44
+ });
45
+
46
+ expect(prompt).toContain("## Requester Profile");
47
+ expect(prompt).toContain("This task was requested by Taras.");
48
+ expect(prompt).toContain("Their communication preferences: language: Ukrainian.");
49
+ expect(prompt).not.toContain("Their stated notes");
50
+ });
51
+
52
+ test("hints that learned preferences should be persisted via manage-user", async () => {
53
+ const prompt = await buildRequesterProfilePrompt({ name: "Taras", role: "CEO" });
54
+
55
+ expect(prompt).toContain(
56
+ "the lead updates `comms` (tone, language, verbosity) via `manage-user`",
57
+ );
58
+ });
59
+
60
+ test("ignores empty comms object", async () => {
61
+ await expect(buildRequesterProfilePrompt({ name: "Taras", comms: {} })).resolves.toBe("");
62
+ });
25
63
  });
@@ -20,7 +20,9 @@ import {
20
20
  closeDb,
21
21
  createAgent,
22
22
  createScheduledTask,
23
+ createUser,
23
24
  createWorkflow,
25
+ deleteUser,
24
26
  getDb,
25
27
  getScheduledTaskById,
26
28
  getWorkflowRun,
@@ -226,11 +228,13 @@ describe("scheduled_tasks DB layer — targetType", () => {
226
228
  describe("dispatchScheduleTarget — workflow target", () => {
227
229
  test("triggers the workflow directly and returns its run ID (no implicit-binding lookup)", async () => {
228
230
  const wf = makeWorkflow({ nodes: [{ id: "n1", type: "echo", config: { value: "hi" } }] });
231
+ const requester = createUser({ name: "Workflow Schedule Requester" });
229
232
  const schedule = createScheduledTask({
230
233
  name: `dispatch-workflow-${crypto.randomUUID()}`,
231
234
  intervalMs: 60_000,
232
235
  targetType: "workflow",
233
236
  workflowId: wf.id,
237
+ createdBy: requester.id,
234
238
  });
235
239
 
236
240
  const result = await dispatchScheduleTarget(schedule);
@@ -239,6 +243,40 @@ describe("dispatchScheduleTarget — workflow target", () => {
239
243
 
240
244
  const run = getWorkflowRun(result.workflowRunIds![0]!);
241
245
  expect(run?.workflowId).toBe(wf.id);
246
+ expect(run?.createdBy).toBe(requester.id);
247
+ });
248
+
249
+ test("runs an implicitly bound workflow without dangling attribution after owner deletion", async () => {
250
+ const requester = createUser({ name: "Deleted Schedule Requester" });
251
+ const schedule = createScheduledTask({
252
+ name: `dispatch-deleted-owner-${crypto.randomUUID()}`,
253
+ intervalMs: 60_000,
254
+ taskTemplate: "Fallback task that should not run",
255
+ createdBy: requester.id,
256
+ });
257
+ const wf = createWorkflow({
258
+ name: `implicit-schedule-workflow-${crypto.randomUUID()}`,
259
+ definition: { nodes: [{ id: "n1", type: "echo", config: { value: "hi" } }] },
260
+ triggers: [{ type: "schedule", scheduleId: schedule.id }],
261
+ createdByAgentId: agentId,
262
+ });
263
+
264
+ expect(deleteUser(requester.id)).toBe(true);
265
+ // Simulate an orphan created before deleteUser learned to clean audit
266
+ // columns whose FK was dropped by migration 103.
267
+ getDb()
268
+ .prepare("UPDATE scheduled_tasks SET created_by = ? WHERE id = ?")
269
+ .run(requester.id, schedule.id);
270
+ const reloaded = getScheduledTaskById(schedule.id)!;
271
+ expect(reloaded.createdBy).toBe(requester.id);
272
+
273
+ const result = await dispatchScheduleTarget(reloaded);
274
+ expect(result.triggeredWorkflows).toBe(true);
275
+ expect(result.task).toBeUndefined();
276
+ expect(result.workflowRunIds).toHaveLength(1);
277
+ const run = getWorkflowRun(result.workflowRunIds![0]!);
278
+ expect(run?.workflowId).toBe(wf.id);
279
+ expect(run?.createdBy).toBeUndefined();
242
280
  });
243
281
 
244
282
  test("throws when the target workflow is disabled", async () => {
@@ -3,6 +3,7 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
3
3
  import { createHash } from "node:crypto";
4
4
  import { readdirSync, readFileSync, unlinkSync } from "node:fs";
5
5
  import { join } from "node:path";
6
+ import { fileURLToPath } from "node:url";
6
7
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
8
  import { createAgent, deleteSwarmConfig, getDb, upsertSwarmConfig } from "../be/db";
8
9
  import { runMigrations } from "../be/migrations/runner";
@@ -52,7 +53,7 @@ function removeDbFiles(path: string) {
52
53
  }
53
54
 
54
55
  function migrationsDir() {
55
- return new URL("../be/migrations", import.meta.url).pathname;
56
+ return fileURLToPath(new URL("../be/migrations", import.meta.url));
56
57
  }
57
58
 
58
59
  function migrationSql(file: string) {
@@ -539,6 +540,17 @@ describe("script connections", () => {
539
540
  // which this migration-112-only fixture creates.
540
541
  markMigrationApplied(database, "129_asset_keys_apps_scripts.sql");
541
542
  markMigrationApplied(database, "130_scratch_script_retention_grace.sql");
543
+ // 131 indexes session_logs, which this migration-112-only fixture does not create.
544
+ markMigrationApplied(database, "131_session_logs_created_at_index.sql");
545
+ // 132 alters active_sessions, which this partial fixture does not create.
546
+ markMigrationApplied(database, "132_multi_runtime_instances.sql");
547
+ // 133 alters workflow_runs, which this migration-112-only fixture does not create.
548
+ markMigrationApplied(database, "133_workflow_run_trigger_type.sql");
549
+ // 135 backfills task_attachments, which this migration-112-only fixture does not create.
550
+ markMigrationApplied(database, "135_backfill_task_pull_request_attachments.sql");
551
+ // 136 alters agent_tasks, which this migration-112-only fixture only creates partially.
552
+ markMigrationApplied(database, "136_task_requester_provenance.sql");
553
+ markMigrationApplied(database, "137_memory_retrieval_composite_index.sql");
542
554
 
543
555
  const id = crypto.randomUUID();
544
556
  const now = new Date().toISOString();
@@ -0,0 +1,375 @@
1
+ /**
2
+ * Runtime identity through the script path: MCP script tools → /api/scripts/run
3
+ * → SwarmConfigPayload (system context) → swarm SDK headers → the same
4
+ * work-acquisition gates the worker's own MCP/HTTP calls hit. Identity is
5
+ * process context end to end — scripts never supply or override it.
6
+ */
7
+
8
+ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test } from "bun:test";
9
+ import { unlink } from "node:fs/promises";
10
+ import { createServer as createHttpServer, type Server } from "node:http";
11
+ import {
12
+ closeDb,
13
+ createAgent,
14
+ createTaskExtended,
15
+ getActiveTaskCount,
16
+ getDb,
17
+ getTaskById,
18
+ initDb,
19
+ } from "../be/db";
20
+ import { upsertRuntimeInstance } from "../be/multi-runtime";
21
+ import { handleMcpBridge } from "../http/mcp-bridge";
22
+ import { handlePoll } from "../http/poll";
23
+ import { getPathSegments, parseQueryParams } from "../http/utils";
24
+ import { runScript } from "../scripts-runtime/loader";
25
+ import { SwarmConfig } from "../scripts-runtime/swarm-config";
26
+ import { createSwarmSdk } from "../scripts-runtime/swarm-sdk";
27
+ import { proxyScriptsApi } from "../tools/script-common";
28
+
29
+ const TEST_DB_PATH = "./test-scripts-runtime-identity.sqlite";
30
+ const TEST_PORT = 14113 + (process.pid % 1000);
31
+ const baseUrl = `http://localhost:${TEST_PORT}`;
32
+ const API_KEY = "scripts-runtime-identity-key-1234567890";
33
+
34
+ const savedEnv = { ...process.env };
35
+ const originalFetch = globalThis.fetch;
36
+ let server: Server;
37
+
38
+ async function removeDbFiles(path: string): Promise<void> {
39
+ for (const suffix of ["", "-wal", "-shm"]) {
40
+ await unlink(path + suffix).catch(() => {});
41
+ }
42
+ }
43
+
44
+ /** Test server exposing the two real handlers the script SDK dispatches through. */
45
+ function createTestServer(): Server {
46
+ return createHttpServer(async (req, res) => {
47
+ const myAgentId = (req.headers["x-agent-id"] as string | undefined) ?? undefined;
48
+ const segments = getPathSegments(req.url ?? "");
49
+ const query = parseQueryParams(req.url ?? "");
50
+ if (req.url?.startsWith("/api/poll")) {
51
+ if (await handlePoll(req, res, segments, query, myAgentId)) return;
52
+ }
53
+ if (req.url?.startsWith("/api/mcp-bridge")) {
54
+ if (await handleMcpBridge(req, res, segments, query, myAgentId)) return;
55
+ }
56
+ res.writeHead(404, { "Content-Type": "application/json" });
57
+ res.end(JSON.stringify({ error: "Not found" }));
58
+ });
59
+ }
60
+
61
+ function makeAgent(maxTasks = 1): string {
62
+ const id = crypto.randomUUID();
63
+ createAgent({ id, name: "sri-agent", isLead: false, status: "idle", capabilities: [], maxTasks });
64
+ return id;
65
+ }
66
+
67
+ function registerRuntime(agentId: string): string {
68
+ const rt = crypto.randomUUID();
69
+ upsertRuntimeInstance({ id: rt, agentId, reportedSlots: 1 });
70
+ return rt;
71
+ }
72
+
73
+ function makeRuntimeStale(runtimeInstanceId: string, minutesAgo = 30): void {
74
+ const when = new Date(Date.now() - minutesAgo * 60 * 1000).toISOString();
75
+ getDb()
76
+ .prepare("UPDATE runtime_instances SET last_seen_at = ? WHERE id = ?")
77
+ .run(when, runtimeInstanceId);
78
+ }
79
+
80
+ function sdkFor(agentId: string, runtimeInstanceId?: string) {
81
+ return createSwarmSdk(
82
+ new SwarmConfig({
83
+ system: {
84
+ apiKey: { value: API_KEY, isSecret: true },
85
+ agentId: { value: agentId, isSecret: false },
86
+ mcpBaseUrl: { value: baseUrl, isSecret: false },
87
+ ...(runtimeInstanceId
88
+ ? { runtimeInstanceId: { value: runtimeInstanceId, isSecret: false as const } }
89
+ : {}),
90
+ },
91
+ user: {},
92
+ }),
93
+ );
94
+ }
95
+
96
+ beforeAll(async () => {
97
+ await removeDbFiles(TEST_DB_PATH);
98
+ initDb(TEST_DB_PATH);
99
+ server = createTestServer();
100
+ await new Promise<void>((resolve) => {
101
+ server.listen(TEST_PORT, () => resolve());
102
+ });
103
+ });
104
+
105
+ afterAll(async () => {
106
+ await new Promise<void>((resolve) => {
107
+ server.close(() => resolve());
108
+ });
109
+ closeDb();
110
+ await removeDbFiles(TEST_DB_PATH);
111
+ });
112
+
113
+ beforeEach(() => {
114
+ process.env.AGENT_SWARM_API_KEY = API_KEY;
115
+ delete process.env.API_KEY;
116
+ });
117
+
118
+ // Full env restore — a leaked AGENT_SWARM_API_KEY outranks the legacy API_KEY
119
+ // that other suites' spawned servers authenticate with.
120
+ afterEach(() => {
121
+ globalThis.fetch = originalFetch;
122
+ for (const key of Object.keys(process.env)) {
123
+ if (!(key in savedEnv)) delete process.env[key];
124
+ }
125
+ for (const [key, value] of Object.entries(savedEnv)) {
126
+ if (value === undefined) delete process.env[key];
127
+ else process.env[key] = value;
128
+ }
129
+ });
130
+
131
+ describe("script-run proxy forwards runtime identity", () => {
132
+ test("X-Runtime-Instance-ID from the MCP request context reaches the scripts API", async () => {
133
+ let captured: Record<string, string> | undefined;
134
+ globalThis.fetch = (async (_input: unknown, init?: RequestInit) => {
135
+ captured = init?.headers as Record<string, string>;
136
+ return new Response(JSON.stringify({ ok: true }), { status: 200 });
137
+ }) as typeof globalThis.fetch;
138
+
139
+ await proxyScriptsApi({
140
+ method: "POST",
141
+ path: "/api/scripts/run",
142
+ body: { name: "x" },
143
+ requestInfo: {
144
+ sessionId: "s",
145
+ agentId: "agent-1",
146
+ runtimeInstanceId: "rt-proxy-1",
147
+ sourceTaskId: "task-1",
148
+ contextKey: undefined,
149
+ callOrigin: "mcp",
150
+ },
151
+ successMessage: () => "ok",
152
+ });
153
+
154
+ expect(captured?.["X-Agent-ID"]).toBe("agent-1");
155
+ expect(captured?.["X-Runtime-Instance-ID"]).toBe("rt-proxy-1");
156
+ });
157
+
158
+ test("no runtime identity in the request context → no header", async () => {
159
+ let captured: Record<string, string> | undefined;
160
+ globalThis.fetch = (async (_input: unknown, init?: RequestInit) => {
161
+ captured = init?.headers as Record<string, string>;
162
+ return new Response(JSON.stringify({ ok: true }), { status: 200 });
163
+ }) as typeof globalThis.fetch;
164
+
165
+ await proxyScriptsApi({
166
+ method: "GET",
167
+ path: "/api/scripts",
168
+ requestInfo: {
169
+ sessionId: "s",
170
+ agentId: "agent-1",
171
+ runtimeInstanceId: undefined,
172
+ sourceTaskId: undefined,
173
+ contextKey: undefined,
174
+ callOrigin: "mcp",
175
+ },
176
+ successMessage: () => "ok",
177
+ });
178
+
179
+ expect(captured?.["X-Runtime-Instance-ID"]).toBeUndefined();
180
+ });
181
+ });
182
+
183
+ describe("swarm SDK emits the runtime header", () => {
184
+ test("task_poll and the generic MCP bridge both carry X-Runtime-Instance-ID", async () => {
185
+ const seen: Array<{ path: string; runtime: string | null }> = [];
186
+ const stub = Bun.serve({
187
+ port: 0,
188
+ fetch(req) {
189
+ const url = new URL(req.url);
190
+ seen.push({ path: url.pathname, runtime: req.headers.get("x-runtime-instance-id") });
191
+ return Response.json({ trigger: null });
192
+ },
193
+ });
194
+ try {
195
+ const sdk = createSwarmSdk(
196
+ new SwarmConfig({
197
+ system: {
198
+ apiKey: { value: API_KEY, isSecret: true },
199
+ agentId: { value: "agent-sdk", isSecret: false },
200
+ mcpBaseUrl: { value: `http://127.0.0.1:${stub.port}`, isSecret: false },
201
+ runtimeInstanceId: { value: "rt-sdk-1", isSecret: false },
202
+ },
203
+ user: {},
204
+ }),
205
+ );
206
+ await sdk.task_poll();
207
+ await sdk.task_action({ action: "claim", taskId: crypto.randomUUID() });
208
+
209
+ expect(seen).toEqual([
210
+ { path: "/api/poll", runtime: "rt-sdk-1" },
211
+ { path: "/api/mcp-bridge", runtime: "rt-sdk-1" },
212
+ ]);
213
+ } finally {
214
+ stub.stop(true);
215
+ }
216
+ });
217
+
218
+ test("without a runtime identity the SDK sends none (legacy compatibility)", async () => {
219
+ const seen: Array<string | null> = [];
220
+ const stub = Bun.serve({
221
+ port: 0,
222
+ fetch(req) {
223
+ seen.push(req.headers.get("x-runtime-instance-id"));
224
+ return Response.json({ trigger: null });
225
+ },
226
+ });
227
+ try {
228
+ const sdk = createSwarmSdk(
229
+ new SwarmConfig({
230
+ system: {
231
+ apiKey: { value: API_KEY, isSecret: true },
232
+ agentId: { value: "agent-sdk", isSecret: false },
233
+ mcpBaseUrl: { value: `http://127.0.0.1:${stub.port}`, isSecret: false },
234
+ },
235
+ user: {},
236
+ }),
237
+ );
238
+ await sdk.task_poll();
239
+ expect(seen).toEqual([null]);
240
+ } finally {
241
+ stub.stop(true);
242
+ }
243
+ });
244
+ });
245
+
246
+ describe("script subprocess carries the invoking worker's identity", () => {
247
+ const resources = { memoryMb: 2048, cpuTimeSec: 20, maxStdoutBytes: 1_048_576 };
248
+
249
+ test("ctx.swarm.task_poll() presents X-Runtime-Instance-ID from system config", async () => {
250
+ const seen: Array<string | null> = [];
251
+ const stub = Bun.serve({
252
+ port: 0,
253
+ fetch(req) {
254
+ seen.push(req.headers.get("x-runtime-instance-id"));
255
+ return Response.json({ trigger: null });
256
+ },
257
+ });
258
+ try {
259
+ const output = await runScript({
260
+ agentId: "agent-sub",
261
+ runtimeInstanceId: "rt-subprocess-1",
262
+ mcpBaseUrl: `http://127.0.0.1:${stub.port}`,
263
+ resources,
264
+ source: `
265
+ export default async (_args, ctx) => {
266
+ await ctx.swarm.task_poll();
267
+ return "polled";
268
+ };
269
+ `,
270
+ });
271
+
272
+ expect(output.error).toBeUndefined();
273
+ expect(output.result).toBe("polled");
274
+ expect(seen).toEqual(["rt-subprocess-1"]);
275
+ } finally {
276
+ stub.stop(true);
277
+ }
278
+ });
279
+
280
+ test("without a runtime identity the subprocess sends none", async () => {
281
+ const seen: Array<string | null> = [];
282
+ const stub = Bun.serve({
283
+ port: 0,
284
+ fetch(req) {
285
+ seen.push(req.headers.get("x-runtime-instance-id"));
286
+ return Response.json({ trigger: null });
287
+ },
288
+ });
289
+ try {
290
+ const output = await runScript({
291
+ agentId: "agent-sub",
292
+ mcpBaseUrl: `http://127.0.0.1:${stub.port}`,
293
+ resources,
294
+ source: `
295
+ export default async (_args, ctx) => {
296
+ await ctx.swarm.task_poll();
297
+ return "polled";
298
+ };
299
+ `,
300
+ });
301
+
302
+ expect(output.error).toBeUndefined();
303
+ expect(seen).toEqual([null]);
304
+ } finally {
305
+ stub.stop(true);
306
+ }
307
+ });
308
+ });
309
+
310
+ describe("script SDK dispatch hits the real runtime gates", () => {
311
+ test("a live runtime acquires pending work through task_poll; a stale one does not", async () => {
312
+ process.env.MULTI_RUNTIME_ENABLED = "true";
313
+ const agentId = makeAgent(1);
314
+ const live = registerRuntime(agentId);
315
+ const stale = registerRuntime(agentId);
316
+ makeRuntimeStale(stale);
317
+ const task = createTaskExtended("script-poll-work", { agentId });
318
+
319
+ const staleResult = (await sdkFor(agentId, stale).task_poll()) as {
320
+ data: { trigger: { type?: string } | null };
321
+ };
322
+ expect(staleResult.data.trigger).toBeNull();
323
+ expect(getTaskById(task.id)?.status).toBe("pending");
324
+
325
+ const liveResult = (await sdkFor(agentId, live).task_poll()) as {
326
+ data: { trigger: { type?: string } | null };
327
+ };
328
+ expect(liveResult.data.trigger?.type).toBe("task_assigned");
329
+ expect(getTaskById(task.id)?.status).toBe("in_progress");
330
+ expect(getActiveTaskCount(agentId)).toBe(1);
331
+ });
332
+
333
+ test("task_action claim through the bridge respects the live-runtime gate", async () => {
334
+ process.env.MULTI_RUNTIME_ENABLED = "true";
335
+ const agentId = makeAgent(1);
336
+ const live = registerRuntime(agentId);
337
+ const stale = registerRuntime(agentId);
338
+ makeRuntimeStale(stale);
339
+ const task = createTaskExtended("script-claim-work");
340
+
341
+ await sdkFor(agentId, stale).task_action({ action: "claim", taskId: task.id });
342
+ expect(getTaskById(task.id)?.agentId ?? null).toBeNull();
343
+ await sdkFor(agentId).task_action({ action: "claim", taskId: task.id });
344
+ expect(getTaskById(task.id)?.agentId ?? null).toBeNull();
345
+
346
+ await sdkFor(agentId, live).task_action({ action: "claim", taskId: task.id });
347
+ expect(getTaskById(task.id)?.agentId).toBe(agentId);
348
+ expect(getTaskById(task.id)?.status).toBe("in_progress");
349
+ });
350
+
351
+ test("task_action accept through the bridge respects the live-runtime gate", async () => {
352
+ process.env.MULTI_RUNTIME_ENABLED = "true";
353
+ const agentId = makeAgent(1);
354
+ const live = registerRuntime(agentId);
355
+ const stale = registerRuntime(agentId);
356
+ makeRuntimeStale(stale);
357
+ const task = createTaskExtended("script-accept-work", { offeredTo: agentId });
358
+
359
+ await sdkFor(agentId, stale).task_action({ action: "accept", taskId: task.id });
360
+ expect(getTaskById(task.id)?.status).toBe("offered");
361
+
362
+ await sdkFor(agentId, live).task_action({ action: "accept", taskId: task.id });
363
+ expect(getTaskById(task.id)?.status).toBe("pending");
364
+ expect(getTaskById(task.id)?.agentId).toBe(agentId);
365
+ });
366
+
367
+ test("with the flag off, the SDK dispatches without runtime identity as before", async () => {
368
+ delete process.env.MULTI_RUNTIME_ENABLED;
369
+ const agentId = makeAgent(1);
370
+ const task = createTaskExtended("legacy-claim-work");
371
+
372
+ await sdkFor(agentId).task_action({ action: "claim", taskId: task.id });
373
+ expect(getTaskById(task.id)?.agentId).toBe(agentId);
374
+ });
375
+ });
@@ -1,4 +1,5 @@
1
1
  import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+ import { fileURLToPath } from "node:url";
2
3
  import { createApiRegistryClient } from "../scripts-runtime/api-client";
3
4
  import { runScript } from "../scripts-runtime/loader";
4
5
  import {
@@ -327,7 +328,7 @@ describe("runScript", () => {
327
328
  const tmpdir = `${process.env.TMPDIR ?? "/tmp"}/script-runtime-test-${crypto.randomUUID()}`;
328
329
  await Bun.$`mkdir -p ${tmpdir}`;
329
330
  try {
330
- const runtimeSrc = new URL("../scripts-runtime", import.meta.url).pathname;
331
+ const runtimeSrc = fileURLToPath(new URL("../scripts-runtime", import.meta.url));
331
332
  await Bun.$`bun build ${runtimeSrc}/eval-harness.ts --target bun --no-splitting --outfile ${tmpdir}/eval-harness.bundle.js`.quiet();
332
333
  await Bun.$`bun build ${runtimeSrc}/stdlib/index.ts --target bun --no-splitting --outfile ${tmpdir}/stdlib.bundle.js`.quiet();
333
334
  await Bun.$`bun build ${runtimeSrc}/swarm-sdk.ts --target bun --no-splitting --outfile ${tmpdir}/swarm-sdk.bundle.js`.quiet();
@@ -377,7 +378,7 @@ describe("runScript", () => {
377
378
  const tmpdir = `${process.env.TMPDIR ?? "/tmp"}/script-runtime-test-${crypto.randomUUID()}`;
378
379
  await Bun.$`mkdir -p ${tmpdir}`;
379
380
  try {
380
- const runtimeSrc = new URL("../scripts-runtime", import.meta.url).pathname;
381
+ const runtimeSrc = fileURLToPath(new URL("../scripts-runtime", import.meta.url));
381
382
  await Bun.$`bun build ${runtimeSrc}/eval-harness.ts --target bun --no-splitting --outfile ${tmpdir}/eval-harness.bundle.js`.quiet();
382
383
  await Bun.$`bun build ${runtimeSrc}/stdlib/index.ts --target bun --no-splitting --outfile ${tmpdir}/stdlib.bundle.js`.quiet();
383
384
  await Bun.$`bun build ${runtimeSrc}/swarm-sdk.ts --target bun --no-splitting --outfile ${tmpdir}/swarm-sdk.bundle.js`.quiet();
@@ -410,7 +411,7 @@ describe("runScript", () => {
410
411
  const tmpdir = `${process.env.TMPDIR ?? "/tmp"}/script-runtime-test-${crypto.randomUUID()}`;
411
412
  await Bun.$`mkdir -p ${tmpdir}`;
412
413
  try {
413
- const runtimeSrc = new URL("../scripts-runtime", import.meta.url).pathname;
414
+ const runtimeSrc = fileURLToPath(new URL("../scripts-runtime", import.meta.url));
414
415
  await Bun.$`bun build ${runtimeSrc}/eval-harness.ts --target bun --no-splitting --outfile ${tmpdir}/eval-harness.bundle.js`.quiet();
415
416
  await Bun.$`bun build ${runtimeSrc}/stdlib/index.ts --target bun --no-splitting --outfile ${tmpdir}/stdlib.bundle.js`.quiet();
416
417
  await Bun.$`bun build ${runtimeSrc}/swarm-sdk.ts --target bun --no-splitting --outfile ${tmpdir}/swarm-sdk.bundle.js`.quiet();
@@ -98,6 +98,53 @@ describe("script SDK allowlist", () => {
98
98
  }
99
99
  });
100
100
 
101
+ test("schedule_list maps includeFull to the full HTTP field and keeps the default slim", async () => {
102
+ const requestUrls: URL[] = [];
103
+ const httpServer = Bun.serve({
104
+ port: 0,
105
+ fetch(req) {
106
+ const url = new URL(req.url);
107
+ requestUrls.push(url);
108
+ const full = url.searchParams.get("fields") === "full";
109
+ return Response.json({
110
+ schedules: [
111
+ full
112
+ ? { id: "schedule-1", taskTemplate: "full template" }
113
+ : { id: "schedule-1", taskTemplatePreview: "full..." },
114
+ ],
115
+ count: 1,
116
+ });
117
+ },
118
+ });
119
+ const config = new SwarmConfig({
120
+ system: {
121
+ apiKey: { value: "sdk-test-key", isSecret: true },
122
+ agentId: { value: "sdk-test-agent", isSecret: false },
123
+ mcpBaseUrl: { value: `http://127.0.0.1:${httpServer.port}`, isSecret: false },
124
+ },
125
+ user: {},
126
+ });
127
+ const sdk = createSwarmSdk(config);
128
+
129
+ try {
130
+ const full = await sdk.schedule_list({ includeFull: true });
131
+ expect(
132
+ (full as { data: { schedules: Array<{ taskTemplate?: string }> } }).data.schedules[0]
133
+ ?.taskTemplate,
134
+ ).toBe("full template");
135
+ expect(requestUrls[0]?.searchParams.get("fields")).toBe("full");
136
+
137
+ const slim = await sdk.schedule_list();
138
+ expect(
139
+ (slim as { data: { schedules: Array<{ taskTemplatePreview?: string }> } }).data.schedules[0]
140
+ ?.taskTemplatePreview,
141
+ ).toBe("full...");
142
+ expect(requestUrls[1]?.searchParams.has("fields")).toBe(false);
143
+ } finally {
144
+ httpServer.stop(true);
145
+ }
146
+ });
147
+
101
148
  test("bundled swarm-sdk.d.ts exposes only allowlisted methods", async () => {
102
149
  const types = await Bun.file("src/scripts-runtime/types/swarm-sdk.d.ts").text();
103
150
  for (const name of SDK_ALLOWLIST) {
@@ -79,6 +79,12 @@ afterAll(async () => {
79
79
  describe("send-task: requestedByUserId inheritance", () => {
80
80
  const server = new McpServer({ name: "test-send-task", version: "1.0.0" });
81
81
  registerSendTaskTool(server);
82
+ const requesterWasInherited = (taskId: string): number | undefined =>
83
+ getDb()
84
+ .query<{ inherited: number }, [string]>(
85
+ "SELECT requestedByUserIdInherited AS inherited FROM agent_tasks WHERE id = ?",
86
+ )
87
+ .get(taskId)?.inherited;
82
88
 
83
89
  test("child pool task inherits requestedByUserId from caller's sourceTaskId", async () => {
84
90
  // Parent task has no agentId so the auto-route won't force a lead assignment.
@@ -98,6 +104,7 @@ describe("send-task: requestedByUserId inheritance", () => {
98
104
  expect(s.task).toBeDefined();
99
105
  const created = getTaskById(s.task!.id);
100
106
  expect(created?.requestedByUserId).toBe(userAId);
107
+ expect(requesterWasInherited(s.task!.id)).toBe(1);
101
108
  });
102
109
 
103
110
  test("explicit requestedByUserId in args wins over inherited value", async () => {
@@ -116,6 +123,7 @@ describe("send-task: requestedByUserId inheritance", () => {
116
123
  expect(s.success).toBe(true);
117
124
  const created = getTaskById(s.task!.id);
118
125
  expect(created?.requestedByUserId).toBe(userBId);
126
+ expect(requesterWasInherited(s.task!.id)).toBe(0);
119
127
  });
120
128
 
121
129
  test("no crash when caller has no sourceTaskId and no requestedByUserId arg", async () => {
@@ -152,6 +160,7 @@ describe("send-task: requestedByUserId inheritance", () => {
152
160
  expect(s.success).toBe(true);
153
161
  const created = getTaskById(s.task!.id);
154
162
  expect(created?.requestedByUserId).toBe(userAId);
163
+ expect(requesterWasInherited(s.task!.id)).toBe(1);
155
164
  });
156
165
 
157
166
  test("skips creating a child when source task already owns a Linear tracker contextKey", async () => {