@desplega.ai/agent-swarm 1.149.1 → 1.151.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 (538) hide show
  1. package/README.md +18 -3
  2. package/dist/{acp-adapter-vhkzfpjg.js → acp-adapter-ex8hzjfc.js} +5 -5
  3. package/dist/{actions-9d2694yk.js → actions-2bgpyhww.js} +39 -11
  4. package/dist/{app-fq2ggynt.js → app-1706zhr1.js} +7 -7
  5. package/dist/{artifact-gj9arwrr.js → artifact-td4q4dch.js} +1 -1
  6. package/dist/{assistant-edb6n9nn.js → assistant-bavke5p7.js} +44 -20
  7. package/dist/{azure-openai-responses-cqar64rc.js → azure-openai-responses-f38x2avq.js} +13 -13
  8. package/dist/{boot-reembed-msweabbp.js → boot-reembed-3cmrrmpa.js} +9 -9
  9. package/dist/{boot-reembed-q5wd3tha.js → boot-reembed-nafcmmmc.js} +14 -16
  10. package/dist/{boot-scrub-logs-3g1az933.js → boot-scrub-logs-cd6a2sq0.js} +5 -5
  11. package/dist/{claude-adapter-v3rnzsdc.js → claude-adapter-wfp2gzvz.js} +15 -16
  12. package/dist/{claude-managed-adapter-qvsmh6mv.js → claude-managed-adapter-dsmkcrjy.js} +3 -3
  13. package/dist/{claude-sdk-session-qxrpsna1.js → claude-sdk-session-bht9jjak.js} +15 -16
  14. package/dist/{cli-5m0qmvdp.js → cli-0qb1z6s2.js} +1 -1
  15. package/dist/{cli-9d8ntetj.js → cli-2658f6at.js} +1 -1
  16. package/dist/{cli-7evafqvy.js → cli-2tbvf1z1.js} +85 -69
  17. package/dist/{cli-k351hv5k.js → cli-3v9xhjcn.js} +24 -12
  18. package/dist/{cli-1ebpar1q.js → cli-4wppfkcm.js} +350 -20
  19. package/dist/{cli-4260r1tg.js → cli-5jd2z7av.js} +97 -84
  20. package/dist/{cli-cv7s5zdq.js → cli-5tpgq60f.js} +9865 -715
  21. package/dist/{cli-6skz3rk8.js → cli-7tgcstn2.js} +9 -4
  22. package/dist/{cli-01g3x1ca.js → cli-9004vfdf.js} +108 -56
  23. package/dist/{cli-1c94wvcw.js → cli-a1z3h107.js} +124 -99
  24. package/dist/{cli-9br8ypfv.js → cli-czjtgfys.js} +2 -2
  25. package/dist/{cli-5my3bjsd.js → cli-d3p82erb.js} +7 -7
  26. package/dist/{cli-e29gmdmw.js → cli-dv6cdjs2.js} +2 -1
  27. package/dist/{cli-wby1xnnb.js → cli-f1wky6zp.js} +1 -1
  28. package/dist/{cli-zvz62chv.js → cli-fm4ytsdk.js} +4 -4
  29. package/dist/{cli-tg8qj8ee.js → cli-frcta2w4.js} +6 -6
  30. package/dist/{cli-7xx10h0a.js → cli-g69fdc9g.js} +2 -2
  31. package/dist/{cli-cmp8g3sw.js → cli-gbfwcd3k.js} +44 -9
  32. package/dist/{handlers-nsg0bcye.js → cli-gjyvhmmh.js} +97 -45
  33. package/dist/{cli-08b07b4r.js → cli-ht5rs63n.js} +1 -1
  34. package/dist/{cli-jecc01cb.js → cli-j4nv2wqy.js} +2 -4
  35. package/dist/{cli-cd60fv6j.js → cli-j9xczhaq.js} +1 -1
  36. package/dist/cli-pf9q3696.js +172 -0
  37. package/dist/{cli-xm87hzbw.js → cli-pgj4a5fb.js} +1 -1
  38. package/dist/{cli-jezx9b9c.js → cli-ppm4975n.js} +9 -3
  39. package/dist/{cli-3z634pqe.js → cli-q88st9aj.js} +2 -2
  40. package/dist/{cli-3sysyjve.js → cli-r2q6pevn.js} +142 -49
  41. package/dist/{cli-07jd68a1.js → cli-re8z5p8p.js} +1 -1
  42. package/dist/{cli-qgd4sqz5.js → cli-s14agm5s.js} +121 -14
  43. package/dist/{cli-svbrpf5q.js → cli-sjaae6ff.js} +9872 -424
  44. package/dist/{cli-73gq0zxn.js → cli-vwm416m6.js} +13 -13
  45. package/dist/{cli-4wtq5jjv.js → cli-wbyk465z.js} +1 -1
  46. package/dist/{cli-3apbzgyk.js → cli-z61ykvam.js} +19 -14
  47. package/dist/{cli-e06a8a7y.js → cli-zkxymas5.js} +4 -3
  48. package/dist/cli.js +26 -25
  49. package/dist/{codex-adapter-jyw7a91z.js → codex-adapter-jjjk8w4m.js} +8 -9
  50. package/dist/{codex-hook-hfrt9shr.js → codex-hook-8jexc34x.js} +3 -3
  51. package/dist/{codex-session-runner-bmngj0s5.js → codex-session-runner-nd6ayxkx.js} +8 -9
  52. package/dist/{commands-n755g391.js → commands-9v4psyq1.js} +5 -5
  53. package/dist/{db-s1cgh5gv.js → db-wnt58tpe.js} +5 -5
  54. package/dist/dispatcher-05jh1aqk.js +74 -0
  55. package/dist/{e2b-p04vmtx0.js → e2b-413b8t4v.js} +1 -1
  56. package/dist/{google-generative-ai-tsskx7te.js → google-generative-ai-ndjmj5fa.js} +7 -7
  57. package/dist/{google-vertex-ppw8ydpa.js → google-vertex-fr665tqs.js} +10 -10
  58. package/dist/handlers-tw90pkh5.js +65 -0
  59. package/dist/{hook-jf0xe8zh.js → hook-123e2scb.js} +11 -12
  60. package/dist/{hook-h9cqrm8q.js → hook-sxe3r349.js} +11 -12
  61. package/dist/{http-nc6pydw5.js → http-ecsar8ez.js} +1442 -581
  62. package/dist/{index-ezxkds2n.js → index-16js521z.js} +335 -107
  63. package/dist/{index-769nc012.js → index-hw26vj40.js} +25 -19
  64. package/dist/{index-0wdy6gwc.js → index-nnrbpcjp.js} +25 -17
  65. package/dist/index-ymn7em0p.js +25 -0
  66. package/dist/{index-x1zcj09z.js → index-zj518gf9.js} +26 -20
  67. package/dist/{keepalive-nesm3xb6.js → keepalive-asqtcm4y.js} +7 -7
  68. package/dist/{lead-bwy6t2za.js → lead-xbfr7fvt.js} +40 -48
  69. package/dist/{maintenance-8w393zf0.js → maintenance-qma55qvw.js} +9 -9
  70. package/dist/{oauth-refresh-sweep-sh8xjy08.js → oauth-refresh-sweep-3s04c6qw.js} +6 -6
  71. package/dist/{onboard-25ps29h1.js → onboard-drskd386.js} +13 -4
  72. package/dist/{openai-codex-responses-g7w48y30.js → openai-codex-responses-wy6a43wd.js} +13 -13
  73. package/dist/{openai-responses-nmeyptke.js → openai-responses-6xwf7bzg.js} +19 -19
  74. package/dist/{opencode-adapter-dce4wskd.js → opencode-adapter-28axqa09.js} +4 -4
  75. package/dist/{otel-impl-b93gxnz5.js → otel-impl-943c96er.js} +1 -1
  76. package/dist/{pi-mono-adapter-zvshk5cj.js → pi-mono-adapter-jcqt71cg.js} +17 -19
  77. package/dist/{pricing-refresh-5v56x7gb.js → pricing-refresh-mx51az05.js} +7 -7
  78. package/dist/{rbac-roles-x55cnezn.js → rbac-roles-78baecqd.js} +13 -9
  79. package/dist/{rbac-roles-0p0s6pbj.js → rbac-roles-wcmrg6sv.js} +7 -7
  80. package/dist/{render-v2-hyd45w8b.js → render-v2-8zjntgf5.js} +7 -7
  81. package/dist/{seed-pricing-49gbgn96.js → seed-pricing-jqjs6cws.js} +6 -6
  82. package/dist/{setup-qvq64vzq.js → setup-4pks0vfk.js} +2 -2
  83. package/dist/work-objects-wangt792.js +165 -0
  84. package/dist/{worker-scj60cxg.js → worker-p2mr422x.js} +40 -48
  85. package/openapi.json +1365 -268
  86. package/package.json +4 -3
  87. package/src/agentmail/handlers.ts +95 -66
  88. package/src/be/budget-refusal-notify.ts +1 -0
  89. package/src/be/db/tasks/read.ts +3 -0
  90. package/src/be/db/tasks/write.ts +13 -1
  91. package/src/be/db-client.ts +5 -0
  92. package/src/be/db.ts +15 -5
  93. package/src/be/extensions/db.ts +461 -0
  94. package/src/be/extensions/validate.ts +107 -0
  95. package/src/be/memory/boot-reembed.ts +17 -12
  96. package/src/be/memory/index.ts +12 -0
  97. package/src/be/memory/providers/openai-embedding.ts +4 -0
  98. package/src/be/memory/types.ts +2 -0
  99. package/src/be/migrations/153_deferred_task_wait_members.sql +25 -0
  100. package/src/be/migrations/154_routing_source.sql +3 -0
  101. package/src/be/migrations/155_extensions.sql +66 -0
  102. package/src/be/migrations/156_inbox_item_state_read_at.sql +7 -0
  103. package/src/be/rbac-roles.ts +3 -2
  104. package/src/be/scripts/typecheck.ts +59 -41
  105. package/src/be/seed/schedules-seeder.ts +25 -2
  106. package/src/be/seed/workflows-seeder.ts +17 -0
  107. package/src/be/seed-scripts/catalog/delegate.ts +3 -1
  108. package/src/be/seed-skills/bundled-files.generated.json +1 -1
  109. package/src/be/seed-skills/index.ts +7 -0
  110. package/src/be/steering.ts +1 -0
  111. package/src/be/swarm-config-guard.ts +3 -1
  112. package/src/commands/credential-refresh.ts +70 -0
  113. package/src/commands/onboard/compose-generator.ts +14 -2
  114. package/src/commands/onboard/env-generator.ts +7 -0
  115. package/src/commands/provider-credentials.ts +2 -25
  116. package/src/commands/runner.ts +24 -57
  117. package/src/extensions/apply-task-create.ts +135 -0
  118. package/src/extensions/bundle-path.ts +10 -0
  119. package/src/extensions/contract-runtime.ts +7 -0
  120. package/src/extensions/contract-types.generated.ts +4 -0
  121. package/src/extensions/contract.ts +270 -0
  122. package/src/extensions/ctx.ts +186 -0
  123. package/src/extensions/dispatcher.ts +479 -0
  124. package/src/extensions/identity.ts +44 -0
  125. package/src/extensions/lifecycle.ts +281 -0
  126. package/src/extensions/loader.ts +207 -0
  127. package/src/extensions/post-bridge.ts +161 -0
  128. package/src/github/handlers.ts +185 -140
  129. package/src/gitlab/handlers.ts +97 -77
  130. package/src/heartbeat/heartbeat.ts +155 -83
  131. package/src/http/agents.ts +1 -1
  132. package/src/http/all-routes.ts +1 -0
  133. package/src/http/approval-requests.ts +1 -0
  134. package/src/http/apps.ts +14 -9
  135. package/src/http/config.ts +4 -1
  136. package/src/http/core.ts +38 -2
  137. package/src/http/extensions.ts +547 -0
  138. package/src/http/inbox-state.ts +2 -0
  139. package/src/http/index.ts +41 -3
  140. package/src/http/mcp-bridge.ts +96 -62
  141. package/src/http/mcp.ts +1 -1
  142. package/src/http/script-runs.ts +5 -0
  143. package/src/http/status.ts +6 -4
  144. package/src/http/tasks.ts +51 -30
  145. package/src/integrations/kapso/inbound.ts +15 -10
  146. package/src/jira/sync.ts +26 -16
  147. package/src/linear/sync.ts +26 -16
  148. package/src/prompts/session-templates.ts +18 -13
  149. package/src/providers/claude-adapter.ts +21 -8
  150. package/src/rbac/admission.ts +1 -1
  151. package/src/rbac/elevated-agents.ts +24 -0
  152. package/src/rbac/legacy-policy.ts +35 -6
  153. package/src/rbac/permissions.ts +8 -0
  154. package/src/scheduler/deferred-task-waits.ts +138 -27
  155. package/src/scheduler/schedule-task.ts +54 -24
  156. package/src/scheduler/scheduler.ts +108 -125
  157. package/src/scripts-runtime/executors/native.ts +5 -1
  158. package/src/scripts-runtime/executors/types.ts +5 -0
  159. package/src/scripts-runtime/import-allowlist.ts +79 -19
  160. package/src/scripts-runtime/sdk-allowlist.ts +4 -0
  161. package/src/scripts-runtime/swarm-config.ts +8 -0
  162. package/src/scripts-runtime/swarm-sdk.ts +3 -0
  163. package/src/scripts-runtime/types/stdlib.d.ts +16 -3
  164. package/src/scripts-runtime/types/swarm-extension.d.ts +562 -0
  165. package/src/scripts-runtime/types/swarm-sdk.d.ts +16 -3
  166. package/src/server.ts +6 -0
  167. package/src/slack/HEURISTICS.md +3 -1
  168. package/src/slack/actions.ts +21 -16
  169. package/src/slack/app.ts +2 -0
  170. package/src/slack/assistant.ts +4 -12
  171. package/src/slack/blocks.ts +1 -1
  172. package/src/slack/handlers.ts +107 -19
  173. package/src/slack/inbound-files.ts +9 -5
  174. package/src/slack/render-v2.ts +188 -38
  175. package/src/slack/router.ts +13 -3
  176. package/src/slack/steering.ts +3 -3
  177. package/src/slack/thread-buffer.ts +66 -23
  178. package/src/slack/work-objects.test.ts +252 -0
  179. package/src/slack/work-objects.ts +118 -0
  180. package/src/tasks/errors.ts +9 -0
  181. package/src/tasks/sibling-awareness.ts +35 -1
  182. package/src/tasks/worker-follow-up.ts +67 -11
  183. package/src/tests/acp-adapter.test.ts +10 -10
  184. package/src/tests/agent-fs-live-url.test.ts +40 -0
  185. package/src/tests/agent-fs-provision-seeder.test.ts +12 -12
  186. package/src/tests/api-key-tracking.test.ts +18 -18
  187. package/src/tests/api-key.test.ts +4 -2
  188. package/src/tests/apps-rbac.test.ts +1 -1
  189. package/src/tests/apps-spike5.test.ts +1 -1
  190. package/src/tests/apps-sync-engine.test.ts +8 -8
  191. package/src/tests/apps-user-config.test.ts +1 -1
  192. package/src/tests/artifact-sdk.test.ts +8 -8
  193. package/src/tests/asset-key-mcp.test.ts +2 -0
  194. package/src/tests/automation-preflight-alert.test.ts +1 -1
  195. package/src/tests/base-prompt.test.ts +7 -6
  196. package/src/tests/budget-user-scope.test.ts +1 -1
  197. package/src/tests/budgets-routes.test.ts +1 -1
  198. package/src/tests/claude-adapter-binary.test.ts +22 -40
  199. package/src/tests/claude-adapter-otel.test.ts +13 -7
  200. package/src/tests/claude-adapter.test.ts +8 -6
  201. package/src/tests/claude-hook-credentials.test.ts +3 -3
  202. package/src/tests/claude-managed-adapter.test.ts +4 -4
  203. package/src/tests/claude-managed-setup.test.ts +3 -3
  204. package/src/tests/claude-sdk-lifecycle.test.ts +1 -1
  205. package/src/tests/claude-sdk-transport.test.ts +6 -6
  206. package/src/tests/claude-stop-hook.test.ts +8 -8
  207. package/src/tests/claude-worker-parity.test.ts +3 -3
  208. package/src/tests/codex-adapter-pool-auth-revalidate.test.ts +5 -5
  209. package/src/tests/codex-adapter.test.ts +7 -5
  210. package/src/tests/codex-app-server.test.ts +2 -2
  211. package/src/tests/codex-login.test.ts +15 -15
  212. package/src/tests/codex-oauth-credential-info.test.ts +4 -4
  213. package/src/tests/codex-oauth-keep-warm.test.ts +4 -4
  214. package/src/tests/codex-oauth-refresh-lock.test.ts +1 -1
  215. package/src/tests/codex-oauth-storage.test.ts +7 -7
  216. package/src/tests/codex-pool.test.ts +1 -1
  217. package/src/tests/codex-steering-hook.test.ts +1 -1
  218. package/src/tests/codex-swarm-events.test.ts +1 -1
  219. package/src/tests/config-api-only-keys.test.ts +1 -1
  220. package/src/tests/config-session-auth.test.ts +2 -2
  221. package/src/tests/connection-embedded-auth.test.ts +20 -18
  222. package/src/tests/core-auth.test.ts +1 -1
  223. package/src/tests/create-page-tool.test.ts +3 -3
  224. package/src/tests/credential-refresh.test.ts +115 -0
  225. package/src/tests/credential-wait.test.ts +2 -2
  226. package/src/tests/credentials.test.ts +6 -4
  227. package/src/tests/db-queries-oauth.test.ts +40 -40
  228. package/src/tests/defer-task.test.ts +73 -8
  229. package/src/tests/deferred-task-wait-migration.test.ts +64 -0
  230. package/src/tests/deferred-task-wake.test.ts +349 -8
  231. package/src/tests/devin-adapter.test.ts +1 -1
  232. package/src/tests/devin-api.test.ts +1 -1
  233. package/src/tests/docs-index-footprint.test.js +3 -3
  234. package/src/tests/e2b-dispatch.test.ts +21 -19
  235. package/src/tests/e2e-claude-transport.test.ts +3 -3
  236. package/src/tests/e2e-redact.test.ts +6 -6
  237. package/src/tests/ensure-token.test.ts +68 -68
  238. package/src/tests/entrypoint-api-readiness.test.ts +1 -1
  239. package/src/tests/entrypoint-codex-oauth-seed.test.ts +13 -13
  240. package/src/tests/env-flag.test.ts +1 -0
  241. package/src/tests/events-http.test.ts +1 -1
  242. package/src/tests/extensions-db.test.ts +216 -0
  243. package/src/tests/extensions-dispatcher.test.ts +275 -0
  244. package/src/tests/extensions-example-heartbeat-remediate.test.ts +69 -0
  245. package/src/tests/extensions-example-mcp-install.test.ts +63 -0
  246. package/src/tests/extensions-example-no-exclamation-marks.test.ts +72 -0
  247. package/src/tests/extensions-example-rewrite-and-suppress.test.ts +70 -0
  248. package/src/tests/extensions-example-route-slack-channel.test.ts +217 -0
  249. package/src/tests/extensions-heartbeat.test.ts +291 -0
  250. package/src/tests/extensions-http.test.ts +431 -0
  251. package/src/tests/extensions-lead-equivalence.test.ts +49 -0
  252. package/src/tests/extensions-lifecycle.test.ts +209 -0
  253. package/src/tests/extensions-loader.test.ts +95 -0
  254. package/src/tests/extensions-mcp-tools.test.ts +245 -0
  255. package/src/tests/extensions-pre-task.test.ts +487 -0
  256. package/src/tests/extensions-run-context.test.ts +41 -0
  257. package/src/tests/extensions-tool-call.test.ts +288 -0
  258. package/src/tests/extensions-validate.test.ts +196 -0
  259. package/src/tests/fixtures/extensions/bad-import/hooks.ts +8 -0
  260. package/src/tests/fixtures/extensions/bad-import/manifest.json +9 -0
  261. package/src/tests/fixtures/extensions/bad-return-shape/hooks.ts +10 -0
  262. package/src/tests/fixtures/extensions/bad-return-shape/manifest.json +9 -0
  263. package/src/tests/fixtures/extensions/block-tasks-from-source/hooks.ts +22 -0
  264. package/src/tests/fixtures/extensions/block-tasks-from-source/manifest.json +9 -0
  265. package/src/tests/fixtures/extensions/ignore-channel/hooks.ts +22 -0
  266. package/src/tests/fixtures/extensions/ignore-channel/manifest.json +9 -0
  267. package/src/tests/fixtures/extensions/load.ts +10 -0
  268. package/src/tests/fixtures/extensions/minimal/hooks.ts +7 -0
  269. package/src/tests/fixtures/extensions/minimal/manifest.json +9 -0
  270. package/src/tests/fixtures/extensions/never-fail-long-tasks/hooks.ts +11 -0
  271. package/src/tests/fixtures/extensions/never-fail-long-tasks/manifest.json +9 -0
  272. package/src/tests/fixtures/extensions/no-exclamation-marks/hooks.ts +23 -0
  273. package/src/tests/fixtures/extensions/no-exclamation-marks/manifest.json +9 -0
  274. package/src/tests/fixtures/extensions/post-logger/hooks.ts +9 -0
  275. package/src/tests/fixtures/extensions/post-logger/manifest.json +9 -0
  276. package/src/tests/fixtures/extensions/priority-a/hooks.ts +7 -0
  277. package/src/tests/fixtures/extensions/priority-a/manifest.json +9 -0
  278. package/src/tests/fixtures/extensions/priority-b/hooks.ts +7 -0
  279. package/src/tests/fixtures/extensions/priority-b/manifest.json +9 -0
  280. package/src/tests/fixtures/extensions/record-only-on-tag/hooks.ts +16 -0
  281. package/src/tests/fixtures/extensions/record-only-on-tag/manifest.json +9 -0
  282. package/src/tests/fixtures/extensions/reserved-assets/hooks.ts +5 -0
  283. package/src/tests/fixtures/extensions/reserved-assets/manifest.json +10 -0
  284. package/src/tests/fixtures/extensions/rewrite-progress-prefix/hooks.ts +22 -0
  285. package/src/tests/fixtures/extensions/rewrite-progress-prefix/manifest.json +9 -0
  286. package/src/tests/fixtures/extensions/rewrite-task-priority/hooks.ts +7 -0
  287. package/src/tests/fixtures/extensions/rewrite-task-priority/manifest.json +9 -0
  288. package/src/tests/fixtures/extensions/route-channel-to-agent/hooks.ts +26 -0
  289. package/src/tests/fixtures/extensions/route-channel-to-agent/manifest.json +9 -0
  290. package/src/tests/fixtures/extensions/slow/hooks.ts +10 -0
  291. package/src/tests/fixtures/extensions/slow/manifest.json +9 -0
  292. package/src/tests/fixtures/extensions/suppress-lead-follow-up/hooks.ts +12 -0
  293. package/src/tests/fixtures/extensions/suppress-lead-follow-up/manifest.json +9 -0
  294. package/src/tests/fixtures/extensions/throws/hooks.ts +12 -0
  295. package/src/tests/fixtures/extensions/throws/manifest.json +9 -0
  296. package/src/tests/fixtures/extensions/worker-runtime/hooks.ts +5 -0
  297. package/src/tests/fixtures/extensions/worker-runtime/manifest.json +9 -0
  298. package/src/tests/fs-routes.test.ts +1 -1
  299. package/src/tests/gitlab-auth.test.ts +10 -10
  300. package/src/tests/harness-provider-resolution.test.ts +8 -5
  301. package/src/tests/heartbeat-reroute-decision.test.ts +1 -0
  302. package/src/tests/helper-credentials.test.ts +137 -0
  303. package/src/tests/http/context-routes.test.ts +2 -2
  304. package/src/tests/http-api-integration.test.ts +5 -2
  305. package/src/tests/http-users.test.ts +1 -1
  306. package/src/tests/identity-refresh.test.ts +2 -2
  307. package/src/tests/internal-ai/complete-structured.test.ts +2 -2
  308. package/src/tests/internal-ai/credentials.test.ts +19 -17
  309. package/src/tests/jira-metadata.test.ts +1 -1
  310. package/src/tests/jira-oauth.test.ts +5 -5
  311. package/src/tests/jira-sync.test.ts +1 -1
  312. package/src/tests/jira-webhook-lifecycle.test.ts +3 -3
  313. package/src/tests/jira-webhook.test.ts +2 -2
  314. package/src/tests/kapso-inbound.test.ts +2 -2
  315. package/src/tests/kv-http.test.ts +1 -1
  316. package/src/tests/kv-namespace-resolution.test.ts +1 -1
  317. package/src/tests/kv-page-proxy.test.ts +2 -2
  318. package/src/tests/launch-password-rejection.test.ts +2 -2
  319. package/src/tests/linear-webhook.test.ts +1 -1
  320. package/src/tests/mcp-oauth-dcr-reuse.test.ts +6 -6
  321. package/src/tests/mcp-oauth-ensure-token.test.ts +28 -22
  322. package/src/tests/mcp-oauth-manual-client.test.ts +16 -12
  323. package/src/tests/mcp-oauth-queries.test.ts +35 -32
  324. package/src/tests/mcp-oauth-resolve-secrets.test.ts +8 -8
  325. package/src/tests/mcp-oauth-wrapper.test.ts +20 -20
  326. package/src/tests/mcp-user-route.test.ts +1 -1
  327. package/src/tests/memory-edges.test.ts +1 -1
  328. package/src/tests/memory-edit-authorization.test.ts +32 -0
  329. package/src/tests/memory-rate-endpoint.test.ts +1 -1
  330. package/src/tests/memory-rate-tool.test.ts +2 -2
  331. package/src/tests/memory-rater-e2e.test.ts +1 -1
  332. package/src/tests/memory-rater-llm-summarizer.test.ts +3 -3
  333. package/src/tests/memory-rater-llm.test.ts +1 -1
  334. package/src/tests/model-control.test.ts +3 -0
  335. package/src/tests/multi-runtime-enabled-stats.test.ts +2 -2
  336. package/src/tests/multi-runtime-registration.test.ts +111 -17
  337. package/src/tests/oauth-access-token-tool.test.ts +18 -18
  338. package/src/tests/oauth-callback-flow.test.ts +8 -8
  339. package/src/tests/oauth-credential-bindings.test.ts +29 -27
  340. package/src/tests/oauth-keepalive.test.ts +10 -10
  341. package/src/tests/oauth-presets.test.ts +13 -5
  342. package/src/tests/oauth-refresh-failure.test.ts +9 -9
  343. package/src/tests/oauth-refresh-sweep.test.ts +20 -13
  344. package/src/tests/oauth-wrapper.test.ts +1 -1
  345. package/src/tests/onboard-compose.test.ts +47 -3
  346. package/src/tests/onboard-env.test.ts +26 -26
  347. package/src/tests/onboard-non-interactive.test.ts +4 -4
  348. package/src/tests/opencode-adapter.test.ts +1 -1
  349. package/src/tests/opencode-plugin.test.ts +2 -2
  350. package/src/tests/otel-session-cost-metrics.test.ts +1 -1
  351. package/src/tests/page-proxy-authed.test.ts +2 -2
  352. package/src/tests/page-proxy.test.ts +2 -2
  353. package/src/tests/page-session.test.ts +7 -7
  354. package/src/tests/page-viewer-auth.test.ts +2 -2
  355. package/src/tests/pages-authed-mode.test.ts +1 -1
  356. package/src/tests/pages-http.test.ts +1 -1
  357. package/src/tests/pages-password-mode.test.ts +1 -1
  358. package/src/tests/pages-public-authed-401.test.ts +1 -1
  359. package/src/tests/pages-public-html.test.ts +2 -2
  360. package/src/tests/pages-view-count.test.ts +1 -1
  361. package/src/tests/pi-mono-adapter.test.ts +10 -10
  362. package/src/tests/pi-mono-extension.test.ts +2 -2
  363. package/src/tests/preload.ts +5 -0
  364. package/src/tests/pricing-routes.test.ts +1 -1
  365. package/src/tests/profile-sync-warning.test.ts +8 -8
  366. package/src/tests/profile-sync.test.ts +8 -8
  367. package/src/tests/prompt-template-session.test.ts +36 -5
  368. package/src/tests/provider-process-group.test.ts +95 -4
  369. package/src/tests/providers/pi-cost.test.ts +1 -1
  370. package/src/tests/queue-stall-alarm.test.ts +1 -1
  371. package/src/tests/rbac-admission.test.ts +19 -4
  372. package/src/tests/rbac-charact-http.test.ts +1 -1
  373. package/src/tests/rbac-e2e-helpers.ts +1 -1
  374. package/src/tests/rbac-engine.test.ts +39 -0
  375. package/src/tests/realtime-room-auth.test.ts +1 -1
  376. package/src/tests/realtime-room-surfaces.test.ts +1 -1
  377. package/src/tests/reload-config.test.ts +45 -1
  378. package/src/tests/routing-affinity-inventory.test.ts +1 -1
  379. package/src/tests/routing-reason-contract.test.ts +76 -3
  380. package/src/tests/routing-reason-inventory.test.ts +11 -6
  381. package/src/tests/routing-source.test.ts +53 -0
  382. package/src/tests/runner-agent-fs-provision-retry.test.ts +1 -1
  383. package/src/tests/runner-context-preamble.test.ts +1 -1
  384. package/src/tests/runner-deployment-resume.test.ts +1 -1
  385. package/src/tests/runner-fallback-output.test.ts +1 -1
  386. package/src/tests/runner-identity-refresh.test.ts +1 -1
  387. package/src/tests/runner-skills-refresh.test.ts +1 -1
  388. package/src/tests/schedule-target-type.test.ts +3 -3
  389. package/src/tests/scheduled-tasks.test.ts +14 -5
  390. package/src/tests/scheduler-occurrence.test.ts +347 -0
  391. package/src/tests/script-apis-mcp.test.ts +1 -1
  392. package/src/tests/script-connections-http.test.ts +61 -58
  393. package/src/tests/script-connections-mcp.test.ts +1 -1
  394. package/src/tests/script-connections.test.ts +3 -1
  395. package/src/tests/script-runs-http.test.ts +15 -7
  396. package/src/tests/script-workflows-runtime-e2e.test.ts +1 -1
  397. package/src/tests/scripts-boot-reembed.test.ts +4 -0
  398. package/src/tests/scripts-embeddings.test.ts +4 -0
  399. package/src/tests/scripts-external-api.test.ts +1 -1
  400. package/src/tests/scripts-http.test.ts +1 -1
  401. package/src/tests/scripts-import-allowlist.test.ts +44 -1
  402. package/src/tests/scripts-mcp-e2e.test.ts +81 -1
  403. package/src/tests/scripts-only-gating.test.ts +4 -2
  404. package/src/tests/scripts-retention.test.ts +1 -1
  405. package/src/tests/scripts-runtime-identity.test.ts +2 -2
  406. package/src/tests/scripts-runtime-secret-egress.test.ts +1 -1
  407. package/src/tests/scripts-runtime.test.ts +6 -4
  408. package/src/tests/scripts-typecheck.test.ts +21 -0
  409. package/src/tests/sdk-allowlist.test.ts +1 -1
  410. package/src/tests/secret-scrubber.test.ts +38 -31
  411. package/src/tests/seed-registry.test.ts +46 -8
  412. package/src/tests/seed-schedules.test.ts +44 -9
  413. package/src/tests/seed-workflows.test.ts +21 -9
  414. package/src/tests/send-task-output-schema.test.ts +9 -1
  415. package/src/tests/send-task-requested-by.test.ts +1 -0
  416. package/src/tests/send-task-slack-routing-guard.test.ts +2 -0
  417. package/src/tests/session-costs-codex-recompute.test.ts +1 -1
  418. package/src/tests/session-costs-golden.test.ts +2 -2
  419. package/src/tests/session-costs-model-key-normalize.test.ts +2 -2
  420. package/src/tests/session-costs-recompute-all-providers.test.ts +1 -1
  421. package/src/tests/session-tokens.test.ts +1 -1
  422. package/src/tests/shutdown-error-scrubbing.test.ts +1 -1
  423. package/src/tests/slack-blocks.test.ts +12 -1
  424. package/src/tests/slack-file-tools-attachments.test.ts +1 -1
  425. package/src/tests/slack-inbound-files.test.ts +222 -86
  426. package/src/tests/slack-reaction-config.test.ts +5 -4
  427. package/src/tests/slack-read-boundaries.test.ts +1 -1
  428. package/src/tests/slack-render-v2.test.ts +196 -4
  429. package/src/tests/slack-router.test.ts +17 -2
  430. package/src/tests/slack-steering.test.ts +19 -0
  431. package/src/tests/status.test.ts +19 -7
  432. package/src/tests/steering-core.test.ts +1 -1
  433. package/src/tests/steering-enabled.test.ts +6 -8
  434. package/src/tests/steering-transport.test.ts +4 -4
  435. package/src/tests/store-progress-output-guidance.test.ts +34 -0
  436. package/src/tests/swarm-tool-result-gate.test.ts +11 -11
  437. package/src/tests/swarm-x-tool.test.ts +7 -5
  438. package/src/tests/sync-ai-toolbox-skills.test.ts +19 -0
  439. package/src/tests/task-supersede-resume.test.ts +1 -1
  440. package/src/tests/task-tool-preload-mcp.test.ts +4 -1
  441. package/src/tests/task-tools-ctx.test.ts +2 -0
  442. package/src/tests/tasks-requested-by-filter.test.ts +2 -2
  443. package/src/tests/telemetry-init.test.ts +11 -3
  444. package/src/tests/tool-annotations.test.ts +1 -1
  445. package/src/tests/tool-output-agent-id.test.ts +1 -0
  446. package/src/tests/tool-registrar-no-input.test.ts +42 -1
  447. package/src/tests/tracker-fold.test.ts +7 -7
  448. package/src/tests/tracker-tools.test.ts +3 -3
  449. package/src/tests/ui-followup-lead-delegation.test.ts +1 -0
  450. package/src/tests/unified-oauth-migration.test.ts +5 -5
  451. package/src/tests/user-token-rest-auth.test.ts +1 -1
  452. package/src/tests/user-token-routes.test.ts +1 -1
  453. package/src/tests/whoami-route.test.ts +1 -1
  454. package/src/tests/workflow-agent-task.test.ts +15 -13
  455. package/src/tests/workflow-ctx.test.ts +1 -1
  456. package/src/tests/workflow-e2e.test.ts +1 -1
  457. package/src/tests/workflow-engine-v2.test.ts +3 -1
  458. package/src/tests/workflow-executors.test.ts +2 -2
  459. package/src/tests/workflow-http-v2.test.ts +8 -6
  460. package/src/tests/workflow-input-redaction.test.ts +7 -5
  461. package/src/tests/workflow-integration-io.test.ts +2 -2
  462. package/src/tests/workflow-llm.test.ts +5 -5
  463. package/src/tests/workflow-registry.test.ts +1 -1
  464. package/src/tests/workflow-swarm-script.test.ts +1 -1
  465. package/src/tests/workflow-triggers-v2.test.ts +23 -21
  466. package/src/tests/x-composio.test.ts +9 -7
  467. package/src/tests/x402-client.test.ts +1 -1
  468. package/src/tests/x402-config.test.ts +28 -41
  469. package/src/tools/defer-task.ts +156 -24
  470. package/src/tools/extension-common.ts +84 -0
  471. package/src/tools/extension-install.ts +65 -0
  472. package/src/tools/extension-list.ts +66 -0
  473. package/src/tools/script-common.ts +43 -18
  474. package/src/tools/send-task.ts +87 -105
  475. package/src/tools/skills/skill-publish.ts +1 -0
  476. package/src/tools/store-progress.ts +1 -1
  477. package/src/tools/task-action.ts +43 -22
  478. package/src/tools/task-tool-ctx.ts +2 -0
  479. package/src/tools/templates.ts +2 -1
  480. package/src/tools/tool-config.ts +3 -1
  481. package/src/tools/utils.ts +140 -16
  482. package/src/types.ts +144 -0
  483. package/src/utils/constants.ts +7 -1
  484. package/src/utils/credentials.test.ts +16 -16
  485. package/src/utils/multi-runtime.ts +2 -2
  486. package/src/utils/steering-enabled.ts +4 -4
  487. package/src/workflows/executors/agent-task.ts +18 -3
  488. package/templates/ai-toolbox.manifest.json +7 -1
  489. package/templates/extensions/README.md +17 -0
  490. package/templates/extensions/notify-on-complete/README.md +11 -0
  491. package/templates/extensions/notify-on-complete/hooks.ts +61 -0
  492. package/templates/extensions/notify-on-complete/manifest.json +7 -0
  493. package/templates/extensions/require-ticket-ref/README.md +11 -0
  494. package/templates/extensions/require-ticket-ref/hooks.ts +30 -0
  495. package/templates/extensions/require-ticket-ref/manifest.json +7 -0
  496. package/templates/extensions/require-verification-note/README.md +11 -0
  497. package/templates/extensions/require-verification-note/hooks.ts +32 -0
  498. package/templates/extensions/require-verification-note/manifest.json +7 -0
  499. package/templates/schedules/daily-hn-briefing/config.json +1 -1
  500. package/templates/schedules/gtm-weekly-review/config.json +1 -1
  501. package/templates/schedules/weekly-code-health-reports/config.json +1 -1
  502. package/templates/schedules/weekly-dependabot-triage/config.json +1 -1
  503. package/templates/schedules/weekly-dora-metrics/config.json +1 -1
  504. package/templates/schedules/weekly-harness-upgrade-check/config.json +1 -1
  505. package/templates/skills/delegate-work/files/scripts/codex-exec.sh +1 -1
  506. package/templates/skills/scheduling/content.md +2 -0
  507. package/templates/skills/serply-search/SKILL.md +95 -0
  508. package/templates/skills/serply-search/config.json +13 -0
  509. package/templates/skills/serply-search/content.md +90 -0
  510. package/templates/skills/swarm-extensions/config.json +14 -0
  511. package/templates/skills/swarm-extensions/content.md +120 -0
  512. package/templates/skills/swarm-scripts/SKILL.md +2 -2
  513. package/templates/skills/swarm-scripts/content.md +2 -2
  514. package/templates/workflows/claude-code-changelog-watch/config.json +1 -1
  515. package/templates/workflows/gsc-topic-miner/config.json +1 -1
  516. package/templates/workflows/linear-drain-loop/config.json +1 -1
  517. package/templates/workflows/llm-safe-release-context/config.json +1 -1
  518. package/templates/workflows/ralph-loop/config.json +1 -1
  519. package/dist/cli-7fehefdf.js +0 -562
  520. package/dist/cli-ayvy5kde.js +0 -164
  521. package/dist/cli-ddfw1mc4.js +0 -4322
  522. package/dist/cli-hzqmgcm2.js +0 -3066
  523. package/dist/cli-j4d4f6hv.js +0 -338
  524. package/dist/cli-qgm2pfpq.js +0 -6377
  525. package/dist/cli-sdzq01t9.js +0 -823
  526. package/dist/cli-xvkmedgn.js +0 -332
  527. package/dist/cli-y3pq4v9g.js +0 -664
  528. package/dist/{anthropic-messages-vs2rabzp.js → anthropic-messages-2y8nktar.js} +19 -19
  529. package/dist/{claude-managed-setup-f5pmsyrp.js → claude-managed-setup-48dwg9bv.js} +3 -3
  530. package/dist/{cli-81r683bv.js → cli-ch38q5z4.js} +0 -0
  531. package/dist/{cli-6664w7y4.js → cli-kj76p1ez.js} +8 -8
  532. package/dist/{cli-c71mg51n.js → cli-r2fpe7cx.js} +5 -5
  533. package/dist/{cli-4xz3pmrq.js → cli-y6x0gqtg.js} +16 -16
  534. package/dist/{index-aa3sa6s8.js → index-2wsdsnmm.js} +1 -1
  535. package/dist/{mistral-conversations-4ryk7h83.js → mistral-conversations-qhr84ph3.js} +9 -9
  536. package/dist/{openai-completions-0bqmsajw.js → openai-completions-32rdwmze.js} +19 -19
  537. package/dist/{openrouter-images-qx18fyp8.js → openrouter-images-9f9ek4rw.js} +3 -3
  538. package/dist/{pi-messages-79jvzz1d.js → pi-messages-b1mbr6gp.js} +4 -4
