@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
@@ -18,6 +18,7 @@ import {
18
18
  getTaskById,
19
19
  initDb,
20
20
  startTask,
21
+ supersedeTask,
21
22
  updateScheduledTask,
22
23
  } from "../be/db";
23
24
  import {
@@ -28,7 +29,9 @@ import {
28
29
  type TaskWakeEvent,
29
30
  } from "../scheduler/deferred-task-waits";
30
31
  import { dispatchScheduleTarget, executeSchedule } from "../scheduler/scheduler";
32
+ import { createResumeFollowUp } from "../tasks/worker-follow-up";
31
33
  import { registerDeferTaskTool } from "../tools/defer-task";
34
+ import type { AgentTask } from "../types";
32
35
 
33
36
  type Result = { structuredContent: { success: boolean; message: string; scheduleId?: string } };
34
37
  type Tool = {
@@ -133,10 +136,218 @@ async function waitState(scheduleId: string) {
133
136
  childTaskId: string | null;
134
137
  resolvedAt: string | null;
135
138
  updated_at: string;
136
- }>("SELECT * FROM deferred_task_waits WHERE scheduleId = ?", [scheduleId]);
139
+ }>(
140
+ `SELECT w.*, m.taskId FROM deferred_task_waits w
141
+ JOIN deferred_task_wait_members m ON m.scheduleId = w.scheduleId
142
+ WHERE w.scheduleId = ?`,
143
+ [scheduleId],
144
+ );
145
+ }
146
+
147
+ async function resumeTask(parentId: string) {
148
+ const result = await createResumeFollowUp({ parentId, reason: "manual_supersede" });
149
+ if (result.kind !== "created") throw new Error(`Resume creation failed: ${result.kind}`);
150
+ return result.task;
151
+ }
152
+
153
+ async function awaitWatchedTask(scheduleId: string, taskId: string) {
154
+ const deadline = Date.now() + 3000;
155
+ while (Date.now() < deadline) {
156
+ if ((await waitState(scheduleId))?.taskId === taskId) return;
157
+ await Bun.sleep(5);
158
+ }
159
+ throw new Error("Event bus did not transfer the watched task");
160
+ }
161
+
162
+ async function multiFixture(mode?: "all" | "any", event: TaskWakeEvent = "settled") {
163
+ const parent = await activeTask();
164
+ const producers = [await activeTask(), await activeTask()];
165
+ const result = await defer(parent.id, producers[0]!.id, event, {
166
+ wakeOn: { taskIds: producers.map((producer) => producer.id), event, mode },
167
+ });
168
+ expect(result.structuredContent.success).toBe(true);
169
+ const schedule = (await getScheduledTaskById(result.structuredContent.scheduleId!))!;
170
+ return { parent, producers, schedule };
137
171
  }
138
172
 
