@desplega.ai/agent-swarm 2.5.3 → 2.6.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 (349) hide show
  1. package/README.md +2 -2
  2. package/dist/{acp-adapter-gr2fcyyj.js → acp-adapter-dgfakfpw.js} +9 -9
  3. package/dist/{actions-wv766ghf.js → actions-fbqma8aa.js} +21 -21
  4. package/dist/{amp-adapter-speb906t.js → amp-adapter-en0jy6hs.js} +4 -4
  5. package/dist/{app-ag29n14f.js → app-k8a5cgpp.js} +3 -3
  6. package/dist/{assistant-3x8bz3eg.js → assistant-a16433gr.js} +30 -26
  7. package/dist/{boot-reembed-jsmfrpzf.js → boot-reembed-6c1sjfnr.js} +1 -1
  8. package/dist/{boot-reembed-5b3d2eea.js → boot-reembed-f4nn561n.js} +1 -1
  9. package/dist/{boot-scrub-sweep-s73gerxy.js → boot-scrub-sweep-d4w9gajf.js} +4 -4
  10. package/dist/{boot-seeds-5zmax2wr.js → boot-seeds-9bmkspyw.js} +4 -4
  11. package/dist/{claude-adapter-hswdnm3t.js → claude-adapter-nys1kjxa.js} +9 -9
  12. package/dist/{claude-managed-adapter-cvbzj1h6.js → claude-managed-adapter-55207973.js} +3 -3
  13. package/dist/{claude-managed-setup-3k8m3hhc.js → claude-managed-setup-3cm35gey.js} +11 -4
  14. package/dist/{claude-sdk-session-fkbyk66x.js → claude-sdk-session-nw5yd8cp.js} +9313 -9260
  15. package/dist/{cli-210xd1f4.js → cli-03gg9pd1.js} +4 -4
  16. package/dist/{cli-kxpfx2xr.js → cli-0z1xjzwa.js} +2 -2
  17. package/dist/cli-1tem6ns9.js +894 -0
  18. package/dist/{cli-25j01dpj.js → cli-24j1jeeb.js} +3 -3
  19. package/dist/{cli-ta8bd7th.js → cli-2sjhpsns.js} +2 -2
  20. package/dist/{cli-p859hcc8.js → cli-39aet655.js} +2 -2
  21. package/dist/{cli-g1cw7xtt.js → cli-39rrw8yp.js} +1 -1
  22. package/dist/{cli-e2ar4bp1.js → cli-3aqd4jaa.js} +121 -47
  23. package/dist/{cli-jjznk3f7.js → cli-3fwqy71j.js} +69 -35
  24. package/dist/{cli-p9rafdrb.js → cli-3gfvmzht.js} +7 -7
  25. package/dist/{cli-03vpq45j.js → cli-3ndmpxxv.js} +2 -2
  26. package/dist/{cli-aecdcr77.js → cli-4hxrf0p4.js} +228 -74
  27. package/dist/{cli-mz9afxxf.js → cli-4rf0f5vg.js} +15 -3
  28. package/dist/{cli-qzme5949.js → cli-5rj83bam.js} +284 -202
  29. package/dist/{cli-fsdprwrg.js → cli-5sckpqcx.js} +2 -2
  30. package/dist/{cli-cz5nmdxt.js → cli-683y0b3a.js} +2 -2
  31. package/dist/{cli-3ngdk5te.js → cli-6y51ryva.js} +2 -2
  32. package/dist/{cli-n4c8e3s5.js → cli-708wdy32.js} +1 -1
  33. package/dist/{cli-pasyvjmm.js → cli-737pyqxr.js} +1 -1
  34. package/dist/{cli-gx4rm0qx.js → cli-7enjwya9.js} +1 -1
  35. package/dist/{cli-6x9ma5w5.js → cli-803188pb.js} +1 -1
  36. package/dist/{cli-avdvgcay.js → cli-9abdgn5t.js} +28 -9
  37. package/dist/{cli-99xgw26r.js → cli-a2rr0fmg.js} +1 -1
  38. package/dist/{cli-e5bxnjps.js → cli-a34pr3w1.js} +7 -7
  39. package/dist/{cli-502eqv93.js → cli-abkm71vr.js} +1 -1
  40. package/dist/{cli-mmhj8vd7.js → cli-ag2yrnt5.js} +11 -2
  41. package/dist/{cli-n45mr7zh.js → cli-aj8djyqt.js} +5 -5
  42. package/dist/{cli-1fc85mvt.js → cli-art7c1zh.js} +5 -5
  43. package/dist/{cli-40jsnrjm.js → cli-az25fn5f.js} +10 -8
  44. package/dist/{cli-9s05422w.js → cli-bqztfyve.js} +1 -1
  45. package/dist/{cli-bn0rewsy.js → cli-c081bj3b.js} +2 -2
  46. package/dist/{cli-svrj71pg.js → cli-c2habvsf.js} +12 -12
  47. package/dist/{cli-s49hpn37.js → cli-ca6banwz.js} +85 -19
  48. package/dist/{cli-hhxjnjzs.js → cli-cz37jqss.js} +12 -10
  49. package/dist/{cli-6n8axjv1.js → cli-d2dpf95n.js} +2 -2
  50. package/dist/{cli-tx00c0h6.js → cli-dqygeeh5.js} +17 -1
  51. package/dist/{cli-z21whrrg.js → cli-emhjcj2y.js} +17 -17
  52. package/dist/{cli-e8jyq7d8.js → cli-esakngh8.js} +7 -3
  53. package/dist/{cli-ha9rmwgq.js → cli-fb8m699c.js} +2 -2
  54. package/dist/{cli-35gtk1p6.js → cli-ffvsj2qw.js} +44 -13
  55. package/dist/{cli-ce0ak9ms.js → cli-g20jjwt4.js} +26 -26
  56. package/dist/{cli-kxawjrxg.js → cli-g8ka5j4j.js} +772 -194
  57. package/dist/{cli-vwn81jbs.js → cli-gf95q3bm.js} +5 -5
  58. package/dist/{cli-s53vp709.js → cli-gnsb1zxa.js} +2 -2
  59. package/dist/{cli-nwyw3zhg.js → cli-gxee0bqa.js} +1 -1
  60. package/dist/{cli-gpv60278.js → cli-hfeb68z4.js} +7 -7
  61. package/dist/{cli-0ebey9hg.js → cli-j6rktc91.js} +1 -1
  62. package/dist/{cli-ewm3pwbq.js → cli-k21gx5ht.js} +46 -23
  63. package/dist/{cli-egjggehm.js → cli-kf4jree9.js} +2 -2
  64. package/dist/{cli-dabehxhp.js → cli-kt907da6.js} +1 -1
  65. package/dist/{cli-p0rzjkya.js → cli-m5a9m0ak.js} +32 -13
  66. package/dist/{cli-fzcn3d87.js → cli-m70bajzr.js} +3 -3
  67. package/dist/{cli-w1zwctj2.js → cli-mtnj4fwa.js} +11 -5
  68. package/dist/{cli-gpsz7g88.js → cli-mvjz5an3.js} +16 -12
  69. package/dist/{cli-hcpj2xqz.js → cli-n7asmce6.js} +225 -3
  70. package/dist/{cli-y561h5a9.js → cli-nn6vx2mq.js} +5 -5
  71. package/dist/cli-nq677csy.js +140 -0
  72. package/dist/{cli-a5ebc95n.js → cli-pfk92eqz.js} +18 -15
  73. package/dist/{cli-p6zs9h4y.js → cli-rr7zx19j.js} +767 -249
  74. package/dist/{cli-qgf8emrn.js → cli-sqma6vw1.js} +10 -7
  75. package/dist/{cli-0xk505e8.js → cli-sxgtf5fw.js} +1 -1
  76. package/dist/{cli-nzfnt2gs.js → cli-tf1n0r0b.js} +1 -1
  77. package/dist/{cli-qqzmjxkf.js → cli-v49fkghp.js} +11 -6
  78. package/dist/{cli-x77gen2p.js → cli-w2656ka1.js} +7 -7
  79. package/dist/{cli-5kqnemen.js → cli-yvy48785.js} +1 -1
  80. package/dist/{cli-8s5wx3nz.js → cli-zqne1qcj.js} +44 -17
  81. package/dist/cli.js +22 -22
  82. package/dist/{codex-adapter-n8ka66vf.js → codex-adapter-d95q4qsn.js} +7 -7
  83. package/dist/{codex-hook-e2he7aca.js → codex-hook-brpnycn5.js} +1 -1
  84. package/dist/{codex-login-rn7c5gxt.js → codex-login-8cg0vhf2.js} +1 -1
  85. package/dist/{codex-session-runner-fsebts37.js → codex-session-runner-gdwd8h0c.js} +8 -8
  86. package/dist/{commands-t04rt2pc.js → commands-jqmgk54b.js} +2 -2
  87. package/dist/{controller-rgna04r2.js → controller-z3jq589f.js} +30 -4
  88. package/dist/{controllers-connect-bqm1rtft.js → controllers-connect-r2za39ef.js} +23 -23
  89. package/dist/{cursor-adapter-swr2k95y.js → cursor-adapter-4mr4jgkw.js} +2 -3
  90. package/dist/{db-a4m7haw0.js → db-ppv2tze7.js} +3 -3
  91. package/dist/{db-24ck09xy.js → db-w36wwb5g.js} +4 -2
  92. package/dist/{devin-adapter-4ycy4vrf.js → devin-adapter-byen2cxt.js} +1 -1
  93. package/dist/{dispatcher-nwf9ss0q.js → dispatcher-pv5h77vf.js} +20 -20
  94. package/dist/{docker-nfy2gc8t.js → docker-fdzyewpz.js} +4 -4
  95. package/dist/{dsh-adapter-kpdnhjst.js → dsh-adapter-d4pdpb3r.js} +2 -3
  96. package/dist/{e2b-9gkdhm3c.js → e2b-222hsajc.js} +2 -2
  97. package/dist/{e2b-cjtf8k1z.js → e2b-fn3zacvt.js} +2 -2
  98. package/dist/{easter-egg-3d-e4qv8dxj.js → easter-egg-3d-4sp4dvmc.js} +1 -1
  99. package/dist/{event-bus-3z78ep5n.js → event-bus-pghg8hqy.js} +1 -1
  100. package/dist/{execute-0xpz8950.js → execute-jev839y1.js} +1 -1
  101. package/dist/{external-channel-j1e2p34p.js → external-channel-y6nkzhnn.js} +2 -2
  102. package/dist/{grok-adapter-qtjz0whk.js → grok-adapter-t08b9ntm.js} +10 -10
  103. package/dist/handlers-eqsnmv95.js +41 -0
  104. package/dist/{heartbeat-vtesda5d.js → heartbeat-vn31xwdk.js} +23 -23
  105. package/dist/{hook-b2hhj6rd.js → hook-6d0zbvvt.js} +6 -5
  106. package/dist/{hook-8wqaszda.js → hook-cpjs8q51.js} +13 -7
  107. package/dist/{http-mxcqhhcs.js → http-9c6medda.js} +1085 -136
  108. package/dist/{index-spwxjgay.js → index-38t35h0n.js} +20 -20
  109. package/dist/{index-1r8wfpzs.js → index-g6k9tctj.js} +1 -1
  110. package/dist/{index-srg522m6.js → index-k0yt1tvq.js} +20 -20
  111. package/dist/{index-31ceyf42.js → index-pkma1yf8.js} +1 -1
  112. package/dist/{index-n1vbvta9.js → index-qhjf2htm.js} +29 -24
  113. package/dist/index-yqgbfgaj.js +43 -0
  114. package/dist/{keepalive-dsd8rtgx.js → keepalive-em0117bp.js} +4 -4
  115. package/dist/lead-23e7z59t.js +79 -0
  116. package/dist/{maintenance-jqmqwnet.js → maintenance-kbme3qy9.js} +1 -1
  117. package/dist/{oauth-refresh-sweep-q2c3bnvm.js → oauth-refresh-sweep-xysj4yse.js} +3 -3
  118. package/dist/{onboard-w4yd7s8t.js → onboard-p6q0vz97.js} +3 -3
  119. package/dist/{opencode-adapter-9tppvxar.js → opencode-adapter-esv3k9yn.js} +7 -7
  120. package/dist/{otel-impl-f36byzbd.js → otel-impl-54qhqh1z.js} +2 -2
  121. package/dist/{pi-mono-adapter-tqsge1ay.js → pi-mono-adapter-mymbwhj0.js} +41 -14
  122. package/dist/{pricing-refresh-xvak5p4v.js → pricing-refresh-yb75wv33.js} +3 -3
  123. package/dist/{rbac-roles-gfn30fb8.js → rbac-roles-1vp2mw4p.js} +2 -2
  124. package/dist/{rbac-roles-682k2mt5.js → rbac-roles-dexhc9x8.js} +1 -1
  125. package/dist/render-v2-tjay5hhf.js +16 -0
  126. package/dist/{resume-2dvq348g.js → resume-8ayf37g5.js} +11 -9
  127. package/dist/{runtime-bt5pkpm1.js → runtime-hqe5cqcd.js} +1 -1
  128. package/dist/{session-access-7f21ekq9.js → session-access-fdhp987s.js} +4 -4
  129. package/dist/session-source-backfill-qgtc86z1.js +110 -0
  130. package/dist/{setup-epffxtbk.js → setup-4rrmqcq3.js} +1 -1
  131. package/dist/{virtual-modules-mnfbh5mr.js → virtual-modules-52km00d6.js} +2 -2
  132. package/dist/{work-objects-xz1a5rqk.js → work-objects-p28fgd70.js} +23 -23
  133. package/dist/worker-e3w7vye1.js +79 -0
  134. package/dist/{x-pbpezr0s.js → x-wxrzvzx8.js} +2 -2
  135. package/openapi.json +1323 -16
  136. package/package.json +5 -5
  137. package/plugin/commands/create-pr.md +2 -2
  138. package/plugin/pi-skills/create-pr/SKILL.md +2 -2
  139. package/src/assets/source-key.ts +95 -0
  140. package/src/be/db/api-keys.ts +80 -0
  141. package/src/be/db/approvals.ts +48 -0
  142. package/src/be/db/session-labels.ts +28 -0
  143. package/src/be/db/session-moves.ts +5 -6
  144. package/src/be/db/session-tree.ts +153 -14
  145. package/src/be/db/tasks/read.ts +11 -0
  146. package/src/be/db/tasks/write.ts +21 -6
  147. package/src/be/db/workflow-idempotency.ts +183 -0
  148. package/src/be/db/workflow-runs.ts +500 -7
  149. package/src/be/db-retention.ts +61 -6
  150. package/src/be/db.ts +126 -131
  151. package/src/be/memory/providers/openai-embedding.ts +5 -1
  152. package/src/be/migrations/242_workflow_idempotency_keys.sql +40 -0
  153. package/src/be/migrations/244_api_key_validation.sql +4 -0
  154. package/src/be/onboarding.ts +4 -0
  155. package/src/be/scripts/typecheck.ts +1 -1
  156. package/src/be/sealed-json.ts +1 -0
  157. package/src/be/seed-scripts/catalog/compound-insights.inline.ts +2 -2
  158. package/src/be/seed-scripts/catalog/compound-insights.ts +2 -2
  159. package/src/be/seed-skills/bundled-files.generated.json +1 -1
  160. package/src/be/session-source-backfill.ts +163 -0
  161. package/src/be/sql-connections.ts +7 -1
  162. package/src/be/swarm-config-guard.ts +3 -0
  163. package/src/be/users.ts +9 -0
  164. package/src/comb/review-batch.ts +28 -1
  165. package/src/comb/templates.ts +7 -2
  166. package/src/commands/claude-managed-setup.ts +7 -1
  167. package/src/commands/controller.ts +33 -0
  168. package/src/commands/credential-refresh.ts +76 -1
  169. package/src/commands/credential-wait.ts +2 -2
  170. package/src/commands/provider-credentials.ts +210 -170
  171. package/src/commands/runner.ts +31 -6
  172. package/src/extensions/apply-task-create.ts +64 -11
  173. package/src/extensions/contract-types.generated.ts +1 -1
  174. package/src/extensions/contract.ts +30 -0
  175. package/src/extensions/dispatcher.ts +4 -0
  176. package/src/extensions/post-bridge.ts +8 -0
  177. package/src/hooks/context-bound-note.ts +196 -0
  178. package/src/hooks/hook.ts +12 -0
  179. package/src/http/all-routes.ts +2 -0
  180. package/src/http/api-keys.ts +53 -0
  181. package/src/http/assets.ts +15 -0
  182. package/src/http/codex-oauth-accounts.ts +191 -0
  183. package/src/http/comb.ts +15 -2
  184. package/src/http/config.ts +49 -17
  185. package/src/http/core.ts +6 -0
  186. package/src/http/fleet-benchmark.ts +5 -2
  187. package/src/http/idempotency-key.ts +117 -0
  188. package/src/http/index.ts +11 -0
  189. package/src/http/integrations.ts +5 -1
  190. package/src/http/kv.ts +8 -3
  191. package/src/http/onboarding.ts +6 -1
  192. package/src/http/pool-keys.ts +354 -0
  193. package/src/http/sessions.ts +37 -12
  194. package/src/http/stats.ts +1 -0
  195. package/src/http/status.ts +26 -12
  196. package/src/http/tasks.ts +3 -0
  197. package/src/http/used-by.ts +7 -2
  198. package/src/http/workflows.ts +207 -33
  199. package/src/integrations/chat-sdk/inbound.ts +27 -0
  200. package/src/kv-reserved-namespaces.ts +19 -0
  201. package/src/prompts/context-bound-note.ts +48 -0
  202. package/src/prompts/session-templates.ts +25 -1
  203. package/src/providers/acp-targets.ts +1 -1
  204. package/src/providers/amp-adapter.ts +30 -8
  205. package/src/providers/claude-adapter.ts +34 -5
  206. package/src/providers/claude-managed-adapter.ts +42 -9
  207. package/src/providers/claude-sdk-session.ts +10 -0
  208. package/src/providers/claude-session-events.ts +13 -0
  209. package/src/providers/codex-adapter.ts +83 -11
  210. package/src/providers/opencode-adapter.ts +18 -0
  211. package/src/providers/pi-mono-adapter.ts +35 -0
  212. package/src/runtime-providers/docker.ts +6 -2
  213. package/src/scheduler/scheduler.ts +3 -2
  214. package/src/scripts-runtime/swarm-sdk.ts +7 -1
  215. package/src/scripts-runtime/types/stdlib.d.ts +1 -1
  216. package/src/scripts-runtime/types/swarm-extension.d.ts +52 -8
  217. package/src/scripts-runtime/types/swarm-sdk.d.ts +1 -1
  218. package/src/slack/assistant.ts +7 -0
  219. package/src/slack/channel-labels.ts +74 -0
  220. package/src/slack/handlers.ts +8 -0
  221. package/src/slack/inbound-files.ts +5 -2
  222. package/src/slack/thread-buffer.ts +4 -0
  223. package/src/tasks/sibling-awareness.ts +12 -3
  224. package/src/telemetry-context.ts +5 -6
  225. package/src/telemetry-identity.ts +3 -3
  226. package/src/tests/amp-adapter.test.ts +97 -0
  227. package/src/tests/api-keys-validation.test.ts +170 -0
  228. package/src/tests/approval-requests.test.ts +10 -7
  229. package/src/tests/approval-sweeps.test.ts +12 -6
  230. package/src/tests/base-prompt.test.ts +1 -1
  231. package/src/tests/claude-hook-output.test.ts +79 -1
  232. package/src/tests/claude-managed-adapter.test.ts +321 -0
  233. package/src/tests/claude-routes-e2e.test.ts +7 -2
  234. package/src/tests/codex-adapter.test.ts +163 -0
  235. package/src/tests/codex-credential-pools.test.ts +120 -0
  236. package/src/tests/codex-gateway-route.test.ts +165 -0
  237. package/src/tests/codex-oauth-accounts.test.ts +357 -0
  238. package/src/tests/comb-review-batch.test.ts +63 -2
  239. package/src/tests/config-pool-keys.test.ts +533 -0
  240. package/src/tests/context-bound-note.test.ts +269 -0
  241. package/src/tests/credential-check.test.ts +290 -2
  242. package/src/tests/credential-refresh.test.ts +172 -1
  243. package/src/tests/db-retention.test.ts +9 -3
  244. package/src/tests/db-task-read-contract.test.ts +1 -0
  245. package/src/tests/db-task-write-contract.test.ts +1 -3
  246. package/src/tests/docs-index-footprint.test.js +3 -3
  247. package/src/tests/extensions-pre-task.test.ts +325 -0
  248. package/src/tests/fetch-resolved-env.test.ts +17 -0
  249. package/src/tests/fixtures/extensions/record-blocks/hooks.ts +10 -0
  250. package/src/tests/fixtures/extensions/record-blocks/manifest.json +7 -0
  251. package/src/tests/fixtures/extensions/record-task-create/hooks.ts +9 -0
  252. package/src/tests/fixtures/extensions/record-task-create/manifest.json +7 -0
  253. package/src/tests/fixtures/fake-amp.ts +22 -8
  254. package/src/tests/fixtures/held-template-render.ts +42 -0
  255. package/src/tests/fixtures/workflow-idempotency-trigger.child.ts +73 -0
  256. package/src/tests/fleet-benchmark.test.ts +8 -0
  257. package/src/tests/get-workflow-run-tool.test.ts +98 -0
  258. package/src/tests/integrations-http.test.ts +32 -2
  259. package/src/tests/internal-ai/complete-structured.test.ts +37 -1
  260. package/src/tests/internal-ai/credentials.test.ts +23 -0
  261. package/src/tests/kv-http.test.ts +32 -0
  262. package/src/tests/kv-tool.test.ts +25 -0
  263. package/src/tests/multi-runtime-registration.test.ts +12 -12
  264. package/src/tests/onboarding.test.ts +108 -1
  265. package/src/tests/openai-embedding-credentials.test.ts +35 -0
  266. package/src/tests/openapi-route-headers.test.ts +2 -0
  267. package/src/tests/opencode-adapter.test.ts +134 -0
  268. package/src/tests/openrouter-attribution-call-sites.test.ts +2 -2
  269. package/src/tests/org-website.test.ts +36 -0
  270. package/src/tests/pi-mono-adapter.test.ts +105 -0
  271. package/src/tests/prompt-template-session.test.ts +4 -3
  272. package/src/tests/provider-key-probes.test.ts +181 -0
  273. package/src/tests/rbac-e2e-helpers.test.ts +88 -1
  274. package/src/tests/rbac-e2e-helpers.ts +36 -11
  275. package/src/tests/runner-push-delivery.test.ts +25 -0
  276. package/src/tests/runtime-controller-liveness.test.ts +133 -0
  277. package/src/tests/runtime-providers-docker.test.ts +2 -0
  278. package/src/tests/script-connections.test.ts +5 -0
  279. package/src/tests/script-run-config-scope.test.ts +91 -3
  280. package/src/tests/script-runs-retention.test.ts +212 -0
  281. package/src/tests/sdk-allowlist.test.ts +39 -0
  282. package/src/tests/session-logs.test.ts +70 -0
  283. package/src/tests/session-source-backfill.test.ts +429 -0
  284. package/src/tests/session-source-keys.test.ts +236 -0
  285. package/src/tests/session-source-labels.test.ts +419 -0
  286. package/src/tests/sessions-detail-batch.test.ts +2 -0
  287. package/src/tests/sessions-move.test.ts +131 -0
  288. package/src/tests/sessions-tree.test.ts +336 -2
  289. package/src/tests/slack-inbound-files.test.ts +103 -23
  290. package/src/tests/status.test.ts +101 -18
  291. package/src/tests/tasks-workflow-run-filter.test.ts +146 -0
  292. package/src/tests/test-net.ts +31 -0
  293. package/src/tests/transcription.test.ts +8 -0
  294. package/src/tests/used-by-route.test.ts +11 -0
  295. package/src/tests/workflow-cascade-cancel.test.ts +1341 -0
  296. package/src/tests/workflow-foreach-supersede-retry.test.ts +488 -0
  297. package/src/tests/workflow-http-v2.test.ts +131 -0
  298. package/src/tests/workflow-idempotency.test.ts +915 -0
  299. package/src/tests/workflow-step-order.test.ts +92 -0
  300. package/src/tests/workflow-sub-workflow.test.ts +30 -0
  301. package/src/tools/get-tasks.ts +8 -0
  302. package/src/tools/kv/kv-read-auth.ts +4 -2
  303. package/src/tools/task-action.ts +1 -1
  304. package/src/tools/workflows/cancel-workflow-run.ts +1 -1
  305. package/src/tools/workflows/get-workflow-run.ts +14 -4
  306. package/src/types.ts +24 -0
  307. package/src/utils/company-profile.ts +51 -1
  308. package/src/utils/context-window.ts +8 -6
  309. package/src/utils/credentials.ts +5 -2
  310. package/src/utils/internal-ai/complete-structured.ts +5 -0
  311. package/src/utils/internal-ai/credentials.ts +4 -3
  312. package/src/utils/provider-key-probes.ts +328 -0
  313. package/src/utils/transcription.ts +4 -1
  314. package/src/workflows/approval-cancel.ts +15 -13
  315. package/src/workflows/approval-notifications.ts +5 -1
  316. package/src/workflows/checkpoint.ts +6 -3
  317. package/src/workflows/engine.ts +30 -8
  318. package/src/workflows/executors/foreach.ts +2 -2
  319. package/src/workflows/executors/sub-workflow.ts +8 -1
  320. package/src/workflows/foreach-driver.ts +10 -3
  321. package/src/workflows/foreach-join.ts +29 -6
  322. package/src/workflows/recovery.ts +6 -1
  323. package/src/workflows/resume.ts +168 -56
  324. package/src/workflows/retry-poller.ts +49 -3
  325. package/src/workflows/task-step-routing.ts +29 -7
  326. package/templates/ai-toolbox.manifest.json +8 -8
  327. package/templates/community/open-prompt-showreel/PLAYBOOK.md +5 -6
  328. package/templates/community/open-prompt-showreel/lead-prompt.md +1 -1
  329. package/templates/community/open-prompt-showreel/skills/open-prompt-showreel/SKILL.md +4 -4
  330. package/templates/skills/agent-browser/SKILL.md +4 -4
  331. package/templates/skills/agent-browser/content.md +4 -4
  332. package/templates/skills/code-quality/config.json +1 -1
  333. package/templates/skills/code-quality/content.md +2 -2
  334. package/templates/skills/delegate-work/content.md +29 -25
  335. package/templates/skills/delegate-work/files/scripts/codex-exec.sh +5 -5
  336. package/templates/skills/qa/content.md +2 -2
  337. package/templates/skills/researching/content.md +4 -0
  338. package/templates/skills/swarm-scripts/SKILL.md +1 -0
  339. package/templates/skills/swarm-scripts/content.md +1 -0
  340. package/templates/skills/work-on-task/SKILL.md +1 -1
  341. package/templates/skills/work-on-task/content.md +1 -1
  342. package/dist/cli-7421jzsw.js +0 -84
  343. package/dist/cli-e45e4m6b.js +0 -413
  344. package/dist/cli-zw88zjba.js +0 -307
  345. package/dist/handlers-19xr6dnz.js +0 -41
  346. package/dist/index-kw2mmat7.js +0 -43
  347. package/dist/lead-zyerk1x2.js +0 -79
  348. package/dist/render-v2-hgytz8nn.js +0 -12
  349. package/dist/worker-42t8emjt.js +0 -79
