@desplega.ai/agent-swarm 1.149.1 → 1.151.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (538) hide show
  1. package/README.md +18 -3
  2. package/dist/{acp-adapter-vhkzfpjg.js → acp-adapter-ex8hzjfc.js} +5 -5
  3. package/dist/{actions-9d2694yk.js → actions-2bgpyhww.js} +39 -11
  4. package/dist/{app-fq2ggynt.js → app-1706zhr1.js} +7 -7
  5. package/dist/{artifact-gj9arwrr.js → artifact-td4q4dch.js} +1 -1
  6. package/dist/{assistant-edb6n9nn.js → assistant-bavke5p7.js} +44 -20
  7. package/dist/{azure-openai-responses-cqar64rc.js → azure-openai-responses-f38x2avq.js} +13 -13
  8. package/dist/{boot-reembed-msweabbp.js → boot-reembed-3cmrrmpa.js} +9 -9
  9. package/dist/{boot-reembed-q5wd3tha.js → boot-reembed-nafcmmmc.js} +14 -16
  10. package/dist/{boot-scrub-logs-3g1az933.js → boot-scrub-logs-cd6a2sq0.js} +5 -5
  11. package/dist/{claude-adapter-v3rnzsdc.js → claude-adapter-wfp2gzvz.js} +15 -16
  12. package/dist/{claude-managed-adapter-qvsmh6mv.js → claude-managed-adapter-dsmkcrjy.js} +3 -3
  13. package/dist/{claude-sdk-session-qxrpsna1.js → claude-sdk-session-bht9jjak.js} +15 -16
  14. package/dist/{cli-5m0qmvdp.js → cli-0qb1z6s2.js} +1 -1
  15. package/dist/{cli-9d8ntetj.js → cli-2658f6at.js} +1 -1
  16. package/dist/{cli-7evafqvy.js → cli-2tbvf1z1.js} +85 -69
  17. package/dist/{cli-k351hv5k.js → cli-3v9xhjcn.js} +24 -12
  18. package/dist/{cli-1ebpar1q.js → cli-4wppfkcm.js} +350 -20
  19. package/dist/{cli-4260r1tg.js → cli-5jd2z7av.js} +97 -84
  20. package/dist/{cli-cv7s5zdq.js → cli-5tpgq60f.js} +9865 -715
  21. package/dist/{cli-6skz3rk8.js → cli-7tgcstn2.js} +9 -4
  22. package/dist/{cli-01g3x1ca.js → cli-9004vfdf.js} +108 -56
  23. package/dist/{cli-1c94wvcw.js → cli-a1z3h107.js} +124 -99
  24. package/dist/{cli-9br8ypfv.js → cli-czjtgfys.js} +2 -2
  25. package/dist/{cli-5my3bjsd.js → cli-d3p82erb.js} +7 -7
  26. package/dist/{cli-e29gmdmw.js → cli-dv6cdjs2.js} +2 -1
  27. package/dist/{cli-wby1xnnb.js → cli-f1wky6zp.js} +1 -1
  28. package/dist/{cli-zvz62chv.js → cli-fm4ytsdk.js} +4 -4
  29. package/dist/{cli-tg8qj8ee.js → cli-frcta2w4.js} +6 -6
  30. package/dist/{cli-7xx10h0a.js → cli-g69fdc9g.js} +2 -2
  31. package/dist/{cli-cmp8g3sw.js → cli-gbfwcd3k.js} +44 -9
  32. package/dist/{handlers-nsg0bcye.js → cli-gjyvhmmh.js} +97 -45
  33. package/dist/{cli-08b07b4r.js → cli-ht5rs63n.js} +1 -1
  34. package/dist/{cli-jecc01cb.js → cli-j4nv2wqy.js} +2 -4
  35. package/dist/{cli-cd60fv6j.js → cli-j9xczhaq.js} +1 -1
  36. package/dist/cli-pf9q3696.js +172 -0
  37. package/dist/{cli-xm87hzbw.js → cli-pgj4a5fb.js} +1 -1
  38. package/dist/{cli-jezx9b9c.js → cli-ppm4975n.js} +9 -3
  39. package/dist/{cli-3z634pqe.js → cli-q88st9aj.js} +2 -2
  40. package/dist/{cli-3sysyjve.js → cli-r2q6pevn.js} +142 -49
  41. package/dist/{cli-07jd68a1.js → cli-re8z5p8p.js} +1 -1
  42. package/dist/{cli-qgd4sqz5.js → cli-s14agm5s.js} +121 -14
  43. package/dist/{cli-svbrpf5q.js → cli-sjaae6ff.js} +9872 -424
  44. package/dist/{cli-73gq0zxn.js → cli-vwm416m6.js} +13 -13
  45. package/dist/{cli-4wtq5jjv.js → cli-wbyk465z.js} +1 -1
  46. package/dist/{cli-3apbzgyk.js → cli-z61ykvam.js} +19 -14
  47. package/dist/{cli-e06a8a7y.js → cli-zkxymas5.js} +4 -3
  48. package/dist/cli.js +26 -25
  49. package/dist/{codex-adapter-jyw7a91z.js → codex-adapter-jjjk8w4m.js} +8 -9
  50. package/dist/{codex-hook-hfrt9shr.js → codex-hook-8jexc34x.js} +3 -3
  51. package/dist/{codex-session-runner-bmngj0s5.js → codex-session-runner-nd6ayxkx.js} +8 -9
  52. package/dist/{commands-n755g391.js → commands-9v4psyq1.js} +5 -5
  53. package/dist/{db-s1cgh5gv.js → db-wnt58tpe.js} +5 -5
  54. package/dist/dispatcher-05jh1aqk.js +74 -0
  55. package/dist/{e2b-p04vmtx0.js → e2b-413b8t4v.js} +1 -1
  56. package/dist/{google-generative-ai-tsskx7te.js → google-generative-ai-ndjmj5fa.js} +7 -7
  57. package/dist/{google-vertex-ppw8ydpa.js → google-vertex-fr665tqs.js} +10 -10
  58. package/dist/handlers-tw90pkh5.js +65 -0
  59. package/dist/{hook-jf0xe8zh.js → hook-123e2scb.js} +11 -12
  60. package/dist/{hook-h9cqrm8q.js → hook-sxe3r349.js} +11 -12
  61. package/dist/{http-nc6pydw5.js → http-ecsar8ez.js} +1442 -581
  62. package/dist/{index-ezxkds2n.js → index-16js521z.js} +335 -107
  63. package/dist/{index-769nc012.js → index-hw26vj40.js} +25 -19
  64. package/dist/{index-0wdy6gwc.js → index-nnrbpcjp.js} +25 -17
  65. package/dist/index-ymn7em0p.js +25 -0
  66. package/dist/{index-x1zcj09z.js → index-zj518gf9.js} +26 -20
  67. package/dist/{keepalive-nesm3xb6.js → keepalive-asqtcm4y.js} +7 -7
  68. package/dist/{lead-bwy6t2za.js → lead-xbfr7fvt.js} +40 -48
  69. package/dist/{maintenance-8w393zf0.js → maintenance-qma55qvw.js} +9 -9
  70. package/dist/{oauth-refresh-sweep-sh8xjy08.js → oauth-refresh-sweep-3s04c6qw.js} +6 -6
  71. package/dist/{onboard-25ps29h1.js → onboard-drskd386.js} +13 -4
  72. package/dist/{openai-codex-responses-g7w48y30.js → openai-codex-responses-wy6a43wd.js} +13 -13
  73. package/dist/{openai-responses-nmeyptke.js → openai-responses-6xwf7bzg.js} +19 -19
  74. package/dist/{opencode-adapter-dce4wskd.js → opencode-adapter-28axqa09.js} +4 -4
  75. package/dist/{otel-impl-b93gxnz5.js → otel-impl-943c96er.js} +1 -1
  76. package/dist/{pi-mono-adapter-zvshk5cj.js → pi-mono-adapter-jcqt71cg.js} +17 -19
  77. package/dist/{pricing-refresh-5v56x7gb.js → pricing-refresh-mx51az05.js} +7 -7
  78. package/dist/{rbac-roles-x55cnezn.js → rbac-roles-78baecqd.js} +13 -9
  79. package/dist/{rbac-roles-0p0s6pbj.js → rbac-roles-wcmrg6sv.js} +7 -7
  80. package/dist/{render-v2-hyd45w8b.js → render-v2-8zjntgf5.js} +7 -7
  81. package/dist/{seed-pricing-49gbgn96.js → seed-pricing-jqjs6cws.js} +6 -6
  82. package/dist/{setup-qvq64vzq.js → setup-4pks0vfk.js} +2 -2
  83. package/dist/work-objects-wangt792.js +165 -0
  84. package/dist/{worker-scj60cxg.js → worker-p2mr422x.js} +40 -48
  85. package/openapi.json +1365 -268
  86. package/package.json +4 -3
  87. package/src/agentmail/handlers.ts +95 -66
  88. package/src/be/budget-refusal-notify.ts +1 -0
  89. package/src/be/db/tasks/read.ts +3 -0
  90. package/src/be/db/tasks/write.ts +13 -1
  91. package/src/be/db-client.ts +5 -0
  92. package/src/be/db.ts +15 -5
  93. package/src/be/extensions/db.ts +461 -0
  94. package/src/be/extensions/validate.ts +107 -0
  95. package/src/be/memory/boot-reembed.ts +17 -12
  96. package/src/be/memory/index.ts +12 -0
  97. package/src/be/memory/providers/openai-embedding.ts +4 -0
  98. package/src/be/memory/types.ts +2 -0
  99. package/src/be/migrations/153_deferred_task_wait_members.sql +25 -0
  100. package/src/be/migrations/154_routing_source.sql +3 -0
  101. package/src/be/migrations/155_extensions.sql +66 -0
  102. package/src/be/migrations/156_inbox_item_state_read_at.sql +7 -0
  103. package/src/be/rbac-roles.ts +3 -2
  104. package/src/be/scripts/typecheck.ts +59 -41
  105. package/src/be/seed/schedules-seeder.ts +25 -2
  106. package/src/be/seed/workflows-seeder.ts +17 -0
  107. package/src/be/seed-scripts/catalog/delegate.ts +3 -1
  108. package/src/be/seed-skills/bundled-files.generated.json +1 -1
  109. package/src/be/seed-skills/index.ts +7 -0
  110. package/src/be/steering.ts +1 -0
  111. package/src/be/swarm-config-guard.ts +3 -1
  112. package/src/commands/credential-refresh.ts +70 -0
  113. package/src/commands/onboard/compose-generator.ts +14 -2
  114. package/src/commands/onboard/env-generator.ts +7 -0
  115. package/src/commands/provider-credentials.ts +2 -25
  116. package/src/commands/runner.ts +24 -57
  117. package/src/extensions/apply-task-create.ts +135 -0
  118. package/src/extensions/bundle-path.ts +10 -0
  119. package/src/extensions/contract-runtime.ts +7 -0
  120. package/src/extensions/contract-types.generated.ts +4 -0
  121. package/src/extensions/contract.ts +270 -0
  122. package/src/extensions/ctx.ts +186 -0
  123. package/src/extensions/dispatcher.ts +479 -0
  124. package/src/extensions/identity.ts +44 -0
  125. package/src/extensions/lifecycle.ts +281 -0
  126. package/src/extensions/loader.ts +207 -0
  127. package/src/extensions/post-bridge.ts +161 -0
  128. package/src/github/handlers.ts +185 -140
  129. package/src/gitlab/handlers.ts +97 -77
  130. package/src/heartbeat/heartbeat.ts +155 -83
  131. package/src/http/agents.ts +1 -1
  132. package/src/http/all-routes.ts +1 -0
  133. package/src/http/approval-requests.ts +1 -0
  134. package/src/http/apps.ts +14 -9
  135. package/src/http/config.ts +4 -1
  136. package/src/http/core.ts +38 -2
  137. package/src/http/extensions.ts +547 -0
  138. package/src/http/inbox-state.ts +2 -0
  139. package/src/http/index.ts +41 -3
  140. package/src/http/mcp-bridge.ts +96 -62
  141. package/src/http/mcp.ts +1 -1
  142. package/src/http/script-runs.ts +5 -0
  143. package/src/http/status.ts +6 -4
  144. package/src/http/tasks.ts +51 -30
  145. package/src/integrations/kapso/inbound.ts +15 -10
  146. package/src/jira/sync.ts +26 -16
  147. package/src/linear/sync.ts +26 -16
  148. package/src/prompts/session-templates.ts +18 -13
  149. package/src/providers/claude-adapter.ts +21 -8
  150. package/src/rbac/admission.ts +1 -1
  151. package/src/rbac/elevated-agents.ts +24 -0
  152. package/src/rbac/legacy-policy.ts +35 -6
  153. package/src/rbac/permissions.ts +8 -0
  154. package/src/scheduler/deferred-task-waits.ts +138 -27
  155. package/src/scheduler/schedule-task.ts +54 -24
  156. package/src/scheduler/scheduler.ts +108 -125
  157. package/src/scripts-runtime/executors/native.ts +5 -1
  158. package/src/scripts-runtime/executors/types.ts +5 -0
  159. package/src/scripts-runtime/import-allowlist.ts +79 -19
  160. package/src/scripts-runtime/sdk-allowlist.ts +4 -0
  161. package/src/scripts-runtime/swarm-config.ts +8 -0
  162. package/src/scripts-runtime/swarm-sdk.ts +3 -0
  163. package/src/scripts-runtime/types/stdlib.d.ts +16 -3
  164. package/src/scripts-runtime/types/swarm-extension.d.ts +562 -0
  165. package/src/scripts-runtime/types/swarm-sdk.d.ts +16 -3
  166. package/src/server.ts +6 -0
  167. package/src/slack/HEURISTICS.md +3 -1
  168. package/src/slack/actions.ts +21 -16
  169. package/src/slack/app.ts +2 -0
  170. package/src/slack/assistant.ts +4 -12
  171. package/src/slack/blocks.ts +1 -1
  172. package/src/slack/handlers.ts +107 -19
  173. package/src/slack/inbound-files.ts +9 -5
  174. package/src/slack/render-v2.ts +188 -38
  175. package/src/slack/router.ts +13 -3
  176. package/src/slack/steering.ts +3 -3
  177. package/src/slack/thread-buffer.ts +66 -23
  178. package/src/slack/work-objects.test.ts +252 -0
  179. package/src/slack/work-objects.ts +118 -0
  180. package/src/tasks/errors.ts +9 -0
  181. package/src/tasks/sibling-awareness.ts +35 -1
  182. package/src/tasks/worker-follow-up.ts +67 -11
  183. package/src/tests/acp-adapter.test.ts +10 -10
  184. package/src/tests/agent-fs-live-url.test.ts +40 -0
  185. package/src/tests/agent-fs-provision-seeder.test.ts +12 -12
  186. package/src/tests/api-key-tracking.test.ts +18 -18
  187. package/src/tests/api-key.test.ts +4 -2
  188. package/src/tests/apps-rbac.test.ts +1 -1
  189. package/src/tests/apps-spike5.test.ts +1 -1
  190. package/src/tests/apps-sync-engine.test.ts +8 -8
  191. package/src/tests/apps-user-config.test.ts +1 -1
  192. package/src/tests/artifact-sdk.test.ts +8 -8
  193. package/src/tests/asset-key-mcp.test.ts +2 -0
  194. package/src/tests/automation-preflight-alert.test.ts +1 -1
  195. package/src/tests/base-prompt.test.ts +7 -6
  196. package/src/tests/budget-user-scope.test.ts +1 -1
  197. package/src/tests/budgets-routes.test.ts +1 -1
  198. package/src/tests/claude-adapter-binary.test.ts +22 -40
  199. package/src/tests/claude-adapter-otel.test.ts +13 -7
  200. package/src/tests/claude-adapter.test.ts +8 -6
  201. package/src/tests/claude-hook-credentials.test.ts +3 -3
  202. package/src/tests/claude-managed-adapter.test.ts +4 -4
  203. package/src/tests/claude-managed-setup.test.ts +3 -3
  204. package/src/tests/claude-sdk-lifecycle.test.ts +1 -1
  205. package/src/tests/claude-sdk-transport.test.ts +6 -6
  206. package/src/tests/claude-stop-hook.test.ts +8 -8
  207. package/src/tests/claude-worker-parity.test.ts +3 -3
  208. package/src/tests/codex-adapter-pool-auth-revalidate.test.ts +5 -5
  209. package/src/tests/codex-adapter.test.ts +7 -5
  210. package/src/tests/codex-app-server.test.ts +2 -2
  211. package/src/tests/codex-login.test.ts +15 -15
  212. package/src/tests/codex-oauth-credential-info.test.ts +4 -4
  213. package/src/tests/codex-oauth-keep-warm.test.ts +4 -4
  214. package/src/tests/codex-oauth-refresh-lock.test.ts +1 -1
  215. package/src/tests/codex-oauth-storage.test.ts +7 -7
  216. package/src/tests/codex-pool.test.ts +1 -1
  217. package/src/tests/codex-steering-hook.test.ts +1 -1
  218. package/src/tests/codex-swarm-events.test.ts +1 -1
  219. package/src/tests/config-api-only-keys.test.ts +1 -1
  220. package/src/tests/config-session-auth.test.ts +2 -2
  221. package/src/tests/connection-embedded-auth.test.ts +20 -18
  222. package/src/tests/core-auth.test.ts +1 -1
  223. package/src/tests/create-page-tool.test.ts +3 -3
  224. package/src/tests/credential-refresh.test.ts +115 -0
  225. package/src/tests/credential-wait.test.ts +2 -2
  226. package/src/tests/credentials.test.ts +6 -4
  227. package/src/tests/db-queries-oauth.test.ts +40 -40
  228. package/src/tests/defer-task.test.ts +73 -8
  229. package/src/tests/deferred-task-wait-migration.test.ts +64 -0
  230. package/src/tests/deferred-task-wake.test.ts +349 -8
  231. package/src/tests/devin-adapter.test.ts +1 -1
  232. package/src/tests/devin-api.test.ts +1 -1
  233. package/src/tests/docs-index-footprint.test.js +3 -3
  234. package/src/tests/e2b-dispatch.test.ts +21 -19
  235. package/src/tests/e2e-claude-transport.test.ts +3 -3
  236. package/src/tests/e2e-redact.test.ts +6 -6
  237. package/src/tests/ensure-token.test.ts +68 -68
  238. package/src/tests/entrypoint-api-readiness.test.ts +1 -1
  239. package/src/tests/entrypoint-codex-oauth-seed.test.ts +13 -13
  240. package/src/tests/env-flag.test.ts +1 -0
  241. package/src/tests/events-http.test.ts +1 -1
  242. package/src/tests/extensions-db.test.ts +216 -0
  243. package/src/tests/extensions-dispatcher.test.ts +275 -0
  244. package/src/tests/extensions-example-heartbeat-remediate.test.ts +69 -0
  245. package/src/tests/extensions-example-mcp-install.test.ts +63 -0
  246. package/src/tests/extensions-example-no-exclamation-marks.test.ts +72 -0
  247. package/src/tests/extensions-example-rewrite-and-suppress.test.ts +70 -0
  248. package/src/tests/extensions-example-route-slack-channel.test.ts +217 -0
  249. package/src/tests/extensions-heartbeat.test.ts +291 -0
  250. package/src/tests/extensions-http.test.ts +431 -0
  251. package/src/tests/extensions-lead-equivalence.test.ts +49 -0
  252. package/src/tests/extensions-lifecycle.test.ts +209 -0
  253. package/src/tests/extensions-loader.test.ts +95 -0
  254. package/src/tests/extensions-mcp-tools.test.ts +245 -0
  255. package/src/tests/extensions-pre-task.test.ts +487 -0
  256. package/src/tests/extensions-run-context.test.ts +41 -0
  257. package/src/tests/extensions-tool-call.test.ts +288 -0
  258. package/src/tests/extensions-validate.test.ts +196 -0
  259. package/src/tests/fixtures/extensions/bad-import/hooks.ts +8 -0
  260. package/src/tests/fixtures/extensions/bad-import/manifest.json +9 -0
  261. package/src/tests/fixtures/extensions/bad-return-shape/hooks.ts +10 -0
  262. package/src/tests/fixtures/extensions/bad-return-shape/manifest.json +9 -0
  263. package/src/tests/fixtures/extensions/block-tasks-from-source/hooks.ts +22 -0
  264. package/src/tests/fixtures/extensions/block-tasks-from-source/manifest.json +9 -0
  265. package/src/tests/fixtures/extensions/ignore-channel/hooks.ts +22 -0
  266. package/src/tests/fixtures/extensions/ignore-channel/manifest.json +9 -0
  267. package/src/tests/fixtures/extensions/load.ts +10 -0
  268. package/src/tests/fixtures/extensions/minimal/hooks.ts +7 -0
  269. package/src/tests/fixtures/extensions/minimal/manifest.json +9 -0
  270. package/src/tests/fixtures/extensions/never-fail-long-tasks/hooks.ts +11 -0
  271. package/src/tests/fixtures/extensions/never-fail-long-tasks/manifest.json +9 -0
  272. package/src/tests/fixtures/extensions/no-exclamation-marks/hooks.ts +23 -0
  273. package/src/tests/fixtures/extensions/no-exclamation-marks/manifest.json +9 -0
  274. package/src/tests/fixtures/extensions/post-logger/hooks.ts +9 -0
  275. package/src/tests/fixtures/extensions/post-logger/manifest.json +9 -0
  276. package/src/tests/fixtures/extensions/priority-a/hooks.ts +7 -0
  277. package/src/tests/fixtures/extensions/priority-a/manifest.json +9 -0
  278. package/src/tests/fixtures/extensions/priority-b/hooks.ts +7 -0
  279. package/src/tests/fixtures/extensions/priority-b/manifest.json +9 -0
  280. package/src/tests/fixtures/extensions/record-only-on-tag/hooks.ts +16 -0
  281. package/src/tests/fixtures/extensions/record-only-on-tag/manifest.json +9 -0
  282. package/src/tests/fixtures/extensions/reserved-assets/hooks.ts +5 -0
  283. package/src/tests/fixtures/extensions/reserved-assets/manifest.json +10 -0
  284. package/src/tests/fixtures/extensions/rewrite-progress-prefix/hooks.ts +22 -0
  285. package/src/tests/fixtures/extensions/rewrite-progress-prefix/manifest.json +9 -0
  286. package/src/tests/fixtures/extensions/rewrite-task-priority/hooks.ts +7 -0
  287. package/src/tests/fixtures/extensions/rewrite-task-priority/manifest.json +9 -0
  288. package/src/tests/fixtures/extensions/route-channel-to-agent/hooks.ts +26 -0
  289. package/src/tests/fixtures/extensions/route-channel-to-agent/manifest.json +9 -0
  290. package/src/tests/fixtures/extensions/slow/hooks.ts +10 -0
  291. package/src/tests/fixtures/extensions/slow/manifest.json +9 -0
  292. package/src/tests/fixtures/extensions/suppress-lead-follow-up/hooks.ts +12 -0
  293. package/src/tests/fixtures/extensions/suppress-lead-follow-up/manifest.json +9 -0
  294. package/src/tests/fixtures/extensions/throws/hooks.ts +12 -0
  295. package/src/tests/fixtures/extensions/throws/manifest.json +9 -0
  296. package/src/tests/fixtures/extensions/worker-runtime/hooks.ts +5 -0
  297. package/src/tests/fixtures/extensions/worker-runtime/manifest.json +9 -0
  298. package/src/tests/fs-routes.test.ts +1 -1
  299. package/src/tests/gitlab-auth.test.ts +10 -10
  300. package/src/tests/harness-provider-resolution.test.ts +8 -5
  301. package/src/tests/heartbeat-reroute-decision.test.ts +1 -0
  302. package/src/tests/helper-credentials.test.ts +137 -0
  303. package/src/tests/http/context-routes.test.ts +2 -2
  304. package/src/tests/http-api-integration.test.ts +5 -2
  305. package/src/tests/http-users.test.ts +1 -1
  306. package/src/tests/identity-refresh.test.ts +2 -2
  307. package/src/tests/internal-ai/complete-structured.test.ts +2 -2
  308. package/src/tests/internal-ai/credentials.test.ts +19 -17
  309. package/src/tests/jira-metadata.test.ts +1 -1
  310. package/src/tests/jira-oauth.test.ts +5 -5
  311. package/src/tests/jira-sync.test.ts +1 -1
  312. package/src/tests/jira-webhook-lifecycle.test.ts +3 -3
  313. package/src/tests/jira-webhook.test.ts +2 -2
  314. package/src/tests/kapso-inbound.test.ts +2 -2
  315. package/src/tests/kv-http.test.ts +1 -1
  316. package/src/tests/kv-namespace-resolution.test.ts +1 -1
  317. package/src/tests/kv-page-proxy.test.ts +2 -2
  318. package/src/tests/launch-password-rejection.test.ts +2 -2
  319. package/src/tests/linear-webhook.test.ts +1 -1
  320. package/src/tests/mcp-oauth-dcr-reuse.test.ts +6 -6
  321. package/src/tests/mcp-oauth-ensure-token.test.ts +28 -22
  322. package/src/tests/mcp-oauth-manual-client.test.ts +16 -12
  323. package/src/tests/mcp-oauth-queries.test.ts +35 -32
  324. package/src/tests/mcp-oauth-resolve-secrets.test.ts +8 -8
  325. package/src/tests/mcp-oauth-wrapper.test.ts +20 -20
  326. package/src/tests/mcp-user-route.test.ts +1 -1
  327. package/src/tests/memory-edges.test.ts +1 -1
  328. package/src/tests/memory-edit-authorization.test.ts +32 -0
  329. package/src/tests/memory-rate-endpoint.test.ts +1 -1
  330. package/src/tests/memory-rate-tool.test.ts +2 -2
  331. package/src/tests/memory-rater-e2e.test.ts +1 -1
  332. package/src/tests/memory-rater-llm-summarizer.test.ts +3 -3
  333. package/src/tests/memory-rater-llm.test.ts +1 -1
  334. package/src/tests/model-control.test.ts +3 -0
  335. package/src/tests/multi-runtime-enabled-stats.test.ts +2 -2
  336. package/src/tests/multi-runtime-registration.test.ts +111 -17
  337. package/src/tests/oauth-access-token-tool.test.ts +18 -18
  338. package/src/tests/oauth-callback-flow.test.ts +8 -8
  339. package/src/tests/oauth-credential-bindings.test.ts +29 -27
  340. package/src/tests/oauth-keepalive.test.ts +10 -10
  341. package/src/tests/oauth-presets.test.ts +13 -5
  342. package/src/tests/oauth-refresh-failure.test.ts +9 -9
  343. package/src/tests/oauth-refresh-sweep.test.ts +20 -13
  344. package/src/tests/oauth-wrapper.test.ts +1 -1
  345. package/src/tests/onboard-compose.test.ts +47 -3
  346. package/src/tests/onboard-env.test.ts +26 -26
  347. package/src/tests/onboard-non-interactive.test.ts +4 -4
  348. package/src/tests/opencode-adapter.test.ts +1 -1
  349. package/src/tests/opencode-plugin.test.ts +2 -2
  350. package/src/tests/otel-session-cost-metrics.test.ts +1 -1
  351. package/src/tests/page-proxy-authed.test.ts +2 -2
  352. package/src/tests/page-proxy.test.ts +2 -2
  353. package/src/tests/page-session.test.ts +7 -7
  354. package/src/tests/page-viewer-auth.test.ts +2 -2
  355. package/src/tests/pages-authed-mode.test.ts +1 -1
  356. package/src/tests/pages-http.test.ts +1 -1
  357. package/src/tests/pages-password-mode.test.ts +1 -1
  358. package/src/tests/pages-public-authed-401.test.ts +1 -1
  359. package/src/tests/pages-public-html.test.ts +2 -2
  360. package/src/tests/pages-view-count.test.ts +1 -1
  361. package/src/tests/pi-mono-adapter.test.ts +10 -10
  362. package/src/tests/pi-mono-extension.test.ts +2 -2
  363. package/src/tests/preload.ts +5 -0
  364. package/src/tests/pricing-routes.test.ts +1 -1
  365. package/src/tests/profile-sync-warning.test.ts +8 -8
  366. package/src/tests/profile-sync.test.ts +8 -8
  367. package/src/tests/prompt-template-session.test.ts +36 -5
  368. package/src/tests/provider-process-group.test.ts +95 -4
  369. package/src/tests/providers/pi-cost.test.ts +1 -1
  370. package/src/tests/queue-stall-alarm.test.ts +1 -1
  371. package/src/tests/rbac-admission.test.ts +19 -4
  372. package/src/tests/rbac-charact-http.test.ts +1 -1
  373. package/src/tests/rbac-e2e-helpers.ts +1 -1
  374. package/src/tests/rbac-engine.test.ts +39 -0
  375. package/src/tests/realtime-room-auth.test.ts +1 -1
  376. package/src/tests/realtime-room-surfaces.test.ts +1 -1
  377. package/src/tests/reload-config.test.ts +45 -1
  378. package/src/tests/routing-affinity-inventory.test.ts +1 -1
  379. package/src/tests/routing-reason-contract.test.ts +76 -3
  380. package/src/tests/routing-reason-inventory.test.ts +11 -6
  381. package/src/tests/routing-source.test.ts +53 -0
  382. package/src/tests/runner-agent-fs-provision-retry.test.ts +1 -1
  383. package/src/tests/runner-context-preamble.test.ts +1 -1
  384. package/src/tests/runner-deployment-resume.test.ts +1 -1
  385. package/src/tests/runner-fallback-output.test.ts +1 -1
  386. package/src/tests/runner-identity-refresh.test.ts +1 -1
  387. package/src/tests/runner-skills-refresh.test.ts +1 -1
  388. package/src/tests/schedule-target-type.test.ts +3 -3
  389. package/src/tests/scheduled-tasks.test.ts +14 -5
  390. package/src/tests/scheduler-occurrence.test.ts +347 -0
  391. package/src/tests/script-apis-mcp.test.ts +1 -1
  392. package/src/tests/script-connections-http.test.ts +61 -58
  393. package/src/tests/script-connections-mcp.test.ts +1 -1
  394. package/src/tests/script-connections.test.ts +3 -1
  395. package/src/tests/script-runs-http.test.ts +15 -7
  396. package/src/tests/script-workflows-runtime-e2e.test.ts +1 -1
  397. package/src/tests/scripts-boot-reembed.test.ts +4 -0
  398. package/src/tests/scripts-embeddings.test.ts +4 -0
  399. package/src/tests/scripts-external-api.test.ts +1 -1
  400. package/src/tests/scripts-http.test.ts +1 -1
  401. package/src/tests/scripts-import-allowlist.test.ts +44 -1
  402. package/src/tests/scripts-mcp-e2e.test.ts +81 -1
  403. package/src/tests/scripts-only-gating.test.ts +4 -2
  404. package/src/tests/scripts-retention.test.ts +1 -1
  405. package/src/tests/scripts-runtime-identity.test.ts +2 -2
  406. package/src/tests/scripts-runtime-secret-egress.test.ts +1 -1
  407. package/src/tests/scripts-runtime.test.ts +6 -4
  408. package/src/tests/scripts-typecheck.test.ts +21 -0
  409. package/src/tests/sdk-allowlist.test.ts +1 -1
  410. package/src/tests/secret-scrubber.test.ts +38 -31
  411. package/src/tests/seed-registry.test.ts +46 -8
  412. package/src/tests/seed-schedules.test.ts +44 -9
  413. package/src/tests/seed-workflows.test.ts +21 -9
  414. package/src/tests/send-task-output-schema.test.ts +9 -1
  415. package/src/tests/send-task-requested-by.test.ts +1 -0
  416. package/src/tests/send-task-slack-routing-guard.test.ts +2 -0
  417. package/src/tests/session-costs-codex-recompute.test.ts +1 -1
  418. package/src/tests/session-costs-golden.test.ts +2 -2
  419. package/src/tests/session-costs-model-key-normalize.test.ts +2 -2
  420. package/src/tests/session-costs-recompute-all-providers.test.ts +1 -1
  421. package/src/tests/session-tokens.test.ts +1 -1
  422. package/src/tests/shutdown-error-scrubbing.test.ts +1 -1
  423. package/src/tests/slack-blocks.test.ts +12 -1
  424. package/src/tests/slack-file-tools-attachments.test.ts +1 -1
  425. package/src/tests/slack-inbound-files.test.ts +222 -86
  426. package/src/tests/slack-reaction-config.test.ts +5 -4
  427. package/src/tests/slack-read-boundaries.test.ts +1 -1
  428. package/src/tests/slack-render-v2.test.ts +196 -4
  429. package/src/tests/slack-router.test.ts +17 -2
  430. package/src/tests/slack-steering.test.ts +19 -0
  431. package/src/tests/status.test.ts +19 -7
  432. package/src/tests/steering-core.test.ts +1 -1
  433. package/src/tests/steering-enabled.test.ts +6 -8
  434. package/src/tests/steering-transport.test.ts +4 -4
  435. package/src/tests/store-progress-output-guidance.test.ts +34 -0
  436. package/src/tests/swarm-tool-result-gate.test.ts +11 -11
  437. package/src/tests/swarm-x-tool.test.ts +7 -5
  438. package/src/tests/sync-ai-toolbox-skills.test.ts +19 -0
  439. package/src/tests/task-supersede-resume.test.ts +1 -1
  440. package/src/tests/task-tool-preload-mcp.test.ts +4 -1
  441. package/src/tests/task-tools-ctx.test.ts +2 -0
  442. package/src/tests/tasks-requested-by-filter.test.ts +2 -2
  443. package/src/tests/telemetry-init.test.ts +11 -3
  444. package/src/tests/tool-annotations.test.ts +1 -1
  445. package/src/tests/tool-output-agent-id.test.ts +1 -0
  446. package/src/tests/tool-registrar-no-input.test.ts +42 -1
  447. package/src/tests/tracker-fold.test.ts +7 -7
  448. package/src/tests/tracker-tools.test.ts +3 -3
  449. package/src/tests/ui-followup-lead-delegation.test.ts +1 -0
  450. package/src/tests/unified-oauth-migration.test.ts +5 -5
  451. package/src/tests/user-token-rest-auth.test.ts +1 -1
  452. package/src/tests/user-token-routes.test.ts +1 -1
  453. package/src/tests/whoami-route.test.ts +1 -1
  454. package/src/tests/workflow-agent-task.test.ts +15 -13
  455. package/src/tests/workflow-ctx.test.ts +1 -1
  456. package/src/tests/workflow-e2e.test.ts +1 -1
  457. package/src/tests/workflow-engine-v2.test.ts +3 -1
  458. package/src/tests/workflow-executors.test.ts +2 -2
  459. package/src/tests/workflow-http-v2.test.ts +8 -6
  460. package/src/tests/workflow-input-redaction.test.ts +7 -5
  461. package/src/tests/workflow-integration-io.test.ts +2 -2
  462. package/src/tests/workflow-llm.test.ts +5 -5
  463. package/src/tests/workflow-registry.test.ts +1 -1
  464. package/src/tests/workflow-swarm-script.test.ts +1 -1
  465. package/src/tests/workflow-triggers-v2.test.ts +23 -21
  466. package/src/tests/x-composio.test.ts +9 -7
  467. package/src/tests/x402-client.test.ts +1 -1
  468. package/src/tests/x402-config.test.ts +28 -41
  469. package/src/tools/defer-task.ts +156 -24
  470. package/src/tools/extension-common.ts +84 -0
  471. package/src/tools/extension-install.ts +65 -0
  472. package/src/tools/extension-list.ts +66 -0
  473. package/src/tools/script-common.ts +43 -18
  474. package/src/tools/send-task.ts +87 -105
  475. package/src/tools/skills/skill-publish.ts +1 -0
  476. package/src/tools/store-progress.ts +1 -1
  477. package/src/tools/task-action.ts +43 -22
  478. package/src/tools/task-tool-ctx.ts +2 -0
  479. package/src/tools/templates.ts +2 -1
  480. package/src/tools/tool-config.ts +3 -1
  481. package/src/tools/utils.ts +140 -16
  482. package/src/types.ts +144 -0
  483. package/src/utils/constants.ts +7 -1
  484. package/src/utils/credentials.test.ts +16 -16
  485. package/src/utils/multi-runtime.ts +2 -2
  486. package/src/utils/steering-enabled.ts +4 -4
  487. package/src/workflows/executors/agent-task.ts +18 -3
  488. package/templates/ai-toolbox.manifest.json +7 -1
  489. package/templates/extensions/README.md +17 -0
  490. package/templates/extensions/notify-on-complete/README.md +11 -0
  491. package/templates/extensions/notify-on-complete/hooks.ts +61 -0
  492. package/templates/extensions/notify-on-complete/manifest.json +7 -0
  493. package/templates/extensions/require-ticket-ref/README.md +11 -0
  494. package/templates/extensions/require-ticket-ref/hooks.ts +30 -0
  495. package/templates/extensions/require-ticket-ref/manifest.json +7 -0
  496. package/templates/extensions/require-verification-note/README.md +11 -0
  497. package/templates/extensions/require-verification-note/hooks.ts +32 -0
  498. package/templates/extensions/require-verification-note/manifest.json +7 -0
  499. package/templates/schedules/daily-hn-briefing/config.json +1 -1
  500. package/templates/schedules/gtm-weekly-review/config.json +1 -1
  501. package/templates/schedules/weekly-code-health-reports/config.json +1 -1
  502. package/templates/schedules/weekly-dependabot-triage/config.json +1 -1
  503. package/templates/schedules/weekly-dora-metrics/config.json +1 -1
  504. package/templates/schedules/weekly-harness-upgrade-check/config.json +1 -1
  505. package/templates/skills/delegate-work/files/scripts/codex-exec.sh +1 -1
  506. package/templates/skills/scheduling/content.md +2 -0
  507. package/templates/skills/serply-search/SKILL.md +95 -0
  508. package/templates/skills/serply-search/config.json +13 -0
  509. package/templates/skills/serply-search/content.md +90 -0
  510. package/templates/skills/swarm-extensions/config.json +14 -0
  511. package/templates/skills/swarm-extensions/content.md +120 -0
  512. package/templates/skills/swarm-scripts/SKILL.md +2 -2
  513. package/templates/skills/swarm-scripts/content.md +2 -2
  514. package/templates/workflows/claude-code-changelog-watch/config.json +1 -1
  515. package/templates/workflows/gsc-topic-miner/config.json +1 -1
  516. package/templates/workflows/linear-drain-loop/config.json +1 -1
  517. package/templates/workflows/llm-safe-release-context/config.json +1 -1
  518. package/templates/workflows/ralph-loop/config.json +1 -1
  519. package/dist/cli-7fehefdf.js +0 -562
  520. package/dist/cli-ayvy5kde.js +0 -164
  521. package/dist/cli-ddfw1mc4.js +0 -4322
  522. package/dist/cli-hzqmgcm2.js +0 -3066
  523. package/dist/cli-j4d4f6hv.js +0 -338
  524. package/dist/cli-qgm2pfpq.js +0 -6377
  525. package/dist/cli-sdzq01t9.js +0 -823
  526. package/dist/cli-xvkmedgn.js +0 -332
  527. package/dist/cli-y3pq4v9g.js +0 -664
  528. package/dist/{anthropic-messages-vs2rabzp.js → anthropic-messages-2y8nktar.js} +19 -19
  529. package/dist/{claude-managed-setup-f5pmsyrp.js → claude-managed-setup-48dwg9bv.js} +3 -3
  530. package/dist/{cli-81r683bv.js → cli-ch38q5z4.js} +0 -0
  531. package/dist/{cli-6664w7y4.js → cli-kj76p1ez.js} +8 -8
  532. package/dist/{cli-c71mg51n.js → cli-r2fpe7cx.js} +5 -5
  533. package/dist/{cli-4xz3pmrq.js → cli-y6x0gqtg.js} +16 -16
  534. package/dist/{index-aa3sa6s8.js → index-2wsdsnmm.js} +1 -1
  535. package/dist/{mistral-conversations-4ryk7h83.js → mistral-conversations-qhr84ph3.js} +9 -9
  536. package/dist/{openai-completions-0bqmsajw.js → openai-completions-32rdwmze.js} +19 -19
  537. package/dist/{openrouter-images-qx18fyp8.js → openrouter-images-9f9ek4rw.js} +3 -3
  538. package/dist/{pi-messages-79jvzz1d.js → pi-messages-b1mbr6gp.js} +4 -4