@@ -0,0 +1,487 @@
1
+ import { afterAll, beforeAll, beforeEach, describe, expect, spyOn, test } from "bun:test";
2
+ import type { IncomingMessage, ServerResponse } from "node:http";
3
+ import { Readable } from "node:stream";
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import {
6
+ closeDb,
7
+ completeTask,
8
+ createAgent,
9
+ createScheduledTask,
10
+ createTaskExtended,
11
+ createWorkflow,
12
+ createWorkflowRun,
13
+ createWorkflowRunStep,
14
+ getDbClient,
15
+ getKv,
16
+ getTaskByWorkflowRunStepId,
17
+ initDb,
18
+ startTask,
19
+ } from "../be/db";
20
+ import * as dbClient from "../be/db-client";
21
+ import { installExtension, listExtensionRuns } from "../be/extensions/db";
22
+ import { disableExtension, enableExtension, stopExtensionRuntime } from "../extensions/lifecycle";
23
+ import { handleTasks } from "../http/tasks";
24
+ import { dispatchScheduleTarget } from "../scheduler/scheduler";
25
+ import { createTaskWithSiblingAwareness } from "../tasks/sibling-awareness";
26
+ import { createWorkerTaskFollowUp } from "../tasks/worker-follow-up";
27
+ import { registerSendTaskTool } from "../tools/send-task";
28
+ import { registerTaskActionTool } from "../tools/task-action";
29
+ import { markExtensionRequestOrigin } from "../tools/utils";
30
+ import type { ExtensionInstallBody } from "../types";
31
+ import { workflowEventBus } from "../workflows/event-bus";
32
+ import { AgentTaskExecutor } from "../workflows/executors/agent-task";
33
+ import { loadBundleFixture } from "./fixtures/extensions/load";
34
+
35
+ const TEST_DB_PATH = "./test-extensions-pre-task.sqlite";
36
+
37
+ async function removeDbFiles(): Promise<void> {
38
+ for (const suffix of ["", "-wal", "-shm"]) {
39
+ await Bun.file(TEST_DB_PATH + suffix)
40
+ .delete()
41
+ .catch(() => {});
42
+ }
43
+ }
44
+
45
+ async function installAndEnable(name: string, config?: Record<string, unknown>) {
46
+ const bundle = await loadBundleFixture(name);
47
+ const installed = await installExtension({ ...bundle, config });
48
+ return await enableExtension(installed.extension.id);
49
+ }
50
+
51
+ async function installSource(name: string, hooks: string) {
52
+ const template = await loadBundleFixture("minimal");
53
+ const bundle: ExtensionInstallBody = {
54
+ manifest: { ...template.manifest, name, description: name },
55
+ files: { "hooks.ts": hooks },
56
+ };
57
+ const installed = await installExtension(bundle);
58
+ return await enableExtension(installed.extension.id);
59
+ }
60
+
61
+ type TestResponse = { status: number; body: Record<string, unknown> };
62
+
63
+ async function postTask(body: Record<string, unknown>): Promise<TestResponse> {
64
+ const req = Readable.from([Buffer.from(JSON.stringify(body))]) as IncomingMessage;
65
+ req.method = "POST";
66
+ req.url = "/api/tasks";
67
+ req.headers = { "content-type": "application/json" };
68
+ let status = 200;
69
+ let text = "";
70
+ const res = {
71
+ headersSent: false,
72
+ writableEnded: false,
73
+ setHeader() {},
74
+ writeHead(code: number) {
75
+ status = code;
76
+ this.headersSent = true;
77
+ return this;
78
+ },
79
+ end(chunk?: unknown) {
80
+ if (chunk !== undefined) text += String(chunk);
81
+ this.writableEnded = true;
82
+ return this;
83
+ },
84
+ } as unknown as ServerResponse;
85
+
86
+ expect(await handleTasks(req, res, ["api", "tasks"], new URLSearchParams(), undefined)).toBe(
87
+ true,
88
+ );
89
+ return { status, body: JSON.parse(text) as Record<string, unknown> };
90
+ }
91
+
92
+ function registeredSendTask(server: McpServer) {
93
+ return (
94
+ server as unknown as {
95
+ _registeredTools: Record<
96
+ string,
97
+ { handler: (args: unknown, extra: unknown) => Promise<Record<string, unknown>> }
98
+ >;
99
+ }
100
+ )._registeredTools["send-task"]!;
101
+ }
102
+
103
+ function registeredTaskAction(server: McpServer) {
104
+ return (
105
+ server as unknown as {
106
+ _registeredTools: Record<
107
+ string,
108
+ { handler: (args: unknown, extra: unknown) => Promise<Record<string, unknown>> }
109
+ >;
110
+ }
111
+ )._registeredTools["task-action"]!;
112
+ }
113
+
114
+ function sendTaskArgs(task: string) {
115
+ return {
116
+ task,
117
+ offerMode: false,
118
+ leadOnly: false,
119
+ allowDuplicate: true,
120
+ overrideSlackContext: false,
121
+ };
122
+ }
123
+
124
+ describe("extension task boundaries", () => {
125
+ beforeAll(async () => {
126
+ await removeDbFiles();
127
+ initDb(TEST_DB_PATH);
128
+ });
129
+
130
+ afterAll(async () => {
131
+ await stopExtensionRuntime();
132
+ closeDb();
133
+ await removeDbFiles();
134
+ });
135
+
136
+ beforeEach(async () => {
137
+ await stopExtensionRuntime();
138
+ const client = getDbClient();
139
+ await client.run("DELETE FROM agent_tasks");
140
+ await client.run("DELETE FROM extensions");
141
+ await client.run("DELETE FROM agents");
142
+ });
143
+
144
+ test("REST task creation applies rewrites and reports blocks as 422", async () => {
145
+ const rewrite = await installAndEnable("rewrite-task-priority");
146
+ const created = await postTask({ task: "rewrite this task" });
147
+ expect(created.status).toBe(201);
148
+ expect(created.body.priority).toBe(1);
149
+ expect(await listExtensionRuns(rewrite.id)).toMatchObject([
150
+ { event: "pre.task.create", action: "modify" },
151
+ ]);
152
+
153
+ await stopExtensionRuntime();
154
+ await getDbClient().run("DELETE FROM extensions");
155
+ const blocker = await installAndEnable("block-tasks-from-source", { source: "rest" });
156
+ const blocked = await postTask({ task: "block this task" });
157
+ expect(blocked.body.extension).toMatchObject({ name: "block-tasks-from-source" });
158
+ expect(blocked).toEqual({
159
+ status: 422,
160
+ body: {
161
+ error: "Task source rest is blocked",
162
+ extension: { id: blocker.id, name: "block-tasks-from-source" },
163
+ },
164
+ });
165
+
166
+ await disableExtension(blocker.id);
167
+ const retried = await postTask({ task: "block this task" });
168
+ expect(retried.status).toBe(201);
169
+ });
170
+
171
+ test("scheduler and workflow executor provide their task creation origins", async () => {
172
+ const extension = await installSource(
173
+ "record-task-origin",
174
+ `import type { SwarmExtension } from "swarm-extension";
175
+ const extension: SwarmExtension = (api) => {
176
+ api.on("pre.task.create", async (event, ctx) => {
177
+ await ctx.state.set(event.description, event.origin);
178
+ });
179
+ };
180
+ export default extension;
181
+ `,
182
+ );
183
+ const schedule = await createScheduledTask({
184
+ name: "extension-origin-schedule",
185
+ taskTemplate: "scheduled-origin-task",
186
+ intervalMs: 60_000,
187
+ });
188
+ // The production path creates the task inside a transaction; hooks must still fire.
189
+ const dispatched = await dispatchScheduleTarget(schedule);
190
+ expect(dispatched.task?.scheduleId).toBe(schedule.id);
191
+ expect(
192
+ (
193
+ await getKv(
194
+ `task:agent:${extension.agentId}`,
195
+ "ext:record-task-origin:scheduled-origin-task",
196
+ )
197
+ )?.value,
198
+ ).toBe("schedule");
199
+
200
+ const workflow = await createWorkflow({
201
+ name: "extension-origin-workflow",
202
+ definition: { nodes: [], edges: [] },
203
+ });
204
+ const run = await createWorkflowRun({ id: crypto.randomUUID(), workflowId: workflow.id });
205
+ const step = await createWorkflowRunStep({
206
+ id: crypto.randomUUID(),
207
+ runId: run.id,
208
+ nodeId: "origin-task",
209
+ nodeType: "agent-task",
210
+ });
211
+ const executor = new AgentTaskExecutor({
212
+ db: await import("../be/db"),
213
+ eventBus: workflowEventBus,
214
+ interpolate: (template) => template,
215
+ });
216
+ const result = await executor.run({
217
+ config: { template: "workflow-origin-task" },
218
+ context: {},
219
+ meta: {
220
+ runId: run.id,
221
+ stepId: step.id,
222
+ nodeId: "origin-task",
223
+ workflowId: workflow.id,
224
+ dryRun: false,
225
+ },
226
+ });
227
+ expect(result.status).toBe("success");
228
+ expect((await getTaskByWorkflowRunStepId(step.id))?.source).toBe("workflow");
229
+ expect(
230
+ (
231
+ await getKv(
232
+ `task:agent:${extension.agentId}`,
233
+ "ext:record-task-origin:workflow-origin-task",
234
+ )
235
+ )?.value,
236
+ ).toBe("workflow");
237
+ });
238
+
239
+ test("send-task dispatches before its transaction and returns a tool error on block", async () => {
240
+ await installAndEnable("block-tasks-from-source", { source: "mcp" });
241
+ const caller = await createAgent({ name: "pre-task-caller", isLead: false, status: "idle" });
242
+ const observed: boolean[] = [];
243
+ const original = dbClient.isInTransaction;
244
+ const transactionSpy = spyOn(dbClient, "isInTransaction").mockImplementation(() => {
245
+ const active = original();
246
+ observed.push(active);
247
+ return active;
248
+ });
249
+ try {
250
+ const server = new McpServer({ name: "pre-task-send", version: "1.0.0" });
251
+ registerSendTaskTool(server);
252
+ const result = await registeredSendTask(server).handler(sendTaskArgs("blocked tool task"), {
253
+ sessionId: "pre-task",
254
+ requestInfo: { headers: { "x-agent-id": caller.id } },
255
+ });
256
+ expect(result.isError).toBe(true);
257
+ expect(JSON.stringify(result)).toContain("Task source mcp is blocked");
258
+ expect(observed.length).toBeGreaterThan(0);
259
+ expect(observed.every((active) => active === false)).toBe(true);
260
+ } finally {
261
+ transactionSpy.mockRestore();
262
+ }
263
+ });
264
+
265
+ test("task-action create returns a tool error when an extension blocks creation", async () => {
266
+ await installAndEnable("block-tasks-from-source", { source: "mcp" });
267
+ const caller = await createAgent({
268
+ name: "pre-task-action-caller",
269
+ isLead: false,
270
+ status: "idle",
271
+ });
272
+ const server = new McpServer({ name: "pre-task-action", version: "1.0.0" });
273
+ registerTaskActionTool(server);
274
+ const result = await registeredTaskAction(server).handler(
275
+ { action: "create", task: "blocked action task", leadOnly: false },
276
+ {
277
+ sessionId: "pre-task-action",
278
+ requestInfo: { headers: { "x-agent-id": caller.id } },
279
+ },
280
+ );
281
+ expect(result.isError).toBe(true);
282
+ expect(JSON.stringify(result)).toContain("Task source mcp is blocked");
283
+ });
284
+
285
+ test("drops derived fields and fails open for invalid option rewrites", async () => {
286
+ const warning = spyOn(console, "warn").mockImplementation(() => {});
287
+ try {
288
+ await installSource(
289
+ "drop-task-status",
290
+ `import type { SwarmExtension } from "swarm-extension";
291
+ const extension: SwarmExtension = (api) => {
292
+ api.on("pre.task.create", () => ({ action: "modify", data: { status: "draft" } as never }));
293
+ };
294
+ export default extension;
295
+ `,
296
+ );
297
+ const task = await createTaskWithSiblingAwareness("keep task status", { source: "api" });
298
+ expect(task.status).toBe("unassigned");
299
+ expect(warning).toHaveBeenCalledWith(
300
+ expect.stringContaining('ignored disallowed key "status"'),
301
+ );
302
+ } finally {
303
+ warning.mockRestore();
304
+ }
305
+
306
+ await stopExtensionRuntime();
307
+ await getDbClient().run("DELETE FROM extensions");
308
+ const invalid = await installSource(
309
+ "invalid-task-priority",
310
+ `import type { SwarmExtension } from "swarm-extension";
311
+ const extension: SwarmExtension = (api) => {
312
+ api.on("pre.task.create", () => ({ action: "modify", data: { priority: "urgent" } as never }));
313
+ };
314
+ export default extension;
315
+ `,
316
+ );
317
+ const task = await createTaskWithSiblingAwareness("keep task priority", { source: "api" });
318
+ expect(task.priority).toBe(50);
319
+ expect(await listExtensionRuns(invalid.id)).toMatchObject([
320
+ { event: "pre.task.create", action: "error" },
321
+ ]);
322
+ });
323
+
324
+ test("extension tool calls derive their origin and skip the creating extension", async () => {
325
+ const creator = await installAndEnable("rewrite-task-priority");
326
+ await installAndEnable("block-tasks-from-source", {
327
+ source: "extension:rewrite-task-priority",
328
+ });
329
+ const server = new McpServer({ name: "extension-origin-send", version: "1.0.0" });
330
+ registerSendTaskTool(server);
331
+ const extra = markExtensionRequestOrigin({
332
+ sessionId: "extension",
333
+ requestInfo: { headers: { "x-agent-id": creator.agentId! } },
334
+ });
335
+ const result = await registeredSendTask(server).handler(
336
+ sendTaskArgs("extension-created task"),
337
+ extra,
338
+ );
339
+ expect(result.isError).toBe(true);
340
+ expect(JSON.stringify(result)).toContain(
341
+ "Task source extension:rewrite-task-priority is blocked",
342
+ );
343
+ expect(await listExtensionRuns(creator.id)).toEqual([]);
344
+ });
345
+
346
+ test("follow-up hooks suppress Slack tasks and preserve normal follow-up creation", async () => {
347
+ const extension = await installAndEnable("suppress-lead-follow-up");
348
+ const lead = await createAgent({ name: "follow-up-lead", isLead: true, status: "idle" });
349
+ const worker = await createAgent({ name: "follow-up-worker", isLead: false, status: "idle" });
350
+
351
+ const slackTask = await createTaskExtended("Slack work", {
352
+ agentId: worker.id,
353
+ source: "slack",
354
+ });
355
+ await startTask(slackTask.id);
356
+ const completedSlack = await completeTask(slackTask.id, "done in Slack");
357
+ expect(
358
+ await createWorkerTaskFollowUp({
359
+ task: completedSlack!,
360
+ status: "completed",
361
+ output: "done in Slack",
362
+ }),
363
+ ).toBeNull();
364
+
365
+ const apiTask = await createTaskExtended("API work", {
366
+ agentId: worker.id,
367
+ source: "api",
368
+ });
369
+ await startTask(apiTask.id);
370
+ const completedApi = await completeTask(apiTask.id, "done by API");
371
+ const followUp = await createWorkerTaskFollowUp({
372
+ task: completedApi!,
373
+ status: "completed",
374
+ output: "done by API",
375
+ });
376
+ expect(followUp).toMatchObject({ agentId: lead.id, parentTaskId: apiTask.id, priority: 50 });
377
+
378
+ const chronological = (await listExtensionRuns(extension.id)).reverse();
379
+ expect(chronological.map(({ event, action }) => ({ event, action }))).toEqual([
380
+ { event: "pre.task.followUp", action: "block" },
381
+ { event: "pre.task.followUp", action: "continue" },
382
+ { event: "pre.task.create", action: "continue" },
383
+ ]);
384
+
385
+ await disableExtension(extension.id);
386
+ const secondSlackTask = await createTaskExtended("Second Slack work", {
387
+ agentId: worker.id,
388
+ source: "slack",
389
+ });
390
+ await startTask(secondSlackTask.id);
391
+ const secondCompletedSlack = await completeTask(secondSlackTask.id, "done after disable");
392
+ expect(
393
+ await createWorkerTaskFollowUp({
394
+ task: secondCompletedSlack!,
395
+ status: "completed",
396
+ output: "done after disable",
397
+ }),
398
+ ).toMatchObject({ agentId: lead.id, parentTaskId: secondSlackTask.id });
399
+ });
400
+
401
+ test("follow-up hooks rewrite the description, assignee, priority, and follow-up config", async () => {
402
+ await createAgent({ name: "rewrite-follow-up-lead", isLead: true, status: "idle" });
403
+ const worker = await createAgent({
404
+ name: "rewrite-follow-up-worker",
405
+ isLead: false,
406
+ status: "idle",
407
+ });
408
+ const alternate = await createAgent({
409
+ name: "rewrite-follow-up-target",
410
+ isLead: false,
411
+ status: "idle",
412
+ });
413
+ await installSource(
414
+ "rewrite-follow-up",
415
+ `import { modify, type SwarmExtension } from "swarm-extension";
416
+ const extension: SwarmExtension = (api) => {
417
+ api.on("pre.task.followUp", () => modify({
418
+ description: "Review the rewritten result",
419
+ agentId: ${JSON.stringify(alternate.id)},
420
+ priority: 7,
421
+ followUpConfig: { disabled: true },
422
+ }));
423
+ };
424
+ export default extension;
425
+ `,
426
+ );
427
+
428
+ const task = await createTaskExtended("Rewrite follow-up source", {
429
+ agentId: worker.id,
430
+ source: "api",
431
+ });
432
+ await startTask(task.id);
433
+ const completed = await completeTask(task.id, "done");
434
+ const followUp = await createWorkerTaskFollowUp({
435
+ task: completed!,
436
+ status: "completed",
437
+ output: "done",
438
+ });
439
+
440
+ expect(followUp).toMatchObject({
441
+ task: "Review the rewritten result",
442
+ agentId: alternate.id,
443
+ priority: 7,
444
+ followUpConfig: { disabled: true },
445
+ });
446
+ });
447
+
448
+ test("follow-up hooks that violate the task schema are ignored and the follow-up still lands", async () => {
449
+ const lead = await createAgent({
450
+ name: "invalid-follow-up-lead",
451
+ isLead: true,
452
+ status: "idle",
453
+ });
454
+ const worker = await createAgent({
455
+ name: "invalid-follow-up-worker",
456
+ isLead: false,
457
+ status: "idle",
458
+ });
459
+ const extension = await installSource(
460
+ "invalid-follow-up",
461
+ `import { modify, type SwarmExtension } from "swarm-extension";
462
+ const extension: SwarmExtension = (api) => {
463
+ api.on("pre.task.followUp", () => modify({ priority: -1 }));
464
+ };
465
+ export default extension;
466
+ `,
467
+ );
468
+
469
+ const task = await createTaskExtended("Invalid follow-up source", {
470
+ agentId: worker.id,
471
+ source: "api",
472
+ });
473
+ await startTask(task.id);
474
+ const completed = await completeTask(task.id, "done");
475
+ const followUp = await createWorkerTaskFollowUp({
476
+ task: completed!,
477
+ status: "completed",
478
+ output: "done",
479
+ });
480
+
481
+ expect(followUp).toMatchObject({ agentId: lead.id, parentTaskId: task.id, priority: 50 });
482
+ const runs = await listExtensionRuns(extension.id);
483
+ expect(runs.some((run) => run.event === "pre.task.followUp" && run.action === "error")).toBe(
484
+ true,
485
+ );
486
+ });
487
+ });
@@ -0,0 +1,41 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { runContext } from "@/extensions/dispatcher";
3
+
4
+ describe("runContext", () => {
5
+ test("tool events record the calling agent and the tool name", () => {
6
+ expect(
7
+ runContext("pre.tool.call", {
8
+ tool: "store-progress",
9
+ args: { status: "completed" },
10
+ requestInfo: { agentId: "agent-1", callOrigin: "mcp" },
11
+ }),
12
+ ).toEqual({ agentId: "agent-1", subject: "tool store-progress" });
13
+ });
14
+
15
+ test("task creation records origin and a short description", () => {
16
+ const context = runContext("pre.task.create", {
17
+ origin: "rest",
18
+ description: `token=sk-ant-${"a".repeat(40)} ${"x".repeat(200)}`,
19
+ options: { agentId: "pinned-agent" },
20
+ });
21
+ expect(context.agentId).toBe("pinned-agent");
22
+ expect(context.subject?.startsWith("rest: ")).toBe(true);
23
+ expect(context.subject?.length).toBeLessThan(100);
24
+ expect(context.subject?.includes("sk-ant-")).toBe(false);
25
+ });
26
+
27
+ test("post task events record the task's agent", () => {
28
+ expect(
29
+ runContext("post.task.completed", {
30
+ task: { id: "task-1", agentId: "agent-2", task: "Do the thing" },
31
+ output: "done",
32
+ }),
33
+ ).toEqual({ agentId: "agent-2", subject: "task task-1: Do the thing" });
34
+ });
35
+
36
+ test("slack events carry the channel and no agent", () => {
37
+ expect(runContext("pre.slack.route", { channelId: "C1", userId: "U1", text: "hello" })).toEqual(
38
+ { agentId: null, subject: "channel C1: hello" },
39
+ );
40
+ });
41
+ });