@desplega.ai/agent-swarm 1.150.0 → 1.152.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 (311) hide show
  1. package/README.md +11 -0
  2. package/dist/{acp-adapter-d5mzggyk.js → acp-adapter-d05sg0q4.js} +5 -5
  3. package/dist/{actions-wn6ew0p7.js → actions-4sc0v342.js} +38 -11
  4. package/dist/{anthropic-messages-ec35ktvx.js → anthropic-messages-2y8nktar.js} +16 -16
  5. package/dist/{app-dty7p9hg.js → app-2d0v6kt6.js} +7 -7
  6. package/dist/{assistant-q4vhgjtx.js → assistant-zs6wdnrf.js} +41 -16
  7. package/dist/{azure-openai-responses-g85jv7m8.js → azure-openai-responses-f38x2avq.js} +13 -13
  8. package/dist/{boot-reembed-n15q6s9x.js → boot-reembed-3dwdnnsr.js} +13 -15
  9. package/dist/{boot-reembed-92292pgf.js → boot-reembed-vhh33520.js} +8 -8
  10. package/dist/{boot-scrub-logs-44pc955m.js → boot-scrub-logs-e1fnabcg.js} +5 -5
  11. package/dist/{claude-adapter-cjegjbje.js → claude-adapter-h58xqzzd.js} +12 -13
  12. package/dist/{claude-managed-adapter-p3ts5m9m.js → claude-managed-adapter-dsmkcrjy.js} +3 -3
  13. package/dist/{claude-sdk-session-7ywbn12v.js → claude-sdk-session-pbaczbgt.js} +12 -13
  14. package/dist/{cli-9gqddmxs.js → cli-4fqn2vpg.js} +8 -8
  15. package/dist/{cli-mzb0n642.js → cli-4wppfkcm.js} +350 -20
  16. package/dist/{cli-h505yhex.js → cli-5bqt4gwa.js} +2 -2
  17. package/dist/{cli-hbagregr.js → cli-5p007m2q.js} +88 -13
  18. package/dist/{cli-3ckzxdt1.js → cli-6k2athpf.js} +94 -81
  19. package/dist/{cli-3mh119qv.js → cli-72vksk1n.js} +9855 -693
  20. package/dist/{cli-taym3dmg.js → cli-87hdfyde.js} +13 -13
  21. package/dist/{cli-zthatyrx.js → cli-8fy9dmpv.js} +5 -5
  22. package/dist/{cli-asvrh7kj.js → cli-9xvdm3bx.js} +1 -1
  23. package/dist/{cli-72xdszyx.js → cli-awb59f08.js} +2 -2
  24. package/dist/{cli-vr6qj104.js → cli-cs9z1hw3.js} +1 -1
  25. package/dist/{cli-kt7wevqh.js → cli-cwvey7n1.js} +164 -56
  26. package/dist/{cli-5my3bjsd.js → cli-d3p82erb.js} +7 -7
  27. package/dist/{cli-7kwmt3r5.js → cli-evgy5w3y.js} +9 -3
  28. package/dist/{cli-111ey3ra.js → cli-f1f9n77e.js} +5 -5
  29. package/dist/{cli-ttje5a4z.js → cli-fhdssm2v.js} +105 -56
  30. package/dist/{cli-a45d5v9b.js → cli-fm4ytsdk.js} +2 -2
  31. package/dist/{cli-qp69dd5d.js → cli-gacq5hg8.js} +116 -14
  32. package/dist/{cli-hmyynq9b.js → cli-ht5rs63n.js} +1 -1
  33. package/dist/{cli-dyx0237s.js → cli-j4nv2wqy.js} +2 -4
  34. package/dist/{cli-gm0fhz13.js → cli-jh9txj4n.js} +2 -1
  35. package/dist/{cli-8chp8xab.js → cli-nd5s3m4y.js} +2 -2
  36. package/dist/{cli-zd21jrrc.js → cli-p2hx1pxm.js} +9872 -424
  37. package/dist/cli-pf9q3696.js +172 -0
  38. package/dist/{cli-7fesa1ky.js → cli-pgj4a5fb.js} +1 -1
  39. package/dist/{cli-07jd68a1.js → cli-re8z5p8p.js} +1 -1
  40. package/dist/{cli-ryz8q8p1.js → cli-t1mykkng.js} +40 -40
  41. package/dist/{cli-ck1vqzdj.js → cli-t6axtdcg.js} +9 -4
  42. package/dist/{cli-1mx65de9.js → cli-wbyk465z.js} +1 -1
  43. package/dist/{cli-kyysm7m3.js → cli-wp7cejrg.js} +1 -1
  44. package/dist/{cli-n75jt793.js → cli-z61ykvam.js} +6 -5
  45. package/dist/{cli-ceb5y160.js → cli-zdcdh02m.js} +1 -1
  46. package/dist/{cli-cmp8g3sw.js → cli-zfzznn7c.js} +45 -9
  47. package/dist/cli.js +22 -21
  48. package/dist/{codex-adapter-rn3aq7jm.js → codex-adapter-v3cz462q.js} +8 -9
  49. package/dist/{codex-hook-gthcmm0g.js → codex-hook-8jexc34x.js} +2 -2
  50. package/dist/{codex-session-runner-bge0cbsw.js → codex-session-runner-de1a5g5z.js} +8 -9
  51. package/dist/{commands-26phw0pc.js → commands-wfqqpn95.js} +5 -5
  52. package/dist/{db-jxjx6s1b.js → db-9zzv5y05.js} +5 -5
  53. package/dist/dispatcher-hfm1jbgx.js +74 -0
  54. package/dist/{google-generative-ai-tsskx7te.js → google-generative-ai-ndjmj5fa.js} +7 -7
  55. package/dist/{google-vertex-pkhpkbq9.js → google-vertex-fr665tqs.js} +10 -10
  56. package/dist/handlers-wrrhsack.js +65 -0
  57. package/dist/{hook-jw0ztn96.js → hook-9skye8zd.js} +9 -10
  58. package/dist/{hook-t6fqsggw.js → hook-fgjv62k5.js} +9 -10
  59. package/dist/{http-hyc7s57s.js → http-7grttjfq.js} +1434 -579
  60. package/dist/{index-bcyx5djw.js → index-29gxx9ar.js} +25 -17
  61. package/dist/{index-wtwpthe1.js → index-30pz2h9w.js} +25 -19
  62. package/dist/{index-1d9r6a74.js → index-4ek1df87.js} +26 -20
  63. package/dist/{index-sfhaq59d.js → index-myfwqqyq.js} +120 -102
  64. package/dist/index-pfhzrh1d.js +25 -0
  65. package/dist/{keepalive-fbsak4ya.js → keepalive-j75m2m7k.js} +7 -7
  66. package/dist/{lead-rdzdm627.js → lead-a1hpjt0a.js} +41 -49
  67. package/dist/{maintenance-25z1c1vf.js → maintenance-tngjzqdq.js} +8 -8
  68. package/dist/{node-209sxmg3.js → node-x1g4v0pq.js} +9 -9
  69. package/dist/{oauth-refresh-sweep-j3d7gtse.js → oauth-refresh-sweep-em1fn044.js} +6 -6
  70. package/dist/{onboard-dkcwrc75.js → onboard-fv7ezvwx.js} +13 -4
  71. package/dist/{openai-codex-responses-pm5gnyfa.js → openai-codex-responses-wy6a43wd.js} +11 -11
  72. package/dist/{openai-responses-vwvk1taj.js → openai-responses-6xwf7bzg.js} +13 -13
  73. package/dist/{opencode-adapter-dce4wskd.js → opencode-adapter-28axqa09.js} +4 -4
  74. package/dist/{otel-impl-8rgyy3j8.js → otel-impl-6vbb568y.js} +1 -1
  75. package/dist/{pi-mono-adapter-4s2bbkgq.js → pi-mono-adapter-jcqt71cg.js} +14 -16
  76. package/dist/{pricing-refresh-9cek71nq.js → pricing-refresh-nmm949an.js} +7 -7
  77. package/dist/{rbac-roles-vk8mqp9v.js → rbac-roles-0fz5qryx.js} +7 -7
  78. package/dist/{rbac-roles-z98s2ypy.js → rbac-roles-xtp05syf.js} +13 -9
  79. package/dist/{render-v2-3j26g0qh.js → render-v2-fpb8whme.js} +7 -7
  80. package/dist/{seed-pricing-7rxhcnzm.js → seed-pricing-zwa8bs3t.js} +6 -6
  81. package/dist/{setup-e2t7vz0c.js → setup-3ybwbdpd.js} +2 -2
  82. package/dist/{work-objects-8d3nd3x2.js → work-objects-rmayxcx7.js} +41 -16
  83. package/dist/{worker-0x9fn7nq.js → worker-rng4jhnh.js} +41 -49
  84. package/openapi.json +1351 -265
  85. package/package.json +2 -1
  86. package/src/agentmail/handlers.ts +95 -71
  87. package/src/be/db-client.ts +5 -0
  88. package/src/be/db.ts +12 -4
  89. package/src/be/extensions/db.ts +461 -0
  90. package/src/be/extensions/validate.ts +107 -0
  91. package/src/be/memory/boot-reembed.ts +17 -12
  92. package/src/be/memory/index.ts +12 -0
  93. package/src/be/memory/providers/openai-embedding.ts +4 -0
  94. package/src/be/memory/types.ts +2 -0
  95. package/src/be/migrations/155_extensions.sql +66 -0
  96. package/src/be/migrations/156_inbox_item_state_read_at.sql +7 -0
  97. package/src/be/rbac-roles.ts +3 -2
  98. package/src/be/scripts/typecheck.ts +56 -38
  99. package/src/be/seed-skills/index.ts +7 -0
  100. package/src/be/swarm-config-guard.ts +10 -2
  101. package/src/commands/onboard/compose-generator.ts +14 -2
  102. package/src/commands/onboard/env-generator.ts +7 -0
  103. package/src/commands/runner.ts +1 -1
  104. package/src/extensions/apply-task-create.ts +135 -0
  105. package/src/extensions/bundle-path.ts +10 -0
  106. package/src/extensions/contract-runtime.ts +7 -0
  107. package/src/extensions/contract-types.generated.ts +4 -0
  108. package/src/extensions/contract.ts +270 -0
  109. package/src/extensions/ctx.ts +186 -0
  110. package/src/extensions/dispatcher.ts +479 -0
  111. package/src/extensions/identity.ts +44 -0
  112. package/src/extensions/lifecycle.ts +303 -0
  113. package/src/extensions/loader.ts +207 -0
  114. package/src/extensions/post-bridge.ts +161 -0
  115. package/src/github/handlers.ts +185 -149
  116. package/src/gitlab/handlers.ts +97 -81
  117. package/src/heartbeat/heartbeat.ts +152 -83
  118. package/src/http/agents.ts +1 -1
  119. package/src/http/all-routes.ts +1 -0
  120. package/src/http/apps.ts +14 -10
  121. package/src/http/core.ts +34 -0
  122. package/src/http/extensions.ts +552 -0
  123. package/src/http/inbox-state.ts +2 -0
  124. package/src/http/index.ts +25 -0
  125. package/src/http/mcp-bridge.ts +96 -62
  126. package/src/http/mcp.ts +1 -1
  127. package/src/http/status.ts +6 -4
  128. package/src/http/tasks.ts +51 -35
  129. package/src/integrations/kapso/inbound.ts +15 -11
  130. package/src/jira/sync.ts +26 -18
  131. package/src/linear/sync.ts +26 -18
  132. package/src/prompts/session-templates.ts +5 -4
  133. package/src/rbac/admission.ts +1 -1
  134. package/src/rbac/elevated-agents.ts +24 -0
  135. package/src/rbac/legacy-policy.ts +47 -6
  136. package/src/rbac/permissions.ts +8 -0
  137. package/src/scheduler/deferred-task-waits.ts +133 -51
  138. package/src/scheduler/schedule-task.ts +50 -24
  139. package/src/scheduler/scheduler.ts +4 -2
  140. package/src/scripts-runtime/executors/native.ts +5 -1
  141. package/src/scripts-runtime/executors/types.ts +5 -0
  142. package/src/scripts-runtime/import-allowlist.ts +79 -19
  143. package/src/scripts-runtime/sdk-allowlist.ts +8 -0
  144. package/src/scripts-runtime/swarm-config.ts +8 -0
  145. package/src/scripts-runtime/swarm-sdk.ts +3 -0
  146. package/src/scripts-runtime/types/stdlib.d.ts +13 -0
  147. package/src/scripts-runtime/types/swarm-extension.d.ts +562 -0
  148. package/src/scripts-runtime/types/swarm-sdk.d.ts +13 -0
  149. package/src/server.ts +18 -0
  150. package/src/slack/HEURISTICS.md +1 -1
  151. package/src/slack/actions.ts +21 -17
  152. package/src/slack/blocks.ts +1 -1
  153. package/src/slack/handlers.ts +98 -5
  154. package/src/slack/inbound-files.ts +6 -2
  155. package/src/slack/render-v2.ts +181 -39
  156. package/src/slack/responses.ts +7 -4
  157. package/src/slack/router.ts +13 -3
  158. package/src/slack/steering.ts +3 -3
  159. package/src/slack/task-output.ts +21 -0
  160. package/src/slack/watcher.ts +3 -2
  161. package/src/slack/work-objects.ts +2 -1
  162. package/src/tasks/errors.ts +9 -0
  163. package/src/tasks/sibling-awareness.ts +35 -1
  164. package/src/tasks/worker-follow-up.ts +64 -12
  165. package/src/tests/base-prompt.test.ts +1 -0
  166. package/src/tests/deferred-task-wake.test.ts +161 -9
  167. package/src/tests/docs-index-footprint.test.js +3 -3
  168. package/src/tests/env-flag.test.ts +1 -0
  169. package/src/tests/extensions-db.test.ts +216 -0
  170. package/src/tests/extensions-dispatcher.test.ts +275 -0
  171. package/src/tests/extensions-example-heartbeat-remediate.test.ts +69 -0
  172. package/src/tests/extensions-example-mcp-install.test.ts +63 -0
  173. package/src/tests/extensions-example-no-exclamation-marks.test.ts +72 -0
  174. package/src/tests/extensions-example-rewrite-and-suppress.test.ts +70 -0
  175. package/src/tests/extensions-example-route-slack-channel.test.ts +217 -0
  176. package/src/tests/extensions-heartbeat.test.ts +291 -0
  177. package/src/tests/extensions-http.test.ts +512 -0
  178. package/src/tests/extensions-lead-equivalence.test.ts +70 -0
  179. package/src/tests/extensions-lifecycle.test.ts +209 -0
  180. package/src/tests/extensions-loader.test.ts +95 -0
  181. package/src/tests/extensions-mcp-tools.test.ts +353 -0
  182. package/src/tests/extensions-pre-task.test.ts +487 -0
  183. package/src/tests/extensions-run-context.test.ts +41 -0
  184. package/src/tests/extensions-tool-call.test.ts +288 -0
  185. package/src/tests/extensions-validate.test.ts +196 -0
  186. package/src/tests/fixtures/extensions/bad-import/hooks.ts +8 -0
  187. package/src/tests/fixtures/extensions/bad-import/manifest.json +9 -0
  188. package/src/tests/fixtures/extensions/bad-return-shape/hooks.ts +10 -0
  189. package/src/tests/fixtures/extensions/bad-return-shape/manifest.json +9 -0
  190. package/src/tests/fixtures/extensions/block-tasks-from-source/hooks.ts +22 -0
  191. package/src/tests/fixtures/extensions/block-tasks-from-source/manifest.json +9 -0
  192. package/src/tests/fixtures/extensions/ignore-channel/hooks.ts +22 -0
  193. package/src/tests/fixtures/extensions/ignore-channel/manifest.json +9 -0
  194. package/src/tests/fixtures/extensions/load.ts +10 -0
  195. package/src/tests/fixtures/extensions/minimal/hooks.ts +7 -0
  196. package/src/tests/fixtures/extensions/minimal/manifest.json +9 -0
  197. package/src/tests/fixtures/extensions/never-fail-long-tasks/hooks.ts +11 -0
  198. package/src/tests/fixtures/extensions/never-fail-long-tasks/manifest.json +9 -0
  199. package/src/tests/fixtures/extensions/no-exclamation-marks/hooks.ts +23 -0
  200. package/src/tests/fixtures/extensions/no-exclamation-marks/manifest.json +9 -0
  201. package/src/tests/fixtures/extensions/post-logger/hooks.ts +9 -0
  202. package/src/tests/fixtures/extensions/post-logger/manifest.json +9 -0
  203. package/src/tests/fixtures/extensions/priority-a/hooks.ts +7 -0
  204. package/src/tests/fixtures/extensions/priority-a/manifest.json +9 -0
  205. package/src/tests/fixtures/extensions/priority-b/hooks.ts +7 -0
  206. package/src/tests/fixtures/extensions/priority-b/manifest.json +9 -0
  207. package/src/tests/fixtures/extensions/record-only-on-tag/hooks.ts +16 -0
  208. package/src/tests/fixtures/extensions/record-only-on-tag/manifest.json +9 -0
  209. package/src/tests/fixtures/extensions/reserved-assets/hooks.ts +5 -0
  210. package/src/tests/fixtures/extensions/reserved-assets/manifest.json +10 -0
  211. package/src/tests/fixtures/extensions/rewrite-progress-prefix/hooks.ts +22 -0
  212. package/src/tests/fixtures/extensions/rewrite-progress-prefix/manifest.json +9 -0
  213. package/src/tests/fixtures/extensions/rewrite-task-priority/hooks.ts +7 -0
  214. package/src/tests/fixtures/extensions/rewrite-task-priority/manifest.json +9 -0
  215. package/src/tests/fixtures/extensions/route-channel-to-agent/hooks.ts +26 -0
  216. package/src/tests/fixtures/extensions/route-channel-to-agent/manifest.json +9 -0
  217. package/src/tests/fixtures/extensions/slow/hooks.ts +10 -0
  218. package/src/tests/fixtures/extensions/slow/manifest.json +9 -0
  219. package/src/tests/fixtures/extensions/suppress-lead-follow-up/hooks.ts +12 -0
  220. package/src/tests/fixtures/extensions/suppress-lead-follow-up/manifest.json +9 -0
  221. package/src/tests/fixtures/extensions/throws/hooks.ts +12 -0
  222. package/src/tests/fixtures/extensions/throws/manifest.json +9 -0
  223. package/src/tests/fixtures/extensions/worker-runtime/hooks.ts +5 -0
  224. package/src/tests/fixtures/extensions/worker-runtime/manifest.json +9 -0
  225. package/src/tests/fixtures/store-progress-malformed-output.json +4 -0
  226. package/src/tests/memory-edit-authorization.test.ts +32 -0
  227. package/src/tests/multi-runtime-enabled-stats.test.ts +2 -2
  228. package/src/tests/multi-runtime-registration.test.ts +14 -14
  229. package/src/tests/onboard-compose.test.ts +44 -0
  230. package/src/tests/preload.ts +5 -0
  231. package/src/tests/rbac-admission-e2e.test.ts +35 -4
  232. package/src/tests/rbac-admission.test.ts +18 -3
  233. package/src/tests/rbac-e2e-helpers.ts +18 -0
  234. package/src/tests/rbac-engine.test.ts +39 -0
  235. package/src/tests/rbac-mcp-admission-e2e.test.ts +22 -4
  236. package/src/tests/realtime-room-auth.test.ts +1 -1
  237. package/src/tests/reload-config.test.ts +61 -0
  238. package/src/tests/routing-affinity-inventory.test.ts +1 -1
  239. package/src/tests/script-connections.test.ts +2 -0
  240. package/src/tests/scripts-boot-reembed.test.ts +4 -0
  241. package/src/tests/scripts-embeddings.test.ts +4 -0
  242. package/src/tests/scripts-import-allowlist.test.ts +44 -1
  243. package/src/tests/scripts-mcp-e2e.test.ts +80 -0
  244. package/src/tests/scripts-only-gating.test.ts +6 -0
  245. package/src/tests/scripts-runtime-identity.test.ts +1 -1
  246. package/src/tests/slack-blocks.test.ts +12 -1
  247. package/src/tests/slack-render-v2.test.ts +100 -5
  248. package/src/tests/slack-router.test.ts +17 -2
  249. package/src/tests/slack-steering.test.ts +19 -0
  250. package/src/tests/slack-task-output.test.ts +36 -0
  251. package/src/tests/status.test.ts +12 -0
  252. package/src/tests/steering-enabled.test.ts +6 -8
  253. package/src/tests/store-progress-attachments-handler.test.ts +100 -1
  254. package/src/tests/swarm-config-reserved-keys.test.ts +6 -1
  255. package/src/tests/task-tool-preload-mcp.test.ts +4 -1
  256. package/src/tests/tool-annotations.test.ts +1 -1
  257. package/src/tests/tool-registrar-no-input.test.ts +42 -1
  258. package/src/tools/defer-task.ts +1 -1
  259. package/src/tools/extension-activate-version.ts +38 -0
  260. package/src/tools/extension-common.ts +85 -0
  261. package/src/tools/extension-delete.ts +29 -0
  262. package/src/tools/extension-disable.ts +36 -0
  263. package/src/tools/extension-enable.ts +36 -0
  264. package/src/tools/extension-install.ts +65 -0
  265. package/src/tools/extension-list.ts +66 -0
  266. package/src/tools/script-common.ts +43 -18
  267. package/src/tools/send-task.ts +69 -107
  268. package/src/tools/store-progress.ts +30 -0
  269. package/src/tools/task-action.ts +43 -22
  270. package/src/tools/task-tool-ctx.ts +2 -0
  271. package/src/tools/tool-config.ts +7 -1
  272. package/src/tools/utils.ts +140 -16
  273. package/src/types.ts +133 -0
  274. package/src/utils/multi-runtime.ts +2 -2
  275. package/src/utils/steering-enabled.ts +4 -4
  276. package/src/workflows/executors/agent-task.ts +13 -3
  277. package/templates/extensions/README.md +17 -0
  278. package/templates/extensions/notify-on-complete/README.md +11 -0
  279. package/templates/extensions/notify-on-complete/hooks.ts +61 -0
  280. package/templates/extensions/notify-on-complete/manifest.json +7 -0
  281. package/templates/extensions/require-ticket-ref/README.md +11 -0
  282. package/templates/extensions/require-ticket-ref/hooks.ts +30 -0
  283. package/templates/extensions/require-ticket-ref/manifest.json +7 -0
  284. package/templates/extensions/require-verification-note/README.md +11 -0
  285. package/templates/extensions/require-verification-note/hooks.ts +32 -0
  286. package/templates/extensions/require-verification-note/manifest.json +7 -0
  287. package/templates/skills/agent-browser/SKILL.md +84 -0
  288. package/templates/skills/agent-browser/content.md +21 -0
  289. package/templates/skills/scheduling/content.md +2 -0
  290. package/templates/skills/swarm-extensions/config.json +14 -0
  291. package/templates/skills/swarm-extensions/content.md +120 -0
  292. package/dist/cli-b8x19vbh.js +0 -823
  293. package/dist/cli-n1yj5fd5.js +0 -164
  294. package/dist/cli-q04q68be.js +0 -3066
  295. package/dist/cli-qgm2pfpq.js +0 -6377
  296. package/dist/cli-rnsr8asm.js +0 -4323
  297. package/dist/cli-rwp3ksm7.js +0 -545
  298. package/dist/cli-xjb9wghj.js +0 -668
  299. package/dist/cli-xvkmedgn.js +0 -332
  300. package/dist/cli-ydk0wjj8.js +0 -338
  301. package/dist/handlers-vr1djg4n.js +0 -41
  302. package/dist/{claude-managed-setup-f5pmsyrp.js → claude-managed-setup-48dwg9bv.js} +3 -3
  303. package/dist/{cli-81r683bv.js → cli-ch38q5z4.js} +0 -0
  304. package/dist/{cli-wk0gjrn3.js → cli-kj76p1ez.js} +6 -6
  305. package/dist/{cli-c71mg51n.js → cli-r2fpe7cx.js} +5 -5
  306. package/dist/{cli-8qx7jnrk.js → cli-y6x0gqtg.js} +16 -16
  307. package/dist/{index-aa3sa6s8.js → index-2wsdsnmm.js} +1 -1
  308. package/dist/{mistral-conversations-4ryk7h83.js → mistral-conversations-qhr84ph3.js} +9 -9
  309. package/dist/{openai-completions-qdcmkfnr.js → openai-completions-32rdwmze.js} +17 -17
  310. package/dist/{openrouter-images-qx18fyp8.js → openrouter-images-9f9ek4rw.js} +3 -3
  311. package/dist/{pi-messages-79jvzz1d.js → pi-messages-b1mbr6gp.js} +4 -4