@@ -8,6 +8,7 @@ import {
8
8
  registerTelegramWebhook,
9
9
  } from "../integrations/chat-sdk/status";
10
10
  import { buildSlackManifest } from "../slack/manifest";
11
+ import { selectCredential } from "../utils/credentials";
11
12
  import { ensureHttpPermission } from "./config";
12
13
  import { resolveConfigValue, resolveMcpBaseUrl } from "./config-values";
13
14
  import { route } from "./route-def";
@@ -231,7 +232,10 @@ export function createIntegrationsHandler(deps: TestConnectionDeps = {}) {
231
232
  }
232
233
 
233
234
  try {
234
- const client = buildClient(apiKey);
235
+ // A comma pool is one key per call, as the runner does.
236
+ const client = buildClient(
237
+ selectCredential(apiKey, undefined, "ANTHROPIC_API_KEY").selected,
238
+ );
235
239
  const agent = await client.beta.agents.retrieve(agentId);
236
240
  // `agent.model` is `BetaManagedAgentsModelConfig` ({id, speed}). Flatten
237
241
  // to a string so the UI can render it directly without type guards.
package/src/http/kv.ts CHANGED
@@ -12,7 +12,11 @@ import {
12
12
  upsertKv,
13
13
  } from "../be/db";
14
14
  import { mcpOverflowAuthError } from "../kv-overflow";
15
- import { reservedNamespaceError, reservedRoomKeyError } from "../kv-reserved-namespaces";
15
+ import {
16
+ privateNamespaceError,
17
+ reservedNamespaceError,
18
+ reservedRoomKeyError,
19
+ } from "../kv-reserved-namespaces";
16
20
  import { hasKvViewArgs, resolveKvView } from "../kv-view";
17
21
  import { can } from "../rbac";
18
22
  import { agentContextKey, pageContextKey } from "../tasks/context-key";
@@ -405,8 +409,9 @@ function authorizeRead(
405
409
  namespace: string,
406
410
  ctx: AuthCtx,
407
411
  ): { status: number; message: string } | null {
408
- const overflowAuthErr = mcpOverflowAuthError(namespace, ctx.callerAgentId);
409
- return overflowAuthErr ? { status: 403, message: overflowAuthErr } : null;
412
+ const readErr =
413
+ privateNamespaceError(namespace) ?? mcpOverflowAuthError(namespace, ctx.callerAgentId);
414
+ return readErr ? { status: 403, message: readErr } : null;
410
415
  }
411
416
 
412
417
  function encodeValueOrError(
@@ -21,6 +21,7 @@ import {
21
21
  } from "../be/onboarding";
22
22
  import { assertUrlSafe, publicEndpointSsrfOptions } from "../oauth/mcp-wrapper";
23
23
  import { ProviderNameSchema } from "../types";
24
+ import { splitCredentialPool } from "../utils/credentials";
24
25
  import { getOpenRouterAttributionHeaders } from "../utils/openrouter-base-url";
25
26
  import { ensureConfigAdmin } from "./config";
26
27
  import { scheduleIntegrationsReload } from "./core";
@@ -36,11 +37,14 @@ async function buildSignals(state: OnboardingState): Promise<OnboardingSignals>
36
37
  const provider = ProviderNameSchema.safeParse(entry.provider);
37
38
  if (!provider.success) continue;
38
39
  const rollup = await rollupCredStatusForProvider(provider.data);
40
+ const live = rollup.latestLiveTest;
39
41
  providers.push({
40
42
  provider: provider.data,
41
43
  state: rollup.state,
42
44
  workers: rollup.workers,
43
45
  verifiedWorkers: rollup.verifiedWorkers,
46
+ error: live && !live.ok ? (live.error ?? null) : null,
47
+ warning: live?.ok ? (live.warning ?? null) : null,
44
48
  });
45
49
  }
46
50
 
@@ -241,7 +245,8 @@ async function handleMemoryProbe(body: OnboardingMemoryRequest): Promise<Onboard
241
245
  body.preset === "existing"
242
246
  ? (process.env.EMBEDDING_API_KEY ?? process.env.OPENAI_API_KEY)
243
247
  : undefined;
244
- const apiKey = explicitKey ?? reusedKey ?? implicitKey;
248
+ // Test and save the same pool member.
249
+ const apiKey = splitCredentialPool(explicitKey ?? reusedKey ?? implicitKey ?? "")[0];
245
250
 
246
251
  if (!apiKey) {
247
252
  return {
@@ -0,0 +1,354 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import { baseUrlEnvVar, providerForKeyVar } from "@desplega/model-catalog";
3
+ import { z } from "zod";
4
+ import {
5
+ deleteSwarmConfigByKey,
6
+ getDbClient,
7
+ getKeyStatuses,
8
+ getSwarmConfigs,
9
+ recordKeyValidation,
10
+ } from "../be/db";
11
+ import { retirePoolKeyStatus } from "../be/db/api-keys";
12
+ import { upsertSwarmConfigWithPolicyMirror } from "../be/multi-runtime";
13
+ import { validateConfigEntry } from "../be/swarm-config-guard";
14
+ import type { SwarmConfig } from "../types";
15
+ import { CREDENTIAL_POOL_VARS, splitCredentialPool } from "../utils/credentials";
16
+ import { checkProviderPoolKey } from "../utils/provider-key-probes";
17
+ import { registerEnvSecrets, scrubSecrets } from "../utils/secret-scrubber";
18
+ import { ensureConfigAdmin, requestSecretReadScope, runConfigWriteSideEffects } from "./config";
19
+ import { getDeploymentEnvValue } from "./core";
20
+ import { route } from "./route-def";
21
+ import { jsonError } from "./utils";
22
+
23
+ export const PoolKeyStatusSchema = z.enum(["ok", "rejected", "rate_limited", "unchecked"]);
24
+ export type PoolKeyStatus = z.infer<typeof PoolKeyStatusSchema>;
25
+ export const PoolKeySchema = z.object({
26
+ index: z.number().int().min(0),
27
+ suffix: z.string(),
28
+ status: PoolKeyStatusSchema,
29
+ error: z.string().nullable(),
30
+ checkedAt: z.string().nullable(),
31
+ rateLimitedUntil: z.string().nullable(),
32
+ });
33
+ export type PoolKey = z.infer<typeof PoolKeySchema>;
34
+ export const PoolKeysResponseSchema = z.object({
35
+ key: z.string(),
36
+ source: z.enum(["stored", "env", "none"]),
37
+ readOnly: z.boolean(),
38
+ keys: z.array(PoolKeySchema),
39
+ });
40
+ export type PoolKeysResponse = z.infer<typeof PoolKeysResponseSchema>;
41
+ export const PoolKeyCheckResultSchema = z.enum(["verified", "rejected", "not_tested"]);
42
+ export type PoolKeyCheckResult = z.infer<typeof PoolKeyCheckResultSchema>;
43
+ export const PoolKeyCheckSchema = z.object({
44
+ suffix: z.string(),
45
+ result: PoolKeyCheckResultSchema,
46
+ httpStatus: z.number().nullable(),
47
+ error: z.string().nullable(),
48
+ });
49
+ export type PoolKeyCheck = z.infer<typeof PoolKeyCheckSchema>;
50
+
51
+ const poolVar = z
52
+ .string()
53
+ .refine(
54
+ (key) => key !== "CODEX_OAUTH" && CREDENTIAL_POOL_VARS.includes(key),
55
+ "Choose a credential pool variable.",
56
+ );
57
+ const getPoolKeys = route({
58
+ method: "get",
59
+ path: "/api/config/pool-keys",
60
+ pattern: ["api", "config", "pool-keys"],
61
+ summary: "List credential pool suffixes and status",
62
+ tags: ["Config"],
63
+ query: z.object({ key: poolVar }),
64
+ responses: {
65
+ 200: { description: "Credential pool", schema: PoolKeysResponseSchema },
66
+ 400: { description: "Invalid pool variable" },
67
+ 403: { description: "Secret read permission required" },
68
+ },
69
+ });
70
+ const addPoolKeys = route({
71
+ method: "post",
72
+ path: "/api/config/pool-keys",
73
+ pattern: ["api", "config", "pool-keys"],
74
+ summary: "Test and append keys to a credential pool",
75
+ tags: ["Config"],
76
+ rbac: { permission: "config.write.any" },
77
+ body: z.object({
78
+ key: poolVar,
79
+ value: z.string(),
80
+ // The OpenRouter card can switch away from a stored gateway before the new key is saved.
81
+ baseUrl: z
82
+ .string()
83
+ .nullable()
84
+ .optional()
85
+ .describe(
86
+ "Base URL for the check only. Null tests the provider default. Omit to use the stored URL.",
87
+ ),
88
+ }),
89
+ responses: {
90
+ 200: {
91
+ description: "Updated credential pool and checks",
92
+ schema: PoolKeysResponseSchema.extend({ checks: z.array(PoolKeyCheckSchema) }),
93
+ },
94
+ 400: { description: "Invalid key" },
95
+ 403: { description: "Config administration and permission to read all secrets required" },
96
+ 409: { description: "Duplicate key or deployment env pool" },
97
+ 422: {
98
+ description: "Provider rejected a key. Nothing was saved.",
99
+ schema: z.object({ error: z.string(), checks: z.array(PoolKeyCheckSchema) }),
100
+ },
101
+ },
102
+ });
103
+ const deletePoolKey = route({
104
+ method: "delete",
105
+ path: "/api/config/pool-keys",
106
+ pattern: ["api", "config", "pool-keys"],
107
+ summary: "Remove one credential pool member",
108
+ tags: ["Config"],
109
+ rbac: { permission: "config.delete.any" },
110
+ query: z.object({
111
+ key: poolVar,
112
+ index: z.coerce.number().int().min(0),
113
+ suffix: z.string().min(1).max(5),
114
+ }),
115
+ responses: {
116
+ 200: { description: "Updated credential pool", schema: PoolKeysResponseSchema },
117
+ 400: { description: "Invalid pool member" },
118
+ 403: { description: "Config administration and permission to read all secrets required" },
119
+ 409: { description: "The pool changed or comes from the deployment env" },
120
+ },
121
+ });
122
+
123
+ const ENV_POOL_ERROR = "These keys come from the deployment env. Change them there.";
124
+
125
+ async function readPool(key: string) {
126
+ const [row] = await getSwarmConfigs({ scope: "global", key });
127
+ const envValue = getDeploymentEnvValue(key);
128
+ const source = row ? "stored" : envValue ? "env" : "none";
129
+ const value = row?.value ?? envValue ?? "";
130
+ registerEnvSecrets({ [key]: value });
131
+ return { row, source, members: splitCredentialPool(value) } as const;
132
+ }
133
+
134
+ async function poolResponse(key: string): Promise<PoolKeysResponse> {
135
+ const pool = await readPool(key);
136
+ const statuses = await getKeyStatuses(key, "global", null);
137
+ return {
138
+ key,
139
+ source: pool.source,
140
+ readOnly: pool.source === "env",
141
+ keys: pool.members.map((member, index) => {
142
+ const suffix = member.slice(-5);
143
+ const row = statuses.find((status) => status.keySuffix === suffix);
144
+ const rejected = !!row?.lastValidationFailedAt;
145
+ const limited =
146
+ row?.status === "rate_limited" &&
147
+ !!row.rateLimitedUntil &&
148
+ Date.parse(row.rateLimitedUntil) > Date.now();
149
+ return {
150
+ index,
151
+ suffix,
152
+ status: rejected ? "rejected" : limited ? "rate_limited" : row ? "ok" : "unchecked",
153
+ error: rejected ? scrubSecrets(row?.lastValidationError) || null : null,
154
+ checkedAt: rejected ? row!.lastValidationFailedAt : (row?.updatedAt ?? null),
155
+ rateLimitedUntil: row?.rateLimitedUntil ?? null,
156
+ };
157
+ }),
158
+ };
159
+ }
160
+
161
+ function duplicateError(members: readonly string[], additions: string[]): string | null {
162
+ const seen = new Set(members);
163
+ for (const member of additions) {
164
+ if (seen.has(member)) return `The key ending ${member.slice(-5)} is already in the pool.`;
165
+ seen.add(member);
166
+ }
167
+ return null;
168
+ }
169
+
170
+ async function savePool(key: string, members: string[], row: SwarmConfig | undefined) {
171
+ const value = members.join(",");
172
+ const validationError = validateConfigEntry({
173
+ scope: "global",
174
+ key,
175
+ value,
176
+ exportToEnv: row?.exportToEnv,
177
+ });
178
+ if (validationError) return validationError;
179
+ await upsertSwarmConfigWithPolicyMirror({
180
+ scope: "global",
181
+ key,
182
+ value,
183
+ isSecret: true,
184
+ description: row?.description ?? null,
185
+ envPath: row?.envPath ?? null,
186
+ exportToEnv: row?.exportToEnv,
187
+ });
188
+ return null;
189
+ }
190
+
191
+ /** Every response lists the pool's key suffixes and statuses, so each route needs this gate. */
192
+ async function ensureSecretRead(req: IncomingMessage, res: ServerResponse): Promise<boolean> {
193
+ if ((await requestSecretReadScope(req)) === "all") return true;
194
+ jsonError(res, "Reading credential pools requires permission to read all secrets.", 403);
195
+ return false;
196
+ }
197
+
198
+ export async function handlePoolKeys(
199
+ req: IncomingMessage,
200
+ res: ServerResponse,
201
+ pathSegments: string[],
202
+ queryParams: URLSearchParams,
203
+ ): Promise<boolean> {
204
+ if (getPoolKeys.match(req.method, pathSegments)) {
205
+ const parsed = await getPoolKeys.parse(req, res, pathSegments, queryParams);
206
+ if (!parsed) return true;
207
+ if (!(await ensureSecretRead(req, res))) return true;
208
+ getPoolKeys.respond(res, 200, await poolResponse(parsed.query.key));
209
+ return true;
210
+ }
211
+ if (addPoolKeys.match(req.method, pathSegments)) {
212
+ const parsed = await addPoolKeys.parse(req, res, pathSegments, queryParams);
213
+ if (!parsed) return true;
214
+ if (!(await ensureConfigAdmin(req, res, "config.write.any"))) return true;
215
+ if (!(await ensureSecretRead(req, res))) return true;
216
+ const { key, value, baseUrl } = parsed.body;
217
+ const additions = value
218
+ .split(",")
219
+ .map((part) => part.trim())
220
+ .filter(Boolean);
221
+ if (!additions.length) {
222
+ jsonError(res, "Enter a key.", 400);
223
+ return true;
224
+ }
225
+ if (additions.some((part) => /\s/.test(part))) {
226
+ jsonError(res, "A key cannot contain spaces or line breaks.", 400);
227
+ return true;
228
+ }
229
+ registerEnvSecrets({ [key]: additions.join(",") });
230
+ const initial = await readPool(key);
231
+ if (initial.source === "env") {
232
+ jsonError(res, ENV_POOL_ERROR, 409);
233
+ return true;
234
+ }
235
+ const duplicate = duplicateError(initial.members, additions);
236
+ if (duplicate) {
237
+ jsonError(res, scrubSecrets(duplicate), 409);
238
+ return true;
239
+ }
240
+ const env = { ...process.env };
241
+ const provider = providerForKeyVar(key);
242
+ const baseUrlKey =
243
+ key === "OPENROUTER_API_KEY"
244
+ ? "OPENROUTER_BASE_URL"
245
+ : provider
246
+ ? baseUrlEnvVar(provider)
247
+ : undefined;
248
+ if (baseUrlKey && baseUrl !== undefined) {
249
+ env[baseUrlKey] = baseUrl ?? "";
250
+ } else if (baseUrlKey) {
251
+ const [row] = await getSwarmConfigs({ scope: "global", key: baseUrlKey });
252
+ if (row) env[baseUrlKey] = row.value;
253
+ }
254
+ const checks = await Promise.all(
255
+ additions.map(async (member): Promise<PoolKeyCheck> => {
256
+ try {
257
+ const probe = await checkProviderPoolKey(key, member, env);
258
+ return {
259
+ suffix: member.slice(-5),
260
+ result: probe.skipped
261
+ ? "not_tested"
262
+ : probe.ok
263
+ ? "verified"
264
+ : probe.httpStatus === 401
265
+ ? "rejected"
266
+ : "not_tested",
267
+ httpStatus: probe.httpStatus ?? null,
268
+ error: scrubSecrets(probe.error?.replaceAll(member, "[REDACTED]")) || null,
269
+ };
270
+ } catch {
271
+ return {
272
+ suffix: member.slice(-5),
273
+ result: "not_tested",
274
+ httpStatus: null,
275
+ error: "The provider check did not complete.",
276
+ };
277
+ }
278
+ }),
279
+ );
280
+ const rejected = checks.find((check) => check.result === "rejected");
281
+ if (rejected) {
282
+ addPoolKeys.respond(res, 422, {
283
+ error: scrubSecrets(
284
+ `The key ending ${rejected.suffix} was rejected (HTTP 401). Nothing was saved.`,
285
+ ),
286
+ checks,
287
+ });
288
+ return true;
289
+ }
290
+ const result = await getDbClient().transaction(async () => {
291
+ const current = await readPool(key);
292
+ if (current.source === "env") return { error: ENV_POOL_ERROR, status: 409 };
293
+ const duplicate = duplicateError(current.members, additions);
294
+ if (duplicate) return { error: duplicate, status: 409 };
295
+ const members = [...current.members, ...additions];
296
+ const error = await savePool(key, members, current.row);
297
+ if (error) return { error, status: 400 };
298
+ await recordKeyValidation(
299
+ key,
300
+ checks.flatMap((check, index) =>
301
+ check.result === "verified"
302
+ ? [{ keyIndex: current.members.length + index, keySuffix: check.suffix, ok: true }]
303
+ : [],
304
+ ),
305
+ "global",
306
+ null,
307
+ { retireAbsent: false },
308
+ );
309
+ return { value: members.join(",") };
310
+ });
311
+ if ("error" in result) {
312
+ jsonError(res, scrubSecrets(result.error), result.status);
313
+ return true;
314
+ }
315
+ await runConfigWriteSideEffects("global", key, result.value);
316
+ addPoolKeys.respond(res, 200, { ...(await poolResponse(key)), checks });
317
+ return true;
318
+ }
319
+ if (deletePoolKey.match(req.method, pathSegments)) {
320
+ const parsed = await deletePoolKey.parse(req, res, pathSegments, queryParams);
321
+ if (!parsed) return true;
322
+ if (!(await ensureConfigAdmin(req, res, "config.delete.any"))) return true;
323
+ if (!(await ensureSecretRead(req, res))) return true;
324
+ const { key, index, suffix } = parsed.query;
325
+ const result = await getDbClient().transaction(async () => {
326
+ const current = await readPool(key);
327
+ if (current.source === "env") return { error: ENV_POOL_ERROR, status: 409 };
328
+ if (!current.members[index] || current.members[index].slice(-5) !== suffix) {
329
+ return { error: "The key list changed. Reload it and try again.", status: 409 };
330
+ }
331
+ const members = current.members.filter((_, position) => position !== index);
332
+ if (members.length) {
333
+ const error = await savePool(key, members, current.row);
334
+ if (error) return { error, status: 400 };
335
+ } else {
336
+ await deleteSwarmConfigByKey("global", null, key);
337
+ }
338
+ await retirePoolKeyStatus(
339
+ key,
340
+ suffix,
341
+ members.map((member, keyIndex) => ({ keySuffix: member.slice(-5), keyIndex })),
342
+ );
343
+ return { value: members.join(",") };
344
+ });
345
+ if ("error" in result) {
346
+ jsonError(res, scrubSecrets(result.error), result.status);
347
+ return true;
348
+ }
349
+ await runConfigWriteSideEffects("global", key, result.value);
350
+ deletePoolKey.respond(res, 200, await poolResponse(key));
351
+ return true;
352
+ }
353
+ return false;
354
+ }
@@ -6,6 +6,7 @@ import {
6
6
  normalizeAssetKey,
7
7
  parseAssetKey,
8
8
  } from "../assets/key";
9
+ import { SOURCE_GROUPS_READ_ONLY, sourceMoveError, sourceOfKey } from "../assets/source-key";
9
10
  import { AssetKeyAuthorizationError, authorizeAssetKeyWrite } from "../be/asset-key-auth";
10
11
  import { resolveHttpAuditUserId } from "../be/audit-user";
11
12
  import {
@@ -20,13 +21,19 @@ import {
20
21
  getTaskById,
21
22
  listRecentSessions,
22
23
  } from "../be/db";
24
+ import { getSessionLabels } from "../be/db/session-labels";
23
25
  import {
24
26
  listPrefixRoots,
25
27
  listSharedPrefixRoots,
26
28
  moveSessionChains,
27
29
  type SessionKeyMove,
28
30
  } from "../be/db/session-moves";
29
- import { getSessionReadState, getSessionTree, triageSessionList } from "../be/db/session-tree";
31
+ import {
32
+ getSessionGroupKey,
33
+ getSessionReadState,
34
+ getSessionTree,
35
+ triageSessionList,
36
+ } from "../be/db/session-tree";
30
37
  import { getUserGrant } from "../be/rbac-roles";
31
38
  import {
32
39
  getSessionRoot,
@@ -53,6 +60,7 @@ import {
53
60
  import { can, isRbacEnabled } from "../rbac";
54
61
  import { sessionAccessTopic } from "../realtime/auth";
55
62
  import { realtimeBus } from "../realtime/bus";
63
+ import { warmSlackChannelLabels } from "../slack/channel-labels";
56
64
  import {
57
65
  type AgentTask,
58
66
  AgentTaskSchema,
@@ -152,6 +160,7 @@ const SessionTreeGroupSchema = SessionSectionCountsSchema.extend({
152
160
  lastActivityAt: z.string(),
153
161
  });
154
162
  const SessionTreeResponseSchema = z.object({
163
+ labels: z.record(z.string(), z.string()).optional(),
155
164
  groups: z.array(SessionTreeGroupSchema),
156
165
  inbox: SessionSectionCountsSchema,
157
166
  later: SessionSectionCountsSchema,
@@ -738,7 +747,7 @@ async function checkSessionMove(
738
747
  actor: SessionActor,
739
748
  req: IncomingMessage,
740
749
  myAgentId: string | undefined,
741
- options: { ownerOnly?: boolean } = {},
750
+ options: { ownerOnly?: boolean; destination?: string | null } = {},
742
751
  ): Promise<{ reason: string } | { taskIds: string[] }> {
743
752
  const root = await getTaskById(rootTaskId);
744
753
  if (!root) return { reason: SESSION_MOVE_UNAVAILABLE };
@@ -752,6 +761,8 @@ async function checkSessionMove(
752
761
  if (!hasSessionAccess(access, "write")) return { reason: "Session write access required" };
753
762
  }
754
763
  if (root.parentTaskId) return { reason: "Task is not a session root" };
764
+ const sourceError = sourceMoveError(options.destination ?? null, root);
765
+ if (sourceError) return { reason: sourceError };
755
766
  const tasks = await getRootTaskChain(root.id);
756
767
  for (const task of tasks) {
757
768
  if (!(await canMutateTaskNamespace(task, myAgentId, req))) {
@@ -778,6 +789,8 @@ const getSession = route({
778
789
  chain: z.array(TaskWithSteeringSchema),
779
790
  access: SessionAccessSchema,
780
791
  owner: SessionOwnerSchema.nullable(),
792
+ /** The group of `root` with the list's rule: a source thread with two or more roots is its own group. */
793
+ groupKey: z.string(),
781
794
  approvals: z.array(SessionApprovalSchema).optional(),
782
795
  /** The viewer's read state, with the list's rule. Omitted for callers that act for no user. */
783
796
  readState: z.enum(["unread", "read", "later", "done"]).optional(),
@@ -1012,15 +1025,18 @@ export async function handleSessions(
1012
1025
  const parsed = await sessionTree.parse(req, res, pathSegments, queryParams);
1013
1026
  if (!parsed) return true;
1014
1027
  const actor = await resolveSessionActor(req, myAgentId);
1015
- sessionTree.respond(
1016
- res,
1017
- 200,
1018
- await getSessionTree(actor, undefined, {
1019
- mine: parsed.query.show !== "all",
1020
- hideSource: parsed.query.hideSource,
1021
- showSource: parsed.query.showSource,
1022
- }),
1028
+ const tree = await getSessionTree(actor, undefined, {
1029
+ mine: parsed.query.show !== "all",
1030
+ hideSource: parsed.query.hideSource,
1031
+ showSource: parsed.query.showSource,
1032
+ });
1033
+ // Labels cover hidden sources too: a pane or tab of a hidden source still names its group.
1034
+ const { sourceGroups, slackChannels, threadLabels, ...counts } = tree;
1035
+ const labels = { ...threadLabels, ...(await getSessionLabels(sourceGroups)) };
1036
+ warmSlackChannelLabels(
1037
+ slackChannels.map((id) => `shared/slack/${id}/`).filter((key) => labels[key] === undefined),
1023
1038
  );
1039
+ sessionTree.respond(res, 200, { ...counts, labels });
1024
1040
  return true;
1025
1041
  }
1026
1042
 
@@ -1054,7 +1070,9 @@ export async function handleSessions(
1054
1070
  const skipped: { rootTaskId: string; reason: string }[] = [];
1055
1071
  const moves: SessionKeyMove[] = [];
1056
1072
  for (const rootId of new Set(parsed.body.rootTaskIds)) {
1057
- const checked = await checkSessionMove(rootId, actor, req, myAgentId);
1073
+ const checked = await checkSessionMove(rootId, actor, req, myAgentId, {
1074
+ destination: key,
1075
+ });
1058
1076
  if ("reason" in checked) {
1059
1077
  skipped.push({ rootTaskId: rootId, reason: checked.reason });
1060
1078
  } else {
@@ -1076,6 +1094,10 @@ export async function handleSessions(
1076
1094
  const parsed = await moveSessionPrefix.parse(req, res, pathSegments, queryParams);
1077
1095
  if (!parsed) return true;
1078
1096
  const from = normalizeAssetKey(parsed.body.from);
1097
+ if (sourceOfKey(from) || (parsed.body.to && sourceOfKey(normalizeAssetKey(parsed.body.to)))) {
1098
+ jsonError(res, SOURCE_GROUPS_READ_ONLY, 400);
1099
+ return true;
1100
+ }
1079
1101
  if (parseAssetKey(from).relativeSegments.length === 0) {
1080
1102
  jsonError(res, "Cannot move a namespace root", 400);
1081
1103
  return true;
@@ -1126,6 +1148,7 @@ export async function handleSessions(
1126
1148
  }
1127
1149
  const checked = await checkSessionMove(root.id, actor, req, myAgentId, {
1128
1150
  ownerOnly: true,
1151
+ destination: nextKey,
1129
1152
  });
1130
1153
  if ("reason" in checked) {
1131
1154
  skipped.push({ rootTaskId: root.id, reason: checked.reason });
@@ -1175,7 +1198,7 @@ export async function handleSessions(
1175
1198
  const chain = await getRootTaskChain(parsed.params.rootTaskId);
1176
1199
  // Load steering, citations and pending approvals for the already loaded chain.
1177
1200
  const tasks = [root, ...chain];
1178
- const [steering, citations, approvals, read] = await Promise.all([
1201
+ const [steering, citations, approvals, read, groupKey] = await Promise.all([
1179
1202
  getTaskSteeringFieldsForTasks(tasks),
1180
1203
  getTaskCitationsForTasks(tasks.map((task) => task.id)),
1181
1204
  getPendingApprovalsForTasks(tasks.map((task) => task.id)),
@@ -1184,6 +1207,7 @@ export async function handleSessions(
1184
1207
  actor.kind === "user" && actor.userId
1185
1208
  ? getSessionReadState(sessionRoot.id, actor.userId)
1186
1209
  : Promise.resolve(null),
1210
+ getSessionGroupKey(root),
1187
1211
  ]);
1188
1212
  const decorate = (task: AgentTask) => ({
1189
1213
  ...task,
@@ -1195,6 +1219,7 @@ export async function handleSessions(
1195
1219
  chain: chain.map(decorate),
1196
1220
  access,
1197
1221
  owner,
1222
+ groupKey,
1198
1223
  ...(approvals.length ? { approvals } : {}),
1199
1224
  ...(read ?? {}),
1200
1225
  });
package/src/http/stats.ts CHANGED
@@ -86,6 +86,7 @@ const SwarmMetricsSchema = z.object({
86
86
  agentLog: RetentionTableStatsSchema.optional(),
87
87
  events: RetentionTableStatsSchema.optional(),
88
88
  contextVersions: RetentionTableStatsSchema.optional(),
89
+ scriptRuns: RetentionTableStatsSchema.optional(),
89
90
  }),
90
91
  });
91
92