139
173
  describe("defer-task wakeOn", () => {
174
+ test.each([
175
+ "completed",
176
+ "failed",
177
+ "cancelled",
178
+ ])("all is the default and waits for the final member to be %s", async (status) => {
179
+ const { producers, schedule } = await multiFixture();
180
+ await completeTask(producers[0]!.id, "first result");
181
+ await reconcileDeferredTaskWaits(producers[0]!.id);
182
+ expect(await children(schedule.id)).toHaveLength(0);
183
+ // The dispatch entry point must also enforce the whole set.
184
+ expect(await dispatchDeferredTaskWait(schedule.id, "task.completed")).toEqual({});
185
+ if (status === "completed") await completeTask(producers[1]!.id, "second result");
186
+ else if (status === "failed") await failTask(producers[1]!.id, "second result failed");
187
+ else await cancelTask(producers[1]!.id, "second result cancelled");
188
+ await reconcileDeferredTaskWaits(producers[1]!.id);
189
+ const found = await children(schedule.id);
190
+ expect(found).toHaveLength(1);
191
+ expect(found[0]!.task).toContain("all tasks matched settled");
192
+ expect(found[0]!.task).toContain(`${producers[1]!.id} (${status})`);
193
+ expect((await getScheduledTaskById(schedule.id))?.enabled).toBe(false);
194
+ });
195
+
196
+ test.each([
197
+ "completed",
198
+ "failed",
199
+ "cancelled",
200
+ ])("explicit any wakes on the first %s member", async (status) => {
201
+ const { producers, schedule } = await multiFixture("any");
202
+ if (status === "completed") await completeTask(producers[1]!.id, "ready");
203
+ else if (status === "failed") await failTask(producers[1]!.id, "failed");
204
+ else await cancelTask(producers[1]!.id, "cancelled");
205
+ await reconcileDeferredTaskWaits(producers[1]!.id);
206
+ expect(await children(schedule.id)).toHaveLength(1);
207
+ expect((await children(schedule.id))[0]!.task).toContain("any tasks matched settled");
208
+ expect((await getTaskById(producers[0]!.id))?.status).toBe("in_progress");
209
+ await completeTask(producers[0]!.id, "later");
210
+ await reconcileDeferredTaskWaits();
211
+ await executeSchedule(schedule);
212
+ expect(await children(schedule.id)).toHaveLength(1);
213
+ });
214
+
215
+ test("all follows one member through repeated deferrals and restart without extending the ceiling", async () => {
216
+ const { producers, schedule } = await multiFixture("all");
217
+ await completeTask(producers[0]!.id, "ready");
218
+ let current = producers[1]!;
219
+ for (let step = 0; step < 2; step++) {
220
+ const deferred = await defer(current.id, current.id, "settled", { wakeOn: undefined });
221
+ expect(deferred.structuredContent.success).toBe(true);
222
+ await reconcileDeferredTaskWaits(current.id);
223
+ expect(await children(schedule.id)).toHaveLength(0);
224
+ expect(await dispatchDeferredTaskWait(schedule.id, "task.completed")).toEqual({});
225
+ const successor = (await getScheduledTaskById(deferred.structuredContent.scheduleId!))!;
226
+ await executeSchedule(successor);
227
+ current = (await children(successor.id))[0]!;
228
+ closeDb();
229
+ initDb(path);
230
+ await initDeferredTaskWaits();
231
+ expect(await children(schedule.id)).toHaveLength(0);
232
+ expect((await getScheduledTaskById(schedule.id))?.nextRunAt).toBe(schedule.nextRunAt);
233
+ }
234
+ await completeTask(current.id, "final result");
235
+ const child = await awaitChild(schedule.id);
236
+ expect(child.task).toContain(`${current.id} (completed)`);
237
+ expect(child.task).toContain(`${producers[0]!.id} (completed)`);
238
+ expect(await children(schedule.id)).toHaveLength(1);
239
+ });
240
+
241
+ test("any ignores a deferred completion until another member really settles", async () => {
242
+ const { producers, schedule } = await multiFixture("any");
243
+ await defer(producers[0]!.id, producers[0]!.id, "settled", { wakeOn: undefined });
244
+ await reconcileDeferredTaskWaits();
245
+ expect(await children(schedule.id)).toHaveLength(0);
246
+ await failTask(producers[1]!.id, "real failure");
247
+ await reconcileDeferredTaskWaits(producers[1]!.id);
248
+ expect(await children(schedule.id)).toHaveLength(1);
249
+ });
250
+
251
+ test("the set's ceiling fires before all settle and later events cannot duplicate it", async () => {
252
+ const { producers, schedule } = await multiFixture();
253
+ await completeTask(producers[0]!.id, "ready");
254
+ await reconcileDeferredTaskWaits();
255
+ expect(await children(schedule.id)).toHaveLength(0);
256
+ await executeSchedule(schedule);
257
+ expect((await children(schedule.id))[0]!.task).toContain("ceiling expired");
258
+ await completeTask(producers[1]!.id, "late");
259
+ await reconcileDeferredTaskWaits();
260
+ expect(await children(schedule.id)).toHaveLength(1);
261
+ expect((await waitState(schedule.id))?.firedBy).toBe("ceiling");
262
+ });
263
+
264
+ test.each([
265
+ "all",
266
+ "any",
267
+ ] as const)("%s concurrent terminal events and the ceiling create exactly one child", async (mode) => {
268
+ const { producers, schedule } = await multiFixture(mode);
269
+ await initDeferredTaskWaits();
270
+ await Promise.all([
271
+ completeTask(producers[0]!.id, "ready"),
272
+ failTask(producers[1]!.id, "failed"),
273
+ executeSchedule(schedule),
274
+ ]);
275
+ await reconcileDeferredTaskWaits();
276
+ const found = await children(schedule.id);
277
+ expect(found).toHaveLength(1);
278
+ expect((await waitState(schedule.id))?.childTaskId).toBe(found[0]!.id);
279
+ });
280
+
281
+ test.each([
282
+ "all",
283
+ "any",
284
+ ] as const)("%s respects event filters when a member fails", async (mode) => {
285
+ const { producers, schedule } = await multiFixture(mode, "task.completed");
286
+ await failTask(producers[0]!.id, "wrong event");
287
+ await reconcileDeferredTaskWaits();
288
+ expect(await children(schedule.id)).toHaveLength(0);
289
+ await completeTask(producers[1]!.id, "right event");
290
+ await reconcileDeferredTaskWaits();
291
+ expect(await children(schedule.id)).toHaveLength(mode === "all" ? 0 : 1);
292
+ await executeSchedule(schedule);
293
+ expect(await children(schedule.id)).toHaveLength(1);
294
+ });
295
+
296
+ test("one invalid member rejects the entire set before any schedule or completion is committed", async () => {
297
+ const parent = await activeTask();
298
+ const valid = await activeTask();
299
+ const terminal = await activeTask();
300
+ await completeTask(terminal.id, "done");
301
+ for (const mode of ["all", "any"]) {
302
+ for (const [id, message] of [
303
+ [terminal.id, "already completed"],
304
+ [crypto.randomUUID(), "not found"],
305
+ [parent.id, "Cannot wake on"],
306
+ ]) {
307
+ const result = await defer(parent.id, valid.id, "settled", {
308
+ wakeOn: { taskIds: [valid.id, id], event: "settled", mode },
309
+ });
310
+ expect(result.structuredContent.success).toBe(false);
311
+ expect(result.structuredContent.message).toContain(message!);
312
+ }
313
+ }
314
+ expect((await getTaskById(parent.id))?.status).toBe("in_progress");
315
+ expect(
316
+ await getDbClient().query("SELECT id FROM scheduled_tasks WHERE parentTaskId = ?", [
317
+ parent.id,
318
+ ]),
319
+ ).toHaveLength(0);
320
+ });
321
+
322
+ test("multi-ID schema rejects empty, duplicate, ambiguous, or missing IDs and invalid modes", async () => {
323
+ const parent = await activeTask();
324
+ const producer = await activeTask();
325
+ const base = { taskId: parent.id, summary: "done", note: "wait", delayMs: 60000 };
326
+ for (const fields of [
327
+ {},
328
+ { taskIds: [] },
329
+ { taskIds: [""] },
330
+ { taskIds: [producer.id, producer.id] },
331
+ { taskId: producer.id, taskIds: [producer.id] },
332
+ { taskIds: [producer.id], mode: "some" },
333
+ ]) {
334
+ expect(
335
+ tool.inputSchema.safeParse({ ...base, wakeOn: { event: "settled", ...fields } }).success,
336
+ ).toBe(false);
337
+ }
338
+ const wakeOn = { taskIds: [producer.id], event: "settled" };
339
+ expect(tool.inputSchema.safeParse({ ...base, wakeOn }).success).toBe(true);
340
+ for (const extra of [
341
+ { delayMs: undefined },
342
+ { runAt: new Date(Date.now() + 60000).toISOString() },
343
+ ]) {
344
+ expect(
345
+ (await defer(parent.id, producer.id, "settled", { wakeOn, ...extra })).structuredContent
346
+ .success,
347
+ ).toBe(false);
348
+ }
349
+ });
350
+
140
351
  test("ordinary schedules neither suppress a completion nor retarget its waiters", async () => {
141
352
  const { producer, schedule } = await fixture();
142
353
  const ordinary = await createScheduledTask({
@@ -392,7 +603,143 @@ describe("defer-task wakeOn", () => {
392
603
  expect(await children(schedule.id)).toHaveLength(1);
393
604
  });
394
605
 
395
- test("unmatched task or event does not wake; cancelled producer still has a ceiling", async () => {
606
+ test("settled wakes on the cancellation bus and consumes the ceiling once", async () => {
607
+ await initDeferredTaskWaits();
608
+ const { producer, schedule } = await fixture();
609
+ await cancelTask(producer.id, "cancelled");
610
+ expect((await awaitChild(schedule.id)).task).toContain(
611
+ `Wake-up cause: task.cancelled for task ${producer.id}`,
612
+ );
613
+ expect(await waitState(schedule.id)).toMatchObject({ firedBy: "task.cancelled" });
614
+ await executeSchedule(schedule);
615
+ expect(await children(schedule.id)).toHaveLength(1);
616
+ });
617
+
618
+ test("restart reconciles cancellation missed while the listener was stopped", async () => {
619
+ const { producer, schedule } = await fixture();
620
+ await cancelTask(producer.id, "cancelled while stopped");
621
+ closeDb();
622
+ initDb(path);
623
+ await initDeferredTaskWaits();
624
+ expect(await waitState(schedule.id)).toMatchObject({
625
+ status: "fired",
626
+ firedBy: "task.cancelled",
627
+ });
628
+ expect(await children(schedule.id)).toHaveLength(1);
629
+ });
630
+
631
+ test.each([
632
+ "task.completed",
633
+ "task.failed",
634
+ ] as const)("%s does not wake on cancellation and retains the ceiling", async (event) => {
635
+ await initDeferredTaskWaits();
636
+ const { producer, schedule } = await fixture(event);
637
+ await cancelTask(producer.id, "cancelled");
638
+ await reconcileDeferredTaskWaits(producer.id);
639
+ expect(await dispatchDeferredTaskWait(schedule.id, "task.cancelled")).toEqual({});
640
+ expect(await children(schedule.id)).toHaveLength(0);
641
+ expect(await waitState(schedule.id)).toMatchObject({ status: "pending" });
642
+ await executeSchedule(schedule);
643
+ expect(await waitState(schedule.id)).toMatchObject({ firedBy: "ceiling" });
644
+ });
645
+
646
+ test.each([
647
+ "resume first",
648
+ "supersede first",
649
+ ])("superseded follows the resume without waking or extending the ceiling: %s", async (order) => {
650
+ await initDeferredTaskWaits();
651
+ for (const event of ["settled", "task.completed", "task.failed"] as const) {
652
+ const { producer, schedule } = await fixture(event);
653
+ let resume: AgentTask | undefined;
654
+ if (order === "resume first") {
655
+ resume = await resumeTask(producer.id);
656
+ await reconcileDeferredTaskWaits();
657
+ expect(await waitState(schedule.id)).toMatchObject({ taskId: producer.id });
658
+ }
659
+ await supersedeTask(producer.id, {
660
+ reason: "manual_supersede",
661
+ resumeTaskId: resume?.id ?? null,
662
+ });
663
+ if (!resume) {
664
+ await reconcileDeferredTaskWaits(producer.id);
665
+ expect(await waitState(schedule.id)).toMatchObject({
666
+ taskId: producer.id,
667
+ status: "pending",
668
+ });
669
+ resume = await resumeTask(producer.id);
670
+ }
671
+ await awaitWatchedTask(schedule.id, resume.id);
672
+ expect(await waitState(schedule.id)).toMatchObject({ status: "pending", firedBy: null });
673
+ expect(await children(schedule.id)).toHaveLength(0);
674
+ expect((await getScheduledTaskById(schedule.id))?.nextRunAt).toBe(schedule.nextRunAt);
675
+ if (event === "task.failed") await failTask(resume.id, "resume failed");
676
+ else await completeTask(resume.id, "resume completed");
677
+ expect((await awaitChild(schedule.id)).task).toContain(`for task ${resume.id}`);
678
+ await executeSchedule(schedule);
679
+ expect(await children(schedule.id)).toHaveLength(1);
680
+ }
681
+ });
682
+
683
+ test("superseded without a resume holds to its ceiling and a late resume cannot retarget a fired wait", async () => {
684
+ await initDeferredTaskWaits();
685
+ const { producer, schedule } = await fixture();
686
+ await supersedeTask(producer.id, { reason: "manual_supersede", resumeTaskId: null });
687
+ // An ordinary follow-up is not the replacement for superseded work.
688
+ await createTaskExtended("unrelated follow-up", {
689
+ parentTaskId: producer.id,
690
+ taskType: "follow-up",
691
+ });
692
+ await reconcileDeferredTaskWaits(producer.id);
693
+ expect(await waitState(schedule.id)).toMatchObject({ taskId: producer.id, status: "pending" });
694
+ expect(await children(schedule.id)).toHaveLength(0);
695
+ await executeSchedule(schedule);
696
+ const fired = await waitState(schedule.id);
697
+ expect(fired).toMatchObject({ firedBy: "ceiling" });
698
+ const resume = await resumeTask(producer.id);
699
+ await completeTask(resume.id, "too late");
700
+ await reconcileDeferredTaskWaits();
701
+ expect(await waitState(schedule.id)).toEqual(fired);
702
+ expect(await children(schedule.id)).toHaveLength(1);
703
+ });
704
+
705
+ test("restart follows a missed supersede chain to an already-terminal resume", async () => {
706
+ const { producer, schedule } = await fixture();
707
+ let current = producer;
708
+ for (let step = 0; step < 2; step++) {
709
+ await supersedeTask(current.id, { reason: "manual_supersede", resumeTaskId: null });
710
+ current = await resumeTask(current.id);
711
+ }
712
+ await completeTask(current.id, "final result");
713
+ closeDb();
714
+ initDb(path);
715
+ await initDeferredTaskWaits();
716
+ expect(await waitState(schedule.id)).toMatchObject({ taskId: current.id, status: "fired" });
717
+ expect((await awaitChild(schedule.id)).task).toContain(`for task ${current.id}`);
718
+ expect(await children(schedule.id)).toHaveLength(1);
719
+ });
720
+
721
+ test("a wait watching both the superseded parent and its resume collapses to one member", async () => {
722
+ const parent = await activeTask();
723
+ const producer = await activeTask();
724
+ const resume = await resumeTask(producer.id);
725
+ const deferred = await defer(parent.id, producer.id, "settled", {
726
+ wakeOn: { taskIds: [producer.id, resume.id], event: "settled" },
727
+ });
728
+ const scheduleId = deferred.structuredContent.scheduleId!;
729
+ await supersedeTask(producer.id, { reason: "manual_supersede", resumeTaskId: resume.id });
730
+ await reconcileDeferredTaskWaits();
731
+ expect(await waitState(scheduleId)).toMatchObject({ taskId: resume.id, status: "pending" });
732
+ expect(
733
+ await getDbClient().query("SELECT * FROM deferred_task_wait_members WHERE scheduleId = ?", [
734
+ scheduleId,
735
+ ]),
736
+ ).toHaveLength(1);
737
+ await completeTask(resume.id, "done");
738
+ await reconcileDeferredTaskWaits(resume.id);
739
+ expect(await children(scheduleId)).toHaveLength(1);
740
+ });
741
+
742
+ test("unmatched task or event does not wake", async () => {
396
743
  await initDeferredTaskWaits();
397
744
  const { producer, schedule } = await fixture("task.completed");
398
745
  const other = await activeTask();
@@ -400,12 +747,6 @@ describe("defer-task wakeOn", () => {
400
747
  await failTask(producer.id, "wrong event");
401
748
  await reconcileDeferredTaskWaits();
402
749
  expect(await children(schedule.id)).toHaveLength(0);
403
- const cancelled = await fixture();
404
- await cancelTask(cancelled.producer.id, "cancelled");
405
- await reconcileDeferredTaskWaits();
406
- expect(await children(cancelled.schedule.id)).toHaveLength(0);
407
- await executeSchedule(cancelled.schedule);
408
- expect(await children(cancelled.schedule.id)).toHaveLength(1);
409
750
  await executeSchedule(schedule);
410
751
  });
411
752
 
@@ -18,7 +18,7 @@ import { listenOnFreePort } from "./test-net";
18
18
 
19
19
  let TEST_BASE_URL = "";
20
20
  const ORG_ID = "org-adapter-test";
21
- const API_KEY = "cog_adapter_key";
21
+ const API_KEY = "example-cog_adapter_key";
22
22
 
23
23
  // ---------------------------------------------------------------------------
24
24
  // Controllable mock state
@@ -13,7 +13,7 @@ import { listenOnFreePort } from "./test-net";
13
13
 
14
14
  let TEST_BASE_URL = "";
15
15
  const ORG_ID = "org-test-123";
16
- const API_KEY = "cog_test_key";
16
+ const API_KEY = "example-cog_test_key";
17
17
 
18
18
  // Canned responses -----------------------------------------------------------
19
19
  const SESSION_RESPONSE = {
@@ -78,7 +78,7 @@ describe("docs API index footprint", () => {
78
78
  expect(tasks).toContain('"path":"/api/task-templates","method":"get"');
79
79
  });
80
80
 
81
- test("OpenAPI and generated pages keep all 359 operations", async () => {
81
+ test("OpenAPI and generated pages keep all 370 operations", async () => {
82
82
  const spec = await Bun.file(resolve(repoRoot, "openapi.json")).json();
83
83
  const operationKeys = [];
84
84
  const tags = new Set();
@@ -103,8 +103,8 @@ describe("docs API index footprint", () => {
103
103
  expect(spec.paths["/api/agents/{id}/runtime"]?.get).toBeDefined();
104
104
 
105
105
  const generatedKeys = await generatedOperationKeys();
106
- expect(operationKeys).toHaveLength(361);
107
- expect(generatedKeys).toHaveLength(361);
106
+ expect(operationKeys).toHaveLength(372);
107
+ expect(generatedKeys).toHaveLength(372);
108
108
  expect(new Set(generatedKeys)).toEqual(new Set(operationKeys));
109
109
  });
110
110
 
@@ -70,9 +70,9 @@ describe("E2B env helpers", () => {
70
70
  delete process.env.API_KEY;
71
71
  delete process.env.AGENT_SWARM_API_KEY;
72
72
  try {
73
- expect(resolveSwarmApiKey({ API_KEY: "legacy", AGENT_SWARM_API_KEY: "preferred" })).toBe(
74
- "preferred",
75
- );
73
+ expect(
74
+ resolveSwarmApiKey({ API_KEY: "legacy", AGENT_SWARM_API_KEY: "example-preferred" }),
75
+ ).toBe("example-preferred");
76
76
  expect(resolveSwarmApiKey({ API_KEY: "legacy" }, "explicit")).toBe("explicit");
77
77
  expect(() => resolveSwarmApiKey({})).toThrow("Missing swarm API key");
78
78
  } finally {
@@ -92,14 +92,16 @@ describe("E2B env helpers", () => {
92
92
  test("selectEnv and redactWithEnv keep secrets out of logs", () => {
93
93
  const selected = selectEnv(
94
94
  {
95
- API_KEY: "super-secret-value",
96
- E2B_API_KEY: "controller-secret",
95
+ API_KEY: "example-super-secret-value",
96
+ E2B_API_KEY: "example-controller-secret",
97
97
  PATH: "/bin",
98
98
  },
99
99
  ["API_KEY", "PATH"],
100
100
  );
101
- expect(selected).toEqual({ API_KEY: "super-secret-value", PATH: "/bin" });
102
- expect(redactWithEnv("token=super-secret-value", selected)).toContain("[REDACTED:API_KEY]");
101
+ expect(selected).toEqual({ API_KEY: "example-super-secret-value", PATH: "/bin" });
102
+ expect(redactWithEnv("token=example-super-secret-value", selected)).toContain(
103
+ "[REDACTED:API_KEY]",
104
+ );
103
105
  });
104
106
 
105
107
  test("redactObjectWithEnv redacts token-like response fields", () => {
@@ -107,8 +109,8 @@ describe("E2B env helpers", () => {
107
109
  redactObjectWithEnv(
108
110
  {
109
111
  sandboxID: "sbx123",
110
- envdAccessToken: "controller-token-that-should-not-print",
111
- nested: { trafficAccessToken: "traffic-token-that-should-not-print" },
112
+ envdAccessToken: "example-controller-token-that-should-not-print",
113
+ nested: { trafficAccessToken: "example-traffic-token-that-should-not-print" },
112
114
  },
113
115
  {},
114
116
  ),
@@ -232,7 +234,7 @@ describe("E2B dispatch helpers", () => {
232
234
  test("E2B SDK connection options preserve loaded controller endpoints", () => {
233
235
  expect(
234
236
  e2bSdkConnectionOptions(
235
- "controller-key",
237
+ "example-controller-key",
236
238
  {
237
239
  E2B_DOMAIN: "sandbox.example.com",
238
240
  E2B_SANDBOX_URL: "https://sandbox.sandbox.example.com",
@@ -240,7 +242,7 @@ describe("E2B dispatch helpers", () => {
240
242
  "https://api.sandbox.example.com",
241
243
  ),
242
244
  ).toEqual({
243
- apiKey: "controller-key",
245
+ apiKey: "example-controller-key",
244
246
  domain: "sandbox.example.com",
245
247
  sandboxUrl: "https://sandbox.sandbox.example.com",
246
248
  apiUrl: "https://api.sandbox.example.com",
@@ -360,7 +362,7 @@ describe("E2B dispatch helpers", () => {
360
362
  name: "agent swarm/worker",
361
363
  public: true,
362
364
  e2bEnv: {
363
- E2B_API_KEY: "controller-secret",
365
+ E2B_API_KEY: "example-controller-secret",
364
366
  E2B_API_URL: "https://api.e2b.example",
365
367
  },
366
368
  });
@@ -370,7 +372,7 @@ describe("E2B dispatch helpers", () => {
370
372
  expect(calls[0]?.init?.method).toBe("PATCH");
371
373
  expect(calls[0]?.init?.headers).toMatchObject({
372
374
  "Content-Type": "application/json",
373
- "X-API-Key": "controller-secret",
375
+ "X-API-Key": "example-controller-secret",
374
376
  });
375
377
  expect(calls[0]?.init?.body).toBe(JSON.stringify({ public: true }));
376
378
  expect(result.stdout).toBe(
@@ -826,7 +828,7 @@ describe("E2B swarm grouping + deep-link (Phase 4)", () => {
826
828
 
827
829
  test("e2b dashboard deep-link uses camelCase params and hides the key by default", () => {
828
830
  const masked = buildDashboardDeepLink(
829
- { apiUrl: "https://api.example.com", apiKey: "super-secret-key", name: "demo" },
831
+ { apiUrl: "https://api.example.com", apiKey: "example-super-secret-key", name: "demo" },
830
832
  false,
831
833
  );
832
834
  // camelCase params the SPA reads.
@@ -834,7 +836,7 @@ describe("E2B swarm grouping + deep-link (Phase 4)", () => {
834
836
  expect(masked).toContain("name=demo");
835
837
  // Key hidden — the real value MUST NOT appear.
836
838
  expect(masked).toContain("apiKey=<hidden — pass --reveal-key>");
837
- expect(masked).not.toContain("super-secret-key");
839
+ expect(masked).not.toContain("example-super-secret-key");
838
840
  // Never snake_case.
839
841
  expect(masked).not.toContain("api_url");
840
842
  expect(masked).not.toContain("api_key");
@@ -842,10 +844,10 @@ describe("E2B swarm grouping + deep-link (Phase 4)", () => {
842
844
 
843
845
  test("e2b dashboard deep-link embeds the real key only when revealed", () => {
844
846
  const revealed = buildDashboardDeepLink(
845
- { apiUrl: "https://api.example.com", apiKey: "super-secret-key", name: "demo" },
847
+ { apiUrl: "https://api.example.com", apiKey: "example-super-secret-key", name: "demo" },
846
848
  true,
847
849
  );
848
- expect(revealed).toContain("apiKey=super-secret-key");
850
+ expect(revealed).toContain("apiKey=example-super-secret-key");
849
851
  expect(revealed).not.toContain("<hidden");
850
852
  expect(revealed).toContain("apiUrl=https%3A%2F%2Fapi.example.com");
851
853
  });
@@ -876,10 +878,10 @@ describe("E2B swarm grouping + deep-link (Phase 4)", () => {
876
878
  test("onboarding dashboard builder emits camelCase apiUrl/apiKey (not snake_case)", () => {
877
879
  const url = buildOnboardDashboardUrl({
878
880
  apiUrl: "http://localhost:3013",
879
- apiKey: "onboard-key",
881
+ apiKey: "example-onboard-key",
880
882
  });
881
883
  expect(url).toContain("apiUrl=http%3A%2F%2Flocalhost%3A3013");
882
- expect(url).toContain("apiKey=onboard-key");
884
+ expect(url).toContain("apiKey=example-onboard-key");
883
885
  // The bug we fixed: snake_case is silently ignored by the SPA.
884
886
  expect(url).not.toContain("api_url");
885
887
  expect(url).not.toContain("api_key");
@@ -93,7 +93,7 @@ describe("Claude E2E transport harness", () => {
93
93
  });
94
94
 
95
95
  test("writes the agent override and validates transport metadata and Claude costs", async () => {
96
- process.env.ANTHROPIC_API_KEY = "test-anthropic-key";
96
+ process.env.ANTHROPIC_API_KEY = "example-test-anthropic-key";
97
97
  process.env.E2E_CLAUDE_BINARY = "/opt/claude-pinned";
98
98
  process.env.E2E_WORKER_BINARY = "/opt/agent-swarm";
99
99
  const spawnCalls: Parameters<typeof Bun.spawn>[] = [];
@@ -141,7 +141,7 @@ describe("Claude E2E transport harness", () => {
141
141
  });
142
142
 
143
143
  test("fails when Claude metadata or costs do not prove the selected transport", async () => {
144
- process.env.ANTHROPIC_API_KEY = "test-anthropic-key";
144
+ process.env.ANTHROPIC_API_KEY = "example-test-anthropic-key";
145
145
  process.env.E2E_CLAUDE_TRANSPORT = "sdk";
146
146
  process.env.E2E_COST_TIMEOUT_MS = "1";
147
147
  const spawnSpy = spyOn(Bun, "spawn").mockImplementation((() =>
@@ -184,7 +184,7 @@ describe("Claude E2E transport harness", () => {
184
184
 
185
185
  test("does not validate the Claude flag for non-Claude legs", async () => {
186
186
  process.env.E2E_CLAUDE_TRANSPORT = "invalid";
187
- process.env.OPENROUTER_API_KEY = "test-openrouter-key";
187
+ process.env.OPENROUTER_API_KEY = "example-test-openrouter-key";
188
188
  const spawnSpy = spyOn(Bun, "spawn").mockImplementation((() =>
189
189
  fakeWorker()) as typeof Bun.spawn);
190
190
 
@@ -5,10 +5,10 @@ describe("e2e redaction", () => {
5
5
  test("masks exact known values and common token shapes", () => {
6
6
  const text = [
7
7
  "Authorization: Bearer 0123456789abcdef0123456789abcdef",
8
- "OPENAI_API_KEY=sk-proj-abcdefghijklmnopqrstuvwxyz0123",
9
- "anthropic sk-ant-api03-abcdefghijklmnop",
10
- "github ghp_abcdefghijklmnopqrstuvwxyz0123456789",
11
- "slack xoxb-1234567890-abcdefghijk",
8
+ "OPENAI_API_KEY=example-sk-proj-abcdefghijklmnopqrstuvwxyz0123",
9
+ "anthropic example-sk-ant-api03-abcdefghijklmnop",
10
+ "github example-ghp_abcdefghijklmnopqrstuvwxyz0123456789",
11
+ "slack example-xoxb-1234567890-abcdefghijk",
12
12
  'auth.json {"refresh_token":"rt-abcdefghijklmnop","id_token":"x.y.z"}',
13
13
  "jwt eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
14
14
  "my-secret-run-key-value appears here",
@@ -42,7 +42,7 @@ describe("e2e redaction", () => {
42
42
  test("knownSecrets reads provider env values and the codex blob fields", () => {
43
43
  const saved = { ...process.env };
44
44
  try {
45
- process.env.OPENROUTER_API_KEY = "sk-or-v1-known-value-1234";
45
+ process.env.OPENROUTER_API_KEY = "example-sk-or-v1-known-value-1234";
46
46
  process.env.CODEX_OAUTH = JSON.stringify({
47
47
  access: "access-token-value-1234",
48
48
  refresh: "refresh-token-value-1234",
@@ -50,7 +50,7 @@ describe("e2e redaction", () => {
50
50
  accountId: "acct",
51
51
  });
52
52
  const known = knownSecrets(["extra-secret-value"]);
53
- expect(known).toContain("sk-or-v1-known-value-1234");
53
+ expect(known).toContain("example-sk-or-v1-known-value-1234");
54
54
  expect(known).toContain("access-token-value-1234");
55
55
  expect(known).toContain("refresh-token-value-1234");
56
56
  expect(known).toContain("extra-secret-value");