@desplega.ai/agent-swarm 2.7.4 → 2.7.6

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 (326) hide show
  1. package/README.md +1 -1
  2. package/dist/acp-adapter-bp91d37f.js +15 -0
  3. package/dist/{actions-gfrr9vb7.js → actions-yj0eeaqh.js} +19 -18
  4. package/dist/{amp-adapter-0s58gm46.js → amp-adapter-4pg9h34q.js} +3 -3
  5. package/dist/{app-stvq692g.js → app-909e97cr.js} +3 -3
  6. package/dist/{assistant-5s37fr9n.js → assistant-qdyb5fyy.js} +32 -23
  7. package/dist/{boot-reembed-yzy1841n.js → boot-reembed-fk0s9z18.js} +1 -1
  8. package/dist/{boot-reembed-cv82gz7y.js → boot-reembed-gs1rzrnh.js} +1 -1
  9. package/dist/{boot-scrub-sweep-8k4q95ap.js → boot-scrub-sweep-4qfd4kfx.js} +4 -4
  10. package/dist/{boot-seeds-ftt9htah.js → boot-seeds-yr00k13d.js} +4 -4
  11. package/dist/{boxd-515v1t8z.js → boxd-vdmfssd9.js} +5 -5
  12. package/dist/{claude-adapter-t00wrzeh.js → claude-adapter-qta1f1xn.js} +7 -7
  13. package/dist/{claude-managed-adapter-g6vmjccb.js → claude-managed-adapter-dvs2xw5n.js} +2 -2
  14. package/dist/{claude-managed-setup-afpa82f7.js → claude-managed-setup-sc6j4dm0.js} +3 -3
  15. package/dist/{claude-sdk-session-vj0c2azx.js → claude-sdk-session-zgrz6yvc.js} +9175 -9076
  16. package/dist/{cli-gjr2y2xk.js → cli-0agcv1v5.js} +4 -1
  17. package/dist/{cli-edmm9d9y.js → cli-0at8w34w.js} +7 -7
  18. package/dist/{cli-a81qevyg.js → cli-0qatpy8q.js} +20 -36
  19. package/dist/{cli-bcz074zx.js → cli-0yv5z2pg.js} +1 -1
  20. package/dist/{cli-a4btqqft.js → cli-1b7btd38.js} +1 -1
  21. package/dist/{cli-a2jw6ee5.js → cli-1p89xny0.js} +1 -1
  22. package/dist/{cli-v726afdm.js → cli-2pnswgh8.js} +1 -1
  23. package/dist/{cli-jkt52mgt.js → cli-386tyxxg.js} +2 -2
  24. package/dist/{cli-az0bpf8p.js → cli-3ypk9vk1.js} +3 -3
  25. package/dist/{cli-wk9md5px.js → cli-436532ck.js} +4 -4
  26. package/dist/{cli-7n5sbw0z.js → cli-4necwtsv.js} +5 -5
  27. package/dist/cli-54t00gt8.js +151 -0
  28. package/dist/{cli-655xm9ms.js → cli-583frbyr.js} +1 -1
  29. package/dist/{cli-4r12t6w6.js → cli-5c4hjzne.js} +101 -4
  30. package/dist/{cli-hf6zt5m6.js → cli-69szn7w7.js} +17 -1
  31. package/dist/{cli-rtk9g7av.js → cli-73jzwksr.js} +6 -2
  32. package/dist/{cli-jg0nfda6.js → cli-75gkpjvw.js} +52 -21
  33. package/dist/{cli-wcmnan2g.js → cli-89knwags.js} +5 -5
  34. package/dist/{cli-pwyevs29.js → cli-8bc1w0tw.js} +22 -21
  35. package/dist/{cli-t282jgab.js → cli-8snvgs5r.js} +41 -16
  36. package/dist/{cli-vqgnyh76.js → cli-98rw5dt5.js} +15 -3
  37. package/dist/{cli-9cctx1tn.js → cli-a22h291q.js} +1 -1
  38. package/dist/{cli-ycm6180s.js → cli-a7v854rf.js} +5 -3
  39. package/dist/{cli-98pke4vk.js → cli-ae7mrdf9.js} +2 -2
  40. package/dist/{cli-je8yeyc5.js → cli-bjb3z51z.js} +5 -5
  41. package/dist/{cli-mc97xy2s.js → cli-c60vetmm.js} +16 -4
  42. package/dist/{cli-j4kw3rsx.js → cli-cm5yqn35.js} +18 -0
  43. package/dist/{cli-wndv9p7g.js → cli-decfb6v8.js} +6 -6
  44. package/dist/{cli-h55y2bjc.js → cli-dhar9qay.js} +2 -2
  45. package/dist/{cli-8yy525ad.js → cli-drr5v6av.js} +2 -2
  46. package/dist/{cli-5gr1g711.js → cli-dxae2yg7.js} +2 -2
  47. package/dist/{cli-pe09p6dv.js → cli-e3r2neck.js} +8 -7
  48. package/dist/{cli-13jvmew3.js → cli-ekwks2vs.js} +7 -7
  49. package/dist/{cli-jg4p5t36.js → cli-es6va8jk.js} +121 -51
  50. package/dist/{cli-j4kt1mpb.js → cli-faxe6thg.js} +3 -3
  51. package/dist/{cli-tzrpv5sc.js → cli-ftyxyspd.js} +1 -1
  52. package/dist/cli-fw2w0kva.js +47 -0
  53. package/dist/{cli-2dfbv8qr.js → cli-fy6x31ps.js} +1 -0
  54. package/dist/{cli-aq58fk5m.js → cli-g83s9ewh.js} +7 -1
  55. package/dist/cli-gdhpcbhf.js +55 -0
  56. package/dist/{cli-82jg7fp9.js → cli-he5nxaa8.js} +1 -1
  57. package/dist/{cli-8veytvbh.js → cli-he9wchme.js} +1 -1
  58. package/dist/{cli-k76tgq03.js → cli-hgaazk69.js} +2 -2
  59. package/dist/{cli-d2pzqej1.js → cli-hr2e2fft.js} +741 -7
  60. package/dist/{cli-dae4sbgy.js → cli-jm2vfpfd.js} +2 -2
  61. package/dist/{cli-1s1dqn1j.js → cli-k2gzwsj2.js} +297 -140
  62. package/dist/{cli-b1afgtj3.js → cli-kqdg1bsb.js} +3 -3
  63. package/dist/cli-m2z8jagn.js +12078 -0
  64. package/dist/{cli-mvckssyq.js → cli-m48g6p95.js} +5 -5
  65. package/dist/{cli-bwty35gx.js → cli-mthwvjre.js} +1 -1
  66. package/dist/{cli-41z8xwj0.js → cli-nhrvpaj7.js} +34 -8
  67. package/dist/{cli-ahd90bwt.js → cli-nwj0ee2w.js} +6 -6
  68. package/dist/{cli-kvtc7aez.js → cli-p5gp8pcx.js} +44 -14
  69. package/dist/{cli-avgcz4ra.js → cli-pd7vagab.js} +436 -171
  70. package/dist/{cli-zn15c46z.js → cli-q0k1w5zd.js} +1 -1
  71. package/dist/{cli-bq2pcphn.js → cli-rmmmrjkd.js} +1 -1
  72. package/dist/{cli-2qpvmmkw.js → cli-s0qnd4wv.js} +1 -1
  73. package/dist/{cli-kg8hchyg.js → cli-sa02jsqt.js} +1 -1
  74. package/dist/{cli-1yekbyqm.js → cli-shpbx5ee.js} +283 -82
  75. package/dist/{cli-9y354g3j.js → cli-srye3vdt.js} +1 -1
  76. package/dist/{cli-byqfbnjw.js → cli-svj5090z.js} +9 -9
  77. package/dist/{cli-gcbdjt47.js → cli-vfym7wmz.js} +6 -6
  78. package/dist/{cli-v1yw3wha.js → cli-vvpx3gsx.js} +4 -4
  79. package/dist/{cli-a1fhj4n9.js → cli-w6dt9ged.js} +2 -2
  80. package/dist/{cli-1gm8jcyr.js → cli-waqbxg08.js} +5232 -68
  81. package/dist/{cli-5hjced2v.js → cli-wp9sp1qb.js} +6 -6
  82. package/dist/{cli-eny54typ.js → cli-wx3rhnm0.js} +1 -1
  83. package/dist/{cli-s0exq74r.js → cli-xbbndvjy.js} +3 -3
  84. package/dist/{cli-f255yaqn.js → cli-xds5f77d.js} +3 -3
  85. package/dist/{cli-0sbkss7h.js → cli-xnqgpshy.js} +2 -2
  86. package/dist/{cli-0fgr1w9b.js → cli-xnrwazav.js} +20 -3
  87. package/dist/{cli-gf5hbmdr.js → cli-yjdgea32.js} +3 -3
  88. package/dist/{cli-cq1pf336.js → cli-yn4kxs06.js} +124 -11
  89. package/dist/{cli-224epwwf.js → cli-yx4kfngt.js} +3 -3
  90. package/dist/{cli-eqx6k993.js → cli-yy3rhsdw.js} +47 -13
  91. package/dist/{cli-36g23z21.js → cli-zbbwez0r.js} +4 -4
  92. package/dist/{cli-ktmvb20s.js → cli-zm72anhg.js} +12 -12
  93. package/dist/cli.js +26 -25
  94. package/dist/{codex-adapter-g0gctw29.js → codex-adapter-ayd1fb6h.js} +5 -5
  95. package/dist/{codex-hook-vrzz2xhr.js → codex-hook-9efm2x1m.js} +1 -1
  96. package/dist/{codex-login-0gg46ygn.js → codex-login-y0pmff1c.js} +1 -1
  97. package/dist/{codex-session-runner-dr2cxe1c.js → codex-session-runner-jesa32xc.js} +6 -6
  98. package/dist/{commands-1p07cvff.js → commands-hanewgf2.js} +9 -3
  99. package/dist/{controller-z0s7sv6r.js → controller-dt1n3c1q.js} +9 -4
  100. package/dist/{controllers-connect-x9kvvhyk.js → controllers-connect-xx8x64n5.js} +22 -21
  101. package/dist/{cursor-adapter-m5b98pvk.js → cursor-adapter-9s4dwwdw.js} +2 -2
  102. package/dist/{db-capk2raj.js → db-3vttcbv4.js} +3 -3
  103. package/dist/{db-rsh04kh3.js → db-9dv0g1ab.js} +12 -4
  104. package/dist/{devin-adapter-yxm87fe9.js → devin-adapter-r9phbnc9.js} +1 -1
  105. package/dist/{devtools-82fzqra8.js → devtools-qxfgbsqz.js} +1 -1
  106. package/dist/{dispatcher-304q6fwa.js → dispatcher-tx9hd1sv.js} +18 -17
  107. package/dist/{docker-7rxt0rry.js → docker-c1a3gqmf.js} +91 -24
  108. package/dist/{dsh-adapter-btewem0y.js → dsh-adapter-007y4ejs.js} +2 -2
  109. package/dist/{e2b-renc0k8q.js → e2b-a19d28rz.js} +4 -4
  110. package/dist/{e2b-7fc9b9bd.js → e2b-we6hs6q5.js} +6 -6
  111. package/dist/{easter-egg-3d-fm1p3n4d.js → easter-egg-3d-rgbrqb8s.js} +1 -1
  112. package/dist/{embedding-space-s68v49ct.js → embedding-space-ny1e2940.js} +6 -6
  113. package/dist/{embeddings-4ms3604w.js → embeddings-2t386t13.js} +1 -1
  114. package/dist/{event-bus-dfjx2qdh.js → event-bus-g0ka3pbw.js} +1 -1
  115. package/dist/{execute-mcdgccjs.js → execute-pksh0ceh.js} +1 -1
  116. package/dist/{external-channel-9dxwfztn.js → external-channel-phgmhmd7.js} +2 -2
  117. package/dist/fx-adapter-fy8rr921.js +18 -0
  118. package/dist/{google-generative-ai-yvmdxyxa.js → google-generative-ai-8y3m3k4b.js} +1 -1
  119. package/dist/{google-vertex-jrtdh9gs.js → google-vertex-w3v98n7b.js} +1 -1
  120. package/dist/{grok-adapter-05ecv4h0.js → grok-adapter-q0abvpaj.js} +8 -8
  121. package/dist/handlers-by3sfxyx.js +32 -0
  122. package/dist/{heartbeat-3n94heh3.js → heartbeat-kadyek8p.js} +21 -20
  123. package/dist/{hook-aadrn69x.js → hook-5zs3msf6.js} +6 -6
  124. package/dist/{hook-zxwje86c.js → hook-9k551xnj.js} +4 -4
  125. package/dist/{http-xzxk9mst.js → http-mhz9vw1f.js} +272 -103
  126. package/dist/index-4f2pvxae.js +37 -0
  127. package/dist/index-98gz7tmp.js +30 -0
  128. package/dist/{index-5rk6rfzr.js → index-fvzke5kx.js} +1 -1
  129. package/dist/index-k08z2cak.js +33 -0
  130. package/dist/{index-yqhz88yy.js → index-pxn30mse.js} +8 -8
  131. package/dist/{index-d34f6r87.js → index-q361cg2f.js} +1 -1
  132. package/dist/{index-9j4zwcht.js → index-rvr00sg7.js} +168 -675
  133. package/dist/{index-qeb82z37.js → index-vdygbq10.js} +1 -1
  134. package/dist/{keepalive-w4aakbyk.js → keepalive-w56f32cg.js} +4 -4
  135. package/dist/lead-enq35nqp.js +70 -0
  136. package/dist/{maintenance-hbptpk6p.js → maintenance-7mdgxcea.js} +1 -1
  137. package/dist/{oauth-refresh-sweep-217semrv.js → oauth-refresh-sweep-29txy9vk.js} +3 -3
  138. package/dist/{onboard-vz2s5df3.js → onboard-kpsrkarj.js} +5 -5
  139. package/dist/{opencode-adapter-gtfd7d7v.js → opencode-adapter-j25vm3rr.js} +5 -5
  140. package/dist/{otel-impl-aefave37.js → otel-impl-6kkct7a8.js} +2 -2
  141. package/dist/{pi-mono-adapter-f8h5yseh.js → pi-mono-adapter-exhkzhn2.js} +117 -23
  142. package/dist/{pricing-refresh-m1yhh9ya.js → pricing-refresh-n2x7jh6k.js} +3 -3
  143. package/dist/{rbac-roles-rrw8twdz.js → rbac-roles-81ypx6es.js} +1 -1
  144. package/dist/{rbac-roles-svj72cdf.js → rbac-roles-b5htkjby.js} +2 -2
  145. package/dist/{render-v2-amwbnfxh.js → render-v2-qn6jmpk4.js} +3 -3
  146. package/dist/{resume-9cd6wf6z.js → resume-wd30wx23.js} +7 -7
  147. package/dist/{runtime-fp4vrx7m.js → runtime-4hsg7h60.js} +1 -1
  148. package/dist/{session-access-21t6yjyg.js → session-access-5qvfevhk.js} +4 -4
  149. package/dist/{session-source-backfill-pxbzcsc6.js → session-source-backfill-n1czavbr.js} +3 -3
  150. package/dist/{setup-4592tqz4.js → setup-dnb56qy5.js} +3 -3
  151. package/dist/{virtual-modules-gzfnpmnz.js → virtual-modules-76xxv9fs.js} +2 -2
  152. package/dist/{work-objects-t9tcf174.js → work-objects-4n21tfkm.js} +21 -20
  153. package/dist/{worker-follow-up-q32jh4yk.js → worker-follow-up-77hvzxww.js} +18 -17
  154. package/dist/worker-gg1576j3.js +70 -0
  155. package/dist/{x-b6wxntdt.js → x-vmmye1kb.js} +2 -2
  156. package/openapi.json +189 -32
  157. package/package.json +8 -7
  158. package/src/be/db/script-runs.ts +23 -0
  159. package/src/be/db/session-summaries.ts +8 -2
  160. package/src/be/db/session-tree.ts +174 -61
  161. package/src/be/db/session-visibility.ts +5 -4
  162. package/src/be/db/tasks/read.ts +122 -3
  163. package/src/be/db-client.ts +8 -5
  164. package/src/be/db.ts +23 -8
  165. package/src/be/memory/providers/sqlite-store.ts +49 -12
  166. package/src/be/migrations/259_session_summaries_open_tasks.sql +111 -0
  167. package/src/be/migrations/260_runtime_pool_cache.sql +20 -0
  168. package/src/be/migrations/262_session_visibility_indexes.sql +18 -0
  169. package/src/be/migrations/263_task_list_filter_index.sql +11 -0
  170. package/src/be/migrations/264_slack_thread_cover_index.sql +9 -0
  171. package/src/be/migrations/266_script_runs_source_task.sql +11 -0
  172. package/src/be/model-validation.ts +5 -1
  173. package/src/be/pricing-normalize.ts +7 -2
  174. package/src/be/runtime-pools/cache-cleanup.ts +109 -0
  175. package/src/be/runtime-pools/db.ts +65 -6
  176. package/src/be/runtime-pools/remote-controller.ts +5 -0
  177. package/src/be/runtime-pools/supervisor.ts +33 -2
  178. package/src/be/scripts/typecheck.ts +23 -0
  179. package/src/be/seed/schedules-seeder.ts +135 -9
  180. package/src/be/seed/workflows-seeder.ts +65 -1
  181. package/src/be/seed-pricing.ts +23 -0
  182. package/src/be/seed-scripts/catalog/task-failure-audit.ts +5 -1
  183. package/src/be/steering.ts +50 -3
  184. package/src/be/swarm-config-guard.ts +1 -0
  185. package/src/commands/controller.ts +4 -0
  186. package/src/commands/entrypoint-api-readiness.ts +74 -0
  187. package/src/commands/provider-credentials.ts +29 -2
  188. package/src/commands/runner.ts +86 -5
  189. package/src/extensions/contract-types.generated.ts +1 -1
  190. package/src/extensions/contract.ts +27 -1
  191. package/src/extensions/dispatcher.ts +4 -0
  192. package/src/http/agents.ts +1 -0
  193. package/src/http/index.ts +1 -1
  194. package/src/http/list-total-cache.ts +14 -7
  195. package/src/http/mcp-bridge.ts +4 -0
  196. package/src/http/memory.ts +38 -9
  197. package/src/http/script-runs.ts +174 -5
  198. package/src/http/status.ts +10 -2
  199. package/src/http/tasks.ts +4 -2
  200. package/src/integrations/chat-sdk/inbound.ts +15 -5
  201. package/src/integrations/chat-sdk/steering.ts +3 -1
  202. package/src/kv-reserved-namespaces.ts +2 -0
  203. package/src/prompts/base-prompt.ts +4 -1
  204. package/src/prompts/session-templates.ts +26 -0
  205. package/src/providers/acp-adapter.ts +190 -26
  206. package/src/providers/acp-targets.ts +306 -6
  207. package/src/providers/amp-adapter.ts +1 -1
  208. package/src/providers/codex-adapter.ts +9 -44
  209. package/src/providers/codex-oauth/flow.ts +24 -3
  210. package/src/providers/codex-oauth/storage.ts +56 -17
  211. package/src/providers/codex-oauth/subscription.ts +67 -0
  212. package/src/providers/fx-adapter.ts +211 -0
  213. package/src/providers/index.ts +5 -1
  214. package/src/providers/mcp-loopback-proxy.ts +94 -0
  215. package/src/providers/pi-mono-adapter.ts +98 -13
  216. package/src/providers/pricing-sources.md +8 -0
  217. package/src/providers/reasoning-effort.ts +17 -1
  218. package/src/providers/types.ts +2 -1
  219. package/src/runtime-providers/controller-protocol.ts +14 -1
  220. package/src/runtime-providers/controller.ts +5 -0
  221. package/src/runtime-providers/docker.ts +108 -23
  222. package/src/runtime-providers/types.ts +16 -0
  223. package/src/scheduler/scheduler.ts +36 -3
  224. package/src/script-workflows/workflow-ctx.ts +8 -0
  225. package/src/scripts-runtime/sdk-allowlist.ts +1 -0
  226. package/src/scripts-runtime/types/stdlib.d.ts +27 -0
  227. package/src/scripts-runtime/types/swarm-extension.d.ts +33 -0
  228. package/src/scripts-runtime/types/swarm-sdk.d.ts +27 -0
  229. package/src/server-user.ts +1 -0
  230. package/src/server.ts +7 -0
  231. package/src/slack/assistant.ts +17 -1
  232. package/src/slack/commands.ts +5 -1
  233. package/src/slack/handlers.ts +53 -5
  234. package/src/slack/left-threads.ts +88 -0
  235. package/src/slack/steering.ts +3 -0
  236. package/src/slack/thread-buffer.ts +40 -16
  237. package/src/tasks/follow-up-redelegation-guard.ts +155 -0
  238. package/src/tests/acp-adapter.test.ts +79 -0
  239. package/src/tests/base-prompt.test.ts +12 -0
  240. package/src/tests/chat-sdk-inbound.test.ts +30 -0
  241. package/src/tests/chatgpt-subscription-model-groups.test.ts +62 -0
  242. package/src/tests/chatgpt-subscription-pi.test.ts +170 -0
  243. package/src/tests/chatgpt-subscription-session-bench.test.ts +198 -0
  244. package/src/tests/claude-artifacts-read-script.test.ts +209 -0
  245. package/src/tests/codex-oauth-credential-info.test.ts +40 -0
  246. package/src/tests/codex-oauth-refresh-egress.test.ts +194 -0
  247. package/src/tests/context-window.test.ts +7 -0
  248. package/src/tests/db-client.test.ts +21 -0
  249. package/src/tests/db-task-read-contract.test.ts +8 -2
  250. package/src/tests/db-task-write-contract.test.ts +5 -1
  251. package/src/tests/entrypoint-api-readiness.test.ts +416 -210
  252. package/src/tests/explicit-model-validation.test.ts +113 -8
  253. package/src/tests/extensions-run-context.test.ts +11 -0
  254. package/src/tests/extensions-steering.test.ts +353 -0
  255. package/src/tests/extensions-validate.test.ts +5 -0
  256. package/src/tests/fixtures/extensions/steering-policy/hooks.ts +39 -0
  257. package/src/tests/fixtures/extensions/steering-policy/manifest.json +7 -0
  258. package/src/tests/fixtures/fake-fx.ts +167 -0
  259. package/src/tests/fixtures/fx/acp-responses-recorded.json +157 -0
  260. package/src/tests/fixtures/fx/acp-updates-recorded.jsonl +15 -0
  261. package/src/tests/follow-up-redelivery-guard.test.ts +432 -202
  262. package/src/tests/fx-adapter.test.ts +771 -0
  263. package/src/tests/grok-adapter.test.ts +36 -0
  264. package/src/tests/list-total-cache.test.ts +27 -2
  265. package/src/tests/mcp-loopback-proxy.test.ts +193 -0
  266. package/src/tests/memory-fts-populate.test.ts +11 -9
  267. package/src/tests/memory-retrieval-purge.test.ts +80 -0
  268. package/src/tests/migration-260-runtime-pool-cache.test.ts +118 -0
  269. package/src/tests/model-tier-resolution.test.ts +3 -3
  270. package/src/tests/pricing-refresh.test.ts +5 -5
  271. package/src/tests/prompt-template-session.test.ts +5 -3
  272. package/src/tests/provider-registration.test.ts +2 -1
  273. package/src/tests/query-audit-plans.test.ts +30 -2
  274. package/src/tests/runtime-cache-cleanup.test.ts +367 -0
  275. package/src/tests/runtime-e2b-pools.test.ts +41 -0
  276. package/src/tests/runtime-pools-db.test.ts +98 -0
  277. package/src/tests/runtime-pools-http.test.ts +29 -0
  278. package/src/tests/runtime-providers-docker.test.ts +253 -0
  279. package/src/tests/runtime-remote-controller.test.ts +66 -2
  280. package/src/tests/runtime-supervisor.test.ts +66 -2
  281. package/src/tests/script-connections.test.ts +13 -0
  282. package/src/tests/script-runs-http.test.ts +684 -2
  283. package/src/tests/seed-model-profiles.test.ts +2 -2
  284. package/src/tests/seed-schedules.test.ts +138 -0
  285. package/src/tests/seed-scripts.test.ts +26 -0
  286. package/src/tests/session-cost-recompute-unit.test.ts +39 -0
  287. package/src/tests/session-costs-golden.test.ts +26 -0
  288. package/src/tests/session-summaries-plans.test.ts +120 -0
  289. package/src/tests/session-summaries.test.ts +72 -12
  290. package/src/tests/sessions-tree.test.ts +11 -3
  291. package/src/tests/slack-leave-thread.test.ts +571 -0
  292. package/src/tests/slack-render-v2-query-plans.test.ts +24 -2
  293. package/src/tests/status.test.ts +11 -0
  294. package/src/tests/tasks-readable-row.test.ts +155 -0
  295. package/src/tests/tool-annotations.test.ts +2 -1
  296. package/src/tests/workflow-engine-v2.test.ts +42 -0
  297. package/src/tests/workflow-script-principal.test.ts +207 -7
  298. package/src/tools/send-task.ts +35 -47
  299. package/src/tools/slack-leave-thread.ts +104 -0
  300. package/src/tools/steer-task.ts +3 -2
  301. package/src/tools/task-dedup.ts +45 -21
  302. package/src/tools/tool-config.ts +2 -1
  303. package/src/types.ts +57 -0
  304. package/src/utils/codex-auth-failure.ts +30 -0
  305. package/src/utils/context-window.ts +4 -1
  306. package/src/utils/credentials.ts +7 -0
  307. package/src/utils/fx-models.ts +43 -0
  308. package/src/workflows/cooldown.ts +3 -2
  309. package/src/workflows/engine.ts +24 -9
  310. package/templates/schedules/dreaming-daily/config.json +1 -1
  311. package/templates/schedules/dreaming-daily/content.md +6 -14
  312. package/templates/skills/composio-google-docs/content.md +10 -0
  313. package/templates/skills/script-workflows/content.md +19 -1
  314. package/templates/skills/swarm-docs/content.md +2 -0
  315. package/templates/skills/swarm-scripts/SKILL.md +2 -0
  316. package/templates/skills/swarm-scripts/content.md +2 -0
  317. package/templates/workflows/dreaming/content.md +5 -1
  318. package/dist/acp-adapter-96dc64m2.js +0 -15
  319. package/dist/cli-dznrgaep.js +0 -30
  320. package/dist/cli-w3hcrxht.js +0 -6985
  321. package/dist/handlers-jg7v74ty.js +0 -31
  322. package/dist/index-8c1zbz59.js +0 -29
  323. package/dist/index-8k8tmfxd.js +0 -32
  324. package/dist/index-hq617xdj.js +0 -36
  325. package/dist/lead-567kw7x3.js +0 -68
  326. package/dist/worker-fjdqra6q.js +0 -68
