@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
@@ -15,6 +15,7 @@ import {
15
15
  hasCapacity,
16
16
  } from "@/be/db";
17
17
  import { repointTrackerSyncBySwarmId } from "@/be/db-queries/tracker";
18
+ import { applyPreTaskCreate } from "@/extensions/apply-task-create";
18
19
  import { checkSlackRoutingCoherence } from "@/tasks/slack-routing";
19
20
  import { findDuplicateTask } from "@/tools/task-dedup";
20
21
  import { ownerCtx, type ToolCtx } from "@/tools/task-tool-ctx";
@@ -28,6 +29,7 @@ import {
28
29
  import {
29
30
  type AgentTask,
30
31
  AssetKeySchema,
32
+ type CreateTaskOptions,
31
33
  FollowUpConfigSchema,
32
34
  ModelTierSchema,
33
35
  ReasoningEffortSchema,
@@ -69,7 +71,9 @@ export const sendTaskInputSchema = z
69
71
  .string()
70
72
  .max(200)
71
73
  .optional()
72
- .describe("Optional routing context (maximum 200 characters)."),
74
+ .describe(
75
+ "Why this worker fits the task. Required when agentId is supplied: at least 10 characters after trimming whitespace, maximum 200 characters. Optional for implicit parent or pool routing.",
76
+ ),
73
77
  task: z.string().min(1).describe("The task description to send."),
74
78
  key: AssetKeySchema.optional().describe(
75
79
  "Logical namespace key. Child tasks inherit their parent namespace when provided.",
@@ -190,6 +194,14 @@ export const sendTaskInputSchema = z
190
194
  path: ["routingReason"],
191
195
  });
192
196
  }
197
+ if (data.agentId !== undefined && (data.routingNote?.trim().length ?? 0) < 10) {
198
+ ctx.addIssue({
199
+ code: z.ZodIssueCode.custom,
200
+ message:
201
+ "routingNote is required when agentId is supplied (at least 10 characters after trim).",
202
+ path: ["routingNote"],
203
+ });
204
+ }
193
205
  checkOutputSchemaShape(data.outputSchema, ctx);
194
206
  });
195
207
 
