@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
@@ -2,18 +2,23 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { addMinutes } from "date-fns";
3
3
  import * as z from "zod";
4
4
  import {
5
+ getActiveTaskCount,
5
6
  getAgentById,
6
7
  getDb,
7
8
  getOfferedTasksForAgent,
8
9
  getPendingTaskForAgent,
9
10
  getUnassignedTasksCount,
11
+ hasCapacity,
10
12
  incrementEmptyPollCount,
11
13
  MAX_EMPTY_POLLS,
12
14
  resetEmptyPollCount,
13
15
  startTask,
14
16
  updateAgentStatus,
15
17
  } from "@/be/db";
18
+ import { touchRuntimeInstance } from "@/be/multi-runtime";
16
19
  import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
20
+ import type { AgentTask } from "@/types";
21
+ import { isMultiRuntimeEnabled } from "@/utils/multi-runtime";
17
22
  import { looseAgentTaskOutputSchema } from "./get-task-details";
18
23
 
19
24
  const DEFAULT_POLL_INTERVAL_MS = 2000;
@@ -70,6 +75,28 @@ export const registerPollTaskTool = (server: McpServer) => {
70
75
  // touching the bookkeeping path below.
71
76
  const wasBudgetRefused: boolean = false;
72
77
 
78
+ // Second dispatch entrypoint alongside HTTP /api/poll, so it needs the
79
+ // same gate: a process whose runtime is gone must not pick up work
80
+ // beside its replacement. Touch validates ownership+liveness and
81
+ // refreshes it; it cannot revive a retired runtime.
82
+ if (
83
+ isMultiRuntimeEnabled() &&
84
+ !(
85
+ requestInfo.runtimeInstanceId &&
86
+ touchRuntimeInstance(requestInfo.runtimeInstanceId, agentId)
87
+ )
88
+ ) {
89
+ return toolOk("No task available.", {
90
+ details: "No task available for this runtime.",
91
+ data: {
92
+ yourAgentId: requestInfo.agentId,
93
+ offeredTasks: [],
94
+ availableCount: 0,
95
+ waitedForSeconds: 0,
96
+ },
97
+ });
98
+ }
99
+
73
100
  const agent = getAgentById(agentId);
74
101
  if (!agent) {
75
102
  return toolErr(`Agent with ID "${agentId}" not found in the swarm.`, {
@@ -104,26 +131,78 @@ export const registerPollTaskTool = (server: McpServer) => {
104
131
  // Poll for pending tasks
105
132
  while (new Date() < maxTime) {
106
133
  // Fetch and update in a single transaction to avoid race conditions
107
- const startedTask = getDb().transaction(() => {
108
- const agentNow = getAgentById(agentId)!;
134
+ const outcome = getDb().transaction(
135
+ (): AgentTask | "at-capacity" | "runtime-unavailable" | null => {
136
+ // The entry gate only proves liveness when the long poll began;
137
+ // the runtime must be live at the exact moment work is acquired,
138
+ // so revalidate in the same transaction that can start the task.
139
+ // Touch refreshes an already-live row and cannot revive one.
140
+ if (
141
+ isMultiRuntimeEnabled() &&
142
+ !(
143
+ requestInfo.runtimeInstanceId &&
144
+ touchRuntimeInstance(requestInfo.runtimeInstanceId, agentId)
145
+ )
146
+ ) {
147
+ return "runtime-unavailable";
148
+ }
149
+
150
+ const agentNow = getAgentById(agentId)!;
151
+
152
+ if (agentNow.status !== "busy") {
153
+ updateAgentStatus(agentId, "idle");
154
+ }
109
155
 
110
- if (agentNow.status !== "busy") {
111
- updateAgentStatus(agentId, "idle");
112
- }
156
+ const pendingTask = getPendingTaskForAgent(agentId);
157
+ if (!pendingTask) return null;
113
158
 
114
- const pendingTask = getPendingTaskForAgent(agentId);
115
- if (!pendingTask) return null;
159
+ // Logical capacity is decided inside the same transaction as the
160
+ // start transition: several runtimes of one agent race this
161
+ // dispatch, and a check outside it would let each of them start a
162
+ // task past the agent's limit. Same gate as HTTP /api/poll.
163
+ if (!hasCapacity(agentId)) return "at-capacity";
116
164
 
117
- const maybeTask = startTask(pendingTask.id);
165
+ const maybeTask = startTask(pendingTask.id);
118
166
 
119
- if (maybeTask) {
120
- // Update automatically in case the agent forgets xd
121
- updateAgentStatus(agentId, "busy");
122
- }
167
+ if (maybeTask) {
168
+ // Update automatically in case the agent forgets xd
169
+ updateAgentStatus(agentId, "busy");
170
+ }
123
171
 
124
- return maybeTask;
125
- })();
172
+ return maybeTask;
173
+ },
174
+ )();
175
+
176
+ if (outcome === "runtime-unavailable") {
177
+ // The runtime was retired while this call waited: stop immediately
178
+ // rather than keep polling as it, and skip the exit counter — this
179
+ // is a refusal, not an empty poll.
180
+ return toolOk("No task available.", {
181
+ details: "No task available for this runtime.",
182
+ data: {
183
+ yourAgentId: requestInfo.agentId,
184
+ offeredTasks: [],
185
+ availableCount: 0,
186
+ waitedForSeconds: Math.round((Date.now() - now.getTime()) / 1000),
187
+ },
188
+ });
189
+ }
190
+
191
+ if (outcome === "at-capacity") {
192
+ // A capacity refusal is not an empty poll (refused ≠ empty, D-R3):
193
+ // return without advancing the exit counter.
194
+ return toolOk("No task available.", {
195
+ details: `You are at capacity (${getActiveTaskCount(agentId)} active task(s)). Complete a task before polling for more.`,
196
+ data: {
197
+ yourAgentId: requestInfo.agentId,
198
+ offeredTasks: [],
199
+ availableCount: getUnassignedTasksCount(),
200
+ waitedForSeconds: Math.round((Date.now() - now.getTime()) / 1000),
201
+ },
202
+ });
203
+ }
126
204
 
205
+ const startedTask = outcome;
127
206
  if (startedTask) {
128
207
  // Reset empty poll count when task is assigned
129
208
  resetEmptyPollCount(agentId);
@@ -172,6 +172,11 @@ export async function proxyScriptsApi(args: {
172
172
  if (args.requestInfo.sourceTaskId) {
173
173
  headers["X-Source-Task-Id"] = args.requestInfo.sourceTaskId;
174
174
  }
175
+ // Runtime identity is process context from the worker's MCP session — it
176
+ // rides the proxy like the agent identity, never as tool or script input.
177
+ if (args.requestInfo.runtimeInstanceId) {
178
+ headers["X-Runtime-Instance-ID"] = args.requestInfo.runtimeInstanceId;
179
+ }
175
180
  const res = await fetch(`${apiBaseUrl()}${args.path}`, {
176
181
  method: args.method,
177
182
  headers,
@@ -223,11 +223,8 @@ export async function sendTaskHandler(
223
223
 
224
224
  const creatorAgentId = ctx.kind === "owner" ? ctx.agentId : undefined;
225
225
  const sourceTaskId = ctx.kind === "owner" ? ctx.sourceTaskId : undefined;
226
- const callerTask = sourceTaskId ? getTaskById(sourceTaskId) : null;
227
226
  const requestedByUserId =
228
- ctx.kind === "user"
229
- ? ctx.userId
230
- : (inputRequestedByUserId ?? callerTask?.requestedByUserId ?? undefined);
227
+ ctx.kind === "user" ? ctx.userId : (inputRequestedByUserId ?? undefined);
231
228
 
232
229
  if (ctx.kind === "owner" && agentId === ctx.agentId) {
233
230
  return toolErr("Cannot send a task to yourself, are you drunk?", {
@@ -0,0 +1,70 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import * as z from "zod";
3
+ import { getAgentById } from "@/be/db";
4
+ import { can } from "@/rbac";
5
+ import { getSlackApp } from "@/slack/app";
6
+ import { slackMissingScopeMessage } from "@/slack/channel-join";
7
+ import { archiveChannel } from "@/slack/channel-lifecycle";
8
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
9
+
10
+ export const registerSlackArchiveChannelTool = (server: McpServer) => {
11
+ createToolRegistrar(server)(
12
+ "slack-archive-channel",
13
+ {
14
+ title: "Archive a Slack channel",
15
+ description:
16
+ "Archives a Slack channel. Channels that are already archived are treated as a successful no-op, while Slack's general channel cannot be archived. Requires lead privileges.",
17
+ annotations: { openWorldHint: true, destructiveHint: true },
18
+
19
+ inputSchema: z.object({
20
+ channelId: z.string().min(1).describe("The Slack channel ID to archive."),
21
+ }),
22
+ outputSchema: swarmToolOutputSchema({
23
+ channelId: z.string().optional(),
24
+ alreadyArchived: z.boolean().optional(),
25
+ }),
26
+ },
27
+ async ({ channelId }, requestInfo, _meta) => {
28
+ if (!requestInfo.agentId) {
29
+ return toolErr("Agent ID not found.");
30
+ }
31
+
32
+ const agent = getAgentById(requestInfo.agentId);
33
+ if (!agent) {
34
+ return toolErr("Agent not found.");
35
+ }
36
+
37
+ const decision = can({
38
+ principal: { kind: "agent", agentId: agent.id, isLead: agent.isLead },
39
+ verb: "integration.slack.channel.archive",
40
+ resource: { kind: "none" },
41
+ source: "mcp",
42
+ });
43
+ if (!decision.allow) {
44
+ return toolErr("Managing Slack channels requires lead privileges.");
45
+ }
46
+
47
+ const app = getSlackApp();
48
+ if (!app) {
49
+ return toolErr("Slack not configured.");
50
+ }
51
+
52
+ try {
53
+ const result = await archiveChannel(app.client, channelId);
54
+ return toolOk(
55
+ result.alreadyArchived
56
+ ? "Slack channel is already archived."
57
+ : "Slack channel archived successfully.",
58
+ { data: { channelId, ...result } },
59
+ );
60
+ } catch (error) {
61
+ const missingScopeMessage = slackMissingScopeMessage(error);
62
+ if (missingScopeMessage) {
63
+ return toolErr(`Failed to archive Slack channel: ${missingScopeMessage}`);
64
+ }
65
+ const errorMsg = error instanceof Error ? error.message : String(error);
66
+ return toolErr(`Failed to archive Slack channel: ${errorMsg}`);
67
+ }
68
+ },
69
+ );
70
+ };
@@ -0,0 +1,74 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import * as z from "zod";
3
+ import { getAgentById } from "@/be/db";
4
+ import { can } from "@/rbac";
5
+ import { getSlackApp } from "@/slack/app";
6
+ import { slackMissingScopeMessage } from "@/slack/channel-join";
7
+ import { createChannel } from "@/slack/channel-lifecycle";
8
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
9
+
10
+ export const registerSlackCreateChannelTool = (server: McpServer) => {
11
+ createToolRegistrar(server)(
12
+ "slack-create-channel",
13
+ {
14
+ title: "Create a Slack channel",
15
+ description:
16
+ "Creates a public or private Slack channel. The supplied name is normalized to Slack's channel-name rules, and the normalized name is returned. Requires lead privileges.",
17
+ annotations: { openWorldHint: true },
18
+
19
+ inputSchema: z.object({
20
+ name: z.string().min(1).describe("The desired Slack channel name."),
21
+ isPrivate: z
22
+ .boolean()
23
+ .optional()
24
+ .default(false)
25
+ .describe("Whether to create a private channel. Defaults to false."),
26
+ }),
27
+ outputSchema: swarmToolOutputSchema({
28
+ channelId: z.string().optional(),
29
+ name: z.string().optional(),
30
+ isPrivate: z.boolean().optional(),
31
+ }),
32
+ },
33
+ async ({ name, isPrivate }, requestInfo, _meta) => {
34
+ if (!requestInfo.agentId) {
35
+ return toolErr("Agent ID not found.");
36
+ }
37
+
38
+ const agent = getAgentById(requestInfo.agentId);
39
+ if (!agent) {
40
+ return toolErr("Agent not found.");
41
+ }
42
+
43
+ const decision = can({
44
+ principal: { kind: "agent", agentId: agent.id, isLead: agent.isLead },
45
+ verb: "integration.slack.channel.create",
46
+ resource: { kind: "none" },
47
+ source: "mcp",
48
+ });
49
+ if (!decision.allow) {
50
+ return toolErr("Managing Slack channels requires lead privileges.");
51
+ }
52
+
53
+ const app = getSlackApp();
54
+ if (!app) {
55
+ return toolErr("Slack not configured.");
56
+ }
57
+
58
+ try {
59
+ const result = await createChannel(app.client, { name, isPrivate });
60
+ return toolOk("Slack channel created successfully.", {
61
+ details: `Created Slack channel ${result.name} (${result.channelId}).`,
62
+ data: { ...result, isPrivate },
63
+ });
64
+ } catch (error) {
65
+ const missingScopeMessage = slackMissingScopeMessage(error);
66
+ if (missingScopeMessage) {
67
+ return toolErr(`Failed to create Slack channel: ${missingScopeMessage}`);
68
+ }
69
+ const errorMsg = error instanceof Error ? error.message : String(error);
70
+ return toolErr(`Failed to create Slack channel: ${errorMsg}`);
71
+ }
72
+ },
73
+ );
74
+ };
@@ -0,0 +1,78 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import * as z from "zod";
3
+ import { getAgentById } from "@/be/db";
4
+ import { can } from "@/rbac";
5
+ import { getSlackApp } from "@/slack/app";
6
+ import { slackMissingScopeMessage } from "@/slack/channel-join";
7
+ import { inviteToChannel } from "@/slack/channel-lifecycle";
8
+ import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
9
+
10
+ export const registerSlackInviteToChannelTool = (server: McpServer) => {
11
+ createToolRegistrar(server)(
12
+ "slack-invite-to-channel",
13
+ {
14
+ title: "Invite users to a Slack channel",
15
+ description:
16
+ "Invites one or more workspace users to a Slack channel. Users who are already in the channel are treated as a successful no-op. Requires lead privileges.",
17
+ annotations: { openWorldHint: true },
18
+
19
+ inputSchema: z.object({
20
+ channelId: z.string().min(1).describe("The Slack channel ID."),
21
+ userIds: z
22
+ .array(z.string().min(1))
23
+ .min(1)
24
+ .max(100)
25
+ .describe("Slack user IDs to invite (up to 100)."),
26
+ }),
27
+ outputSchema: swarmToolOutputSchema({
28
+ channelId: z.string().optional(),
29
+ userIds: z.array(z.string()).optional(),
30
+ alreadyInChannel: z.boolean().optional(),
31
+ }),
32
+ },
33
+ async ({ channelId, userIds }, requestInfo, _meta) => {
34
+ if (!requestInfo.agentId) {
35
+ return toolErr("Agent ID not found.");
36
+ }
37
+
38
+ const agent = getAgentById(requestInfo.agentId);
39
+ if (!agent) {
40
+ return toolErr("Agent not found.");
41
+ }
42
+
43
+ const decision = can({
44
+ principal: { kind: "agent", agentId: agent.id, isLead: agent.isLead },
45
+ verb: "integration.slack.channel.invite",
46
+ resource: { kind: "none" },
47
+ source: "mcp",
48
+ });
49
+ if (!decision.allow) {
50
+ return toolErr("Managing Slack channels requires lead privileges.");
51
+ }
52
+
53
+ const app = getSlackApp();
54
+ if (!app) {
55
+ return toolErr("Slack not configured.");
56
+ }
57
+
58
+ try {
59
+ const result = await inviteToChannel(app.client, channelId, userIds);
60
+ return toolOk(
61
+ result.alreadyInChannel
62
+ ? "Users are already in the Slack channel."
63
+ : "Users invited to the Slack channel successfully.",
64
+ {
65
+ data: { channelId, userIds, ...result },
66
+ },
67
+ );
68
+ } catch (error) {
69
+ const missingScopeMessage = slackMissingScopeMessage(error);
70
+ if (missingScopeMessage) {
71
+ return toolErr(`Failed to invite users to Slack channel: ${missingScopeMessage}`);
72
+ }
73
+ const errorMsg = error instanceof Error ? error.message : String(error);
74
+ return toolErr(`Failed to invite users to Slack channel: ${errorMsg}`);
75
+ }
76
+ },
77
+ );
78
+ };
@@ -24,6 +24,7 @@ import {
24
24
  import { createWorkerTaskFollowUp } from "@/tasks/worker-follow-up";
25
25
  import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
26
26
  import { AgentTaskStatusSchema, AttachmentInputSchema, isTerminalTaskStatus } from "@/types";
27
+ import { scrubSecrets } from "../utils/secret-scrubber";
27
28
 
28
29
  // Phase 11: the `cost` / `costData` field was removed from this tool's input
29
30
  // schema. Adapters (claude/codex/pi/opencode/devin/claude-managed) are the
@@ -400,7 +401,12 @@ export const registerStoreProgressTool = (server: McpServer) => {
400
401
  } catch {
401
402
  // Non-blocking — task completion memory failure should not affect task status
402
403
  }
403
- })();
404
+ })().catch((err) =>
405
+ console.error(
406
+ "[store-progress] task completion memory write failed:",
407
+ scrubSecrets(err instanceof Error ? err.message : String(err)),
408
+ ),
409
+ );
404
410
  }
405
411
 
406
412
  if (shouldRunTerminalSideEffects) {
@@ -436,7 +442,12 @@ export const registerStoreProgressTool = (server: McpServer) => {
436
442
  err instanceof Error ? err.message : String(err),
437
443
  );
438
444
  }
439
- })();
445
+ })().catch((err) =>
446
+ console.error(
447
+ "[store-progress] server rater run failed:",
448
+ scrubSecrets(err instanceof Error ? err.message : String(err)),
449
+ ),
450
+ );
440
451
  }
441
452
 
442
453
  // Create follow-up task for the lead when a worker task finishes.
@@ -1,6 +1,7 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import * as z from "zod";
3
3
  import { deleteSwarmConfig, getAgentById, getSwarmConfigLookupById } from "@/be/db";
4
+ import { AGENT_MAX_TASKS_CONFIG_KEY, resetAgentMaxTasksMirror } from "@/be/multi-runtime";
4
5
  import { scheduleIntegrationsReload } from "@/http/core";
5
6
  import { can } from "@/rbac";
6
7
  import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
@@ -56,6 +57,14 @@ export const registerDeleteConfigTool = (server: McpServer) => {
56
57
  }
57
58
 
58
59
  const deleted = deleteSwarmConfig(id);
60
+ if (
61
+ deleted &&
62
+ existing.scope === "agent" &&
63
+ existing.scopeId &&
64
+ existing.key === AGENT_MAX_TASKS_CONFIG_KEY
65
+ ) {
66
+ resetAgentMaxTasksMirror(existing.scopeId);
67
+ }
59
68
  if (!deleted) {
60
69
  return toolErr(`Failed to delete config entry "${id}".`, {
61
70
  data: { yourAgentId: requestInfo.agentId },
@@ -1,6 +1,7 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import * as z from "zod";
3
- import { getAgentById, maskSecrets, upsertSwarmConfig } from "@/be/db";
3
+ import { getAgentById, maskSecrets } from "@/be/db";
4
+ import { upsertSwarmConfigWithPolicyMirror } from "@/be/multi-runtime";
4
5
  import {
5
6
  isReservedConfigKey,
6
7
  reservedKeyError,
@@ -110,7 +111,7 @@ export const registerSetConfigTool = (server: McpServer) => {
110
111
  return toolErr(validationError, { data: { yourAgentId: requestInfo.agentId } });
111
112
  }
112
113
 
113
- const config = upsertSwarmConfig({
114
+ const config = upsertSwarmConfigWithPolicyMirror({
114
115
  scope,
115
116
  scopeId: scope === "global" ? null : scopeId,
116
117
  key,
@@ -27,6 +27,7 @@ import {
27
27
  releaseTask,
28
28
  updateTaskClaudeSessionId,
29
29
  } from "@/be/db";
30
+ import { touchRuntimeInstance } from "@/be/multi-runtime";
30
31
  import { assertOwnsTask, ownerCtx, type ToolCtx } from "@/tools/task-tool-ctx";
31
32
  import {
32
33
  createToolRegistrar,
@@ -42,6 +43,7 @@ import {
42
43
  ReasoningEffortSchema,
43
44
  splitLegacyModelAlias,
44
45
  } from "@/types";
46
+ import { isMultiRuntimeEnabled } from "@/utils/multi-runtime";
45
47
  import { looseAgentTaskOutputSchema } from "./get-task-details";
46
48
 
47
49
  export const TaskActionSchema = z.enum([
@@ -140,6 +142,28 @@ function agentOnlyActionResult(): SwarmToolResult {
140
142
  return toolErr("This action is only available to worker agents.");
141
143
  }
142
144
 
145
+ /**
146
+ * Work-acquisition gate shared by claim and accept — the same dispatch gate
147
+ * as HTTP poll and poll-task: a process whose runtime is gone must not take
148
+ * work beside its replacement. Identity travels as request context, never as
149
+ * a tool argument; touch refreshes liveness and cannot revive a retired
150
+ * runtime. Returns null when acquisition may proceed.
151
+ */
152
+ function staleRuntimeResult(ctx: ToolCtx, agentId: string): TaskActionResult | null {
153
+ if (!isMultiRuntimeEnabled()) return null;
154
+ if (
155
+ ctx.kind === "owner" &&
156
+ ctx.runtimeInstanceId &&
157
+ touchRuntimeInstance(ctx.runtimeInstanceId, agentId)
158
+ ) {
159
+ return null;
160
+ }
161
+ return {
162
+ success: false,
163
+ message: "This runtime is no longer registered. Re-register before taking work.",
164
+ };
165
+ }
166
+
143
167
  function taskActionResult(result: TaskActionResult, agentId?: string): SwarmToolResult {
144
168
  const { refusalSideEffects: _omit, success, message, ...rest } = result;
145
169
  const data = {
@@ -285,6 +309,8 @@ export async function taskActionHandler(
285
309
  if (!taskId) {
286
310
  return { success: false, message: "Task ID is required for 'claim' action." };
287
311
  }
312
+ const staleClaimRuntime = staleRuntimeResult(ctx, agentId);
313
+ if (staleClaimRuntime) return staleClaimRuntime;
288
314
  // Check capacity before claiming
289
315
  if (!hasCapacity(agentId)) {
290
316
  const activeCount = getActiveTaskCount(agentId);
@@ -388,6 +414,10 @@ export async function taskActionHandler(
388
414
  if (!taskId) {
389
415
  return { success: false, message: "Task ID is required for 'accept' action." };
390
416
  }
417
+ // Accept is a work-acquisition transition too — gate it before the
418
+ // budget admission below so a retired process cannot take the offer.
419
+ const staleAcceptRuntime = staleRuntimeResult(ctx, agentId);
420
+ if (staleAcceptRuntime) return staleAcceptRuntime;
391
421
  const existingTask = getTaskById(taskId);
392
422
  if (!existingTask) {
393
423
  return { success: false, message: `Task "${taskId}" not found.` };
@@ -3,13 +3,20 @@ import type { AgentTask, User } from "@/types";
3
3
  import { type RequestInfo, type SwarmToolResult, toolErr } from "./utils";
4
4
 
5
5
  export type ToolCtx =
6
- | { kind: "owner"; agentId?: string; sourceTaskId?: string; sessionId?: string }
6
+ | {
7
+ kind: "owner";
8
+ agentId?: string;
9
+ runtimeInstanceId?: string;
10
+ sourceTaskId?: string;
11
+ sessionId?: string;
12
+ }
7
13
  | { kind: "user"; userId: string; user: User; sessionId?: string };
8
14
 
9
15
  export function ownerCtx(info: RequestInfo): ToolCtx {
10
16
  return {
11
17
  kind: "owner",
12
18
  agentId: info.agentId,
19
+ runtimeInstanceId: info.runtimeInstanceId,
13
20
  sourceTaskId: info.sourceTaskId,
14
21
  sessionId: info.sessionId,
15
22
  };
@@ -87,7 +87,7 @@ export const DEFERRED_TOOLS = new Set([
87
87
  "context-history",
88
88
  "context-diff",
89
89
 
90
- // Slack (9)
90
+ // Slack (12)
91
91
  "slack-reply",
92
92
  "slack-read",
93
93
  "slack-upload-file",
@@ -95,6 +95,9 @@ export const DEFERRED_TOOLS = new Set([
95
95
  "slack-list-channels",
96
96
  "slack-post",
97
97
  "slack-start-thread",
98
+ "slack-create-channel",
99
+ "slack-invite-to-channel",
100
+ "slack-archive-channel",
98
101
  "slack-delete",
99
102
  "slack-update",
100
103