@@ -0,0 +1,562 @@
1
+ // biome-ignore-all lint/suspicious/noConfusingVoidType: Extension handlers may explicitly return no result.
2
+ declare module "swarm-extension" {
3
+ export type CreateTaskOptions = {
4
+ key?: string | undefined;
5
+ agentId?: string | null | undefined;
6
+ creatorAgentId?: string | undefined;
7
+ source?:
8
+ | "workflow"
9
+ | "schedule"
10
+ | "slack"
11
+ | "agentmail"
12
+ | "mcp"
13
+ | "api"
14
+ | "ui"
15
+ | "github"
16
+ | "gitlab"
17
+ | "system"
18
+ | "linear"
19
+ | "jira"
20
+ | undefined;
21
+ routingReason?:
22
+ | "skill"
23
+ | "continuity"
24
+ | "overflow"
25
+ | "human_pinned"
26
+ | "reroute_fault"
27
+ | undefined;
28
+ routingSource?: "declared" | "engine_default" | undefined;
29
+ routingNote?: string | undefined;
30
+ taskType?: string | undefined;
31
+ tags?: string[] | undefined;
32
+ priority?: number | undefined;
33
+ dependsOn?: string[] | undefined;
34
+ offeredTo?: string | undefined;
35
+ status?: "draft" | "backlog" | "unassigned" | undefined;
36
+ slackChannelId?: string | undefined;
37
+ slackThreadTs?: string | undefined;
38
+ slackTriggerMessageTs?: string | undefined;
39
+ slackUserId?: string | undefined;
40
+ overrideSlackContext?: boolean | undefined;
41
+ vcsProvider?: "github" | "gitlab" | undefined;
42
+ vcsRepo?: string | undefined;
43
+ vcsEventType?: string | undefined;
44
+ vcsNumber?: number | undefined;
45
+ vcsCommentId?: number | undefined;
46
+ vcsAuthor?: string | undefined;
47
+ vcsUrl?: string | undefined;
48
+ vcsInstallationId?: number | undefined;
49
+ vcsNodeId?: string | undefined;
50
+ agentmailInboxId?: string | undefined;
51
+ agentmailMessageId?: string | undefined;
52
+ agentmailThreadId?: string | undefined;
53
+ mentionMessageId?: string | undefined;
54
+ mentionChannelId?: string | undefined;
55
+ dir?: string | undefined;
56
+ parentTaskId?: string | undefined;
57
+ model?: string | undefined;
58
+ modelTier?: "smol" | "regular" | "smart" | "ultra" | undefined;
59
+ effort?: "off" | "low" | "medium" | "high" | "xhigh" | "max" | undefined;
60
+ scheduleId?: string | undefined;
61
+ workflowRunId?: string | undefined;
62
+ workflowRunStepId?: string | undefined;
63
+ sourceTaskId?: string | undefined;
64
+ outputSchema?: Record<string, unknown> | undefined;
65
+ inheritParentOutputSchema?: boolean | undefined;
66
+ inheritParentRoutingAffinity?: boolean | undefined;
67
+ followUpConfig?:
68
+ | {
69
+ disabled?: boolean | undefined;
70
+ onCompleted?: string | undefined;
71
+ onFailed?: string | undefined;
72
+ }
73
+ | undefined;
74
+ requestedByUserId?: string | undefined;
75
+ contextKey?: string | undefined;
76
+ bypassTrackerContextDedup?: boolean | undefined;
77
+ routingAffinity?:
78
+ | {
79
+ capabilities: string[];
80
+ sourceAgentId?: string | undefined;
81
+ role?: string | undefined;
82
+ harnessProvider?:
83
+ | "claude"
84
+ | "codex"
85
+ | "pi"
86
+ | "devin"
87
+ | "claude-managed"
88
+ | "opencode"
89
+ | "acp"
90
+ | undefined;
91
+ leadOnly?: boolean | undefined;
92
+ }
93
+ | undefined;
94
+ };
95
+
96
+ export type AgentTask = {
97
+ id: string;
98
+ key: string;
99
+ agentId: string | null;
100
+ task: string;
101
+ status:
102
+ | "draft"
103
+ | "backlog"
104
+ | "unassigned"
105
+ | "offered"
106
+ | "reviewing"
107
+ | "pending"
108
+ | "in_progress"
109
+ | "paused"
110
+ | "completed"
111
+ | "failed"
112
+ | "cancelled"
113
+ | "superseded";
114
+ source:
115
+ | "workflow"
116
+ | "schedule"
117
+ | "slack"
118
+ | "agentmail"
119
+ | "mcp"
120
+ | "api"
121
+ | "ui"
122
+ | "github"
123
+ | "gitlab"
124
+ | "system"
125
+ | "linear"
126
+ | "jira";
127
+ tags: string[];
128
+ priority: number;
129
+ dependsOn: string[];
130
+ createdAt: string;
131
+ lastUpdatedAt: string;
132
+ slackReplySent: boolean;
133
+ wasPaused: boolean;
134
+ creatorAgentId?: string | undefined;
135
+ title?: string | undefined;
136
+ routingReason?:
137
+ | "skill"
138
+ | "continuity"
139
+ | "overflow"
140
+ | "human_pinned"
141
+ | "reroute_fault"
142
+ | undefined;
143
+ routingSource?: "declared" | "engine_default" | undefined;
144
+ routingNote?: string | undefined;
145
+ taskType?: string | undefined;
146
+ offeredTo?: string | undefined;
147
+ offeredAt?: string | undefined;
148
+ acceptedAt?: string | undefined;
149
+ rejectionReason?: string | undefined;
150
+ finishedAt?: string | undefined;
151
+ notifiedAt?: string | undefined;
152
+ failureReason?: string | undefined;
153
+ output?: string | undefined;
154
+ progress?: string | undefined;
155
+ slackChannelId?: string | undefined;
156
+ slackThreadTs?: string | undefined;
157
+ slackTriggerMessageTs?: string | undefined;
158
+ slackUserId?: string | undefined;
159
+ slackProgressMessageTs?: string | undefined;
160
+ slackTreeRootMessageTs?: string | undefined;
161
+ vcsProvider?: "github" | "gitlab" | undefined;
162
+ vcsRepo?: string | undefined;
163
+ vcsEventType?: string | undefined;
164
+ vcsNumber?: number | undefined;
165
+ vcsCommentId?: number | undefined;
166
+ vcsAuthor?: string | undefined;
167
+ vcsUrl?: string | undefined;
168
+ vcsInstallationId?: number | undefined;
169
+ vcsNodeId?: string | undefined;
170
+ agentmailInboxId?: string | undefined;
171
+ agentmailMessageId?: string | undefined;
172
+ agentmailThreadId?: string | undefined;
173
+ mentionMessageId?: string | undefined;
174
+ mentionChannelId?: string | undefined;
175
+ dir?: string | undefined;
176
+ parentTaskId?: string | undefined;
177
+ claudeSessionId?: string | undefined;
178
+ model?: string | undefined;
179
+ modelTier?: "smol" | "regular" | "smart" | "ultra" | undefined;
180
+ effort?: "off" | "low" | "medium" | "high" | "xhigh" | "max" | undefined;
181
+ scheduleId?: string | undefined;
182
+ workflowRunId?: string | null | undefined;
183
+ workflowRunStepId?: string | null | undefined;
184
+ contextKey?: string | undefined;
185
+ outputSchema?: Record<string, unknown> | undefined;
186
+ followUpConfig?:
187
+ | {
188
+ disabled?: boolean | undefined;
189
+ onCompleted?: string | undefined;
190
+ onFailed?: string | undefined;
191
+ }
192
+ | undefined;
193
+ compactionCount?: number | undefined;
194
+ peakContextPercent?: number | undefined;
195
+ peakContextTokens?: number | undefined;
196
+ contextWindowSize?: number | undefined;
197
+ credentialKeySuffix?: string | undefined;
198
+ credentialKeyType?: string | undefined;
199
+ requestedByUserId?: string | undefined;
200
+ swarmVersion?: string | undefined;
201
+ provider?:
202
+ | "claude"
203
+ | "codex"
204
+ | "pi"
205
+ | "devin"
206
+ | "claude-managed"
207
+ | "opencode"
208
+ | "acp"
209
+ | undefined;
210
+ providerMeta?: Record<string, unknown> | undefined;
211
+ harnessVariant?: string | undefined;
212
+ harnessVariantMeta?: Record<string, unknown> | undefined;
213
+ totalCostUsd?: number | undefined;
214
+ routingAffinity?:
215
+ | {
216
+ capabilities: string[];
217
+ sourceAgentId?: string | undefined;
218
+ role?: string | undefined;
219
+ harnessProvider?:
220
+ | "claude"
221
+ | "codex"
222
+ | "pi"
223
+ | "devin"
224
+ | "claude-managed"
225
+ | "opencode"
226
+ | "acp"
227
+ | undefined;
228
+ leadOnly?: boolean | undefined;
229
+ }
230
+ | undefined;
231
+ routingAffinityInvalid?: boolean | undefined;
232
+ };
233
+
234
+ export type ActiveSession = {
235
+ id: string;
236
+ agentId: string;
237
+ taskId: string | null;
238
+ triggerType: string;
239
+ inboxMessageId: string | null;
240
+ taskDescription: string | null;
241
+ runnerSessionId: string | null;
242
+ providerSessionId: string | null;
243
+ startedAt: string;
244
+ lastHeartbeatAt: string;
245
+ runtimeInstanceId?: string | null | undefined;
246
+ };
247
+
248
+ export type RequestInfo = {
249
+ sessionId: string | undefined;
250
+ agentId: string | undefined;
251
+ runtimeInstanceId: string | undefined;
252
+ sourceTaskId: string | undefined;
253
+ contextKey: string | undefined;
254
+ callOrigin: "mcp" | "extension" | "script-sdk";
255
+ };
256
+
257
+ import type { SwarmSdk } from "swarm-sdk";
258
+ import type { z } from "zod";
259
+ export type Runtime = "api" | "worker";
260
+ export interface ExtensionManifest {
261
+ name: string;
262
+ description: string;
263
+ version: string;
264
+ runtime: Runtime;
265
+ assets: {
266
+ hooks: string;
267
+ skills?: string[];
268
+ workflows?: string[];
269
+ schedules?: string[];
270
+ };
271
+ homepage?: string;
272
+ author?: string;
273
+ }
274
+ export interface HooksModule {
275
+ default: SwarmExtension;
276
+ config?: z.ZodTypeAny;
277
+ }
278
+ export interface ExtensionState {
279
+ get<T = unknown>(key: string): Promise<T | null>;
280
+ set<T>(key: string, value: T): Promise<void>;
281
+ incr(key: string, by?: number): Promise<number>;
282
+ del(key: string): Promise<void>;
283
+ }
284
+ export interface ExtensionLogger {
285
+ debug(message: string, data?: unknown): void;
286
+ info(message: string, data?: unknown): void;
287
+ warn(message: string, data?: unknown): void;
288
+ error(message: string, data?: unknown): void;
289
+ }
290
+ type ConfigFor<M extends ExtensionManifest> = M extends {
291
+ config: infer C extends z.ZodTypeAny;
292
+ }
293
+ ? z.infer<C>
294
+ : Record<string, unknown>;
295
+ export interface ApiCtx<Config = Record<string, unknown>> {
296
+ swarm: SwarmSdk;
297
+ state: ExtensionState;
298
+ config: Config;
299
+ log: ExtensionLogger;
300
+ signal: AbortSignal;
301
+ event: {
302
+ name: keyof SwarmEventMap;
303
+ at: string;
304
+ extension: {
305
+ id: string;
306
+ version: number;
307
+ };
308
+ };
309
+ }
310
+ export interface WorkerCtx<Config = Record<string, unknown>>
311
+ extends Omit<ApiCtx<Config>, "swarm"> {
312
+ swarm: SwarmSdk;
313
+ worker: {
314
+ agentId: string;
315
+ taskId: string;
316
+ harness: string;
317
+ };
318
+ }
319
+ export type CtxFor<M extends ExtensionManifest> = M["runtime"] extends "api"
320
+ ? ApiCtx<ConfigFor<M>>
321
+ : WorkerCtx<ConfigFor<M>>;
322
+ export type PreResult<Modify> =
323
+ | void
324
+ | {
325
+ action: "continue";
326
+ }
327
+ | {
328
+ action: "modify";
329
+ data: Modify;
330
+ }
331
+ | {
332
+ action: "block";
333
+ reason: string;
334
+ };
335
+ export type TaskCreateOrigin =
336
+ | "rest"
337
+ | "app"
338
+ | "mcp"
339
+ | "slack"
340
+ | "schedule"
341
+ | "workflow"
342
+ | "webhook"
343
+ | "followUp"
344
+ | `extension:${string}`;
345
+ export interface TaskCreateEvent {
346
+ options: CreateTaskOptions;
347
+ description: string;
348
+ origin: TaskCreateOrigin;
349
+ requestInfo?: RequestInfo;
350
+ }
351
+ export type TaskCreateModify = Partial<
352
+ Pick<
353
+ CreateTaskOptions,
354
+ | "agentId"
355
+ | "creatorAgentId"
356
+ | "source"
357
+ | "taskType"
358
+ | "tags"
359
+ | "priority"
360
+ | "dependsOn"
361
+ | "offeredTo"
362
+ | "vcsProvider"
363
+ | "vcsRepo"
364
+ | "vcsEventType"
365
+ | "vcsNumber"
366
+ | "vcsCommentId"
367
+ | "vcsAuthor"
368
+ | "vcsUrl"
369
+ | "vcsInstallationId"
370
+ | "vcsNodeId"
371
+ | "agentmailInboxId"
372
+ | "agentmailMessageId"
373
+ | "agentmailThreadId"
374
+ | "mentionMessageId"
375
+ | "mentionChannelId"
376
+ | "dir"
377
+ | "model"
378
+ | "modelTier"
379
+ | "effort"
380
+ | "outputSchema"
381
+ | "followUpConfig"
382
+ | "bypassTrackerContextDedup"
383
+ >
384
+ > & {
385
+ description?: string;
386
+ };
387
+ export interface TaskFollowUpEvent {
388
+ completedTask: AgentTask;
389
+ status: "completed" | "failed";
390
+ output?: string;
391
+ failureReason?: string;
392
+ workerAgentId: string;
393
+ leadAgentId: string;
394
+ summary: string;
395
+ }
396
+ export type TaskFollowUpModify = Partial<
397
+ Pick<CreateTaskOptions, "agentId" | "priority" | "followUpConfig">
398
+ > & {
399
+ description?: string;
400
+ };
401
+ export interface SlackRouteEvent {
402
+ channelId: string;
403
+ userId: string;
404
+ text: string;
405
+ threadTs?: string;
406
+ botMentioned: boolean;
407
+ threadContext?: {
408
+ channelId: string;
409
+ threadTs: string;
410
+ };
411
+ }
412
+ export interface SlackRouteModify {
413
+ target:
414
+ | {
415
+ kind: "agent";
416
+ agentId: string;
417
+ }
418
+ | {
419
+ kind: "lead";
420
+ }
421
+ | {
422
+ kind: "broadcast";
423
+ };
424
+ }
425
+ export type HeartbeatClassification = "no-session" | "stale-session" | "fresh-stalled";
426
+ export type HeartbeatAction = "supersede-resume" | "fail" | "record";
427
+ export interface HeartbeatRemediateEvent {
428
+ task: AgentTask;
429
+ session?: ActiveSession;
430
+ classification: HeartbeatClassification;
431
+ proposedAction: HeartbeatAction;
432
+ reason: string;
433
+ taskAgeMs: number;
434
+ sessionHeartbeatAgeMs?: number;
435
+ }
436
+ export interface HeartbeatRemediateModify {
437
+ proposedAction: HeartbeatAction;
438
+ }
439
+ export interface ToolCallEvent {
440
+ tool: string;
441
+ args: unknown;
442
+ requestInfo: RequestInfo;
443
+ }
444
+ export interface ToolCallModify {
445
+ args: unknown;
446
+ }
447
+ export interface TaskEvent {
448
+ task: AgentTask;
449
+ }
450
+ export interface TaskCompletedEvent extends TaskEvent {
451
+ output: string;
452
+ }
453
+ export interface TaskFailedEvent extends TaskEvent {
454
+ failureReason: string;
455
+ }
456
+ export interface TaskSupersededEvent extends TaskEvent {
457
+ supersededBy: string;
458
+ }
459
+ export interface TaskProgressEvent extends TaskEvent {
460
+ progress: string;
461
+ }
462
+ export interface SlackMessageEvent {
463
+ channelId: string;
464
+ userId: string;
465
+ text: string;
466
+ threadTs?: string;
467
+ taskId?: string;
468
+ }
469
+ export interface ToolCallCompletedEvent extends ToolCallEvent {
470
+ result: unknown;
471
+ durationMs: number;
472
+ }
473
+ export interface SwarmEventMap {
474
+ "pre.task.create": {
475
+ event: TaskCreateEvent;
476
+ modify: TaskCreateModify;
477
+ result: PreResult<TaskCreateModify>;
478
+ };
479
+ "pre.task.followUp": {
480
+ event: TaskFollowUpEvent;
481
+ modify: TaskFollowUpModify;
482
+ result: PreResult<TaskFollowUpModify>;
483
+ };
484
+ "pre.slack.route": {
485
+ event: SlackRouteEvent;
486
+ modify: SlackRouteModify;
487
+ result: PreResult<SlackRouteModify>;
488
+ };
489
+ "pre.heartbeat.remediate": {
490
+ event: HeartbeatRemediateEvent;
491
+ modify: HeartbeatRemediateModify;
492
+ result: PreResult<HeartbeatRemediateModify>;
493
+ };
494
+ "pre.tool.call": {
495
+ event: ToolCallEvent;
496
+ modify: ToolCallModify;
497
+ result: PreResult<ToolCallModify>;
498
+ };
499
+ "post.task.created": {
500
+ event: TaskEvent;
501
+ modify: never;
502
+ result: void;
503
+ };
504
+ "post.task.completed": {
505
+ event: TaskCompletedEvent;
506
+ modify: never;
507
+ result: void;
508
+ };
509
+ "post.task.failed": {
510
+ event: TaskFailedEvent;
511
+ modify: never;
512
+ result: void;
513
+ };
514
+ "post.task.cancelled": {
515
+ event: TaskEvent;
516
+ modify: never;
517
+ result: void;
518
+ };
519
+ "post.task.superseded": {
520
+ event: TaskSupersededEvent;
521
+ modify: never;
522
+ result: void;
523
+ };
524
+ "post.task.progress": {
525
+ event: TaskProgressEvent;
526
+ modify: never;
527
+ result: void;
528
+ };
529
+ "post.slack.message": {
530
+ event: SlackMessageEvent;
531
+ modify: never;
532
+ result: void;
533
+ };
534
+ "post.tool.call": {
535
+ event: ToolCallCompletedEvent;
536
+ modify: never;
537
+ result: void;
538
+ };
539
+ }
540
+ export interface ExtensionApi<
541
+ M extends ExtensionManifest = ExtensionManifest & {
542
+ runtime: "api";
543
+ },
544
+ > {
545
+ on<E extends keyof SwarmEventMap>(
546
+ event: E,
547
+ handler: (
548
+ event: SwarmEventMap[E]["event"],
549
+ ctx: CtxFor<M>,
550
+ ) => Promise<SwarmEventMap[E]["result"]> | SwarmEventMap[E]["result"],
551
+ opts?: {
552
+ priority?: number;
553
+ },
554
+ ): void;
555
+ }
556
+ export type SwarmExtension<
557
+ M extends ExtensionManifest = ExtensionManifest & {
558
+ runtime: "api";
559
+ },
560
+ > = (api: ExtensionApi<M>) => void;
561
+ export { block, modify } from "./contract-runtime";
562
+ }
@@ -499,6 +499,19 @@ declare module "swarm-sdk" {
499
499
  offset?: number;
500
500
  }): Promise<unknown>;
