@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,461 @@
1
+ import type {
2
+ Extension,
3
+ ExtensionManifest,
4
+ ExtensionRun,
5
+ ExtensionStatus,
6
+ ExtensionVersion,
7
+ } from "../../types";
8
+ import { computeContentHash, getDbClient } from "../db";
9
+
10
+ type ExtensionRow = Omit<Extension, "enabled"> & { enabled: number };
11
+ type ExtensionVersionRow = ExtensionVersion;
12
+ type ExtensionRunRow = ExtensionRun;
13
+
14
+ export type InstallExtensionArgs = {
15
+ manifest: ExtensionManifest;
16
+ files: Record<string, string>;
17
+ priority?: number;
18
+ config?: Record<string, unknown>;
19
+ agentId?: string | null;
20
+ createdBy?: string | null;
21
+ activate?: boolean;
22
+ changeReason?: string | null;
23
+ };
24
+
25
+ export type InstallExtensionResult = {
26
+ extension: Extension;
27
+ isNew: boolean;
28
+ contentDeduped: boolean;
29
+ };
30
+
31
+ function rowToExtension(row: ExtensionRow): Extension {
32
+ return {
33
+ ...row,
34
+ enabled: row.enabled === 1,
35
+ lastError: row.lastError ?? null,
36
+ agentId: row.agentId ?? null,
37
+ createdByAgentId: row.createdByAgentId ?? null,
38
+ };
39
+ }
40
+
41
+ function canonicalFiles(files: Record<string, string>): Record<string, string> {
42
+ return Object.fromEntries(Object.entries(files).sort(([a], [b]) => a.localeCompare(b)));
43
+ }
44
+
45
+ function bundleContentHash(manifest: ExtensionManifest, files: Record<string, string>): string {
46
+ return computeContentHash(JSON.stringify({ manifest, files: canonicalFiles(files) }));
47
+ }
48
+
49
+ export async function installExtension(
50
+ args: InstallExtensionArgs,
51
+ ): Promise<InstallExtensionResult> {
52
+ const files = canonicalFiles(args.files);
53
+ const manifestJson = JSON.stringify(args.manifest);
54
+ const filesJson = JSON.stringify(files);
55
+ const contentHash = bundleContentHash(args.manifest, files);
56
+ const actor = args.createdBy ?? null;
57
+
58
+ return await getDbClient().transaction(async (tx) => {
59
+ const existingRow = await tx.get<ExtensionRow>("SELECT * FROM extensions WHERE name = ?", [
60
+ args.manifest.name,
61
+ ]);
62
+ const existing = existingRow ? rowToExtension(existingRow) : null;
63
+ const now = new Date().toISOString();
64
+
65
+ if (!existing) {
66
+ const id = crypto.randomUUID();
67
+ const row = await tx.get<ExtensionRow>(
68
+ `INSERT INTO extensions (
69
+ id, name, description, runtime, manifestJson, contentHash, version,
70
+ activeVersion, enabled, priority, configJson, status,
71
+ consecutiveFailures, lastError, agentId, createdByAgentId,
72
+ created_by, updated_by, createdAt, updatedAt
73
+ ) VALUES (?, ?, ?, ?, ?, ?, 1, 1, 0, ?, ?, 'disabled', 0, NULL, NULL, ?, ?, ?, ?, ?)
74
+ RETURNING *`,
75
+ [
76
+ id,
77
+ args.manifest.name,
78
+ args.manifest.description,
79
+ args.manifest.runtime,
80
+ manifestJson,
81
+ contentHash,
82
+ args.priority ?? 100,
83
+ JSON.stringify(args.config ?? {}),
84
+ args.agentId ?? null,
85
+ actor,
86
+ actor,
87
+ now,
88
+ now,
89
+ ],
90
+ );
91
+ if (!row) throw new Error("Failed to insert extension");
92
+
93
+ for (const [path, content] of Object.entries(files)) {
94
+ await tx.run(
95
+ `INSERT INTO extension_files (
96
+ id, extensionId, path, content, contentHash, created_by, updated_by
97
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)`,
98
+ [crypto.randomUUID(), id, path, content, computeContentHash(content), actor, actor],
99
+ );
100
+ }
101
+
102
+ await tx.run(
103
+ `INSERT INTO extension_versions (
104
+ id, extensionId, version, manifestJson, filesJson, contentHash,
105
+ changedByAgentId, changedAt, changeReason, created_by, updated_by
106
+ ) VALUES (?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?)`,
107
+ [
108
+ crypto.randomUUID(),
109
+ id,
110
+ manifestJson,
111
+ filesJson,
112
+ contentHash,
113
+ args.agentId ?? null,
114
+ now,
115
+ args.changeReason ?? "Initial installation",
116
+ actor,
117
+ actor,
118
+ ],
119
+ );
120
+
121
+ return { extension: rowToExtension(row), isNew: true, contentDeduped: false };
122
+ }
123
+
124
+ if (existing.contentHash === contentHash) {
125
+ const row = await tx.get<ExtensionRow>(
126
+ `UPDATE extensions SET
127
+ priority = ?, configJson = ?, activeVersion = ?, updated_by = ?, updatedAt = ?
128
+ WHERE id = ?
129
+ RETURNING *`,
130
+ [
131
+ args.priority ?? existing.priority,
132
+ args.config === undefined ? existing.configJson : JSON.stringify(args.config),
133
+ args.activate ? existing.version : existing.activeVersion,
134
+ actor,
135
+ now,
136
+ existing.id,
137
+ ],
138
+ );
139
+ if (!row) throw new Error("Failed to update extension metadata");
140
+ return { extension: rowToExtension(row), isNew: false, contentDeduped: true };
141
+ }
142
+
143
+ const newVersion = existing.version + 1;
144
+ const row = await tx.get<ExtensionRow>(
145
+ `UPDATE extensions SET
146
+ description = ?, runtime = ?, manifestJson = ?, contentHash = ?, version = ?,
147
+ activeVersion = ?, priority = ?, configJson = ?, updated_by = ?, updatedAt = ?
148
+ WHERE id = ?
149
+ RETURNING *`,
150
+ [
151
+ args.manifest.description,
152
+ args.manifest.runtime,
153
+ manifestJson,
154
+ contentHash,
155
+ newVersion,
156
+ args.activate ? newVersion : existing.activeVersion,
157
+ args.priority ?? existing.priority,
158
+ args.config === undefined ? existing.configJson : JSON.stringify(args.config),
159
+ actor,
160
+ now,
161
+ existing.id,
162
+ ],
163
+ );
164
+ if (!row) throw new Error("Failed to update extension");
165
+
166
+ await tx.run("DELETE FROM extension_files WHERE extensionId = ?", [existing.id]);
167
+ for (const [path, content] of Object.entries(files)) {
168
+ await tx.run(
169
+ `INSERT INTO extension_files (
170
+ id, extensionId, path, content, contentHash, created_by, updated_by
171
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)`,
172
+ [
173
+ crypto.randomUUID(),
174
+ existing.id,
175
+ path,
176
+ content,
177
+ computeContentHash(content),
178
+ actor,
179
+ actor,
180
+ ],
181
+ );
182
+ }
183
+
184
+ await tx.run(
185
+ `INSERT INTO extension_versions (
186
+ id, extensionId, version, manifestJson, filesJson, contentHash,
187
+ changedByAgentId, changedAt, changeReason, created_by, updated_by
188
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
189
+ [
190
+ crypto.randomUUID(),
191
+ existing.id,
192
+ newVersion,
193
+ manifestJson,
194
+ filesJson,
195
+ contentHash,
196
+ args.agentId ?? null,
197
+ now,
198
+ args.changeReason ?? null,
199
+ actor,
200
+ actor,
201
+ ],
202
+ );
203
+
204
+ return { extension: rowToExtension(row), isNew: false, contentDeduped: false };
205
+ });
206
+ }
207
+
208
+ export async function getExtensionFiles(extensionId: string): Promise<Record<string, string>> {
209
+ const rows = await getDbClient().query<{ path: string; content: string }>(
210
+ "SELECT path, content FROM extension_files WHERE extensionId = ? ORDER BY path",
211
+ [extensionId],
212
+ );
213
+ return Object.fromEntries(rows.map((row) => [row.path, row.content]));
214
+ }
215
+
216
+ export async function getExtensionById(id: string): Promise<Extension | null> {
217
+ const row = await getDbClient().get<ExtensionRow>("SELECT * FROM extensions WHERE id = ?", [id]);
218
+ return row ? rowToExtension(row) : null;
219
+ }
220
+
221
+ export async function getExtensionByName(name: string): Promise<Extension | null> {
222
+ const row = await getDbClient().get<ExtensionRow>("SELECT * FROM extensions WHERE name = ?", [
223
+ name,
224
+ ]);
225
+ return row ? rowToExtension(row) : null;
226
+ }
227
+
228
+ export async function listExtensions(): Promise<Extension[]> {
229
+ const rows = await getDbClient().query<ExtensionRow>(
230
+ "SELECT * FROM extensions ORDER BY priority ASC, name ASC",
231
+ );
232
+ return rows.map(rowToExtension);
233
+ }
234
+
235
+ export async function listExtensionVersions(extensionId: string): Promise<ExtensionVersion[]> {
236
+ return await getDbClient().query<ExtensionVersionRow>(
237
+ "SELECT * FROM extension_versions WHERE extensionId = ? ORDER BY version DESC",
238
+ [extensionId],
239
+ );
240
+ }
241
+
242
+ export async function getExtensionVersion(
243
+ extensionId: string,
244
+ version: number,
245
+ ): Promise<ExtensionVersion | null> {
246
+ return (
247
+ (await getDbClient().get<ExtensionVersionRow>(
248
+ "SELECT * FROM extension_versions WHERE extensionId = ? AND version = ?",
249
+ [extensionId, version],
250
+ )) ?? null
251
+ );
252
+ }
253
+
254
+ export async function activateExtensionVersionSnapshot(
255
+ extensionId: string,
256
+ version: number,
257
+ updatedBy?: string | null,
258
+ ): Promise<Extension | null> {
259
+ return await getDbClient().transaction(async (tx) => {
260
+ const snapshot = await tx.get<ExtensionVersionRow>(
261
+ "SELECT * FROM extension_versions WHERE extensionId = ? AND version = ?",
262
+ [extensionId, version],
263
+ );
264
+ if (!snapshot) return null;
265
+
266
+ const manifest = JSON.parse(snapshot.manifestJson) as ExtensionManifest;
267
+ const files = JSON.parse(snapshot.filesJson) as Record<string, string>;
268
+ const now = new Date().toISOString();
269
+ const row = await tx.get<ExtensionRow>(
270
+ `UPDATE extensions SET
271
+ description = ?, runtime = ?, manifestJson = ?, contentHash = ?, activeVersion = ?,
272
+ updated_by = ?, updatedAt = ?
273
+ WHERE id = ?
274
+ RETURNING *`,
275
+ [
276
+ manifest.description,
277
+ manifest.runtime,
278
+ snapshot.manifestJson,
279
+ snapshot.contentHash,
280
+ version,
281
+ updatedBy ?? null,
282
+ now,
283
+ extensionId,
284
+ ],
285
+ );
286
+ if (!row) return null;
287
+
288
+ await tx.run("DELETE FROM extension_files WHERE extensionId = ?", [extensionId]);
289
+ for (const [path, content] of Object.entries(files)) {
290
+ await tx.run(
291
+ `INSERT INTO extension_files (
292
+ id, extensionId, path, content, contentHash, created_by, updated_by
293
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)`,
294
+ [
295
+ crypto.randomUUID(),
296
+ extensionId,
297
+ path,
298
+ content,
299
+ computeContentHash(content),
300
+ updatedBy ?? null,
301
+ updatedBy ?? null,
302
+ ],
303
+ );
304
+ }
305
+ return rowToExtension(row);
306
+ });
307
+ }
308
+
309
+ export async function updateExtensionMeta(
310
+ id: string,
311
+ args: {
312
+ priority?: number;
313
+ config?: Record<string, unknown>;
314
+ description?: string;
315
+ updatedBy?: string | null;
316
+ },
317
+ ): Promise<Extension | null> {
318
+ const sets: string[] = [];
319
+ const values: Array<string | number | null> = [];
320
+ if (args.priority !== undefined) {
321
+ sets.push("priority = ?");
322
+ values.push(args.priority);
323
+ }
324
+ if (args.config !== undefined) {
325
+ sets.push("configJson = ?");
326
+ values.push(JSON.stringify(args.config));
327
+ }
328
+ if (args.description !== undefined) {
329
+ sets.push("description = ?");
330
+ values.push(args.description);
331
+ }
332
+ if (sets.length === 0) return getExtensionById(id);
333
+
334
+ sets.push("updated_by = ?", "updatedAt = ?");
335
+ values.push(args.updatedBy ?? null, new Date().toISOString(), id);
336
+ const row = await getDbClient().get<ExtensionRow>(
337
+ `UPDATE extensions SET ${sets.join(", ")} WHERE id = ? RETURNING *`,
338
+ values,
339
+ );
340
+ return row ? rowToExtension(row) : null;
341
+ }
342
+
343
+ export async function setExtensionState(
344
+ id: string,
345
+ args: {
346
+ enabled?: boolean;
347
+ status?: ExtensionStatus;
348
+ activeVersion?: number;
349
+ agentId?: string | null;
350
+ consecutiveFailures?: number;
351
+ lastError?: string | null;
352
+ updatedBy?: string | null;
353
+ },
354
+ ): Promise<Extension | null> {
355
+ const sets: string[] = [];
356
+ const values: Array<string | number | null> = [];
357
+ const fields = [
358
+ ["enabled", args.enabled === undefined ? undefined : args.enabled ? 1 : 0],
359
+ ["status", args.status],
360
+ ["activeVersion", args.activeVersion],
361
+ ["agentId", args.agentId],
362
+ ["consecutiveFailures", args.consecutiveFailures],
363
+ ["lastError", args.lastError],
364
+ ] as const;
365
+ for (const [column, value] of fields) {
366
+ if (value !== undefined) {
367
+ sets.push(`${column} = ?`);
368
+ values.push(value);
369
+ }
370
+ }
371
+ if (sets.length === 0) return getExtensionById(id);
372
+
373
+ sets.push("updated_by = ?", "updatedAt = ?");
374
+ values.push(args.updatedBy ?? null, new Date().toISOString(), id);
375
+ const row = await getDbClient().get<ExtensionRow>(
376
+ `UPDATE extensions SET ${sets.join(", ")} WHERE id = ? RETURNING *`,
377
+ values,
378
+ );
379
+ return row ? rowToExtension(row) : null;
380
+ }
381
+
382
+ export async function recordExtensionFailure(
383
+ id: string,
384
+ lastError: string,
385
+ maxConsecutiveFailures: number,
386
+ ): Promise<Extension | null> {
387
+ const row = await getDbClient().get<ExtensionRow>(
388
+ `UPDATE extensions SET
389
+ consecutiveFailures = consecutiveFailures + 1,
390
+ lastError = ?,
391
+ enabled = CASE WHEN consecutiveFailures + 1 >= ? THEN 0 ELSE enabled END,
392
+ status = CASE
393
+ WHEN consecutiveFailures + 1 >= ? THEN 'auto-disabled'
394
+ ELSE status
395
+ END,
396
+ updatedAt = ?
397
+ WHERE id = ?
398
+ RETURNING *`,
399
+ [lastError, maxConsecutiveFailures, maxConsecutiveFailures, new Date().toISOString(), id],
400
+ );
401
+ return row ? rowToExtension(row) : null;
402
+ }
403
+
404
+ export async function deleteExtension(id: string): Promise<boolean> {
405
+ const result = await getDbClient().run("DELETE FROM extensions WHERE id = ?", [id]);
406
+ return result.changes > 0;
407
+ }
408
+
409
+ export async function insertExtensionRun(args: {
410
+ extensionId: string;
411
+ version: number;
412
+ event: string;
413
+ action: ExtensionRun["action"];
414
+ durationMs?: number | null;
415
+ message?: string | null;
416
+ agentId?: string | null;
417
+ subject?: string | null;
418
+ }): Promise<ExtensionRun> {
419
+ const row = await getDbClient().get<ExtensionRunRow>(
420
+ `INSERT INTO extension_runs (
421
+ id, extensionId, version, event, action, durationMs, message, agentId, subject, createdAt
422
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`,
423
+ [
424
+ crypto.randomUUID(),
425
+ args.extensionId,
426
+ args.version,
427
+ args.event,
428
+ args.action,
429
+ args.durationMs ?? null,
430
+ args.message ?? null,
431
+ args.agentId ?? null,
432
+ args.subject ?? null,
433
+ new Date().toISOString(),
434
+ ],
435
+ );
436
+ if (!row) throw new Error("Failed to insert extension run");
437
+ return row;
438
+ }
439
+
440
+ export async function pruneExtensionRuns(extensionId: string, keep = 500): Promise<void> {
441
+ await getDbClient().run(
442
+ `DELETE FROM extension_runs
443
+ WHERE extensionId = ? AND id IN (
444
+ SELECT id FROM extension_runs
445
+ WHERE extensionId = ?
446
+ ORDER BY createdAt DESC, rowid DESC
447
+ LIMIT -1 OFFSET ?
448
+ )`,
449
+ [extensionId, extensionId, keep],
450
+ );
451
+ }
452
+
453
+ export async function listExtensionRuns(extensionId: string, limit = 100): Promise<ExtensionRun[]> {
454
+ return await getDbClient().query<ExtensionRunRow>(
455
+ `SELECT * FROM extension_runs
456
+ WHERE extensionId = ?
457
+ ORDER BY createdAt DESC, rowid DESC
458
+ LIMIT ?`,
459
+ [extensionId, Math.max(1, Math.min(500, limit))],
460
+ );
461
+ }
@@ -0,0 +1,107 @@
1
+ import { isSafeBundlePath } from "../../extensions/bundle-path";
2
+ import { EXTENSION_TYPE_DEFINITIONS } from "../../extensions/contract-types.generated";
3
+ import { checkImportAllowlist } from "../../scripts-runtime/import-allowlist";
4
+ import { type ExtensionManifest, ExtensionManifestSchema } from "../../types";
5
+ import {
6
+ SCRIPT_SDK_TYPES,
7
+ SCRIPT_STDLIB_MODULE_TYPES,
8
+ typecheckWithAmbient,
9
+ } from "../scripts/typecheck";
10
+
11
+ export type BundleValidationResult =
12
+ | { ok: true; manifest: ExtensionManifest }
13
+ | { ok: false; diagnostics: string[] };
14
+
15
+ export async function validateBundle(input: {
16
+ manifest: unknown;
17
+ files: Record<string, string>;
18
+ }): Promise<BundleValidationResult> {
19
+ const parsedManifest = ExtensionManifestSchema.safeParse(input.manifest);
20
+ const pathDiagnostics = Object.keys(input.files)
21
+ .filter((path) => !isSafeBundlePath(path))
22
+ .map((path) => `Unsafe bundle file path: ${JSON.stringify(path)}`);
23
+ if (!parsedManifest.success) {
24
+ return {
25
+ ok: false,
26
+ diagnostics: [
27
+ ...pathDiagnostics,
28
+ ...parsedManifest.error.issues.map(
29
+ (issue) => `${issue.path.join(".") || "manifest"}: ${issue.message}`,
30
+ ),
31
+ ],
32
+ };
33
+ }
34
+ if (pathDiagnostics.length > 0) return { ok: false, diagnostics: pathDiagnostics };
35
+
36
+ const manifest = parsedManifest.data;
37
+ if (manifest.runtime === "worker") {
38
+ return { ok: false, diagnostics: ['runtime "worker" is not supported in v1'] };
39
+ }
40
+
41
+ for (const asset of ["skills", "workflows", "schedules"] as const) {
42
+ if ((manifest.assets[asset]?.length ?? 0) > 0) {
43
+ return { ok: false, diagnostics: [`assets.${asset} is not supported in v1`] };
44
+ }
45
+ }
46
+
47
+ const hookPath = manifest.assets.hooks;
48
+ if (!(hookPath in input.files)) {
49
+ return {
50
+ ok: false,
51
+ diagnostics: [`assets.hooks "${hookPath}" does not name a file in files`],
52
+ };
53
+ }
54
+ const extraPaths = Object.keys(input.files).filter((path) => path !== hookPath);
55
+ if (extraPaths.length > 0) {
56
+ return {
57
+ ok: false,
58
+ diagnostics: [
59
+ `Only the hooks asset is supported in v1. Unexpected files: ${extraPaths.join(", ")}`,
60
+ ],
61
+ };
62
+ }
63
+
64
+ const source = input.files[hookPath]!;
65
+ const imports = checkImportAllowlist(source, {
66
+ allowedBare: ["swarm-extension", "zod", "stdlib"],
67
+ allowRelative: false,
68
+ strictDynamic: true,
69
+ });
70
+ if (!imports.ok) return { ok: false, diagnostics: [imports.diagnostic] };
71
+
72
+ const typecheck = await typecheckWithAmbient({
73
+ source,
74
+ modules: {
75
+ "swarm-extension": EXTENSION_TYPE_DEFINITIONS,
76
+ "swarm-sdk": SCRIPT_SDK_TYPES,
77
+ stdlib: SCRIPT_STDLIB_MODULE_TYPES,
78
+ },
79
+ checkFile: `/// <reference path="./runtime-globals.d.ts" />
80
+ import * as hooks from "./user-script";
81
+ import type { SwarmExtension } from "swarm-extension";
82
+ import type { ZodTypeAny } from "zod";
83
+ const manifest = ${JSON.stringify(manifest)} as const;
84
+ type Manifest = typeof hooks extends { config: infer Config extends ZodTypeAny }
85
+ ? typeof manifest & { config: Config }
86
+ : typeof manifest;
87
+ const _extension: SwarmExtension<Manifest> = hooks.default;
88
+ type ConfigIsValid = typeof hooks extends { config: infer Config }
89
+ ? Config extends ZodTypeAny ? true : false
90
+ : true;
91
+ const _configIsValid: ConfigIsValid = true;
92
+ void _extension;
93
+ void _configIsValid;
94
+ `,
95
+ });
96
+ if (!typecheck.ok) {
97
+ return {
98
+ ok: false,
99
+ diagnostics: typecheck.structured.map(
100
+ (diagnostic) =>
101
+ `${diagnostic.file}:${diagnostic.line}:${diagnostic.column} TS${diagnostic.code}: ${diagnostic.message}`,
102
+ ),
103
+ };
104
+ }
105
+
106
+ return { ok: true, manifest };
107
+ }
@@ -1,10 +1,15 @@
1
1
  /**
2
- * Startup backfill: detect agent_memory rows with wrong-dimension embeddings
3
- * (not 512d) and re-embed them in the background. Runs once per boot,
4
- * async/non-blocking, idempotent, no-op when the DB is clean.
2
+ * Backfill: detect agent_memory rows with no embedding at all (never
3
+ * embedded — e.g. every row written while no key was configured) or a
4
+ * wrong-dimension embedding (not 512d — legacy rows), and re-embed them in
5
+ * the background. Runs once per boot, and again whenever a config reload
6
+ * turns embeddings on for the first time (see scheduleIntegrationsReload in
7
+ * src/http/core.ts). Async/non-blocking, idempotent, no-op when the DB is
8
+ * clean or no key resolves.
5
9
  *
6
10
  * This is the app-level equivalent of a forward-only migration — SQL can't
7
- * call OpenAI, so the backfill runs at startup instead.
11
+ * call OpenAI, so the backfill runs at startup (and on the on-transition)
12
+ * instead.
8
13
  */
9
14
 
10
15
  import { getDbClient } from "@/be/db";
@@ -13,13 +18,13 @@ import { getEmbeddingProvider, getMemoryStore } from "./index";
13
18
 
14
19
  const VECTOR_BYTES = EMBEDDING_DIMENSIONS * Float32Array.BYTES_PER_ELEMENT;
15
20
  const BATCH_SIZE = 20;
21
+ const INVALID_EMBEDDING_WHERE = `embedding IS NULL OR length(embedding) != ${VECTOR_BYTES}`;
16
22
 
17
23
  export async function runBootReembed(): Promise<void> {
18
24
  const invalidCount =
19
25
  (
20
26
  await getDbClient().get<{ count: number }>(
21
- `SELECT COUNT(*) as count FROM agent_memory
22
- WHERE embedding IS NOT NULL AND length(embedding) != ${VECTOR_BYTES}`,
27
+ `SELECT COUNT(*) as count FROM agent_memory WHERE ${INVALID_EMBEDDING_WHERE}`,
23
28
  )
24
29
  )?.count ?? 0;
25
30
 
@@ -31,17 +36,18 @@ export async function runBootReembed(): Promise<void> {
31
36
  const testEmbed = await provider.embed("test");
32
37
  if (!testEmbed) {
33
38
  console.warn(
34
- `[boot-reembed] skipped: ${invalidCount} wrong-dimension rows found but no OpenAI key configured`,
39
+ `[boot-reembed] skipped: ${invalidCount} missing/wrong-dimension rows found but no OpenAI key configured`,
35
40
  );
36
41
  return;
37
42
  }
38
43
 
39
- console.log(`[boot-reembed] starting: ${invalidCount} rows with wrong embedding dimensions`);
44
+ console.log(
45
+ `[boot-reembed] starting: ${invalidCount} rows missing or with wrong embedding dimensions`,
46
+ );
40
47
 
41
48
  const store = getMemoryStore();
42
49
  const rows = await getDbClient().query<{ id: string; content: string }>(
43
- `SELECT id, content FROM agent_memory
44
- WHERE embedding IS NOT NULL AND length(embedding) != ${VECTOR_BYTES}`,
50
+ `SELECT id, content FROM agent_memory WHERE ${INVALID_EMBEDDING_WHERE}`,
45
51
  );
46
52
 
47
53
  let reembedded = 0;
@@ -69,8 +75,7 @@ export async function runBootReembed(): Promise<void> {
69
75
  const afterInvalid =
70
76
  (
71
77
  await getDbClient().get<{ count: number }>(
72
- `SELECT COUNT(*) as count FROM agent_memory
73
- WHERE embedding IS NOT NULL AND length(embedding) != ${VECTOR_BYTES}`,
78
+ `SELECT COUNT(*) as count FROM agent_memory WHERE ${INVALID_EMBEDDING_WHERE}`,
74
79
  )
75
80
  )?.count ?? 0;
76
81
 
@@ -12,6 +12,18 @@ export function getEmbeddingProvider(): EmbeddingProvider {
12
12
  return embeddingProvider;
13
13
  }
14
14
 
15
+ /**
16
+ * Drop the memoized embedding provider so the next `getEmbeddingProvider()`
17
+ * re-reads process.env. The provider captures its API key once at
18
+ * construction (see OpenAIEmbeddingProvider), so without a reset here a key
19
+ * set after boot (or after the first embed attempt) stays inert until the
20
+ * process restarts. Called from the config reload path — mirrors
21
+ * `resetFileStorageProvider` in src/fs/registry.ts.
22
+ */
23
+ export function resetEmbeddingProvider(): void {
24
+ embeddingProvider = null;
25
+ }
26
+
15
27
  export function getMemoryStore(): MemoryStore {
16
28
  if (!memoryStore) {
17
29
  const { SqliteMemoryStore } =
@@ -28,6 +28,10 @@ export class OpenAIEmbeddingProvider implements EmbeddingProvider {
28
28
  : (process.env.EMBEDDING_MODEL ?? DEFAULT_EMBEDDING_MODEL);
29
29
  }
30
30
 
31
+ isConfigured(): boolean {
32
+ return !!this.apiKey;
33
+ }
34
+
31
35
  private getClient(): OpenAI | null {
32
36
  if (!this.apiKey) return null;
33
37
  if (!this.client)
@@ -9,6 +9,8 @@ export interface EmbeddingProvider {
9
9
  readonly dimensions: number;
10
10
  embed(text: string): Promise<Float32Array | null>;
11
11
  embedBatch(texts: string[]): Promise<(Float32Array | null)[]>;
12
+ /** True when the provider has a usable API key — no network call. */
13
+ isConfigured(): boolean;
12
14
  }
13
15
 
14
16
  // ============================================================================
@@ -0,0 +1,25 @@
1
+ -- One claim and deadline per wait; each member independently follows deferrals.
2
+ ALTER TABLE deferred_task_waits ADD COLUMN mode TEXT NOT NULL DEFAULT 'all'
3
+ CHECK (mode IN ('all', 'any'));
4
+
5
+ CREATE TABLE deferred_task_wait_members (
6
+ scheduleId TEXT NOT NULL REFERENCES deferred_task_waits(scheduleId) ON DELETE CASCADE,
7
+ taskId TEXT NOT NULL,
8
+ created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
9
+ updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
10
+ created_by TEXT,
11
+ updated_by TEXT,
12
+ PRIMARY KEY (scheduleId, taskId)
13
+ );
14
+ CREATE INDEX idx_deferred_task_wait_members_task ON deferred_task_wait_members(taskId);
15
+
16
+ -- Preserve pending and fired legacy waits, including already-retargeted IDs.
17
+ INSERT INTO deferred_task_wait_members
18
+ (scheduleId, taskId, created_at, updated_at, created_by, updated_by)
19
+ SELECT scheduleId, taskId, created_at, updated_at, created_by, updated_by
20
+ FROM deferred_task_waits;
21
+
22
+ DROP INDEX idx_deferred_task_waits_pending;
23
+ ALTER TABLE deferred_task_waits DROP COLUMN taskId;
24
+ CREATE INDEX idx_deferred_task_waits_pending
25
+ ON deferred_task_waits(scheduleId) WHERE status = 'pending';