@@ -0,0 +1,547 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import { z } from "zod";
3
+ import { resolveHttpAuditUserId } from "../be/audit-user";
4
+ import { getAgentById } from "../be/db";
5
+ import {
6
+ deleteExtension,
7
+ getExtensionById,
8
+ getExtensionByName,
9
+ getExtensionFiles,
10
+ installExtension,
11
+ listExtensionRuns,
12
+ listExtensions,
13
+ listExtensionVersions,
14
+ updateExtensionMeta,
15
+ } from "../be/extensions/db";
16
+ import { validateBundle } from "../be/extensions/validate";
17
+ import { EXTENSION_TYPE_DEFINITIONS } from "../extensions/contract-types.generated";
18
+ import {
19
+ activateVersion,
20
+ disableExtension,
21
+ ExtensionLifecycleError,
22
+ enableExtension,
23
+ reloadExtension,
24
+ } from "../extensions/lifecycle";
25
+ import { ExtensionConfigError } from "../extensions/loader";
26
+ import { can, type PermissionVerb, type RbacPrincipal } from "../rbac";
27
+ import {
28
+ ExtensionInstallBodySchema,
29
+ ExtensionManifestSchema,
30
+ ExtensionRunSchema,
31
+ ExtensionSchema,
32
+ ExtensionVersionSchema,
33
+ } from "../types";
34
+ import { getRequestAuth } from "../utils/request-auth-context";
35
+ import { scrubSecrets } from "../utils/secret-scrubber";
36
+ import { route } from "./route-def";
37
+ import { json, jsonError } from "./utils";
38
+
39
+ const idParamsSchema = z.object({ id: z.string().min(1) });
40
+
41
+ const installRoute = route({
42
+ method: "post",
43
+ path: "/api/extensions/install",
44
+ pattern: ["api", "extensions", "install"],
45
+ operationId: "extensions_install",
46
+ summary: "Validate and install an extension bundle",
47
+ tags: ["Extensions"],
48
+ body: ExtensionInstallBodySchema,
49
+ responses: {
50
+ 200: {
51
+ description: "Installed extension",
52
+ schema: z.object({
53
+ extension: ExtensionSchema,
54
+ manifest: ExtensionManifestSchema,
55
+ contentDeduped: z.boolean(),
56
+ }),
57
+ },
58
+ 400: { description: "Bundle validation failed" },
59
+ 403: { description: "Permission denied" },
60
+ },
61
+ rbac: { permission: "extension.write" },
62
+ });
63
+
64
+ const listRoute = route({
65
+ method: "get",
66
+ path: "/api/extensions",
67
+ pattern: ["api", "extensions"],
68
+ operationId: "extensions_list",
69
+ summary: "List installed extensions",
70
+ tags: ["Extensions"],
71
+ responses: {
72
+ 200: {
73
+ description: "Installed extensions",
74
+ schema: z.object({ extensions: z.array(ExtensionSchema) }),
75
+ },
76
+ },
77
+ });
78
+
79
+ const typeDefsRoute = route({
80
+ method: "get",
81
+ path: "/api/extensions/type-defs",
82
+ pattern: ["api", "extensions", "type-defs"],
83
+ operationId: "extensions_type_defs",
84
+ summary: "Get extension authoring type definitions",
85
+ tags: ["Extensions"],
86
+ responses: {
87
+ 200: {
88
+ description: "Generated swarm-extension.d.ts",
89
+ unstructured: "Plain TypeScript declaration text",
90
+ },
91
+ },
92
+ });
93
+
94
+ const getRoute = route({
95
+ method: "get",
96
+ path: "/api/extensions/{id}",
97
+ pattern: ["api", "extensions", null],
98
+ operationId: "extensions_get",
99
+ summary: "Get an extension bundle",
100
+ tags: ["Extensions"],
101
+ params: idParamsSchema,
102
+ responses: {
103
+ 200: {
104
+ description: "Extension bundle",
105
+ schema: z.object({
106
+ extension: ExtensionSchema,
107
+ manifest: ExtensionManifestSchema,
108
+ files: z.record(z.string(), z.string()),
109
+ }),
110
+ },
111
+ 404: { description: "Extension not found" },
112
+ },
113
+ });
114
+
115
+ const versionsRoute = route({
116
+ method: "get",
117
+ path: "/api/extensions/{id}/versions",
118
+ pattern: ["api", "extensions", null, "versions"],
119
+ operationId: "extensions_versions",
120
+ summary: "List extension versions",
121
+ tags: ["Extensions"],
122
+ params: idParamsSchema,
123
+ responses: {
124
+ 200: {
125
+ description: "Extension versions",
126
+ schema: z.object({ versions: z.array(ExtensionVersionSchema) }),
127
+ },
128
+ 404: { description: "Extension not found" },
129
+ },
130
+ });
131
+
132
+ const runsRoute = route({
133
+ method: "get",
134
+ path: "/api/extensions/{id}/runs",
135
+ pattern: ["api", "extensions", null, "runs"],
136
+ operationId: "extensions_runs",
137
+ summary: "List extension run log entries",
138
+ tags: ["Extensions"],
139
+ params: idParamsSchema,
140
+ query: z.object({ limit: z.coerce.number().int().min(1).max(500).optional() }),
141
+ responses: {
142
+ 200: {
143
+ description: "Extension run log",
144
+ schema: z.object({ runs: z.array(ExtensionRunSchema) }),
145
+ },
146
+ 404: { description: "Extension not found" },
147
+ },
148
+ });
149
+
150
+ const patchRoute = route({
151
+ method: "patch",
152
+ path: "/api/extensions/{id}",
153
+ pattern: ["api", "extensions", null],
154
+ operationId: "extensions_update",
155
+ summary: "Update extension priority, config, or description",
156
+ tags: ["Extensions"],
157
+ params: idParamsSchema,
158
+ body: z
159
+ .object({
160
+ priority: z.number().int().optional(),
161
+ config: z.record(z.string(), z.unknown()).optional(),
162
+ description: z.string().optional(),
163
+ })
164
+ .strict(),
165
+ responses: {
166
+ 200: { description: "Updated extension", schema: z.object({ extension: ExtensionSchema }) },
167
+ 400: { description: "Extension config validation failed" },
168
+ 403: { description: "Permission denied" },
169
+ 404: { description: "Extension not found" },
170
+ },
171
+ rbac: { permission: "extension.write" },
172
+ });
173
+
174
+ const deleteRoute = route({
175
+ method: "delete",
176
+ path: "/api/extensions/{id}",
177
+ pattern: ["api", "extensions", null],
178
+ operationId: "extensions_delete",
179
+ summary: "Uninstall a disabled extension",
180
+ tags: ["Extensions"],
181
+ params: idParamsSchema,
182
+ responses: {
183
+ 200: {
184
+ description: "Extension uninstalled",
185
+ schema: z.object({ deleted: z.literal(true) }),
186
+ },
187
+ 403: { description: "Permission denied" },
188
+ 404: { description: "Extension not found" },
189
+ 409: { description: "Enabled extensions cannot be uninstalled" },
190
+ },
191
+ rbac: { permission: "extension.write" },
192
+ });
193
+
194
+ const enableRoute = route({
195
+ method: "post",
196
+ path: "/api/extensions/{id}/enable",
197
+ pattern: ["api", "extensions", null, "enable"],
198
+ operationId: "extensions_enable",
199
+ summary: "Enable an extension",
200
+ description: "Loads and enables an extension. This route is operator-only.",
201
+ tags: ["Extensions"],
202
+ params: idParamsSchema,
203
+ responses: {
204
+ 200: { description: "Enabled extension", schema: z.object({ extension: ExtensionSchema }) },
205
+ 400: { description: "Extension load or config validation failed" },
206
+ 403: { description: "Permission denied" },
207
+ 404: { description: "Extension not found" },
208
+ },
209
+ rbac: { permission: "extension.activate" },
210
+ });
211
+
212
+ const disableRoute = route({
213
+ method: "post",
214
+ path: "/api/extensions/{id}/disable",
215
+ pattern: ["api", "extensions", null, "disable"],
216
+ operationId: "extensions_disable",
217
+ summary: "Disable an extension",
218
+ description: "Unloads and disables an extension. This route is operator-only.",
219
+ tags: ["Extensions"],
220
+ params: idParamsSchema,
221
+ responses: {
222
+ 200: { description: "Disabled extension", schema: z.object({ extension: ExtensionSchema }) },
223
+ 403: { description: "Permission denied" },
224
+ 404: { description: "Extension not found" },
225
+ },
226
+ rbac: { permission: "extension.activate" },
227
+ });
228
+
229
+ const activateVersionRoute = route({
230
+ method: "post",
231
+ path: "/api/extensions/{id}/activate-version",
232
+ pattern: ["api", "extensions", null, "activate-version"],
233
+ operationId: "extensions_activate_version",
234
+ summary: "Activate an extension version",
235
+ description: "Activates a stored version. This route is operator-only.",
236
+ tags: ["Extensions"],
237
+ params: idParamsSchema,
238
+ body: z.object({ version: z.number().int().min(1) }).strict(),
239
+ responses: {
240
+ 200: { description: "Activated extension", schema: z.object({ extension: ExtensionSchema }) },
241
+ 400: { description: "Extension load or config validation failed" },
242
+ 403: { description: "Permission denied" },
243
+ 404: { description: "Extension or version not found" },
244
+ },
245
+ rbac: { permission: "extension.activate" },
246
+ });
247
+
248
+ /** Operator-supplied config may carry tokens; scrub before it leaves the API. */
249
+ function redactExtension<T extends { configJson: string }>(extension: T): T {
250
+ return { ...extension, configJson: scrubSecrets(extension.configJson) };
251
+ }
252
+
253
+ // Matches every marker shape `scrubSecrets` emits: env-key names (`EXT_TOKEN`) and
254
+ // structural pattern names (`github_token`, `sk-ant`).
255
+ const REDACTED_CONFIG_VALUE = /^\[REDACTED:[A-Za-z0-9_.:-]+\]$/;
256
+
257
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
258
+ return typeof value === "object" && value !== null && !Array.isArray(value);
259
+ }
260
+
261
+ /**
262
+ * Walk `incoming` alongside `stored`; wherever the operator submitted an unchanged
263
+ * redaction placeholder, restore the stored value at that path (objects and arrays
264
+ * are recursed so nested secrets survive a dashboard round trip).
265
+ */
266
+ function restoreRedacted(incoming: unknown, stored: unknown): unknown {
267
+ if (typeof incoming === "string") {
268
+ return REDACTED_CONFIG_VALUE.test(incoming) && stored !== undefined ? stored : incoming;
269
+ }
270
+ if (Array.isArray(incoming)) {
271
+ const storedArray = Array.isArray(stored) ? stored : [];
272
+ return incoming.map((item, index) => restoreRedacted(item, storedArray[index]));
273
+ }
274
+ if (isPlainObject(incoming)) {
275
+ const storedObject = isPlainObject(stored) ? stored : {};
276
+ return Object.fromEntries(
277
+ Object.entries(incoming).map(([key, value]) => [
278
+ key,
279
+ restoreRedacted(value, storedObject[key]),
280
+ ]),
281
+ );
282
+ }
283
+ return incoming;
284
+ }
285
+
286
+ function preserveRedactedConfigValues(
287
+ incoming: Record<string, unknown>,
288
+ storedJson: string,
289
+ ): Record<string, unknown> {
290
+ let parsed: unknown;
291
+ try {
292
+ parsed = JSON.parse(storedJson);
293
+ } catch {
294
+ parsed = {};
295
+ }
296
+ return restoreRedacted(incoming, parsed) as Record<string, unknown>;
297
+ }
298
+
299
+ /**
300
+ * Prefer the supplied X-Agent-ID even when the request carries the shared API key.
301
+ * This prevents agents from using that key to bypass the route's declared RBAC permission.
302
+ */
303
+ async function extensionPrincipal(
304
+ req: IncomingMessage,
305
+ callerAgentId: string | undefined,
306
+ ): Promise<RbacPrincipal> {
307
+ if (callerAgentId) {
308
+ const agent = await getAgentById(callerAgentId);
309
+ return { kind: "agent", agentId: callerAgentId, isLead: agent?.isLead ?? false };
310
+ }
311
+ const auth = getRequestAuth(req);
312
+ if (auth?.kind === "operator") return { kind: "operator" };
313
+ if (auth?.kind === "user") return { kind: "user", userId: auth.userId };
314
+ return { kind: "agent", agentId: "", isLead: false };
315
+ }
316
+
317
+ async function requirePermission(
318
+ req: IncomingMessage,
319
+ res: ServerResponse,
320
+ callerAgentId: string | undefined,
321
+ verb: Extract<PermissionVerb, "extension.write" | "extension.activate">,
322
+ ): Promise<RbacPrincipal | null> {
323
+ const principal = await extensionPrincipal(req, callerAgentId);
324
+ const decision = can({ principal, verb, resource: { kind: "none" }, source: "http" });
325
+ if (decision.allow) return principal;
326
+ jsonError(res, `Forbidden: ${decision.reason}`, 403);
327
+ return null;
328
+ }
329
+
330
+ function respondLifecycleError(res: ServerResponse, error: unknown): void {
331
+ const status =
332
+ error instanceof ExtensionConfigError
333
+ ? 400
334
+ : error instanceof ExtensionLifecycleError
335
+ ? error.status
336
+ : 500;
337
+ jsonError(res, scrubSecrets(error instanceof Error ? error.message : String(error)), status);
338
+ }
339
+
340
+ export async function handleExtensions(
341
+ req: IncomingMessage,
342
+ res: ServerResponse,
343
+ pathSegments: string[],
344
+ queryParams: URLSearchParams,
345
+ agentId?: string,
346
+ ): Promise<boolean> {
347
+ if (installRoute.match(req.method, pathSegments)) {
348
+ const parsed = await installRoute.parse(req, res, pathSegments, queryParams);
349
+ if (!parsed) return true;
350
+ const principal = await requirePermission(req, res, agentId, "extension.write");
351
+ if (!principal) return true;
352
+ const validation = await validateBundle(parsed.body);
353
+ if (!validation.ok) {
354
+ json(res, { error: "extension_validation_failed", diagnostics: validation.diagnostics }, 400);
355
+ return true;
356
+ }
357
+ const existing = await getExtensionByName(validation.manifest.name);
358
+ const writerAgentId = principal.kind === "agent" ? principal.agentId : undefined;
359
+ const updatedBy = await resolveHttpAuditUserId(req, writerAgentId);
360
+ const shouldActivate = principal.kind !== "agent" && existing?.enabled === true;
361
+ const result = await installExtension({
362
+ manifest: validation.manifest,
363
+ files: parsed.body.files,
364
+ priority: parsed.body.priority,
365
+ config: parsed.body.config,
366
+ agentId: writerAgentId,
367
+ createdBy: updatedBy,
368
+ activate: shouldActivate,
369
+ });
370
+ let extension = result.extension;
371
+ if (shouldActivate) {
372
+ try {
373
+ extension = await reloadExtension(extension.id, { by: updatedBy });
374
+ } catch (error) {
375
+ respondLifecycleError(res, error);
376
+ return true;
377
+ }
378
+ }
379
+ installRoute.respond(res, 200, {
380
+ extension: redactExtension(extension),
381
+ manifest: validation.manifest,
382
+ contentDeduped: result.contentDeduped,
383
+ });
384
+ return true;
385
+ }
386
+
387
+ if (listRoute.match(req.method, pathSegments)) {
388
+ const parsed = await listRoute.parse(req, res, pathSegments, queryParams);
389
+ if (!parsed) return true;
390
+ listRoute.respond(res, 200, { extensions: (await listExtensions()).map(redactExtension) });
391
+ return true;
392
+ }
393
+
394
+ if (typeDefsRoute.match(req.method, pathSegments)) {
395
+ const parsed = await typeDefsRoute.parse(req, res, pathSegments, queryParams);
396
+ if (!parsed) return true;
397
+ res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
398
+ res.end(EXTENSION_TYPE_DEFINITIONS);
399
+ return true;
400
+ }
401
+
402
+ if (versionsRoute.match(req.method, pathSegments)) {
403
+ const parsed = await versionsRoute.parse(req, res, pathSegments, queryParams);
404
+ if (!parsed) return true;
405
+ if (!(await getExtensionById(parsed.params.id))) {
406
+ jsonError(res, "Extension not found", 404);
407
+ return true;
408
+ }
409
+ versionsRoute.respond(res, 200, {
410
+ versions: await listExtensionVersions(parsed.params.id),
411
+ });
412
+ return true;
413
+ }
414
+
415
+ if (runsRoute.match(req.method, pathSegments)) {
416
+ const parsed = await runsRoute.parse(req, res, pathSegments, queryParams);
417
+ if (!parsed) return true;
418
+ if (!(await getExtensionById(parsed.params.id))) {
419
+ jsonError(res, "Extension not found", 404);
420
+ return true;
421
+ }
422
+ runsRoute.respond(res, 200, {
423
+ runs: await listExtensionRuns(parsed.params.id, parsed.query.limit),
424
+ });
425
+ return true;
426
+ }
427
+
428
+ if (getRoute.match(req.method, pathSegments)) {
429
+ const parsed = await getRoute.parse(req, res, pathSegments, queryParams);
430
+ if (!parsed) return true;
431
+ const extension = await getExtensionById(parsed.params.id);
432
+ if (!extension) {
433
+ jsonError(res, "Extension not found", 404);
434
+ return true;
435
+ }
436
+ getRoute.respond(res, 200, {
437
+ extension: redactExtension(extension),
438
+ manifest: ExtensionManifestSchema.parse(JSON.parse(extension.manifestJson)),
439
+ files: await getExtensionFiles(extension.id),
440
+ });
441
+ return true;
442
+ }
443
+
444
+ if (patchRoute.match(req.method, pathSegments)) {
445
+ const parsed = await patchRoute.parse(req, res, pathSegments, queryParams);
446
+ if (!parsed) return true;
447
+ const principal = await requirePermission(req, res, agentId, "extension.write");
448
+ if (!principal) return true;
449
+ const writerAgentId = principal.kind === "agent" ? principal.agentId : undefined;
450
+ const stored = parsed.body.config ? await getExtensionById(parsed.params.id) : null;
451
+ const config =
452
+ parsed.body.config && stored
453
+ ? preserveRedactedConfigValues(parsed.body.config, stored.configJson)
454
+ : parsed.body.config;
455
+ let extension = await updateExtensionMeta(parsed.params.id, {
456
+ ...parsed.body,
457
+ ...(config === undefined ? {} : { config }),
458
+ updatedBy: await resolveHttpAuditUserId(req, writerAgentId),
459
+ });
460
+ if (!extension) {
461
+ jsonError(res, "Extension not found", 404);
462
+ return true;
463
+ }
464
+ if (extension.enabled) {
465
+ try {
466
+ extension = await reloadExtension(extension.id);
467
+ } catch (error) {
468
+ respondLifecycleError(res, error);
469
+ return true;
470
+ }
471
+ }
472
+ patchRoute.respond(res, 200, { extension: redactExtension(extension) });
473
+ return true;
474
+ }
475
+
476
+ if (deleteRoute.match(req.method, pathSegments)) {
477
+ const parsed = await deleteRoute.parse(req, res, pathSegments, queryParams);
478
+ if (!parsed) return true;
479
+ const principal = await requirePermission(req, res, agentId, "extension.write");
480
+ if (!principal) return true;
481
+ const extension = await getExtensionById(parsed.params.id);
482
+ if (!extension) {
483
+ jsonError(res, "Extension not found", 404);
484
+ return true;
485
+ }
486
+ if (extension.enabled) {
487
+ jsonError(res, "Disable the extension before uninstalling it", 409);
488
+ return true;
489
+ }
490
+ await deleteExtension(extension.id);
491
+ deleteRoute.respond(res, 200, { deleted: true });
492
+ return true;
493
+ }
494
+
495
+ if (enableRoute.match(req.method, pathSegments)) {
496
+ const parsed = await enableRoute.parse(req, res, pathSegments, queryParams);
497
+ if (!parsed) return true;
498
+ const principal = await requirePermission(req, res, agentId, "extension.activate");
499
+ if (!principal) return true;
500
+ const writerAgentId = principal.kind === "agent" ? principal.agentId : undefined;
501
+ try {
502
+ const extension = await enableExtension(parsed.params.id, {
503
+ by: await resolveHttpAuditUserId(req, writerAgentId),
504
+ });
505
+ enableRoute.respond(res, 200, { extension: redactExtension(extension) });
506
+ } catch (error) {
507
+ respondLifecycleError(res, error);
508
+ }
509
+ return true;
510
+ }
511
+
512
+ if (disableRoute.match(req.method, pathSegments)) {
513
+ const parsed = await disableRoute.parse(req, res, pathSegments, queryParams);
514
+ if (!parsed) return true;
515
+ const principal = await requirePermission(req, res, agentId, "extension.activate");
516
+ if (!principal) return true;
517
+ const writerAgentId = principal.kind === "agent" ? principal.agentId : undefined;
518
+ try {
519
+ const extension = await disableExtension(parsed.params.id, {
520
+ by: await resolveHttpAuditUserId(req, writerAgentId),
521
+ });
522
+ disableRoute.respond(res, 200, { extension: redactExtension(extension) });
523
+ } catch (error) {
524
+ respondLifecycleError(res, error);
525
+ }
526
+ return true;
527
+ }
528
+
529
+ if (activateVersionRoute.match(req.method, pathSegments)) {
530
+ const parsed = await activateVersionRoute.parse(req, res, pathSegments, queryParams);
531
+ if (!parsed) return true;
532
+ const principal = await requirePermission(req, res, agentId, "extension.activate");
533
+ if (!principal) return true;
534
+ const writerAgentId = principal.kind === "agent" ? principal.agentId : undefined;
535
+ try {
536
+ const extension = await activateVersion(parsed.params.id, parsed.body.version, {
537
+ by: await resolveHttpAuditUserId(req, writerAgentId),
538
+ });
539
+ activateVersionRoute.respond(res, 200, { extension: redactExtension(extension) });
540
+ } catch (error) {
541
+ respondLifecycleError(res, error);
542
+ }
543
+ return true;
544
+ }
545
+
546
+ return false;
547
+ }
@@ -41,6 +41,7 @@ const upsertState = route({
41
41
  itemId: z.string().min(1),
42
42
  status: InboxItemStatusSchema,
43
43
  snoozeUntil: z.string().datetime().optional(),
44
+ readAt: z.string().datetime().optional(),
44
45
  }),