501
501
 
502
+ // --- write: extensions ---
503
+ extension_install(args: {
504
+ manifest: Record<string, unknown>;
505
+ files: Record<string, string>;
506
+ priority?: number;
507
+ config?: Record<string, unknown>;
508
+ }): Promise<unknown>;
509
+ extension_list(args?: { enabledOnly?: boolean }): Promise<unknown>;
510
+ extension_delete(args: { id: string }): Promise<unknown>;
511
+ extension_enable(args: { id: string }): Promise<unknown>;
512
+ extension_disable(args: { id: string }): Promise<unknown>;
513
+ extension_activate_version(args: { id: string; version: number }): Promise<unknown>;
514
+
502
515
  // --- write: repos ---
503
516
  repo_update(args: Record<string, unknown>): Promise<unknown>;
504
517
 
package/src/server.ts CHANGED
@@ -30,6 +30,12 @@ import { registerDbQueryTool } from "./tools/db-query";
30
30
  import { registerDeferTaskTool } from "./tools/defer-task";
31
31
  import { registerDeleteChannelTool } from "./tools/delete-channel";
32
32
  import { registerDeletePageTool } from "./tools/delete-page";
33
+ import { registerExtensionActivateVersionTool } from "./tools/extension-activate-version";
34
+ import { registerExtensionDeleteTool } from "./tools/extension-delete";
35
+ import { registerExtensionDisableTool } from "./tools/extension-disable";
36
+ import { registerExtensionEnableTool } from "./tools/extension-enable";
37
+ import { registerExtensionInstallTool } from "./tools/extension-install";
38
+ import { registerExtensionListTool } from "./tools/extension-list";
33
39
  import { registerGetMetricsTool } from "./tools/get-metrics";