@@ -269,6 +281,11 @@ export async function sendTaskHandler(
269
281
  if (agentId !== undefined && routingReason === undefined) {
270
282
  return toolErr("routingReason is required when agentId is supplied.");
271
283
  }
284
+ if (agentId !== undefined && (routingNote?.trim().length ?? 0) < 10) {
285
+ return toolErr(
286
+ "routingNote is required when agentId is supplied (at least 10 characters after trim).",
287
+ );
288
+ }
272
289
  if (ctx.kind === "owner" && !ctx.agentId) {
273
290
  return toolErr('Agent ID not found. The MCP client should define the "X-Agent-ID" header.', {
274
291
  data: { yourAgentId: ctx.agentId },
@@ -364,6 +381,55 @@ export async function sendTaskHandler(
364
381
  const effectiveRoutingReason =
365
382
  agentId !== undefined ? routingReason : effectiveAgentId ? "continuity" : undefined;
366
383
  const effectiveRoutingNote = effectiveRoutingReason ? routingNote : undefined;
384
+ const effectiveRoutingSource =
385
+ agentId !== undefined ? "declared" : effectiveAgentId ? "engine_default" : undefined;
386
+
387
+ const requestedTaskOptions: CreateTaskOptions = {
388
+ key: assetKey,
389
+ agentId: offerMode ? undefined : effectiveAgentId,
390
+ offeredTo: offerMode ? effectiveAgentId : undefined,
391
+ creatorAgentId,
392
+ requestedByUserId,
393
+ source: "mcp",
394
+ sourceTaskId,
395
+ taskType,
396
+ tags,
397
+ priority,
398
+ dependsOn,
399
+ dir,
400
+ parentTaskId: effectiveParentTaskId,
401
+ vcsRepo: effectiveVcsRepo,
402
+ model: normalizedModel.model,
403
+ modelTier: normalizedModel.modelTier,
404
+ effort,
405
+ slackChannelId,
406
+ slackThreadTs,
407
+ slackUserId,
408
+ overrideSlackContext,
409
+ followUpConfig,
410
+ outputSchema,
411
+ routingReason: effectiveRoutingReason,
412
+ routingSource: effectiveRoutingSource,
413
+ routingNote: effectiveRoutingNote,
414
+ routingAffinity:
415
+ effectiveLeadOnly || requiredCapabilities?.length
416
+ ? { leadOnly: effectiveLeadOnly, capabilities: requiredCapabilities ?? [] }
417
+ : undefined,
418
+ };
419
+ const preCreate = await applyPreTaskCreate({
420
+ description: task,
421
+ options: requestedTaskOptions,
422
+ origin: "mcp",
423
+ requestInfo: ctx.kind === "owner" ? ctx.requestInfo : undefined,
424
+ });
425
+ if (preCreate.kind === "blocked") {
426
+ return toolErr(preCreate.reason, {
427
+ data: { yourAgentId: creatorAgentId },
428
+ details: JSON.stringify({ extension: preCreate.extension }),
429
+ });
430
+ }
431
+ const taskDescription = preCreate.description;
432
+ const taskOptions = preCreate.options;
367
433
 
368
434
  // The three dedup guards are pure reads, so they run twice: once here as a
369
435
  // fast path (keeping this tool's existing early-exit responses), and once
@@ -388,9 +454,9 @@ export async function sendTaskHandler(
388
454
  // Dedup guard: check for similar recent tasks
389
455
  if (!allowDuplicate && creatorAgentId) {
390
456
  const duplicate = await findDuplicateTask({
391
- taskDescription: task,
392
- creatorAgentId,
393
- targetAgentId: effectiveAgentId ?? undefined,
457
+ taskDescription,
458
+ creatorAgentId: taskOptions.creatorAgentId ?? creatorAgentId,
459
+ targetAgentId: taskOptions.agentId ?? taskOptions.offeredTo,
394
460
  });
395
461
  if (duplicate) {
396
462
  return {
@@ -470,41 +536,13 @@ export async function sendTaskHandler(
470
536
  const raced = await evaluateDedupGuards();
471
537
  if (raced) return { success: raced.ok, message: raced.message, task: raced.task };
472
538
 
473
- const finalTags = tags;
474
-
475
539
  // If no agentId (and no auto-routed agentId), create an unassigned task for the pool
476
- if (!effectiveAgentId) {
477
- const newTask = await createTaskExtended(task, {
478
- key: assetKey,
479
- creatorAgentId,
480
- requestedByUserId,
481
- sourceTaskId,
482
- taskType,
483
- tags: finalTags,
484
- priority,
485
- dependsOn,
486
- dir,
487
- parentTaskId: effectiveParentTaskId,
488
- vcsRepo: effectiveVcsRepo,
489
- model: normalizedModel.model,
490
- modelTier: normalizedModel.modelTier,
491
- effort,
492
- slackChannelId,
493
- slackThreadTs,
494
- slackUserId,
495
- overrideSlackContext,
496
- followUpConfig,
497
- outputSchema,
498
- routingReason: effectiveRoutingReason,
499
- routingNote: effectiveRoutingNote,
500
- routingAffinity:
501
- effectiveLeadOnly || requiredCapabilities?.length
502
- ? { leadOnly: effectiveLeadOnly, capabilities: requiredCapabilities ?? [] }
503
- : undefined,
504
- });
540
+ const targetAgentId = taskOptions.offeredTo ?? taskOptions.agentId ?? undefined;
541
+ if (!targetAgentId) {
542
+ const newTask = await createTaskExtended(taskDescription, taskOptions);
505
543
  await transferTrackerSyncToResumeChild({
506
- parentTaskId: effectiveParentTaskId,
507
- taskType,
544
+ parentTaskId: taskOptions.parentTaskId,
545
+ taskType: taskOptions.taskType,
508
546
  child: newTask,
509
547
  });
510
548
 
@@ -515,16 +553,16 @@ export async function sendTaskHandler(
515
553
  };
516
554
  }
517
555
 
518
- const agent = await getAgentById(effectiveAgentId);
556
+ const agent = await getAgentById(targetAgentId);
519
557
 
520
558
  if (!agent) {
521
559
  return {
522
560
  success: false,
523
- message: `Agent with ID "${effectiveAgentId}" not found.`,
561
+ message: `Agent with ID "${targetAgentId}" not found.`,
524
562
  };
525
563
  }
526
564
 
527
- if (effectiveLeadOnly && !agent.isLead) {
565
+ if (taskOptions.routingAffinity?.leadOnly && !agent.isLead) {
528
566
  return {
529
567
  success: false,
530
568
  message: `Lead-only task requires a Lead agent; "${agent.name}" is not a Lead.`,
@@ -532,48 +570,20 @@ export async function sendTaskHandler(
532
570
  }
533
571
 
534
572
  // For direct assignment (not offer), check if agent has capacity
535
- if (!offerMode && !(await hasCapacity(effectiveAgentId))) {
536
- const activeCount = await getActiveTaskCount(effectiveAgentId);
573
+ if (!taskOptions.offeredTo && !(await hasCapacity(targetAgentId))) {
574
+ const activeCount = await getActiveTaskCount(targetAgentId);
537
575
  return {
538
576
  success: false,
539
577
  message: `Agent "${agent.name}" is at capacity (${activeCount}/${agent.maxTasks ?? 1} tasks). Use offerMode: true to offer the task instead, or wait for a task to complete.`,
540
578
  };
541
579
  }
542
580
 
543
- if (offerMode) {
581
+ if (taskOptions.offeredTo) {
544
582
  // Offer the task to the agent (they must accept/reject)
545
- const newTask = await createTaskExtended(task, {
546
- key: assetKey,
547
- offeredTo: effectiveAgentId,
548
- creatorAgentId,
549
- requestedByUserId,
550
- sourceTaskId,
551
- taskType,
552
- tags: finalTags,
553
- priority,
554
- dependsOn,
555
- dir,
556
- parentTaskId: effectiveParentTaskId,
557
- vcsRepo: effectiveVcsRepo,
558
- model: normalizedModel.model,
559
- modelTier: normalizedModel.modelTier,
560
- effort,
561
- slackChannelId,
562
- slackThreadTs,
563
- slackUserId,
564
- overrideSlackContext,
565
- followUpConfig,
566
- outputSchema,
567
- routingReason: effectiveRoutingReason,
568
- routingNote: effectiveRoutingNote,
569
- routingAffinity:
570
- effectiveLeadOnly || requiredCapabilities?.length
571
- ? { leadOnly: effectiveLeadOnly, capabilities: requiredCapabilities ?? [] }
572
- : undefined,
573
- });
583
+ const newTask = await createTaskExtended(taskDescription, taskOptions);
574
584
  await transferTrackerSyncToResumeChild({
575
- parentTaskId: effectiveParentTaskId,
576
- taskType,
585
+ parentTaskId: taskOptions.parentTaskId,
586
+ taskType: taskOptions.taskType,
577
587
  child: newTask,
578
588
  });
579
589
 
@@ -585,38 +595,10 @@ export async function sendTaskHandler(
585
595
  }
586
596
 
587
597
  // Direct assignment
588
- const newTask = await createTaskExtended(task, {
589
- key: assetKey,
590
- agentId: effectiveAgentId,
591
- creatorAgentId,
592
- requestedByUserId,
593
- sourceTaskId,
594
- taskType,
595
- tags: finalTags,
596
- priority,
597
- dependsOn,
598
- dir,
599
- parentTaskId: effectiveParentTaskId,
600
- vcsRepo: effectiveVcsRepo,
601
- model: normalizedModel.model,
602
- modelTier: normalizedModel.modelTier,
603
- effort,
604
- slackChannelId,
605
- slackThreadTs,
606
- slackUserId,
607
- overrideSlackContext,
608
- followUpConfig,
609
- outputSchema,
610
- routingReason: effectiveRoutingReason,
611
- routingNote: effectiveRoutingNote,
612
- routingAffinity:
613
- effectiveLeadOnly || requiredCapabilities?.length
614
- ? { leadOnly: effectiveLeadOnly, capabilities: requiredCapabilities ?? [] }
615
- : undefined,
616
- });
598
+ const newTask = await createTaskExtended(taskDescription, taskOptions);
617
599
  await transferTrackerSyncToResumeChild({
618
- parentTaskId: effectiveParentTaskId,
619
- taskType,
600
+ parentTaskId: taskOptions.parentTaskId,
601
+ taskType: taskOptions.taskType,
620
602
  child: newTask,
621
603
  });
622
604
 
@@ -72,6 +72,7 @@ To reject: close this task with a rejection reason.`;
72
72
  const task = await createTaskExtended(taskDescription, {
73
73
  agentId: leadAgent.id,
74
74
  routingReason: "skill",
75
+ routingSource: "engine_default",
75
76
  creatorAgentId: requestInfo.agentId,
76
77
  source: "mcp",
77
78
  taskType: "skill-approval",
@@ -102,7 +102,7 @@ export const registerStoreProgressTool = (server: McpServer) => {
102
102
  .string()
103
103
  .optional()
104
104
  .describe(
105
- "The task result (used when completing). For Slack-originated tasks, this is published verbatim in the thread's outcome card: provide a concrete summary scaled to what was asked, including only the outcome and any links or IDs the human needs—not process narration, a transcript, or a restatement of the brief.",
105
+ "The task result (used when completing). For Slack-originated tasks, this is published verbatim in the thread's outcome card. Keep free-text output under 120 words by default. Name the result and every artifact link, plus any IDs the human needs. Link documents instead of inlining them; omit process narration, transcripts, and restatements of the brief. Exceed the target only when requested depth, enumerated results, essential evidence, caveats, or instructions require it, or when the task's outputSchema requires longer output. When the task carries an outputSchema, output must be JSON matching it.",
106
106
  ),
107
107
  failureReason: z
108
108
  .string()
@@ -28,6 +28,7 @@ import {
28
28
  updateTaskClaudeSessionId,
29
29
  } from "@/be/db";
30
30
  import { touchRuntimeInstance } from "@/be/multi-runtime";
31
+ import { applyPreTaskCreate } from "@/extensions/apply-task-create";
31
32
  import { assertOwnsTask, ownerCtx, type ToolCtx } from "@/tools/task-tool-ctx";
32
33
  import {
33
34
  createToolRegistrar,
@@ -39,6 +40,7 @@ import {
39
40
  import {
40
41
  AssetKeySchema,
41
42
  BudgetRefusalCauseSchema,
43
+ type CreateTaskOptions,
42
44
  ModelTierSchema,
43
45
  ReasoningEffortSchema,
44
46
  splitLegacyModelAlias,
@@ -265,7 +267,14 @@ export async function taskActionHandler(
265
267
 
266
268
  const agentId = ctx.agentId;
267
269
  let assetKey: string | undefined;
270
+ let preparedCreate: { description: string; options: CreateTaskOptions } | undefined;
268
271
  if (action === "create") {
272
+ if (!task) {
273
+ return taskActionResult(
274
+ { success: false, message: "Task description is required for 'create' action." },
275
+ agentId,
276
+ );
277
+ }
269
278
  try {
270
279
  assetKey = key
271
280
  ? await authorizeAssetKeyWrite(key, await resolveTaskAuditUserId(ctx.sourceTaskId, agentId))
@@ -279,33 +288,45 @@ export async function taskActionHandler(
279
288
  : String(error);
280
289
  return taskActionResult({ success: false, message }, agentId);
281
290
  }
291
+
292
+ const preCreate = await applyPreTaskCreate({
293
+ description: task,
294
+ options: {
295
+ key: assetKey,
296
+ creatorAgentId: agentId,
297
+ source: "mcp",
298
+ taskType,
299
+ tags,
300
+ priority,
301
+ dependsOn,
302
+ dir,
303
+ model: normalizedModel.model,
304
+ modelTier: normalizedModel.modelTier,
305
+ effort: input.effort,
306
+ routingAffinity:
307
+ leadOnly || requiredCapabilities?.length
308
+ ? { leadOnly, capabilities: requiredCapabilities ?? [] }
309
+ : undefined,
310
+ },
311
+ origin: "mcp",
312
+ requestInfo: ctx.requestInfo,
313
+ });
314
+ if (preCreate.kind === "blocked") {
315
+ return taskActionResult({ success: false, message: preCreate.reason }, agentId);
316
+ }
317
+ preparedCreate = {
318
+ description: preCreate.description,
319
+ options: preCreate.options,
320
+ };
282
321
  }
283
322
 
284
323
  const result = await getDbClient().transaction(async (): Promise<TaskActionResult> => {
285
324
  switch (action) {
286
325
  case "create": {
287
- if (!task) {
288
- return {
289
- success: false,
290
- message: "Task description is required for 'create' action.",
291
- };
292
- }
293
- const newTask = await createTaskExtended(task, {
294
- key: assetKey,
295
- creatorAgentId: agentId,
296
- taskType,
297
- tags,
298
- priority,
299
- dependsOn,
300
- dir,
301
- model: normalizedModel.model,
302
- modelTier: normalizedModel.modelTier,
303
- effort: input.effort,
304
- routingAffinity:
305
- leadOnly || requiredCapabilities?.length
306
- ? { leadOnly, capabilities: requiredCapabilities ?? [] }
307
- : undefined,
308
- });
326
+ const newTask = await createTaskExtended(
327
+ preparedCreate!.description,
328
+ preparedCreate!.options,
329
+ );
309
330
  return {
310
331
  success: true,
311
332
  message: `Created unassigned task "${newTask.id}".`,
@@ -9,6 +9,7 @@ export type ToolCtx =
9
9
  runtimeInstanceId?: string;
10
10
  sourceTaskId?: string;
11
11
  sessionId?: string;
12
+ requestInfo?: RequestInfo;
12
13
  }
13
14
  | { kind: "user"; userId: string; user: User; sessionId?: string };
14
15
 
@@ -19,6 +20,7 @@ export function ownerCtx(info: RequestInfo): ToolCtx {
19
20
  runtimeInstanceId: info.runtimeInstanceId,
20
21
  sourceTaskId: info.sourceTaskId,
21
22
  sessionId: info.sessionId,
23
+ requestInfo: info,
22
24
  };
23
25
  }
24
26
 
@@ -198,6 +198,7 @@ Use the crashed agent's identity above as context for who was on it and what kin
198
198
  Dispatch via \`send-task\` with ALL of:
199
199
  - an explicit \`agentId\` (the chosen worker) — REQUIRED. If you omit it, \`send-task\` auto-routes to the original task's agent, which is the dead worker, and the work re-strands.
200
200
  - \`routingReason: "reroute_fault"\`
201
+ - \`routingNote\`: explain why this worker is the right recovery target (at least 10 characters after trim, maximum 200)
201
202
  - \`taskType: "resume"\`
202
203
  - the tag \`resume-generation:{{generation_next}}\`
203
204
  - \`parentTaskId: {{original_task_id}}\`
@@ -252,7 +253,7 @@ Required capabilities: {{required_capabilities}}{{artifacts_block}}
252
253
 
253
254
  This task has been sitting unassigned because no currently-registered agent matches its required role/capabilities. Pick an agent to take this work over and RE-DELEGATE it — do NOT execute it yourself.
254
255
 
255
- Dispatch via \`send-task\` with an explicit \`agentId\` (REQUIRED — omitting it re-pools the work under the same affinity tag and it will starve again), \`routingReason: "overflow"\`, and \`parentTaskId: {{original_task_id}}\`.
256
+ Dispatch via \`send-task\` with an explicit \`agentId\` (REQUIRED — omitting it re-pools the work under the same affinity tag and it will starve again), \`routingReason: "overflow"\`, a \`routingNote\` explaining the worker choice (at least 10 characters after trim, maximum 200), and \`parentTaskId: {{original_task_id}}\`.
256
257
 
257
258
  This work will NOT fall back to the unassigned pool — you are the only re-delegation path.`,
258
259
  variables: [
@@ -197,13 +197,15 @@ export const DEFERRED_TOOLS = new Set([
197
197
  "room-reset",
198
198
  "room-decode",
199
199
 
200
- // Reusable scripts (6)
200
+ // Reusable scripts and extensions (8)
201
201
  "script-upsert",
202
202
  "script-delete",
203
203
  "script-apis",
204
204
  "launch-script-run",
205
205
  "get-script-run",
206
206
  "list-script-runs",
207
+ "extension-install",
208
+ "extension-list",
207
209
 
208
210
  // External command routes (1)
209
211
  "swarm_x",
@@ -5,6 +5,11 @@ import type {
5
5
  ShapeOutput,
6
6
  ZodRawShapeCompat,
7
7
  } from "@modelcontextprotocol/sdk/server/zod-compat.js";
8
+ import {
9
+ getParseErrorMessage,
10
+ normalizeObjectSchema,
11
+ safeParseAsync,
12
+ } from "@modelcontextprotocol/sdk/server/zod-compat.js";
8
13
  import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
9
14
  import type {
10
15
  CallToolResult,
@@ -23,12 +28,18 @@ import { scrubObject, scrubSecrets } from "../utils/secret-scrubber";
23
28
  type Meta = RequestHandlerExtra<ServerRequest, ServerNotification>;
24
29
 
25
30
  const scriptSdkRequestOrigins = new WeakSet<object>();
31
+ const extensionRequestOrigins = new WeakSet<object>();
26
32
 
27
33
  export function markScriptSdkRequestOrigin<T extends object>(meta: T): T {
28
34
  scriptSdkRequestOrigins.add(meta);
29
35
  return meta;
30
36
  }
31
37
 
38
+ export function markExtensionRequestOrigin<T extends object>(meta: T): T {
39
+ extensionRequestOrigins.add(meta);
40
+ return meta;
41
+ }
42
+
32
43
  export type RequestInfo = {
33
44
  sessionId: string | undefined;
34
45
  agentId: string | undefined;
@@ -36,7 +47,7 @@ export type RequestInfo = {
36
47
  runtimeInstanceId: string | undefined;
37
48
  sourceTaskId: string | undefined;
38
49
  contextKey: string | undefined;
39
- callOrigin: "mcp" | "script-sdk";
50
+ callOrigin: "mcp" | "script-sdk" | "extension";
40
51
  };
41
52
 
42
53
  export const getRequestInfo = (req: Meta): RequestInfo => {
@@ -73,7 +84,11 @@ export const getRequestInfo = (req: Meta): RequestInfo => {
73
84
  runtimeInstanceId: typeof runtimeInstanceId === "string" ? runtimeInstanceId : undefined,
74
85
  sourceTaskId,
75
86
  contextKey,
76
- callOrigin: scriptSdkRequestOrigins.has(req) ? "script-sdk" : "mcp",
87
+ callOrigin: extensionRequestOrigins.has(req)
88
+ ? "extension"
89
+ : scriptSdkRequestOrigins.has(req)
90
+ ? "script-sdk"
91
+ : "mcp",
77
92
  };
78
93
  };
79
94
 
@@ -622,7 +637,7 @@ const ctxControlMiddleware: FinalizeMiddleware = async (result, ctx) => {
622
637
  // Calls made through ctx.swarm.* execute inside a script sandbox, so their
623
638
  // response never enters the model's context. The script SDK has a separate,
624
639
  // much higher hard response limit to protect the sandbox heap.
625
- if (ctx.callOrigin === "script-sdk") return result;
640
+ if (ctx.callOrigin === "script-sdk" || ctx.callOrigin === "extension") return result;
626
641
  if (CTX_CONTROL_EXEMPT_TOOLS.has(ctx.toolName)) return result;
627
642
 
628
643
  const fullWire = composeWireResult(result);
@@ -770,6 +785,85 @@ export function setPreloadedTools(server: McpServer, names: readonly string[]):
770
785
  preloadedToolsByServer.set(server, new Set(names));
771
786
  }
772
787
 
788
+ type ExtensionDispatcher = typeof import("../extensions/dispatcher");
789
+
790
+ async function applyPreToolCall(
791
+ name: string,
792
+ args: unknown,
793
+ requestInfo: RequestInfo,
794
+ inputSchema?: AnySchema | ZodRawShapeCompat,
795
+ ): Promise<{
796
+ args: unknown;
797
+ blocked?: SwarmToolResult;
798
+ dispatcher?: ExtensionDispatcher;
799
+ }> {
800
+ if (requestInfo.callOrigin !== "mcp") return { args };
801
+
802
+ const dispatcher = await import("../extensions/dispatcher");
803
+ const result = await dispatcher.dispatchPre(
804
+ "pre.tool.call",
805
+ { tool: name, args, requestInfo },
806
+ inputSchema
807
+ ? {
808
+ validateModify: async (data) => {
809
+ const schema = normalizeObjectSchema(inputSchema) ?? inputSchema;
810
+ const parsed = await safeParseAsync(schema as AnySchema, data.args);
811
+ if (parsed.success) return { success: true, data: { args: parsed.data } };
812
+
813
+ const message = scrubSecrets(getParseErrorMessage(parsed.error));
814
+ console.warn(
815
+ `[extensions] Ignored invalid arguments from pre.tool.call for ${name}: ${message}`,
816
+ );
817
+ return {
818
+ success: false,
819
+ error: new Error(`Modified arguments for tool "${name}" are invalid: ${message}`),
820
+ };
821
+ },
822
+ }
823
+ : {
824
+ transformModify: ({ extension }) => {
825
+ const message = scrubSecrets(
826
+ `[extensions] Ignored pre.tool.call argument rewrite for tool "${name}" from extension "${extension.name}" because the tool has no input schema`,
827
+ );
828
+ console.warn(message);
829
+ throw new Error(message);
830
+ },
831
+ },
832
+ );
833
+
834
+ if (result.action === "block") {
835
+ return {
836
+ args,
837
+ blocked: toolErr(result.reason, {
838
+ details: `Extension "${result.extension.name}" rejected this tool call.`,
839
+ }),
840
+ dispatcher,
841
+ };
842
+ }
843
+ return {
844
+ args: inputSchema && result.action === "modify" ? result.data.args : args,
845
+ dispatcher,
846
+ };
847
+ }
848
+
849
+ function dispatchPostToolCall(
850
+ dispatcher: ExtensionDispatcher | undefined,
851
+ name: string,
852
+ args: unknown,
853
+ outcome: SwarmToolResult,
854
+ requestInfo: RequestInfo,
855
+ durationMs: number,
856
+ ): void {
857
+ if (!dispatcher) return;
858
+ void dispatcher.dispatchPost("post.tool.call", {
859
+ tool: name,
860
+ args,
861
+ result: outcome,
862
+ requestInfo,
863
+ durationMs,
864
+ });
865
+ }
866
+
773
867
  /**
774
868
  * Creates a tool registration helper that automatically extracts request info
775
869
  * and passes it as the second parameter to the callback.
@@ -806,13 +900,24 @@ export const createToolRegistrar = (server: McpServer) => {
806
900
  return withSpan(
807
901
  "mcp.tool",
808
902
  async (span) => {
809
- const outcome = await (
810
- cb as (
811
- requestInfo: RequestInfo,
812
- meta: Meta,
813
- ) => SwarmToolResult | Promise<SwarmToolResult>
814
- )(requestInfo, meta);
903
+ const pre = await applyPreToolCall(name, {}, requestInfo);
904
+ let durationMs = 0;
905
+ let outcome = pre.blocked;
906
+ if (!outcome) {
907
+ const startedAt = Date.now();
908
+ try {
909
+ outcome = await (
910
+ cb as (
911
+ requestInfo: RequestInfo,
912
+ meta: Meta,
913
+ ) => SwarmToolResult | Promise<SwarmToolResult>
914
+ )(requestInfo, meta);
915
+ } finally {
916
+ durationMs = Date.now() - startedAt;
917
+ }
918
+ }
815
919
  const result = await finalizeSwarmToolResult(name, outcome, requestInfo);
920
+ dispatchPostToolCall(pre.dispatcher, name, {}, outcome, requestInfo, durationMs);
816
921
  span.setAttributes(toolResultAttributes(result));
817
922
  return result;
818
923
  },
@@ -831,14 +936,33 @@ export const createToolRegistrar = (server: McpServer) => {
831
936
  // trace tree. Cardinality is bounded — tool names are a fixed enum.
832
937
  `mcp.tool ${name}`,
833
938
  async (span) => {
834
- const outcome = await (
835
- cb as (
836
- args: InferInput<InputArgs>,
837
- requestInfo: RequestInfo,
838
- meta: Meta,
839
- ) => SwarmToolResult | Promise<SwarmToolResult>
840
- )(args, requestInfo, meta);
939
+ const pre = await applyPreToolCall(name, args, requestInfo, config.inputSchema);
940
+ const effectiveArgs = pre.args as InferInput<InputArgs>;
941
+ let durationMs = 0;
942
+ let outcome = pre.blocked;
943
+ if (!outcome) {
944
+ const startedAt = Date.now();
945
+ try {
946
+ outcome = await (
947
+ cb as (
948
+ args: InferInput<InputArgs>,
949
+ requestInfo: RequestInfo,
950
+ meta: Meta,
951
+ ) => SwarmToolResult | Promise<SwarmToolResult>
952
+ )(effectiveArgs, requestInfo, meta);
953
+ } finally {
954
+ durationMs = Date.now() - startedAt;
955
+ }
956
+ }
841
957
  const result = await finalizeSwarmToolResult(name, outcome, requestInfo);
958
+ dispatchPostToolCall(
959
+ pre.dispatcher,
960
+ name,
961
+ effectiveArgs,
962
+ outcome,
963
+ requestInfo,
964
+ durationMs,
965
+ );
842
966
  span.setAttributes(toolResultAttributes(result));
843
967
  return result;
844
968
  },