@@ -14,9 +14,11 @@
14
14
  */
15
15
 
16
16
  import {
17
+ CHATGPT_SUBSCRIPTION_PROVIDER,
17
18
  claudeCatalogModelId,
18
19
  cursorCatalogRef,
19
20
  dshCatalogRef,
21
+ fxCatalogRef,
20
22
  grokCatalogRef,
21
23
  REASONING_EFFORT_LEVELS,
22
24
  type ReasoningEffortLevel,
@@ -37,7 +39,8 @@ export type ReasoningHarness =
37
39
  | "dsh"
38
40
  | "cursor"
39
41
  | "amp"
40
- | "grok";
42
+ | "grok"
43
+ | "fx";
41
44
 
42
45
  export interface ReasoningCapability {
43
46
  supported: boolean;
@@ -65,6 +68,7 @@ export type ReasoningEffortApplication =
65
68
  | { kind: "amp-effort"; reasoningEffort: ReasoningEffort }
66
69
  | { kind: "cursor-effort"; reasoningEffort: ReasoningEffort }
67
70
  | { kind: "grok-effort"; reasoningEffort: ReasoningEffort }
71
+ | { kind: "fx-effort"; reasoningEffort: ReasoningEffort }
68
72
  | { kind: "noop" };
69
73
 
70
74
  // --- Capability lookup --------------------------------------------------------
@@ -102,9 +106,16 @@ function lookupModel(
102
106
  ({ providerId, modelId } = cursorCatalogRef(model));
103
107
  } else if (harness === "grok") {
104
108
  ({ providerId, modelId } = grokCatalogRef(model));
109
+ } else if (harness === "fx") {
110
+ // Only the AI Gateway route (`vercel/…`) has an effort control.
111
+ const ref = fxCatalogRef(model);
112
+ if (!ref) return undefined;
113
+ ({ providerId, modelId } = ref);
105
114
  } else {
106
115
  ({ providerId, modelId } = splitProviderModel(model));
107
116
  if (!providerId) return undefined;
117
+ // A ChatGPT subscription id names an OpenAI catalog model.
118
+ if (providerId.toLowerCase() === CHATGPT_SUBSCRIPTION_PROVIDER) providerId = "openai";
108
119
  }
109
120
  const facts = runtimeCatalogModel(providerId, modelId);
110
121
  return facts ? { id: modelId, facts } : undefined;
@@ -240,6 +251,10 @@ export function applyReasoningEffort(
240
251
  // Grok's `--reasoning-effort` takes the normalized names as-is; see
241
252
  // `src/providers/grok-adapter.ts`.
242
253
  return { kind: "grok-effort", reasoningEffort: level };
254
+ case "fx":
255
+ // fx's `effort` config option takes the normalized names; the adapter
256
+ // sets it on the AI Gateway route only (see `fxTargetProfile`).
257
+ return { kind: "fx-effort", reasoningEffort: level };
243
258
  default: {
244
259
  const _exhaustive: never = harness;
245
260
  return _exhaustive;
@@ -256,6 +271,7 @@ const REASONING_HARNESSES: ReadonlySet<string> = new Set<ReasoningHarness>([
256
271
  "cursor",
257
272
  "amp",
258
273
  "grok",
274
+ "fx",
259
275
  ]);
260
276
 
261
277
  /**
@@ -66,7 +66,8 @@ export interface CostData {
66
66
  | "dsh"
67
67
  | "cursor"
68
68
  | "amp"
69
- | "grok";
69
+ | "grok"
70
+ | "fx";
70
71
  }
71
72
 
72
73
  import type { ProviderName, SteerMode } from "../types";
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { RuntimeProviderKindSchema } from "../types";
2
+ import { RuntimePoolCacheSchema, RuntimeProviderKindSchema } from "../types";
3
3
 
4
4
  export const CONTROLLER_PROTOCOL_VERSION = 1;
5
5
  export const CONTROLLER_COMMAND_TIMEOUT_MS = 60_000;
@@ -16,6 +16,7 @@ const traitsSchema = z.object({
16
16
  canProvision: z.boolean(),
17
17
  ephemeralDisk: z.boolean(),
18
18
  supportsPause: z.boolean(),
19
+ persistentCache: z.boolean().optional(),
19
20
  maxTtlSec: z.number().positive().optional(),
20
21
  maxContinuousSec: z.number().positive().optional(),
21
22
  });
@@ -31,9 +32,21 @@ const provisionSchema = z.object({
31
32
  .object({ cpu: z.number().positive().optional(), memoryMb: z.number().positive().optional() })
32
33
  .optional(),
33
34
  ttlSec: z.number().positive().optional(),
35
+ cache: RuntimePoolCacheSchema.extend({
36
+ key: z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_.-]+$/),
37
+ }).optional(),
34
38
  });
35
39
  const command = { type: z.literal("cmd"), id: z.string().min(1) };
36
40
  export const ControllerCommandSchema = z.discriminatedUnion("op", [
41
+ z.object({
42
+ ...command,
43
+ op: z.literal("release-cache"),
44
+ payload: z.object({
45
+ key: z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_.-]+$/),
46
+ poolId: z.string().min(1),
47
+ ownerId: z.string().min(1),
48
+ }),
49
+ }),
37
50
  z.object({ ...command, op: z.literal("provision"), payload: provisionSchema }),
38
51
  z.object({
39
52
  ...command,
@@ -1,6 +1,7 @@
1
1
  import { scrubSecrets } from "../utils/secret-scrubber";
2
2
  import type {
3
3
  ProvisionRequest,
4
+ RuntimeCacheReference,
4
5
  RuntimeHandle,
5
6
  RuntimeProvider,
6
7
  RuntimeProviderTraits,
@@ -25,6 +26,7 @@ export interface ControllerCommands {
25
26
  terminate(handle: RuntimeHandle, reason: string): Promise<void>;
26
27
  status(handles: RuntimeHandle[]): Promise<Record<string, RuntimeStatus>>;
27
28
  list(ownerId: string): Promise<RuntimeHandle[]>;
29
+ releaseCache?(cache: RuntimeCacheReference): Promise<void>;
28
30
  renew?(handle: RuntimeHandle, ttlSec: number): Promise<{ endAt: string }>;
29
31
  pause?(handle: RuntimeHandle): Promise<void>;
30
32
  resume?(handle: RuntimeHandle): Promise<void>;
@@ -36,6 +38,7 @@ export class BuiltinController implements ControllerCommands {
36
38
  readonly renew?: ControllerCommands["renew"];
37
39
  readonly pause?: ControllerCommands["pause"];
38
40
  readonly resume?: ControllerCommands["resume"];
41
+ readonly releaseCache?: ControllerCommands["releaseCache"];
39
42
 
40
43
  constructor(
41
44
  readonly id: string,
@@ -47,6 +50,8 @@ export class BuiltinController implements ControllerCommands {
47
50
  this.workerApiUrl = workerApiUrl;
48
51
  if (provider.renew)
49
52
  this.renew = (handle, ttlSec) => this.call(() => provider.renew!(target, handle, ttlSec));
53
+ if (provider.releaseCache)
54
+ this.releaseCache = (cache) => this.call(() => provider.releaseCache!(target, cache));
50
55
  if (provider.pause) this.pause = (handle) => this.call(() => provider.pause!(target, handle));
51
56
  if (provider.resume)
52
57
  this.resume = (handle) => this.call(() => provider.resume!(target, handle));
@@ -4,6 +4,7 @@ import {
4
4
  type ConfigStatus,
5
5
  type ProvisionRequest,
6
6
  RUNTIME_LABELS,
7
+ type RuntimeCacheReference,
7
8
  type RuntimeHandle,
8
9
  type RuntimeProvider,
9
10
  type RuntimeStatus,
@@ -63,6 +64,22 @@ class DockerApiError extends Error {
63
64
  }
64
65
  }
65
66
 
67
+ function isCacheMountError(error: unknown): boolean {
68
+ return error instanceof DockerApiError && /\b(?:mount\w*|volume\w*)\b/i.test(error.message);
69
+ }
70
+
71
+ type CacheVolume = { Name: string; Labels?: Record<string, string> };
72
+
73
+ function assertCacheOwner(volume: CacheVolume, cache: RuntimeCacheReference): void {
74
+ if (
75
+ volume.Name !== cache.key ||
76
+ volume.Labels?.[RUNTIME_LABELS.owner] !== cache.ownerId ||
77
+ volume.Labels?.[RUNTIME_LABELS.poolId] !== cache.poolId ||
78
+ volume.Labels?.["swarm.cache"] !== "true"
79
+ )
80
+ throw new Error(`Cache volume ${cache.key} is not owned by this swarm and pool`);
81
+ }
82
+
66
83
  async function dockerApi<T>(
67
84
  target: RuntimeTargetConfig,
68
85
  method: string,
@@ -119,7 +136,12 @@ async function dockerApi<T>(
119
136
 
120
137
  export class DockerRuntimeProvider implements RuntimeProvider {
121
138
  readonly kind = "docker" as const;
122
- readonly traits = { canProvision: true, ephemeralDisk: true, supportsPause: false };
139
+ readonly traits = {
140
+ canProvision: true,
141
+ ephemeralDisk: true,
142
+ supportsPause: false,
143
+ persistentCache: true,
144
+ };
123
145
 
124
146
  checkConfig(target: RuntimeTargetConfig): ConfigStatus {
125
147
  try {
@@ -132,6 +154,29 @@ export class DockerRuntimeProvider implements RuntimeProvider {
132
154
 
133
155
  async provision(target: RuntimeTargetConfig, req: ProvisionRequest): Promise<RuntimeHandle> {
134
156
  try {
157
+ let cacheWarning: string | undefined;
158
+ let cacheMount: { Type: "volume"; Source: string; Target: string } | undefined;
159
+ if (req.cache) {
160
+ try {
161
+ const volume = await dockerApi<CacheVolume>(target, "POST", "/volumes/create", {
162
+ Name: req.cache.key,
163
+ Labels: {
164
+ [RUNTIME_LABELS.owner]: req.ownerId,
165
+ [RUNTIME_LABELS.poolId]: req.poolId,
166
+ "swarm.cache": "true",
167
+ },
168
+ });
169
+ // Docker reuses an existing name without replacing its labels.
170
+ assertCacheOwner(volume, {
171
+ key: req.cache.key,
172
+ poolId: req.poolId,
173
+ ownerId: req.ownerId,
174
+ });
175
+ cacheMount = { Type: "volume", Source: req.cache.key, Target: req.cache.mountPath };
176
+ } catch (error) {
177
+ cacheWarning = redactWithEnv(scrubSecrets(String(error)), req.env);
178
+ }
179
+ }
135
180
  const path = `/containers/create?name=${encodeURIComponent(`swarm-rt-${req.runtimeId}`)}`;
136
181
  const body = {
137
182
  Image: req.image,
@@ -154,40 +199,80 @@ export class DockerRuntimeProvider implements RuntimeProvider {
154
199
  NetworkMode: target.DOCKER_NETWORK,
155
200
  Memory: req.resources?.memoryMb ? req.resources.memoryMb * 1024 * 1024 : undefined,
156
201
  NanoCpus: req.resources?.cpu ? Math.round(req.resources.cpu * 1e9) : undefined,
202
+ ...(cacheMount ? { Mounts: [cacheMount] } : {}),
157
203
  },
158
204
  };
159
- let created: { Id: string };
160
- try {
161
- created = await dockerApi(target, "POST", path, body);
162
- } catch (err) {
163
- if (
164
- !(err instanceof DockerApiError) ||
165
- err.statusCode !== 404 ||
166
- !err.message.includes("No such image")
167
- ) {
168
- throw err;
205
+ const createAndStart = async (): Promise<string> => {
206
+ let created: { Id: string };
207
+ try {
208
+ created = await dockerApi(target, "POST", path, body);
209
+ } catch (err) {
210
+ if (
211
+ !(err instanceof DockerApiError) ||
212
+ err.statusCode !== 404 ||
213
+ !err.message.includes("No such image")
214
+ )
215
+ throw err;
216
+ const pullParams = new URLSearchParams({ fromImage: req.image });
217
+ if (
218
+ !req.image.includes("@") &&
219
+ req.image.lastIndexOf(":") <= req.image.lastIndexOf("/")
220
+ ) {
221
+ pullParams.set("tag", "latest");
222
+ }
223
+ await dockerApi(target, "POST", `/images/create?${pullParams}`, undefined, true);
224
+ created = await dockerApi(target, "POST", path, body);
169
225
  }
170
- const pullParams = new URLSearchParams({ fromImage: req.image });
171
- if (!req.image.includes("@") && req.image.lastIndexOf(":") <= req.image.lastIndexOf("/")) {
172
- pullParams.set("tag", "latest");
226
+ try {
227
+ await dockerApi(target, "POST", `/containers/${created.Id}/start`);
228
+ } catch (err) {
229
+ try {
230
+ await dockerApi(target, "DELETE", `/containers/${created.Id}?force=true&v=true`);
231
+ } catch (cleanupError) {
232
+ if (
233
+ cacheMount &&
234
+ isCacheMountError(err) &&
235
+ !(cleanupError instanceof DockerApiError && cleanupError.statusCode === 404)
236
+ )
237
+ throw new Error(
238
+ `${String(err)}; failed-start cleanup failed: ${String(cleanupError)}`,
239
+ );
240
+ }
241
+ throw err;
173
242
  }
174
- await dockerApi(target, "POST", `/images/create?${pullParams}`, undefined, true);
175
- created = await dockerApi(target, "POST", path, body);
176
- }
243
+ return created.Id;
244
+ };
245
+ let externalId: string;
177
246
  try {
178
- await dockerApi(target, "POST", `/containers/${created.Id}/start`);
247
+ externalId = await createAndStart();
179
248
  } catch (err) {
180
- await dockerApi(target, "DELETE", `/containers/${created.Id}?force=true&v=true`).catch(
181
- () => {},
182
- );
183
- throw err;
249
+ if (!cacheMount || !isCacheMountError(err)) throw err;
250
+ cacheWarning = redactWithEnv(scrubSecrets(String(err)), req.env);
251
+ delete body.HostConfig.Mounts;
252
+ externalId = await createAndStart();
184
253
  }
185
- return { kind: "docker", externalId: created.Id };
254
+ return {
255
+ kind: "docker",
256
+ externalId,
257
+ ...(cacheWarning ? { meta: { cacheWarning } } : {}),
258
+ };
186
259
  } catch (err) {
187
260
  throw new Error(redactWithEnv(String(err), req.env));
188
261
  }
189
262
  }
190
263
 
264
+ async releaseCache(target: RuntimeTargetConfig, cache: RuntimeCacheReference): Promise<void> {
265
+ const path = `/volumes/${encodeURIComponent(cache.key)}`;
266
+ try {
267
+ const volume = await dockerApi<CacheVolume>(target, "GET", path);
268
+ assertCacheOwner(volume, cache);
269
+ // A volume still attached to any container must remain intact (Docker returns 409).
270
+ await dockerApi(target, "DELETE", path);
271
+ } catch (error) {
272
+ if (!(error instanceof DockerApiError && error.statusCode === 404)) throw error;
273
+ }
274
+ }
275
+
191
276
  async terminate(target: RuntimeTargetConfig, handle: RuntimeHandle): Promise<void> {
192
277
  try {
193
278
  // v=true also removes the container's anonymous volumes (the worker image declares
@@ -19,6 +19,8 @@ export interface RuntimeProviderTraits {
19
19
  ephemeralDisk: boolean;
20
20
  /** Provider can pause and resume a runtime. */
21
21
  supportsPause: boolean;
22
+ /** Can mount a persistent cache shared by all runtimes of one pool. */
23
+ persistentCache?: boolean;
22
24
  /** Provider hard cap on runtime lifetime, if any. */
23
25
  maxTtlSec?: number;
24
26
  /** Continuous runtime limit for the configured infrastructure tier. */
@@ -45,11 +47,23 @@ export interface ProvisionRequest {
45
47
  /** Worker env: MCP_BASE_URL, AGENT_ID, API key or scoped token, pool env. */
46
48
  env: Record<string, string>;
47
49
  resources?: { cpu?: number; memoryMb?: number };
50
+ /** Provider-neutral cache identity, allocated by the API rather than the operator. */
51
+ cache?: { key: string; mountPath: string };
48
52
  ttlSec?: number;
49
53
  /** Ownership labels, used by list() to find runtimes this swarm started. */
50
54
  labels: Record<string, string>;
51
55
  }
52
56
 
57
+ export interface RuntimeCacheReference {
58
+ key: string;
59
+ poolId: string;
60
+ ownerId: string;
61
+ }
62
+
63
+ export function poolCacheKey(poolId: string, generation?: string): string {
64
+ return `swarm-cache-${poolId}${generation ? `-${generation}` : ""}`;
65
+ }
66
+
53
67
  export interface RuntimeHandle {
54
68
  kind: RuntimeProviderKind;
55
69
  /** Container id, sandbox id, or pod name. */
@@ -74,6 +88,8 @@ export interface RuntimeProvider {
74
88
  status(target: RuntimeTargetConfig, handle: RuntimeHandle): Promise<RuntimeStatus>;
75
89
  /** Every runtime this swarm owns on the target, found by labels. Used to reap orphans. */
76
90
  list(target: RuntimeTargetConfig, ownerId: string): Promise<RuntimeHandle[]>;
91
+ /** Remove an owned pool cache after its runtimes stop; never force detach. */
92
+ releaseCache?(target: RuntimeTargetConfig, cache: RuntimeCacheReference): Promise<void>;
77
93
  renew?(
78
94
  target: RuntimeTargetConfig,
79
95
  handle: RuntimeHandle,
@@ -11,6 +11,7 @@ import {
11
11
  import {
12
12
  getDbClient,
13
13
  getDueScheduledTasks,
14
+ getLeadAgent,
14
15
  getScheduledTaskById,
15
16
  getUserById,
16
17
  getWorkflow,
@@ -26,8 +27,9 @@ import {
26
27
  import { buildScriptCredentialBindingsWithFailures } from "@/be/script-credential-broker";
27
28
  import { getScript } from "@/be/scripts/db";
28
29
  import { runScriptWithRunCredential } from "@/be/scripts/run-credential";
30
+ import { seededWorkflowState } from "@/be/seed/workflows-seeder";
29
31
  import { telemetry } from "@/telemetry";
30
- import type { AgentTask, ScheduledTask } from "@/types";
32
+ import type { AgentTask, ScheduledTask, Workflow } from "@/types";
31
33
  import { getExecutorRegistry as getWorkflowExecutorRegistry } from "@/workflows";
32
34
  import { startWorkflowExecution } from "@/workflows/engine";
33
35
  import type { ExecutorRegistry } from "@/workflows/executors/registry";
@@ -252,8 +254,7 @@ export async function dispatchScheduleTarget(
252
254
  };
253
255
  const runId = await startWorkflowExecution(workflow, triggerData, registry, {
254
256
  triggerType: "schedule",
255
- // The creator, as for script schedules: `targetAgentId` is caller-chosen.
256
- triggeredByAgentId: schedule.createdByAgentId,
257
+ triggeredByAgentId: await scheduledWorkflowPrincipal(schedule, workflow),
257
258
  requestedByUserId: schedule.createdBy,
258
259
  });
259
260
  await throwIfWorkflowNeedsSetup(schedule.id, [runId]);
@@ -295,6 +296,38 @@ export async function dispatchScheduleTarget(
295
296
  }
296
297
  }
297
298
 
299
+ /**
300
+ * The agent a scheduled workflow run acts as: the schedule's creator, as for
301
+ * script schedules (`targetAgentId` and trigger data are caller-chosen, so
302
+ * neither is ever the principal). A creator-less (seeded) schedule that fires
303
+ * a seeded workflow runs as the lead, resolved at fire time; with no lead the
304
+ * fire fails instead of running agentless. Operator settings such as inputs
305
+ * or on/off keep that. Any agent can edit a workflow, so one whose nodes or
306
+ * params changed never inherits the lead and its fire fails. Workflows the
307
+ * seeder never wrote keep their previous principal.
308
+ */
309
+ async function scheduledWorkflowPrincipal(
310
+ schedule: ScheduledTask,
311
+ workflow: Workflow,
312
+ ): Promise<string | undefined> {
313
+ if (schedule.createdByAgentId) return schedule.createdByAgentId;
314
+ if (workflow.createdByAgentId) return undefined;
315
+ const seeded = await seededWorkflowState(workflow);
316
+ if (seeded === "unseeded") return undefined;
317
+ if (seeded === "edited") {
318
+ throw new Error(
319
+ `Schedule "${schedule.name}" has no creator and the nodes or params of seeded workflow "${workflow.name}" were edited, so it will not run as the lead. Re-create the schedule as the agent it should run as.`,
320
+ );
321
+ }
322
+ const lead = await getLeadAgent();
323
+ if (!lead) {
324
+ throw new Error(
325
+ `Schedule "${schedule.name}" has no creator and the swarm has no lead to run its workflow as`,
326
+ );
327
+ }
328
+ return lead.id;
329
+ }
330
+
298
331
  async function throwIfWorkflowNeedsSetup(scheduleId: string, runIds: string[]): Promise<void> {
299
332
  for (const runId of runIds) {
300
333
  const run = await getWorkflowRun(runId);
@@ -44,6 +44,14 @@ type AgentTaskConfig = {
44
44
  parentTaskId?: string;
45
45
  requestedByUserId?: string;
46
46
  outputSchema?: Record<string, unknown>;
47
+ /**
48
+ * Dispatch even when the follow-up re-delegation guard blocks this step.
49
+ * The guard applies only to a run launched from a follow-up task in a Slack
50
+ * thread, and decides once for the whole run at its first new step: it
51
+ * blocks a repeat of work already completed in the thread, or a run past the
52
+ * thread's dispatch cap. A blocked run's later new steps are blocked too.
53
+ */
54
+ allowRedelegation?: boolean;
47
55
  /**
48
56
  * Block until the dispatched task reaches a terminal status and journal
49
57
  * its real output. Defaults to `true` — this is the natural semantic for
@@ -104,6 +104,7 @@ export const SDK_TOOL_NAME_MAP = {
104
104
  slack_downloadFile: "slack-download-file",
105
105
  slack_delete: "slack-delete", // external: mutates Slack, destructive
106
106
  slack_update: "slack-update", // external: mutates Slack
107
+ slack_leaveThread: "slack-leave-thread",
107
108
 
108
109
  // ── chat sdk ──
109
110
  chat_reply: "chat-reply", // external: sends to the task's chat thread
@@ -63,6 +63,8 @@ declare module "swarm-sdk" {
63
63
  parentTaskId?: string;
64
64
  requestedByUserId?: string;
65
65
  outputSchema?: Record<string, unknown>;
66
+ /** Dispatch even when the follow-up re-delegation guard blocks this step (a run launched from a follow-up task in a Slack thread, checked at its first new step). */
67
+ allowRedelegation?: boolean;
66
68
  /** Wait for the dispatched task to reach a terminal status before resolving. Default: true. */
67
69
  waitForCompletion?: boolean;
68
70
  /** Max ms to wait for a terminal status before throwing. Default: 2h. Only used when waitForCompletion is true. */
@@ -493,6 +495,11 @@ declare module "swarm-sdk" {
493
495
  slack_downloadFile(args: { url: string }): Promise<unknown>;
494
496
  slack_delete(args: { channelId: string; messageTs: string }): Promise<unknown>;
495
497
  slack_update(args: { channelId: string; messageTs: string; message: string }): Promise<unknown>;
498
+ slack_leaveThread(args: {
499
+ taskId?: string;
500
+ channelId?: string;
501
+ threadTs?: string;
502
+ }): Promise<unknown>;
496
503
 
497
504
  // --- write: chat sdk ---
498
505
  chat_reply(args: { text: string }): Promise<unknown>;
@@ -755,6 +762,26 @@ declare module "swarm-sdk" {
755
762
  label: string,
756
763
  config: { prompt: string; model?: string; schema?: Record<string, unknown> },
757
764
  ): Promise<unknown>;
765
+ /**
766
+ * Dispatch a task and journal its result under `label`.
767
+ *
768
+ * Resolves to the task's OUTPUT on success: the stored output string (JSON
769
+ * text under `outputSchema`, so parse it yourself), or `null` when the task
770
+ * stored none. It is never a task record: on a string, `result.taskId` and
771
+ * `result.status` are undefined; on `null`, reading them throws a TypeError.
772
+ * Other shapes:
773
+ * - `failOnTaskFailure: false` and the task ends failed/cancelled/superseded:
774
+ * `{ taskId, status: "failed", error }`.
775
+ * - `waitForCompletion: false`: the first call still waits up to ~30s while
776
+ * the server polls the task. Done in that window: the output, as on
777
+ * success. Failed or cancelled in that window: throws, or
778
+ * `{ taskId, status: "failed", error }` with `failOnTaskFailure: false`.
779
+ * Still running: `{ taskId, status }`.
780
+ *
781
+ * To get a step's task id, query by its context key:
782
+ * `SELECT id, status, output FROM agent_tasks WHERE contextKey = 'script-run:<runId>:<label>'
783
+ * AND taskType = 'script-run-step' ORDER BY createdAt DESC, rowid DESC LIMIT 1`.
784
+ */
758
785
  agentTask(label: string, config: AgentTaskStepConfig): Promise<unknown>;
759
786
  swarmScript(
760
787
  label: string,
@@ -98,6 +98,7 @@ declare module "swarm-extension" {
98
98
  | "amp"
99
99
  | "cursor"
100
100
  | "grok"
101
+ | "fx"
101
102
  | undefined;
102
103
  leadOnly?: boolean | undefined;
103
104
  }
@@ -233,6 +234,7 @@ declare module "swarm-extension" {
233
234
  | "amp"
234
235
  | "cursor"
235
236
  | "grok"
237
+ | "fx"
236
238
  | undefined;
237
239
  providerMeta?: Record<string, unknown> | undefined;
238
240
  harnessVariant?: string | undefined;
@@ -255,6 +257,7 @@ declare module "swarm-extension" {
255
257
  | "amp"
256
258
  | "cursor"
257
259
  | "grok"
260
+ | "fx"
258
261
  | undefined;
259
262
  leadOnly?: boolean | undefined;
260
263
  }
@@ -276,6 +279,10 @@ declare module "swarm-extension" {
276
279
  runtimeInstanceId?: string | null | undefined;
277
280
  };
278
281
 
282
+ export type SteerMode = "steer" | "queue";
283
+
284
+ export type SteeringSource = "script" | "slack" | "mcp" | "api" | "ui" | "chat";
285
+
279
286
  export type RequestInfo = {
280
287
  sessionId: string | undefined;
281
288
  agentId: string | undefined;
@@ -486,6 +493,27 @@ declare module "swarm-extension" {
486
493
  leadAgentId: string;
487
494
  summary: string;
488
495
  }
496
+ export interface TaskSteerEvent {
497
+ task: AgentTask;
498
+ message: string;
499
+ mode: SteerMode;
500
+ source: SteeringSource;
501
+ sender: {
502
+ kind: "user" | "agent" | "system";
503
+ userId?: string;
504
+ agentId?: string;
505
+ };
506
+ messageTimestamps?: string[];
507
+ /** Slack ingress metadata; not persisted or writable through modify. */
508
+ slack?: {
509
+ channelId: string;
510
+ threadTs: string;
511
+ userId?: string;
512
+ };
513
+ }
514
+ export interface TaskSteerModify {
515
+ message: string;
516
+ }
489
517
  export type TaskFollowUpModify = Partial<
490
518
  Pick<CreateTaskOptions, "agentId" | "priority" | "followUpConfig">
491
519
  > & {
@@ -718,6 +746,11 @@ declare module "swarm-extension" {
718
746
  modify: TaskFollowUpModify;
719
747
  result: PreResult<TaskFollowUpModify>;
720
748
  };
749
+ "pre.task.steer": {
750
+ event: TaskSteerEvent;
751
+ modify: TaskSteerModify;
752
+ result: PreResult<TaskSteerModify>;
753
+ };
721
754
  "pre.slack.route": {
722
755
  event: SlackRouteEvent;
723
756
  modify: SlackRouteModify;
@@ -45,6 +45,8 @@ declare module "swarm-sdk" {
45
45
  parentTaskId?: string;
46
46
  requestedByUserId?: string;
47
47
  outputSchema?: Record<string, unknown>;
48
+ /** Dispatch even when the follow-up re-delegation guard blocks this step (a run launched from a follow-up task in a Slack thread, checked at its first new step). */
49
+ allowRedelegation?: boolean;
48
50
  /** Wait for the dispatched task to reach a terminal status before resolving. Default: true. */
49
51
  waitForCompletion?: boolean;
50
52
  /** Max ms to wait for a terminal status before throwing. Default: 2h. Only used when waitForCompletion is true. */
@@ -475,6 +477,11 @@ declare module "swarm-sdk" {
475
477
  slack_downloadFile(args: { url: string }): Promise<unknown>;
476
478
  slack_delete(args: { channelId: string; messageTs: string }): Promise<unknown>;
477
479
  slack_update(args: { channelId: string; messageTs: string; message: string }): Promise<unknown>;
480
+ slack_leaveThread(args: {
481
+ taskId?: string;
482
+ channelId?: string;
483
+ threadTs?: string;
484
+ }): Promise<unknown>;
478
485
 
479
486
  // --- write: chat sdk ---
480
487
  chat_reply(args: { text: string }): Promise<unknown>;
@@ -737,6 +744,26 @@ declare module "swarm-sdk" {
737
744
  label: string,
738
745
  config: { prompt: string; model?: string; schema?: Record<string, unknown> },
739
746
  ): Promise<unknown>;
747
+ /**
748
+ * Dispatch a task and journal its result under `label`.
749
+ *
750
+ * Resolves to the task's OUTPUT on success: the stored output string (JSON
751
+ * text under `outputSchema`, so parse it yourself), or `null` when the task
752
+ * stored none. It is never a task record: on a string, `result.taskId` and
753
+ * `result.status` are undefined; on `null`, reading them throws a TypeError.
754
+ * Other shapes:
755
+ * - `failOnTaskFailure: false` and the task ends failed/cancelled/superseded:
756
+ * `{ taskId, status: "failed", error }`.
757
+ * - `waitForCompletion: false`: the first call still waits up to ~30s while
758
+ * the server polls the task. Done in that window: the output, as on
759
+ * success. Failed or cancelled in that window: throws, or
760
+ * `{ taskId, status: "failed", error }` with `failOnTaskFailure: false`.
761
+ * Still running: `{ taskId, status }`.
762
+ *
763
+ * To get a step's task id, query by its context key:
764
+ * `SELECT id, status, output FROM agent_tasks WHERE contextKey = 'script-run:<runId>:<label>'
765
+ * AND taskType = 'script-run-step' ORDER BY createdAt DESC, rowid DESC LIMIT 1`.
766
+ */
740
767
  agentTask(label: string, config: AgentTaskStepConfig): Promise<unknown>;
741
768
  swarmScript(
742
769
  label: string,
@@ -130,6 +130,7 @@ export function createUserServer(user: User): McpServer {
130
130
  offerMode: false,
131
131
  leadOnly: false,
132
132
  allowDuplicate: false,
133
+ allowRedelegation: false,
133
134
  overrideSlackContext: false,
134
135
  });
135
136
  });
package/src/server.ts CHANGED
@@ -11,6 +11,7 @@ import { MCP_MAX_TOOL_INPUT_ELEMENTS } from "./mcp-limits";
11
11
  import { resolveTemplate } from "./prompts/resolver";
12
12
  import { isRbacEnabled } from "./rbac";
13
13
  import { getSlackConfiguration } from "./slack/config";
14
+ import { isSlackLeaveThreadDisabled } from "./slack/left-threads";
14
15
  import { registerAcceptSteerTool } from "./tools/accept-steer";
15
16
  import { registerAppActionRunTool } from "./tools/app-action-run";
16
17
  import { registerAppDiffTool } from "./tools/app-diff";
@@ -162,6 +163,7 @@ import { registerSlackCreateChannelTool } from "./tools/slack-create-channel";
162
163
  import { registerSlackDeleteTool } from "./tools/slack-delete";
163
164
  import { registerSlackDownloadFileTool } from "./tools/slack-download-file";
164
165
  import { registerSlackInviteToChannelTool } from "./tools/slack-invite-to-channel";
166
+ import { registerSlackLeaveThreadTool } from "./tools/slack-leave-thread";
165
167
  import { registerSlackListChannelsTool } from "./tools/slack-list-channels";
166
168
  import { registerSlackPostTool } from "./tools/slack-post";
167
169
  import { registerSlackReadTool } from "./tools/slack-read";
@@ -693,6 +695,11 @@ export async function createServer(
693
695
  registerSlackDownloadFileTool(server);
694
696
  registerSlackDeleteTool(server);
695
697
  registerSlackUpdateTool(server);
698
+ // The scripts bridge builds its full-surface server once and caches it, so
699
+ // it always registers the tool; the handler refuses while the flag is set.
700
+ if (opts.fullSurface === true || !isSlackLeaveThreadDisabled()) {
701
+ registerSlackLeaveThreadTool(server);
702
+ }
696
703
  }
697
704
 
698
705
  // End of default-enabled capabilities