34
40
  import { registerGetSwarmTool } from "./tools/get-swarm";
35
41
  import { registerGetTaskDetailsTool } from "./tools/get-task-details";
@@ -338,6 +344,12 @@ export async function createServer(
338
344
  registerScriptSearchTool(server);
339
345
  registerScriptRunTool(server);
340
346
  registerScriptUpsertTool(server);
347
+ registerExtensionDeleteTool(server);
348
+ registerExtensionEnableTool(server);
349
+ registerExtensionDisableTool(server);
350
+ registerExtensionActivateVersionTool(server);
351
+ registerExtensionInstallTool(server);
352
+ registerExtensionListTool(server);
341
353
  registerScriptDeleteTool(server);
342
354
  registerScriptQueryTypesTool(server);
343
355
  registerScriptRunsTools(server);
@@ -398,6 +410,12 @@ export async function createServer(
398
410
  registerScriptApisTool(server);
399
411
  registerScriptRunTool(server);
400
412
  registerScriptUpsertTool(server);
413
+ registerExtensionDeleteTool(server);
414
+ registerExtensionEnableTool(server);
415
+ registerExtensionDisableTool(server);
416
+ registerExtensionActivateVersionTool(server);
417
+ registerExtensionInstallTool(server);
418
+ registerExtensionListTool(server);
401
419
  registerScriptDeleteTool(server);
402
420
  registerScriptQueryTypesTool(server);
403
421
  registerScriptRunsTools(server);
@@ -102,7 +102,7 @@ When the additive buffer flushes normally (not via `!now`), the created task use
102
102
  | `ADDITIVE_SLACK` | `false` | Enables non-mention thread message buffering and batching |
103
103
  | `ADDITIVE_SLACK_BUFFER_MS` | `10000` (10s) | Debounce window in milliseconds for the thread buffer |
104
104
  | `SLACK_THREAD_FOLLOWUP_REQUIRE_MENTION` | `false` | Requires @mention for thread follow-up routing; non-mention thread messages are silently dropped |
105
- | `SLACK_THREAD_STEERING` | `off` | `off` preserves follow-up tasks; `lead` steers the latest in-progress lead task; `all` targets the latest active task |
105
+ | `SLACK_THREAD_STEERING` | `lead` | `off` preserves follow-up tasks; `lead` steers the latest in-progress lead task; `all` targets the latest active task |
106
106
  | `SLACK_THREAD_STEERING_MODE` | `queue` | `queue` appends context; `steer` requests immediate steering and degrades to queue when unsupported |
107
107
 
108
108
  Both are read from environment variables. `ADDITIVE_SLACK` must be exactly `"true"` to enable. `ADDITIVE_SLACK_BUFFER_MS` is parsed as a number with fallback to 10000.
@@ -82,23 +82,27 @@ export function registerActionHandlers(app: App): void {
82
82
  sampleEventType: "view_submission",
83
83
  sampleContext: view.callback_id || "follow_up_submit",
84
84
  });
85
- const followUpTask = await createTaskWithSiblingAwareness(followUpText, {
86
- agentId: lead?.id,
87
- routingReason: lead ? "skill" : undefined,
88
- routingSource: lead ? "engine_default" : undefined,
89
- source: "slack",
90
- parentTaskId: taskId,
91
- slackChannelId: originalTask.slackChannelId,
92
- slackThreadTs: originalTask.slackThreadTs,
93
- slackUserId: body.user.id,
94
- requestedByUserId,
95
- contextKey: originalTask.slackThreadTs
96
- ? slackContextKey({
97
- channelId: originalTask.slackChannelId,
98
- threadTs: originalTask.slackThreadTs,
99
- })
100
- : undefined,
101
- });
85
+ const followUpTask = await createTaskWithSiblingAwareness(
86
+ followUpText,
87
+ {
88
+ agentId: lead?.id,
89
+ routingReason: lead ? "skill" : undefined,
90
+ routingSource: lead ? "engine_default" : undefined,
91
+ source: "slack",
92
+ parentTaskId: taskId,
93
+ slackChannelId: originalTask.slackChannelId,
94
+ slackThreadTs: originalTask.slackThreadTs,
95
+ slackUserId: body.user.id,
96
+ requestedByUserId,
97
+ contextKey: originalTask.slackThreadTs
98
+ ? slackContextKey({
99
+ channelId: originalTask.slackChannelId,
100
+ threadTs: originalTask.slackThreadTs,
101
+ })
102
+ : undefined,
103
+ },
104
+ { origin: "slack" },
105
+ );
102
106
 
103
107
  if (isSlackRenderV2Enabled()) {
104
108
  await ensureSlackThreadTree([followUpTask.id]);
@@ -555,7 +555,7 @@ export function buildTreeBlocks(roots: TreeNode[]): SlackBlock[] {
555
555
  );
556
556
 
557
557
  const treeTexts = roots.map(renderTree);
558
- const blocks: SlackBlock[] = [sectionBlock(treeTexts.join("\n\n"))];
558
+ const blocks: SlackBlock[] = treeTexts.length > 0 ? [sectionBlock(treeTexts.join("\n\n"))] : [];
559
559
 
560
560
  // Attachment blocks for completed nodes, with per-tree-message cap.
561
561
  const attachmentNodes = collectAttachmentNodes(roots);