@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
@@ -45,6 +45,8 @@ import {
45
45
  expireStaleRuntimeInstances,
46
46
  } from "../be/multi-runtime";
47
47
  import { promotePendingSteeringForTask } from "../be/steering";
48
+ import type { HeartbeatAction, HeartbeatClassification } from "../extensions/contract";
49
+ import { dispatchPre, extensionIdForAgent } from "../extensions/dispatcher";
48
50
  import { resolveTemplate } from "../prompts/resolver";
49
51
  import {
50
52
  createPoolStarvationDecisionTask,
@@ -159,6 +161,21 @@ export function maxResumeGenerations(): number {
159
161
 
160
162
  export const RESUME_BUDGET_EXHAUSTED_REASON = "resume_budget_exhausted";
161
163
 
164
+ type StallClassification = HeartbeatClassification;
165
+ type RemediationAction = HeartbeatAction;
166
+
167
+ type RemediationDecision = { action: RemediationAction; reason: string };
168
+
169
+ interface RemediationFacts {
170
+ skipAutoResume: boolean;
171
+ alreadyResumed: boolean;
172
+ resumeBudgetExhausted: boolean;
173
+ supersedeReason: string;
174
+ legacyFailReason: string;
175
+ }
176
+
177
+ const REMEDIATION_ACTIONS = new Set<RemediationAction>(["supersede-resume", "fail", "record"]);
178
+
162
179
  /**
163
180
  * Grace window (minutes) a crash-recovery resume pinned to its original agent
164
181
  * (DES-523 Phase 1) waits to be reclaimed before the reaper concludes the agent
@@ -206,6 +223,11 @@ const HEARTBEAT_CHECKLIST_DISABLE = Boolean(process.env.HEARTBEAT_CHECKLIST_DISA
206
223
 
207
224
  export interface HeartbeatFindings {
208
225
  stalledTasks: AgentTask[];
226
+ extensionSkipped: Array<{
227
+ taskId: string;
228
+ extension: { id: string; name: string };
229
+ reason: string;
230
+ }>;
209
231
  autoFailedTasks: Array<{ taskId: string; agentId: string; reason: string }>;
210
232
  autoResumedTasks: Array<{
211
233
  taskId: string;
@@ -296,6 +318,7 @@ export async function preflightGate(): Promise<boolean> {
296
318
  export async function codeLevelTriage(): Promise<HeartbeatFindings> {
297
319
  const findings: HeartbeatFindings = {
298
320
  stalledTasks: [],
321
+ extensionSkipped: [],
299
322
  autoFailedTasks: [],
300
323
  autoResumedTasks: [],
301
324
  pinnedResumes: [],
@@ -377,131 +400,173 @@ async function detectAndRemediateStalledTasks(findings: HeartbeatFindings): Prom
377
400
  continue;
378
401
  }
379
402
 
403
+ let classification: StallClassification | undefined;
404
+ let remediationOpts: RemediationOptions | undefined;
405
+ let proposed: RemediationDecision | undefined;
406
+
380
407
  if (!session) {
381
- // Case A: No active session — worker is dead
382
408
  if (taskAgeMs >= stallThresholdNoSessionMin() * 60 * 1000) {
383
- await remediateCrashedWorkerTask(findings, task, {
409
+ classification = "no-session";
410
+ remediationOpts = {
384
411
  supersedeReason:
385
412
  "Auto-superseded by heartbeat: worker session not found (no active session for task)",
386
413
  legacyFailReason:
387
414
  "Auto-failed by heartbeat: worker session not found (no active session for task)",
388
415
  shortLabel: "no active session",
389
- });
416
+ };
417
+ proposed = await defaultRemediationDecision(task, remediationOpts);
390
418
  }
391
419
  } else {
392
420
  const isStaleHeartbeat =
393
421
  sessionHeartbeatAgeMs! >= stallThresholdStaleHeartbeatMin() * 60 * 1000;
394
422
 
395
423
  if (isStaleHeartbeat) {
396
- // Case B: Session exists but heartbeat is stale — worker likely crashed
397
424
  if (taskAgeMs >= stallThresholdStaleHeartbeatMin() * 60 * 1000) {
398
- await remediateCrashedWorkerTask(findings, task, {
425
+ classification = "stale-session";
426
+ remediationOpts = {
399
427
  supersedeReason:
400
428
  "Auto-superseded by heartbeat: worker session heartbeat is stale (likely crashed)",
401
429
  legacyFailReason:
402
430
  "Auto-failed by heartbeat: worker session heartbeat is stale (likely crashed)",
403
431
  shortLabel: "stale session heartbeat",
404
432
  cleanupActiveSession: true,
405
- });
433
+ };
434
+ proposed = await defaultRemediationDecision(task, remediationOpts);
406
435
  }
407
436
  } else {
408
- // Case C: Session exists and heartbeat is fresh — ambiguous
409
437
  if (taskAgeMs >= stallThresholdMinutes() * 60 * 1000) {
410
- findings.stalledTasks.push(task);
438
+ classification = "fresh-stalled";
439
+ remediationOpts = {
440
+ supersedeReason:
441
+ "Auto-superseded by heartbeat: task stalled despite a fresh session heartbeat",
442
+ legacyFailReason:
443
+ "Auto-failed by heartbeat: task stalled despite a fresh session heartbeat",
444
+ shortLabel: "fresh session heartbeat",
445
+ };
446
+ proposed = {
447
+ action: "record",
448
+ reason: "Task stalled despite a fresh session heartbeat",
449
+ };
411
450
  }
412
451
  }
413
452
  }
453
+
454
+ if (!classification || !remediationOpts || !proposed) continue;
455
+
456
+ const dispatched = await dispatchPre(
457
+ "pre.heartbeat.remediate",
458
+ {
459
+ task,
460
+ ...(session ? { session } : {}),
461
+ classification,
462
+ proposedAction: proposed.action,
463
+ reason: proposed.reason,
464
+ taskAgeMs,
465
+ ...(sessionHeartbeatAgeMs === null ? {} : { sessionHeartbeatAgeMs }),
466
+ },
467
+ { skipExtensionId: extensionIdForAgent(task.creatorAgentId) },
468
+ );
469
+
470
+ if (dispatched.action === "block") {
471
+ findings.stalledTasks.push(task);
472
+ findings.extensionSkipped.push({
473
+ taskId: task.id,
474
+ extension: dispatched.extension,
475
+ reason: dispatched.reason,
476
+ });
477
+ continue;
478
+ }
479
+
480
+ if (dispatched.action === "modify") {
481
+ const action = dispatched.data.proposedAction;
482
+ if (isRemediationAction(action)) {
483
+ proposed = { ...proposed, action };
484
+ } else {
485
+ console.warn(
486
+ "[Heartbeat] Extension returned an invalid remediation action:",
487
+ scrubSecrets(String(action)),
488
+ );
489
+ }
490
+ }
491
+
492
+ if (proposed.action === "record") {
493
+ findings.stalledTasks.push(task);
494
+ continue;
495
+ }
496
+
497
+ await remediateCrashedWorkerTask(findings, task, remediationOpts, proposed);
414
498
  }
415
499
  }
416
500
 
501
+ function isRemediationAction(value: unknown): value is RemediationAction {
502
+ return typeof value === "string" && REMEDIATION_ACTIONS.has(value as RemediationAction);
503
+ }
504
+
505
+ function decideRemediation(task: AgentTask, facts: RemediationFacts): RemediationDecision {
506
+ if (task.workflowRunStepId != null) {
507
+ return { action: "fail", reason: "superseded_workflow_task" };
508
+ }
509
+ if (facts.skipAutoResume || facts.alreadyResumed) {
510
+ return { action: "fail", reason: facts.legacyFailReason };
511
+ }
512
+ if (facts.resumeBudgetExhausted) {
513
+ return { action: "fail", reason: RESUME_BUDGET_EXHAUSTED_REASON };
514
+ }
515
+ return { action: "supersede-resume", reason: facts.supersedeReason };
516
+ }
517
+
518
+ async function defaultRemediationDecision(
519
+ task: AgentTask,
520
+ opts: RemediationOptions,
521
+ ): Promise<RemediationDecision> {
522
+ const skipAutoResume = SKIP_AUTO_RESUME_TYPES.has(task.taskType ?? "");
523
+ const isWorkflowStep = task.workflowRunStepId != null;
524
+ const alreadyResumed =
525
+ !skipAutoResume && !isWorkflowStep && (await hasNonTerminalResumeChild(task.id));
526
+ const resumeBudgetExhausted = getNextResumeGeneration(task) > maxResumeGenerations();
527
+ return decideRemediation(task, {
528
+ skipAutoResume,
529
+ alreadyResumed,
530
+ resumeBudgetExhausted,
531
+ supersedeReason: opts.supersedeReason,
532
+ legacyFailReason: opts.legacyFailReason,
533
+ });
534
+ }
535
+
536
+ interface RemediationOptions {
537
+ supersedeReason: string;
538
+ legacyFailReason: string;
539
+ shortLabel: string;
540
+ cleanupActiveSession?: boolean;
541
+ }
542
+
417
543
  /**
418
- * Shared remediation for Cases A (no active session) and B (stale heartbeat) of the
419
- * stalled-task detector. Prefers the supersede → resume follow-up path (DES-523) so a
420
- * crashed worker's task gets a fresh "resume" sibling instead of being silently dropped.
544
+ * Applies a selected remediation action for a stalled task. Extensions can replace
545
+ * the selected action before this function runs.
421
546
  *
422
- * Falls back to the legacy `failTask` path when:
423
- * - the task is a system task (heartbeat / boot-triage) — would loop forever,
424
- * - a non-terminal child already exists — a prior sweep already created a resume,
425
- * - `createResumeFollowUp` returns `workflow-skip` — workflow engine owns retries.
547
+ * The supersede and resume path gives crashed work a fresh sibling. The fail path keeps
548
+ * workflow ownership, excluded task types, idempotency, and resume-budget behavior.
426
549
  */
427
550
  async function remediateCrashedWorkerTask(
428
551
  findings: HeartbeatFindings,
429
552
  task: AgentTask,
430
- opts: {
431
- supersedeReason: string;
432
- legacyFailReason: string;
433
- shortLabel: string;
434
- cleanupActiveSession?: boolean;
435
- },
553
+ opts: RemediationOptions,
554
+ decision: RemediationDecision,
436
555
  ): Promise<void> {
437
556
  if (!task.agentId) return; // Type guard — caller already checked.
438
557
 
439
- const skipAutoResume = SKIP_AUTO_RESUME_TYPES.has(task.taskType ?? "");
440
- // Workflow-step tasks: skip supersede entirely so the engine's retry policy
441
- // owns recovery. `createResumeFollowUp` would also bail with `workflow-skip`,
442
- // but checking here avoids leaving the parent in `superseded` with a dangling
443
- // dedicated-reason `failTask` no-op chasing it.
444
- const isWorkflowStep = task.workflowRunStepId != null;
445
- // Idempotency: if a non-terminal `resume` child already exists for this
446
- // parent, a prior sweep already created the resume — fall back to the
447
- // legacy fail path. We filter on `taskType = 'resume'` specifically (not
448
- // any child task) because `send-task` auto-defaults `parentTaskId` to the
449
- // caller's current task, so a crashed worker with delegated subtasks
450
- // would otherwise be incorrectly skipped (PR #594 review).
451
- const alreadyResumed =
452
- !skipAutoResume && !isWorkflowStep && (await hasNonTerminalResumeChild(task.id));
453
-
454
- if (isWorkflowStep) {
455
- const failed = await failTask(task.id, "superseded_workflow_task");
558
+ if (decision.action === "fail") {
559
+ const failed = await failTask(task.id, decision.reason);
456
560
  if (failed) {
457
561
  findings.autoFailedTasks.push({
458
562
  taskId: task.id,
459
563
  agentId: task.agentId,
460
- reason: "superseded_workflow_task",
564
+ reason: decision.reason,
461
565
  });
462
566
  if (opts.cleanupActiveSession) await deleteActiveSession(task.id);
463
- console.log(
464
- `[Heartbeat] Workflow-step task ${task.id.slice(0, 8)} failed — engine will handle retry (${opts.shortLabel})`,
465
- );
466
- const remaining = await getActiveTaskCount(task.agentId);
467
- if (remaining === 0) await restoreAgentIdleAfterRemediation(task.agentId);
468
- }
469
- return;
470
- }
471
-
472
- if (skipAutoResume || alreadyResumed) {
473
- const failed = await failTask(task.id, opts.legacyFailReason);
474
- if (failed) {
475
- findings.autoFailedTasks.push({
476
- taskId: task.id,
477
- agentId: task.agentId,
478
- reason: opts.legacyFailReason,
479
- });
480
- if (opts.cleanupActiveSession) await deleteActiveSession(task.id);
481
- console.log(
482
- `[Heartbeat] Auto-failed task ${task.id.slice(0, 8)} — ${opts.shortLabel} (${
483
- skipAutoResume ? "skipRetry taskType" : "resume already exists"
484
- })`,
485
- );
486
- const remaining = await getActiveTaskCount(task.agentId);
487
- if (remaining === 0) await restoreAgentIdleAfterRemediation(task.agentId);
488
- }
489
- return;
490
- }
491
-
492
- const nextResumeGeneration = getNextResumeGeneration(task);
493
- if (nextResumeGeneration > maxResumeGenerations()) {
494
- const failed = await failTask(task.id, RESUME_BUDGET_EXHAUSTED_REASON);
495
- if (failed) {
496
- findings.autoFailedTasks.push({
497
- taskId: task.id,
498
- agentId: task.agentId,
499
- reason: RESUME_BUDGET_EXHAUSTED_REASON,
500
- });
501
- if (opts.cleanupActiveSession) await deleteActiveSession(task.id);
502
- console.warn(
503
- `[Heartbeat] Auto-failed task ${task.id.slice(0, 8)} — ${RESUME_BUDGET_EXHAUSTED_REASON} (${opts.shortLabel})`,
504
- );
567
+ const message = `[Heartbeat] Auto-failed task ${task.id.slice(0, 8)}: ${decision.reason} (${opts.shortLabel})`;
568
+ if (decision.reason === RESUME_BUDGET_EXHAUSTED_REASON) console.warn(message);
569
+ else console.log(message);
505
570
  const remaining = await getActiveTaskCount(task.agentId);
506
571
  if (remaining === 0) await restoreAgentIdleAfterRemediation(task.agentId);
507
572
  }
@@ -511,7 +576,7 @@ async function remediateCrashedWorkerTask(
511
576
  await beforeHeartbeatSupersedeForTests?.(task);
512
577
 
513
578
  const superseded = await supersedeTask(task.id, {
514
- reason: opts.supersedeReason,
579
+ reason: decision.reason,
515
580
  resumeTaskId: null,
516
581
  });
517
582
  if (!superseded) {
@@ -539,7 +604,7 @@ async function remediateCrashedWorkerTask(
539
604
  taskId: task.id,
540
605
  resumeTaskId: resume.task.id,
541
606
  agentId: task.agentId,
542
- reason: opts.supersedeReason,
607
+ reason: decision.reason,
543
608
  });
544
609
  // Phase 1 (DES-523): when the resume pinned back to the original
545
610
  // (stable-ID) agent, record it so the sweep summary surfaces the pin
@@ -766,6 +831,7 @@ export async function runRebootSweep(): Promise<void> {
766
831
  : preferredAgentId === task.agentId
767
832
  ? "continuity"
768
833
  : "reroute_fault",
834
+ routingSource: preferredAgentId === undefined ? undefined : "engine_default",
769
835
  tags,
770
836
  priority: task.priority,
771
837
  source: task.source,
@@ -1375,6 +1441,7 @@ export async function checkHeartbeatChecklist(): Promise<void> {
1375
1441
  await createTaskExtended(result.text, {
1376
1442
  agentId: lead.id,
1377
1443
  routingReason: "skill",
1444
+ routingSource: "engine_default",
1378
1445
  taskType: "heartbeat-checklist",
1379
1446
  tags: ["checklist", "auto-generated"],
1380
1447
  priority: 60,
@@ -1401,6 +1468,7 @@ export async function runHeartbeatSweep(): Promise<void> {
1401
1468
  if (!(await preflightGate())) {
1402
1469
  const cleanupOnlyFindings: HeartbeatFindings = {
1403
1470
  stalledTasks: [],
1471
+ extensionSkipped: [],
1404
1472
  autoFailedTasks: [],
1405
1473
  autoResumedTasks: [],
1406
1474
  pinnedResumes: [],
@@ -1467,6 +1535,9 @@ function logFindings(findings: HeartbeatFindings): void {
1467
1535
  if (findings.stalledTasks.length > 0) {
1468
1536
  parts.push(`stalled=${findings.stalledTasks.length}`);
1469
1537
  }
1538
+ if (findings.extensionSkipped.length > 0) {
1539
+ parts.push(`extension_skipped=${findings.extensionSkipped.length}`);
1540
+ }
1470
1541
  if (findings.workerHealthFixes.length > 0) {
1471
1542
  parts.push(`health_fixes=${findings.workerHealthFixes.length}`);
1472
1543
  }
@@ -1592,6 +1663,7 @@ export async function createBootTriageTask(): Promise<void> {
1592
1663
  await createTaskExtended(result.text, {
1593
1664
  agentId: lead.id,
1594
1665
  routingReason: "skill",
1666
+ routingSource: "engine_default",
1595
1667
  taskType: "boot-triage",
1596
1668
  tags: ["boot", "triage", "auto-generated"],
1597
1669
  priority: 70, // Higher than regular checklist (60)
@@ -399,7 +399,7 @@ const listAgentRuntimeInstances = route({
399
399
  pattern: ["api", "agents", null, "runtime-instances"],
400
400
  summary: "List runtime instances serving an agent",
401
401
  description:
402
- "Read-only view of the worker processes currently registered for a logical agent. Rows exist only for multi-runtime registrations (MULTI_RUNTIME_ENABLED), so the list is empty in the default configuration. `isLive` combines `status` with `lastSeenAt` freshness against the server's staleness cutoff (`staleThresholdMinutes`); `reportedSlots` is each process's self-reported capacity, distinct from the agent's logical `maxTasks` policy.",
402
+ "Read-only view of the worker processes currently registered for a logical agent. Rows exist only for multi-runtime registrations (MULTI_RUNTIME_ENABLED), so the list is empty when the flag is disabled. `isLive` combines `status` with `lastSeenAt` freshness against the server's staleness cutoff (`staleThresholdMinutes`); `reportedSlots` is each process's self-reported capacity, distinct from the agent's logical `maxTasks` policy.",
403
403
  tags: ["Agents"],
404
404
  params: z.object({ id: z.string() }),
405
405
  responses: {
@@ -23,6 +23,7 @@ import "./ecosystem";
23
23
 
24
24
  import "./api-keys";
25
25
  import "./events";
26
+ import "./extensions";
26
27
  import "./favorites";
27
28
  import "./fs";
28
29
  import "./heartbeat";
@@ -386,6 +386,7 @@ export async function handleApprovalRequests(
386
386
  await createTaskExtended(taskText, {
387
387
  agentId: sourceTask.agentId,
388
388
  routingReason: sourceTask.agentId ? "continuity" : undefined,
389
+ routingSource: sourceTask.agentId ? "engine_default" : undefined,
389
390
  parentTaskId: updated.sourceTaskId,
390
391
  source: "system",
391
392
  taskType: "hitl-follow-up",
package/src/http/apps.ts CHANGED
@@ -1410,15 +1410,20 @@ export async function handleApps(
1410
1410
  action_name: parsed.params.name,
1411
1411
  input_json: JSON.stringify(input),
1412
1412
  });
1413
- const task = await createTaskWithSiblingAwareness(taskPrompt.text, {
1414
- source: "api",
1415
- agentId: action.agentId ?? lead?.id,
1416
- routingReason: action.agentId ? "human_pinned" : lead ? "skill" : undefined,
1417
- // App-spawned tasks group under the app's asset namespace so a
1418
- // swarm-tasks source can pull them back via config.assetKey.
1419
- key: normalizeAssetKey(`shared/app:${app.id}/action:${parsed.params.name}/`),
1420
- ...(actor.startsWith("user:") ? { requestedByUserId: actor.slice("user:".length) } : {}),
1421
- });
1413
+ const task = await createTaskWithSiblingAwareness(
1414
+ taskPrompt.text,
1415
+ {
1416
+ source: "api",
1417
+ agentId: action.agentId ?? lead?.id,
1418
+ routingReason: action.agentId ? "human_pinned" : lead ? "skill" : undefined,
1419
+ routingSource: action.agentId || lead ? "engine_default" : undefined,
1420
+ // App-spawned tasks group under the app's asset namespace so a
1421
+ // swarm-tasks source can pull them back via config.assetKey.
1422
+ key: normalizeAssetKey(`shared/app:${app.id}/action:${parsed.params.name}/`),
1423
+ ...(actor.startsWith("user:") ? { requestedByUserId: actor.slice("user:".length) } : {}),
1424
+ },
1425
+ { origin: "app" },
1426
+ );
1422
1427
  runActionRoute.respond(res, 200, { ok: true, taskId: task.id, status: task.status });
1423
1428
  return true;
1424
1429
  }
@@ -310,7 +310,10 @@ export async function handleConfig(
310
310
  }
311
311
  const presence: Record<string, boolean> = {};
312
312
  for (const key of keys) {
313
- presence[key] = process.env[key] !== undefined && process.env[key] !== "";
313
+ // Organization identity is trimmed by both /status and telemetry.
314
+ // Match that definition without changing presence semantics for secrets.
315
+ const value = key === "SWARM_ORG_NAME" ? process.env[key]?.trim() : process.env[key];
316
+ presence[key] = value !== undefined && value !== "";
314
317
  }
315
318
  envPresence.respond(res, 200, { presence });
316
319
  return true;
package/src/http/core.ts CHANGED
@@ -38,8 +38,10 @@ import { agentWithCapacity, getPathSegments, jsonError, parseQueryParams } from
38
38
 
39
39
  /**
40
40
  * Load global swarm_config entries into process.env.
41
- * When override=false (default, used at startup), existing env vars take precedence.
42
- * When override=true (used for reload), DB values overwrite process.env.
41
+ * When override=true (used at startup, on reload, and in createServer), DB
42
+ * values overwrite process.env, so a dashboard-saved value wins over the
43
+ * deployment env. When override=false, existing env vars take precedence
44
+ * (only used by callers that want env-first semantics; no boot path does).
43
45
  * Reserved keys are filtered before decryption because they must remain
44
46
  * environment-only, even if legacy rows still exist in the DB.
45
47
  * Returns the list of keys that were set/updated.
@@ -139,6 +141,13 @@ export async function reloadGlobalConfigsAndIntegrations(): Promise<ReloadConfig
139
141
  }
140
142
 
141
143
  async function reloadGlobalConfigsAndIntegrationsInner(): Promise<ReloadConfigResult> {
144
+ // Lazy import keeps http/core out of the memory module-init graph (pulls
145
+ // in the OpenAI SDK). Read the pre-reload configured state first — the
146
+ // provider memoizes its API key at construction, so this reflects whatever
147
+ // key (if any) was live before we hydrate the new env below.
148
+ const { getEmbeddingProvider, resetEmbeddingProvider } = await import("../be/memory");
149
+ const wasEmbeddingsConfigured = getEmbeddingProvider().isConfigured();
150
+
142
151
  const updated = await loadGlobalConfigsIntoEnv(true);
143
152
 
144
153
  // File-storage provider selection reads process.env once and memoizes; the
@@ -149,8 +158,35 @@ async function reloadGlobalConfigsAndIntegrationsInner(): Promise<ReloadConfigRe
149
158
  const { resetFileStorageProvider } = await import("../fs/registry");
150
159
  resetFileStorageProvider();
151
160
 
161
+ // Same reasoning as the file-storage provider above: the embedding
162
+ // provider captures EMBEDDING_API_KEY/OPENAI_API_KEY once at construction,
163
+ // so a key set (or rotated) via config reload stays inert until this reset.
164
+ resetEmbeddingProvider();
165
+ const isEmbeddingsConfigured = getEmbeddingProvider().isConfigured();
166
+
152
167
  const integrations: string[] = [];
153
168
 
169
+ if (isEmbeddingsConfigured) {
170
+ integrations.push("embeddings");
171
+ }
172
+
173
+ // Off-to-on transition: existing rows written while no key resolved (or
174
+ // with a stale dimension) never got embedded. Kick off the same backfill
175
+ // boot runs, fire-and-forget — it batches, swallows per-batch errors, and
176
+ // is idempotent, so it's safe to not await here.
177
+ if (!wasEmbeddingsConfigured && isEmbeddingsConfigured) {
178
+ import("../be/memory/boot-reembed")
179
+ .then(({ runBootReembed }) => runBootReembed())
180
+ .catch((err) => {
181
+ console.error("[config-reload] memory backfill failed (non-fatal):", err);
182
+ });
183
+ import("../be/scripts/boot-reembed")
184
+ .then(({ runBootReembedScripts }) => runBootReembedScripts())
185
+ .catch((err) => {
186
+ console.error("[config-reload] script backfill failed (non-fatal):", err);
187
+ });
188
+ }
189
+
154
190
  resetAgentMail();
155
191
  if (initAgentMail()) integrations.push("agentmail");
156
192