45
46
  responses: {
46
47
  200: {
@@ -83,6 +84,7 @@ export async function handleInboxState(
83
84
  itemId: parsed.body.itemId,
84
85
  status: parsed.body.status,
85
86
  snoozeUntil: parsed.body.snoozeUntil,
87
+ readAt: parsed.body.readAt,
86
88
  });
87
89
  upsertState.respond(res, 200, { item });
88
90
  } catch (err) {
package/src/http/index.ts CHANGED
@@ -25,6 +25,11 @@ import {
25
25
  } from "../be/rbac-audit";
26
26
  import { startScratchScriptGc, stopScratchScriptGc } from "../be/scripts/retention";
27
27
  import { seedLegacyCapabilitiesConfig } from "../be/seed-capabilities";
28
+ import {
29
+ loadEnabledExtensions,
30
+ setExtensionLoopbackBaseUrl,
31
+ stopExtensionRuntime,
32
+ } from "../extensions/lifecycle";
28
33
  import { initGitHub } from "../github";
29
34
  import { initGitLab } from "../gitlab";
30
35
  import { stopHeartbeat } from "../heartbeat";
@@ -65,6 +70,7 @@ import { handleCore, loadGlobalConfigsIntoEnv } from "./core";
65
70
  import { handleDbQuery } from "./db-query";
66
71
  import { handleEcosystem } from "./ecosystem";
67
72
  import { handleEvents } from "./events";
73
+ import { handleExtensions } from "./extensions";
68
74
  import { handleFavorites } from "./favorites";
69
75
  import { handleFs } from "./fs";
70
76
  import { handleHeartbeat } from "./heartbeat";
@@ -151,6 +157,7 @@ const globalState = globalThis as typeof globalThis & {
151
157
  __apiGcInterval?: ReturnType<typeof setInterval>;
152
158
  __runId?: string;
153
159
  __closeRealtime?: () => void;
160
+ __stopExtensions?: () => Promise<void>;
154
161
  };
155
162
 
156
163
  const API_GC_INTERVAL_MS = 5 * 60 * 1000;
@@ -216,6 +223,7 @@ function startApiGcInterval() {
216
223
  // Clean up previous server on hot reload
217
224
  if (globalState.__httpServer) {
218
225
  globalState.__closeRealtime?.();
226
+ await globalState.__stopExtensions?.();
219
227
  console.log("[HTTP] Hot reload detected, closing previous server...");
220
228
  globalState.__httpServer.close();
221
229
  }
@@ -363,6 +371,7 @@ const httpServer = createHttpServer(async (req, res) => {
363
371
  () => handleApiKeys(req, res, pathSegments, queryParams),
364
372
  () => handleHeartbeat(req, res, pathSegments),
365
373
  () => handleEvents(req, res, pathSegments, queryParams, myAgentId),
374
+ () => handleExtensions(req, res, pathSegments, queryParams, myAgentId),
366
375
  () => handleFavorites(req, res, pathSegments, queryParams, myAgentId),
367
376
  () => handleUsers(req, res, pathSegments, queryParams),
368
377
  () => handleSessions(req, res, pathSegments, queryParams),
@@ -431,6 +440,7 @@ globalState.__mcpSessionAgents = mcpSessionAgents;
431
440
  globalState.__sessionUsers = sessionUsers;
432
441
  globalState.__transportActivity = transportActivity;
433
442
  globalState.__transportActivityUser = transportActivityUser;
443
+ globalState.__stopExtensions = stopExtensionRuntime;
434
444
 
435
445
  async function shutdown() {
436
446
  globalState.__closeRealtime?.();
@@ -477,6 +487,8 @@ async function shutdown() {
477
487
  // Stop scratch-script retention garbage collector
478
488
  stopScratchScriptGc();
479
489
 
490
+ await stopExtensionRuntime();
491
+
480
492
  // Stop RBAC audit: retention GC, flush interval, final drain, detach sink
481
493
  stopAuditGc();
482
494
  stopAuditWriter();
@@ -549,9 +561,14 @@ startApiGcInterval();
549
561
 
550
562
  // Load global swarm configs before the server starts listening so decrypt/key
551
563
  // failures fail closed instead of leaving the runtime half-initialized.
564
+ // override=true: a value saved in the dashboard wins over the deployment env
565
+ // at boot, matching the reload path and `createServer`. Otherwise a rotated
566
+ // credential saved only in swarm_config silently reverts to the stale env
567
+ // value on the next deploy (2026-09-16 prod Slack outage). Reserved keys
568
+ // (API_KEY, SECRETS_ENCRYPTION_KEY, ...) stay env-only regardless.
552
569
  let startupConfigsInjected: string[] = [];
553
570
  try {
554
- startupConfigsInjected = await loadGlobalConfigsIntoEnv(false);
571
+ startupConfigsInjected = await loadGlobalConfigsIntoEnv(true);
555
572
  } catch (err) {
556
573
  console.error("[startup] Failed to load global swarm configs before listen:", err);
557
574
  process.exitCode = 1;
@@ -605,6 +622,15 @@ try {
605
622
  console.error("[startup] Failed to seed built-in entities:", err);
606
623
  }
607
624
 
625
+ try {
626
+ await loadEnabledExtensions();
627
+ } catch (err) {
628
+ console.error(
629
+ "[startup] Failed to initialize extensions:",
630
+ scrubSecrets(err instanceof Error ? err.message : String(err)),
631
+ );
632
+ }
633
+
608
634
  // Wire the RBAC permission-audit sink into can() and start the batched writer
609
635
  // (2s flush) + retention GC (daily tick) BEFORE the server accepts traffic —
610
636
  // installing it inside the listen callback would leave an unaudited startup
@@ -622,6 +648,10 @@ await initOtel("api");
622
648
 
623
649
  httpServer
624
650
  .listen(port, async () => {
651
+ const boundAddress = httpServer.address();
652
+ if (boundAddress && typeof boundAddress === "object") {
653
+ setExtensionLoopbackBaseUrl(`http://127.0.0.1:${boundAddress.port}`);
654
+ }
625
655
  console.log(`MCP HTTP server running on http://localhost:${port}/mcp`);
626
656
 
627
657
  ensure({
@@ -656,8 +686,16 @@ httpServer
656
686
  await emitBuiltInIntegrationConnectedOnce("github");
657
687
  }
658
688
 
659
- // Start Slack bot (if configured)
660
- await startSlackApp();
689
+ // Start Slack bot (if configured). Never let a Slack failure abort the
690
+ // rest of this callback: on 2026-09-16 an invalid app token made
691
+ // app.start() reject here, and the scheduler, heartbeat, script-run
692
+ // supervisor, and OAuth sweeps below never started while /health stayed
693
+ // green for 7 hours. Slack is one integration, not the boot path.
694
+ try {
695
+ await startSlackApp();
696
+ } catch (err) {
697
+ console.error("[Slack] Failed to start, continuing boot without Slack:", err);
698
+ }
661
699
 
662
700
  // Independent of workers, scheduler targets, and heartbeat agent tasks.
663
701
  startQueueStallAlarm();