@desplega.ai/agent-swarm 2.4.4 → 2.5.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 (382) hide show
  1. package/README.md +4 -2
  2. package/dist/{acp-adapter-thq04adh.js → acp-adapter-11gp67nr.js} +8 -6
  3. package/dist/{actions-j6ywh6x5.js → actions-8p2kze8j.js} +24 -22
  4. package/dist/{amp-adapter-k8b5xmj8.js → amp-adapter-z4qvhacj.js} +2 -2
  5. package/dist/{anthropic-messages-g6pqmqk4.js → anthropic-messages-txs190dd.js} +17 -20
  6. package/dist/{app-rb7vc02s.js → app-0qp0a2gk.js} +3 -3
  7. package/dist/{assistant-ftta1zjf.js → assistant-jedd3ktm.js} +27 -25
  8. package/dist/{azure-openai-responses-9dbmj346.js → azure-openai-responses-9kfwhmn7.js} +18 -13
  9. package/dist/{boot-reembed-xns7h5h9.js → boot-reembed-4xwq5tg0.js} +1 -1
  10. package/dist/{boot-reembed-srp6v6af.js → boot-reembed-n4wq5dd2.js} +1 -1
  11. package/dist/{boot-scrub-sweep-3qqrh2mz.js → boot-scrub-sweep-69h90xcq.js} +4 -4
  12. package/dist/{boot-seeds-9ks8hce7.js → boot-seeds-3vn0m9sg.js} +4 -4
  13. package/dist/{claude-adapter-k5ksyres.js → claude-adapter-221mvad5.js} +8 -6
  14. package/dist/{claude-managed-adapter-h2y889ge.js → claude-managed-adapter-gt6pa35d.js} +2 -2
  15. package/dist/{claude-managed-setup-n5a5eqks.js → claude-managed-setup-c3hp36ph.js} +4 -4
  16. package/dist/{claude-sdk-session-2j3j4kwn.js → claude-sdk-session-8thjsk5e.js} +9729 -9628
  17. package/dist/{cli-b9fcxxev.js → cli-007cnhm1.js} +50 -4
  18. package/dist/{cli-j4rqdf2n.js → cli-03vpq45j.js} +2 -2
  19. package/dist/{cli-amyhm3gj.js → cli-0y6f0ckq.js} +1 -1
  20. package/dist/{cli-4dg55wsk.js → cli-0z3vjrpm.js} +1 -1
  21. package/dist/{cli-pccfmtb5.js → cli-179xcyky.js} +11 -11
  22. package/dist/{cli-3r8samv3.js → cli-1f0gp9vz.js} +3 -2
  23. package/dist/{cli-hj3debgw.js → cli-1pr672hz.js} +3 -3
  24. package/dist/{cli-0bqteszy.js → cli-2nf3b08t.js} +328 -25
  25. package/dist/{cli-t7cyef5g.js → cli-36j4jnk3.js} +4 -4
  26. package/dist/{cli-er83cjbn.js → cli-3qv8naq8.js} +527 -231
  27. package/dist/cli-3zwye9sn.js +1193 -0
  28. package/dist/{cli-60cgh1rv.js → cli-459fk97x.js} +7 -7
  29. package/dist/{cli-xj0re5tm.js → cli-4w24f7z3.js} +1 -1
  30. package/dist/{cli-v3yr2gbg.js → cli-5kzk3472.js} +2198 -968
  31. package/dist/{cli-h7gc0b4r.js → cli-5m84zd2k.js} +55 -18
  32. package/dist/{cli-w6nadkh4.js → cli-5rvg1ddk.js} +5 -5
  33. package/dist/{cli-dvkmp9bt.js → cli-6ctwrf1z.js} +20 -10
  34. package/dist/{cli-9qtjfd60.js → cli-6rg2jn2y.js} +3 -2
  35. package/dist/{cli-2v045xyf.js → cli-7bwtc6yx.js} +3 -3
  36. package/dist/{cli-3cgngyd8.js → cli-7g40g8hq.js} +35 -17
  37. package/dist/{cli-q6r7t299.js → cli-92mqenkc.js} +2379 -154
  38. package/dist/{cli-cynvc9fj.js → cli-9ce2xw98.js} +8 -8
  39. package/dist/{cli-gqvh8d8a.js → cli-bgdtv62r.js} +36 -22
  40. package/dist/{cli-wxbn3yfx.js → cli-cz5nmdxt.js} +2 -2
  41. package/dist/{cli-wt645v7q.js → cli-dabehxhp.js} +1 -1
  42. package/dist/{cli-vfha21c9.js → cli-e5bxnjps.js} +2 -2
  43. package/dist/{cli-p20r8w42.js → cli-fcc5qe3f.js} +763 -268
  44. package/dist/{cli-1ephgwnq.js → cli-fes51ya2.js} +3 -3
  45. package/dist/{cli-1d70crsy.js → cli-ftmq4nvk.js} +2 -2
  46. package/dist/{cli-qkhc5aqy.js → cli-fy1jyyym.js} +13 -13
  47. package/dist/{cli-s95dknxh.js → cli-g1cw7xtt.js} +1 -1
  48. package/dist/{cli-qje9k2nh.js → cli-g9ypd5en.js} +12 -2
  49. package/dist/{cli-74c1wpm0.js → cli-gpv60278.js} +1 -1
  50. package/dist/{cli-pemywhfq.js → cli-ha9rmwgq.js} +2 -1
  51. package/dist/{cli-bnz0r3kc.js → cli-j1eghqfm.js} +5 -5
  52. package/dist/{cli-2zqpkvtt.js → cli-jpxsrxcb.js} +1 -1
  53. package/dist/{cli-ejs3m1sx.js → cli-kxpfx2xr.js} +1 -1
  54. package/dist/{cli-37yb7307.js → cli-mbfj11fq.js} +1 -1
  55. package/dist/{cli-pnp2j5zg.js → cli-mmhj8vd7.js} +2 -2
  56. package/dist/{cli-xf234ws8.js → cli-n4c8e3s5.js} +2 -0
  57. package/dist/{cli-a0kcty51.js → cli-n6kmqngw.js} +2 -2
  58. package/dist/{cli-te6b5wvg.js → cli-nnvjb17s.js} +3 -3
  59. package/dist/{cli-1dmcq2eh.js → cli-nzfnt2gs.js} +1 -1
  60. package/dist/{cli-625tb7aq.js → cli-p859hcc8.js} +2 -2
  61. package/dist/{cli-smvcw9z5.js → cli-pasyvjmm.js} +1 -1
  62. package/dist/cli-pkaqk57w.js +128 -0
  63. package/dist/{cli-8nyhf7za.js → cli-qqzmjxkf.js} +129 -105
  64. package/dist/{cli-zrwpctp4.js → cli-r25qpaxt.js} +161 -63
  65. package/dist/{cli-nc35pxb7.js → cli-rh5smt8t.js} +19 -4
  66. package/dist/{cli-qwkcwjdn.js → cli-rycdaj0a.js} +1 -1
  67. package/dist/{cli-esxerzg7.js → cli-s4sntwt4.js} +2 -2
  68. package/dist/{cli-b8k7epj1.js → cli-segs7hx9.js} +8 -8
  69. package/dist/{cli-36ckw0wx.js → cli-stxdrqs2.js} +2 -2
  70. package/dist/{cli-vdm20wej.js → cli-sy8f9705.js} +4 -4
  71. package/dist/{cli-1m17dc9d.js → cli-ta8bd7th.js} +2 -2
  72. package/dist/{cli-2y91wnbh.js → cli-we0ra057.js} +222 -10
  73. package/dist/{cli-55qmbp6z.js → cli-x21dqn18.js} +5 -5
  74. package/dist/{cli-dqb4n629.js → cli-x6vf4sad.js} +11 -11
  75. package/dist/{cli-d2mb5s8a.js → cli-x77gen2p.js} +680 -440
  76. package/dist/{cli-vq97g1jq.js → cli-xawhpgc9.js} +95 -195
  77. package/dist/{cli-82d3nkc3.js → cli-xd4eqtr2.js} +2 -2
  78. package/dist/{cli-9fhkkpj5.js → cli-xt768r5m.js} +2 -2
  79. package/dist/{cli-zrk6jxv8.js → cli-y4x8ww6s.js} +2 -2
  80. package/dist/{cli-xc27fwzg.js → cli-y9jvxfhk.js} +3 -3
  81. package/dist/{cli-44159k28.js → cli-z63a269p.js} +2 -2
  82. package/dist/{cli-sm8rses9.js → cli-zfdp01hn.js} +8 -5
  83. package/dist/cli.js +31 -29
  84. package/dist/{cloudflare-workers-ai-system-one-ch2wxfcp.js → cloudflare-workers-ai-system-one-43v9qkjk.js} +7 -2
  85. package/dist/{codex-adapter-968xwfy7.js → codex-adapter-tcg1q3jk.js} +6 -4
  86. package/dist/{codex-login-m819c41r.js → codex-login-rn7c5gxt.js} +1 -1
  87. package/dist/{codex-session-runner-ht4z99fa.js → codex-session-runner-xfx4wd9x.js} +7 -5
  88. package/dist/{commands-pcymaeg9.js → commands-ew83nj70.js} +2 -2
  89. package/dist/{controller-fpjyk46r.js → controller-gnq1k1mf.js} +4 -4
  90. package/dist/{controllers-connect-mwzx1fh6.js → controllers-connect-tw1dan9r.js} +26 -24
  91. package/dist/{cursor-adapter-cp4s2e2f.js → cursor-adapter-yq5xgfcg.js} +1 -1
  92. package/dist/{db-xsjg9jqq.js → db-7gbc53fd.js} +14 -2
  93. package/dist/{db-sgd12bt9.js → db-t86xrnes.js} +3 -3
  94. package/dist/{devin-adapter-zgqadvnm.js → devin-adapter-4ycy4vrf.js} +1 -1
  95. package/dist/{dispatcher-x0jfw2c9.js → dispatcher-64bdyhps.js} +23 -21
  96. package/dist/{docker-fkgtr75t.js → docker-nfy2gc8t.js} +2 -2
  97. package/dist/{dsh-adapter-jrq7ma7h.js → dsh-adapter-tvhc4k2d.js} +1 -1
  98. package/dist/{e2b-16d6ch39.js → e2b-9gkdhm3c.js} +2 -2
  99. package/dist/{e2b-zn0b4w5w.js → e2b-cjtf8k1z.js} +2 -2
  100. package/dist/{easter-egg-3d-5cgynaja.js → easter-egg-3d-e4qv8dxj.js} +1 -1
  101. package/dist/{event-bus-9vpk8gmq.js → event-bus-3z78ep5n.js} +1 -1
  102. package/dist/{execute-t4e6m3rw.js → execute-0xpz8950.js} +85 -23
  103. package/dist/{external-channel-8njj4h3b.js → external-channel-e9nw24zv.js} +2 -2
  104. package/dist/{google-generative-ai-bmq8gkf3.js → google-generative-ai-mk9vcpe1.js} +15 -15
  105. package/dist/{google-vertex-b1rvp52s.js → google-vertex-q580kz8k.js} +20 -20
  106. package/dist/{grok-adapter-kkr97v2f.js → grok-adapter-v82srhrx.js} +9 -7
  107. package/dist/handlers-c9ajzhbn.js +41 -0
  108. package/dist/{heartbeat-wd03zhn0.js → heartbeat-2vqs1319.js} +26 -24
  109. package/dist/{hook-xzj2ts2p.js → hook-01nk13wx.js} +7 -5
  110. package/dist/{hook-f0vghj42.js → hook-jzpheegg.js} +9 -7
  111. package/dist/{http-4hwxv62p.js → http-fc1k6ec8.js} +859 -252
  112. package/dist/index-07ef7nkm.js +42 -0
  113. package/dist/{index-g6tbb6ve.js → index-31ceyf42.js} +1 -1
  114. package/dist/index-7yts8n9s.js +43 -0
  115. package/dist/{index-c4ykd0f1.js → index-dr94zbnh.js} +13348 -11611
  116. package/dist/index-q3t40ryv.js +46 -0
  117. package/dist/{index-ww2vznkp.js → index-sepja052.js} +1 -1
  118. package/dist/{index-97q4xn03.js → index-zdycv3ja.js} +135 -79
  119. package/dist/{index-g216q6jr.js → index-zsyx51ap.js} +1 -4
  120. package/dist/{keepalive-hrh7xnr9.js → keepalive-5nh2rhmr.js} +4 -4
  121. package/dist/lead-aayq64jn.js +79 -0
  122. package/dist/{llama-cpp-classify-vv43gswy.js → llama-cpp-classify-ee7vmd7q.js} +4 -1
  123. package/dist/{maintenance-e8wnwnqx.js → maintenance-hjhzdt3k.js} +1 -1
  124. package/dist/{mistral-conversations-1medfwv7.js → mistral-conversations-sd0xf1tn.js} +13 -13
  125. package/dist/{node-3v3f08f7.js → node-jzjj7r9x.js} +10 -9
  126. package/dist/{oauth-refresh-sweep-zm0yj4sj.js → oauth-refresh-sweep-y3n1knmd.js} +3 -3
  127. package/dist/{onboard-dtwcq603.js → onboard-1zq3m7db.js} +2 -2
  128. package/dist/{openai-codex-responses-de9pvs4d.js → openai-codex-responses-79s8xnbh.js} +29 -23
  129. package/dist/{openai-completions-mtqge3nf.js → openai-completions-tdbyts0x.js} +21 -20
  130. package/dist/{openai-decisions-ygk4kkxd.js → openai-decisions-bqhnjyfz.js} +2 -1
  131. package/dist/{openai-responses-dkyng71g.js → openai-responses-dm3t5vqp.js} +26 -17
  132. package/dist/{opencode-adapter-x4tqzhkh.js → opencode-adapter-e56086m1.js} +7 -5
  133. package/dist/{openrouter-images-wvv7camj.js → openrouter-images-ssttr1y4.js} +6 -3
  134. package/dist/{otel-impl-w3fbhpn0.js → otel-impl-h9yyza47.js} +2 -2
  135. package/dist/{pi-messages-nhh1pkz2.js → pi-messages-3nf8bq1a.js} +6 -4
  136. package/dist/{pi-mono-adapter-mcfkn9vf.js → pi-mono-adapter-tzps8f38.js} +9 -37
  137. package/dist/{pricing-refresh-j38dehfs.js → pricing-refresh-nevhat8h.js} +3 -3
  138. package/dist/{rbac-roles-ae8yttq4.js → rbac-roles-4y6ngvzq.js} +1 -1
  139. package/dist/{rbac-roles-ejxx9b35.js → rbac-roles-exbg8xb6.js} +2 -2
  140. package/dist/{registry-rxw10xzg.js → registry-ajhexe44.js} +1 -1
  141. package/dist/{render-v2-n16n0phe.js → render-v2-cfsh13z3.js} +3 -3
  142. package/dist/{resume-z0fgjc38.js → resume-5s2t0dbs.js} +10 -8
  143. package/dist/{runtime-xe3aqy4x.js → runtime-bt5pkpm1.js} +1 -1
  144. package/dist/{session-access-cv3mq6mc.js → session-access-dnbyp3cn.js} +6 -4
  145. package/dist/{setup-mafhj3yt.js → setup-1wj1y77j.js} +1 -1
  146. package/dist/{typesafe-system-one-2hgpayzy.js → typesafe-system-one-bre38hvs.js} +4 -2
  147. package/dist/{virtual-modules-4r3zkc20.js → virtual-modules-mnfbh5mr.js} +8 -2
  148. package/dist/{work-objects-8b86mgjp.js → work-objects-7mg6216g.js} +26 -24
  149. package/dist/worker-nwb789mg.js +79 -0
  150. package/dist/{x-y45mthwn.js → x-pbpezr0s.js} +2 -2
  151. package/openapi.json +793 -6
  152. package/package.json +11 -11
  153. package/src/apps/catalog.generated.json +206 -4
  154. package/src/apps/page-validator.ts +13 -1
  155. package/src/assets/key.ts +17 -0
  156. package/src/azure-devops/handlers.ts +1 -1
  157. package/src/be/chunking.ts +42 -9
  158. package/src/be/db/approvals.ts +18 -0
  159. package/src/be/db/session-moves.ts +164 -0
  160. package/src/be/db/session-tree.ts +384 -0
  161. package/src/be/db/tasks/draft-text.ts +15 -0
  162. package/src/be/db/tasks/read.ts +54 -5
  163. package/src/be/db/workflow-runs.ts +96 -0
  164. package/src/be/db.ts +85 -237
  165. package/src/be/memory/index-content.ts +36 -17
  166. package/src/be/memory/providers/openai-embedding.ts +115 -61
  167. package/src/be/memory/providers/sqlite-store.ts +69 -23
  168. package/src/be/memory/reranker.ts +31 -1
  169. package/src/be/memory/search-candidates.ts +47 -0
  170. package/src/be/memory/types.ts +14 -0
  171. package/src/be/onboarding.ts +3 -0
  172. package/src/be/scripts/typecheck.ts +2 -0
  173. package/src/be/seed-scripts/catalog/heartbeat-jev-gate.ts +106 -54
  174. package/src/be/seed-scripts/index.ts +2 -2
  175. package/src/be/skill-sync.ts +43 -2
  176. package/src/be/swarm-config-guard.ts +25 -1
  177. package/src/be/task-attachment-store.ts +30 -0
  178. package/src/bitbucket/api.ts +81 -0
  179. package/src/bitbucket/auth.ts +64 -0
  180. package/src/bitbucket/handlers.ts +434 -0
  181. package/src/bitbucket/index.ts +28 -0
  182. package/src/bitbucket/templates.ts +134 -0
  183. package/src/bitbucket/types.ts +65 -0
  184. package/src/commands/runner.ts +55 -3
  185. package/src/e2b/env.ts +1 -0
  186. package/src/extensions/catalog.generated.json +4 -4
  187. package/src/extensions/contract-types.generated.ts +1 -1
  188. package/src/extensions/contract.ts +9 -2
  189. package/src/extensions/post-bridge.ts +1 -0
  190. package/src/fs/provider.ts +13 -1
  191. package/src/github/handlers.ts +6 -6
  192. package/src/gitlab/handlers.ts +5 -5
  193. package/src/heartbeat/heartbeat.ts +31 -11
  194. package/src/http/assets.ts +14 -2
  195. package/src/http/core.ts +12 -0
  196. package/src/http/fs.ts +54 -34
  197. package/src/http/index.ts +4 -0
  198. package/src/http/mcp-body.ts +37 -0
  199. package/src/http/mcp-user.ts +3 -5
  200. package/src/http/mcp.ts +6 -7
  201. package/src/http/memory.ts +26 -15
  202. package/src/http/onboarding.ts +1 -0
  203. package/src/http/page-proxy.ts +61 -5
  204. package/src/http/pages-public.ts +27 -44
  205. package/src/http/pages.ts +4 -4
  206. package/src/http/script-runs.ts +2 -0
  207. package/src/http/sessions.ts +366 -12
  208. package/src/http/skills.ts +48 -1
  209. package/src/http/tasks.ts +1 -1
  210. package/src/http/users.ts +13 -1
  211. package/src/http/webhooks.ts +141 -0
  212. package/src/integrations/chat-sdk/adapters.ts +24 -4
  213. package/src/integrations/chat-sdk/attachments.ts +90 -12
  214. package/src/integrations/chat-sdk/facts.ts +138 -0
  215. package/src/integrations/chat-sdk/inbound.ts +156 -8
  216. package/src/integrations/chat-sdk/limits.ts +24 -0
  217. package/src/integrations/chat-sdk/outbound-files.ts +186 -0
  218. package/src/integrations/chat-sdk/outbound.ts +272 -5
  219. package/src/integrations/chat-sdk/relay.ts +4 -1
  220. package/src/integrations/chat-sdk/router.ts +28 -2
  221. package/src/integrations/chat-sdk/steering.ts +9 -0
  222. package/src/integrations/chat-sdk/templates.ts +14 -0
  223. package/src/integrations/chat-sdk/thread-buffer.ts +4 -0
  224. package/src/mcp-limits.ts +20 -0
  225. package/src/pages/browser-sdk.ts +8 -3
  226. package/src/pages/feedback-overlay.ts +1 -1
  227. package/src/providers/acp-targets.ts +1 -1
  228. package/src/providers/amp-adapter.ts +1 -1
  229. package/src/providers/codex-adapter.ts +106 -119
  230. package/src/providers/codex-agents-md.ts +35 -229
  231. package/src/providers/cursor-adapter.ts +1 -1
  232. package/src/providers/pi-mono-adapter.ts +5 -53
  233. package/src/realtime/transport.ts +1 -2
  234. package/src/script-workflows/workflow-ctx.ts +1 -0
  235. package/src/scripts-runtime/types/stdlib.d.ts +2 -0
  236. package/src/scripts-runtime/types/swarm-extension.d.ts +14 -4
  237. package/src/scripts-runtime/types/swarm-sdk.d.ts +2 -0
  238. package/src/server-user.ts +2 -0
  239. package/src/server.ts +2 -0
  240. package/src/slack/assistant.ts +2 -2
  241. package/src/slack/files.ts +2 -0
  242. package/src/slack/handlers.test.ts +158 -0
  243. package/src/slack/handlers.ts +23 -32
  244. package/src/slack/inbound-files.ts +39 -6
  245. package/src/slack/thread-buffer.ts +2 -1
  246. package/src/tasks/context-key.ts +61 -1
  247. package/src/tasks/task-terminal-effects.ts +21 -17
  248. package/src/tests/apps-card-grid.test.ts +197 -0
  249. package/src/tests/apps-catalog-additions.test.ts +120 -0
  250. package/src/tests/apps-tree.test.ts +129 -0
  251. package/src/tests/bitbucket-auth.test.ts +156 -0
  252. package/src/tests/bitbucket-handlers.test.ts +425 -0
  253. package/src/tests/bitbucket-webhook.test.ts +220 -0
  254. package/src/tests/chat-sdk-attachments-reply.test.ts +52 -0
  255. package/src/tests/chat-sdk-config.test.ts +11 -0
  256. package/src/tests/chat-sdk-facts.test.ts +23 -0
  257. package/src/tests/chat-sdk-inbound.test.ts +597 -4
  258. package/src/tests/chat-sdk-outbound-files.test.ts +652 -0
  259. package/src/tests/chat-sdk-telegram-markdown.test.ts +92 -0
  260. package/src/tests/codex-adapter.test.ts +167 -242
  261. package/src/tests/config-api-only-keys.test.ts +9 -0
  262. package/src/tests/context-key.test.ts +50 -0
  263. package/src/tests/core-auth.test.ts +5 -0
  264. package/src/tests/cors-allowlist.test.ts +1 -1
  265. package/src/tests/db-task-read-contract.test.ts +7 -0
  266. package/src/tests/db-task-write-contract.test.ts +6 -0
  267. package/src/tests/docs-index-footprint.test.js +3 -3
  268. package/src/tests/extensions-example-slack-reply-gate.test.ts +262 -91
  269. package/src/tests/fixtures/bitbucket/pullrequest-comment-created-inline.json +174 -0
  270. package/src/tests/fixtures/bitbucket/pullrequest-comment-created.json +170 -0
  271. package/src/tests/fixtures/bitbucket/pullrequest-created-mention.json +132 -0
  272. package/src/tests/fixtures/bitbucket/pullrequest-created-reviewer.json +140 -0
  273. package/src/tests/fixtures/bitbucket/pullrequest-fulfilled.json +134 -0
  274. package/src/tests/fixtures/bitbucket/pullrequest-rejected.json +133 -0
  275. package/src/tests/generate-mcp-docs.test.ts +101 -0
  276. package/src/tests/gitlab-handlers.test.ts +3 -3
  277. package/src/tests/gitlab-vcs-db.test.ts +4 -4
  278. package/src/tests/heartbeat-checklist.test.ts +33 -0
  279. package/src/tests/heartbeat-jev-gate-credentials.test.ts +44 -0
  280. package/src/tests/heartbeat-jev-gate.test.ts +242 -1
  281. package/src/tests/integration-disable-flags.test.ts +36 -0
  282. package/src/tests/kv-page-proxy.test.ts +91 -5
  283. package/src/tests/launch-password-rejection.test.ts +2 -2
  284. package/src/tests/mcp-input-caps.test.ts +294 -0
  285. package/src/tests/mcp-tools.test.ts +1 -1
  286. package/src/tests/memory-chunked-search-starvation.test.ts +200 -0
  287. package/src/tests/memory-embedding-input-cap.test.ts +114 -0
  288. package/src/tests/memory.test.ts +14 -0
  289. package/src/tests/onboarding.test.ts +12 -0
  290. package/src/tests/page-guest-session.test.ts +19 -8
  291. package/src/tests/page-proxy-authed.test.ts +1 -1
  292. package/src/tests/page-proxy.test.ts +17 -10
  293. package/src/tests/page-session.test.ts +102 -1
  294. package/src/tests/pages-authed-mode.test.ts +82 -12
  295. package/src/tests/pages-password-mode.test.ts +5 -5
  296. package/src/tests/pi-codemode.test.ts +8 -2
  297. package/src/tests/preload-env.test.ts +57 -0
  298. package/src/tests/preload.ts +21 -0
  299. package/src/tests/prompt-template-remaining.test.ts +38 -0
  300. package/src/tests/realtime-transport.test.ts +13 -7
  301. package/src/tests/run-root-coverage.test.ts +39 -0
  302. package/src/tests/runner-bitbucket-vcs.test.ts +191 -0
  303. package/src/tests/runner-skills-refresh.test.ts +58 -0
  304. package/src/tests/script-runs-http.test.ts +84 -0
  305. package/src/tests/scripts-http.test.ts +10 -1
  306. package/src/tests/secret-scrubber.test.ts +15 -0
  307. package/src/tests/sessions-move.test.ts +576 -0
  308. package/src/tests/sessions-tree.test.ts +493 -0
  309. package/src/tests/skill-files-http.test.ts +50 -0
  310. package/src/tests/skills-bundle.test.ts +99 -0
  311. package/src/tests/swarm-config-export-to-env.test.ts +23 -3
  312. package/src/tests/task-audio-transcription.test.ts +299 -0
  313. package/src/tests/task-completion-memory-chunking.test.ts +434 -0
  314. package/src/tests/task-terminal-secret-scrub.test.ts +6 -1
  315. package/src/tests/transcription.test.ts +299 -0
  316. package/src/tests/vcs-bitbucket.test.ts +91 -0
  317. package/src/tests/vcs-provider-isolation.test.ts +123 -0
  318. package/src/tests/vcs-provider.test.ts +16 -1
  319. package/src/tests/vcs-tracking.test.ts +3 -3
  320. package/src/tests/workflow-foreach-bodies.test.ts +637 -0
  321. package/src/tests/workflow-foreach-concurrency.test.ts +342 -0
  322. package/src/tests/workflow-foreach-retry.test.ts +537 -0
  323. package/src/tests/workflow-foreach.test.ts +44 -23
  324. package/src/tests/workflow-step-claims.test.ts +6 -6
  325. package/src/tests/workflow-system-one-decision-providers.test.ts +98 -1
  326. package/src/tests/workflow-system-one-decision.test.ts +2 -1
  327. package/src/tests/workflow-tla-races.test.ts +2 -2
  328. package/src/tools/chat-access.ts +10 -0
  329. package/src/tools/chat-post.ts +31 -11
  330. package/src/tools/chat-reply.ts +32 -10
  331. package/src/tools/memory-search.ts +3 -1
  332. package/src/tools/script-delete.ts +1 -1
  333. package/src/tools/system-one-decide.ts +17 -4
  334. package/src/tools/workflows/create-workflow.ts +5 -3
  335. package/src/types.ts +9 -2
  336. package/src/utils/config-env-export.ts +4 -1
  337. package/src/utils/page-session.ts +133 -20
  338. package/src/utils/secret-scrubber.ts +4 -0
  339. package/src/utils/skills-refresh.ts +83 -43
  340. package/src/utils/transcription.ts +246 -0
  341. package/src/vcs/bitbucket.ts +58 -0
  342. package/src/vcs/index.ts +2 -0
  343. package/src/vcs/types.ts +2 -2
  344. package/src/workflows/approval-resolution.ts +4 -1
  345. package/src/workflows/checkpoint.ts +9 -1
  346. package/src/workflows/constants.ts +3 -0
  347. package/src/workflows/definition.ts +48 -17
  348. package/src/workflows/engine.ts +25 -15
  349. package/src/workflows/executors/foreach.ts +45 -99
  350. package/src/workflows/executors/system-one-decision.ts +28 -7
  351. package/src/workflows/executors/system-one-providers.ts +39 -4
  352. package/src/workflows/foreach-body.ts +146 -0
  353. package/src/workflows/foreach-driver.ts +552 -0
  354. package/src/workflows/foreach-join.ts +206 -7
  355. package/src/workflows/node-timeout.ts +21 -0
  356. package/src/workflows/recovery.ts +68 -3
  357. package/src/workflows/resume.ts +221 -43
  358. package/src/workflows/retry-poller.ts +39 -24
  359. package/src/workflows/step-input.ts +59 -0
  360. package/src/workflows/task-step-routing.ts +114 -56
  361. package/templates/extensions/slack-reply-gate/README.md +24 -22
  362. package/templates/extensions/slack-reply-gate/hooks.ts +146 -95
  363. package/templates/extensions/slack-reply-gate/manifest.json +2 -2
  364. package/templates/skills/apps/SKILL.md +28 -6
  365. package/templates/skills/apps/content.md +28 -6
  366. package/templates/skills/chat-sdk/content.md +6 -2
  367. package/templates/skills/code-quality/content.md +2 -1
  368. package/templates/skills/heartbeat-runbook/content.md +5 -5
  369. package/templates/skills/pages/content.md +6 -4
  370. package/templates/skills/slack-interaction/content.md +5 -0
  371. package/templates/skills/swarm-scripts/SKILL.md +2 -2
  372. package/templates/skills/swarm-scripts/content.md +2 -2
  373. package/templates/skills/work-on-task/SKILL.md +1 -1
  374. package/templates/skills/work-on-task/content.md +1 -1
  375. package/dist/cli-51y09c6p.js +0 -2657
  376. package/dist/cli-yw91rnmw.js +0 -13
  377. package/dist/handlers-0pvee9nw.js +0 -39
  378. package/dist/index-gdh8jrdm.js +0 -43
  379. package/dist/index-hg0x2cdv.js +0 -40
  380. package/dist/index-thm0y6a8.js +0 -41
  381. package/dist/lead-ee8vazz2.js +0 -77
  382. package/dist/worker-16s75hm5.js +0 -77
@@ -1,17 +1,36 @@
1
1
  import type { IncomingMessage, ServerResponse } from "node:http";
2
2
  import { z } from "zod";
3
3
  import {
4
+ ASSET_KEY_MAX_LENGTH,
5
+ defaultAssetKey,
6
+ normalizeAssetKey,
7
+ parseAssetKey,
8
+ } from "../assets/key";
9
+ import { AssetKeyAuthorizationError, authorizeAssetKeyWrite } from "../be/asset-key-auth";
10
+ import { resolveHttpAuditUserId } from "../be/audit-user";
11
+ import {
12
+ AssetKeyMovesBlockedError,
13
+ assertAssetKeyMovesAllowed,
4
14
  countSessions,
5
15
  createAgent,
6
16
  getAgentById,
7
17
  getDbClient,
18
+ getPendingApprovalsForTasks,
8
19
  getRootTaskChain,
9
20
  getTaskById,
10
21
  listRecentSessions,
11
22
  } from "../be/db";
23
+ import {
24
+ listPrefixRoots,
25
+ listSharedPrefixRoots,
26
+ moveSessionChains,
27
+ type SessionKeyMove,
28
+ } from "../be/db/session-moves";
29
+ import { getSessionReadState, getSessionTree } from "../be/db/session-tree";
12
30
  import { getUserGrant } from "../be/rbac-roles";
13
31
  import {
14
32
  getSessionRoot,
33
+ hasSessionAccess,
15
34
  listActiveSessionShares,
16
35
  type SessionAccess,
17
36
  sessionAccessForRoot,
@@ -38,6 +57,7 @@ import {
38
57
  type AgentTask,
39
58
  AgentTaskSchema,
40
59
  AgentTaskStatusSchema,
60
+ AssetKeySchema,
41
61
  SessionAccessSchema,
42
62
  SessionOwnerSchema,
43
63
  SessionSharesSchema,
@@ -46,9 +66,10 @@ import {
46
66
  } from "../types";
47
67
  import { getRequestAuth } from "../utils/request-auth-context";
48
68
  import { isRuntimePoolsEnabled } from "../utils/runtime-pools-flag";
69
+ import { canMutateTaskNamespace } from "./assets";
49
70
  import { agentFirstPrincipal } from "./request-principal";
50
71
  import { route } from "./route-def";
51
- import { resolveSessionActor, type SessionActor } from "./session-access";
72
+ import { resolveSessionActor, type SessionActor, sessionAccessFor } from "./session-access";
52
73
  import { jsonError } from "./utils";
53
74
 
54
75
  // ─── Response Schemas ────────────────────────────────────────────────────────
@@ -100,6 +121,61 @@ const SessionListItemSchema = z.object({
100
121
  chainTaskCount: z.number().int(),
101
122
  lastActivityAt: z.string(),
102
123
  latestStatus: AgentTaskStatusSchema,
124
+ readState: z.enum(["unread", "read", "later", "done"]).optional(),
125
+ snoozeUntil: z.string().nullable().optional(),
126
+ needsYou: z.boolean().optional(),
127
+ groupKey: z.string().optional(),
128
+ shared: z.boolean().optional(),
129
+ people: z.array(z.object({ id: z.string(), name: z.string() })).optional(),
130
+ peopleCount: z.number().int().optional(),
131
+ preview: z.string().nullable().optional(),
132
+ /** Active shares with their access level. `userId` null means everyone on the swarm. */
133
+ shares: z
134
+ .array(
135
+ z.object({
136
+ userId: z.string().nullable(),
137
+ name: z.string().nullable(),
138
+ access: z.enum(["read", "write"]),
139
+ }),
140
+ )
141
+ .optional(),
142
+ });
143
+
144
+ const SessionSectionCountsSchema = z.object({
145
+ total: z.number().int(),
146
+ unread: z.number().int(),
147
+ running: z.number().int(),
148
+ needsYou: z.number().int(),
149
+ });
150
+ const SessionTreeGroupSchema = SessionSectionCountsSchema.extend({
151
+ key: z.string(),
152
+ lastActivityAt: z.string(),
153
+ });
154
+ const SessionTreeResponseSchema = z.object({
155
+ groups: z.array(SessionTreeGroupSchema),
156
+ inbox: SessionSectionCountsSchema,
157
+ later: SessionSectionCountsSchema,
158
+ done: SessionSectionCountsSchema,
159
+ noGroup: SessionSectionCountsSchema,
160
+ });
161
+
162
+ const SessionApprovalSchema = z.object({
163
+ id: z.string(),
164
+ status: z.literal("pending"),
165
+ sourceTaskId: z.string(),
166
+ title: z.string(),
167
+ });
168
+
169
+ const SessionKeyFilterSchema = z.string().transform((value, ctx) => {
170
+ try {
171
+ return normalizeAssetKey(value);
172
+ } catch (error) {
173
+ ctx.addIssue({
174
+ code: "custom",
175
+ message: error instanceof Error ? error.message : "Invalid asset key",
176
+ });
177
+ return z.NEVER;
178
+ }
103
179
  });
104
180
 
105
181
  /**
@@ -431,6 +507,21 @@ const listSessions = route({
431
507
  * key plus a trailing `:` (e.g. `task:ui:workflow:abc:`).
432
508
  */
433
509
  contextKeyPrefix: z.string().min(1).optional(),
510
+ /** Literal asset key prefix, including nested groups. */
511
+ keyPrefix: SessionKeyFilterSchema.optional(),
512
+ /** Exact asset group, including direct resource leaves. */
513
+ groupKey: SessionKeyFilterSchema.optional(),
514
+ /** Shared and viewer-personal namespace roots and direct resource leaves. */
515
+ noGroup: z
516
+ .enum(["true", "false"])
517
+ .transform((v) => v === "true")
518
+ .optional(),
519
+ /** Chain-wide read state or Inbox section. */
520
+ triage: z.enum(["inbox", "later", "done", "unread"]).optional(),
521
+ /** Latest chain status or pending human attention. */
522
+ status: z.enum(["running", "failed", "needs"]).optional(),
523
+ /** Requester anywhere in the chain or active share recipient. */
524
+ withUserId: z.string().min(1).optional(),
434
525
  /** `full` restores the legacy shape (full root `AgentTask`); default is slim. */
435
526
  fields: z.enum(["full", "slim"]).optional(),
436
527
  /**
@@ -438,9 +529,16 @@ const listSessions = route({
438
529
  * with an active share to the caller or to everyone. Omit for every
439
530
  * session the caller can open. Callers that act for no user (agents, the
440
531
  * operator key with no picked dashboard user) see every session, and
441
- * an empty list for either scope.
532
+ * an empty list for mine or shared.
533
+ * tree: Sessions that GET /api/session-tree counts: mine plus shared with me,
534
+ * no ownerless roots. A bypass actor sees every root.
442
535
  */
443
- scope: z.enum(["mine", "shared"]).optional(),
536
+ scope: z
537
+ .enum(["mine", "shared", "tree"])
538
+ .optional()
539
+ .describe(
540
+ "mine: owned sessions. shared: sessions shared with me. tree: Sessions that GET /api/session-tree counts: mine plus shared with me, no ownerless roots. A bypass actor sees every root with tree scope.",
541
+ ),
444
542
  }),
445
543
  responses: {
446
544
  200: {
@@ -457,6 +555,126 @@ const listSessions = route({
457
555
  auth: { apiKey: true },
458
556
  });
459
557
 
558
+ const sessionTree = route({
559
+ method: "get",
560
+ path: "/api/session-tree",
561
+ pattern: ["api", "session-tree"],
562
+ summary: "Get session groups and triage counts",
563
+ tags: ["Sessions"],
564
+ responses: {
565
+ 200: { description: "Visible groups and section counts", schema: SessionTreeResponseSchema },
566
+ 401: { description: "Unauthorized" },
567
+ },
568
+ auth: { apiKey: true },
569
+ });
570
+
571
+ const SESSION_MOVE_LIMIT = 200;
572
+ const SESSION_MOVE_OWNER_ONLY = "Only the session owner can move this group";
573
+ const SESSION_MOVE_UNAVAILABLE = "Session not found or not accessible";
574
+
575
+ const SessionMoveResultSchema = z.object({
576
+ moved: z.array(z.string()),
577
+ skipped: z.array(z.object({ rootTaskId: z.string(), reason: z.string() })),
578
+ });
579
+ const sessionMoveRbac = {
580
+ ungated:
581
+ "per-session access, per-task task.fs.mutate, and asset-key write checks in the handler, as PATCH /api/assets/task/{id}/key",
582
+ };
583
+ const sessionMoveResponses = {
584
+ 400: { description: "Invalid namespace or request" },
585
+ 403: { description: "Personal destination not authorized" },
586
+ 409: { description: "Moves blocked until audit warnings are repaired" },
587
+ };
588
+
589
+ const moveSessions = route({
590
+ method: "post",
591
+ path: "/api/session-moves",
592
+ pattern: ["api", "session-moves"],
593
+ summary: "Move sessions into a group or reset their keys",
594
+ tags: ["Sessions"],
595
+ session: { none: "The handler checks each root and reports denied roots as skipped" },
596
+ body: z.object({
597
+ rootTaskIds: z.array(z.string().min(1)).min(1).max(SESSION_MOVE_LIMIT),
598
+ groupKey: AssetKeySchema.nullable(),
599
+ }),
600
+ responses: {
601
+ 200: { description: "Moved and skipped sessions", schema: SessionMoveResultSchema },
602
+ ...sessionMoveResponses,
603
+ },
604
+ auth: { apiKey: true },
605
+ rbac: sessionMoveRbac,
606
+ });
607
+
608
+ const moveSessionPrefix = route({
609
+ method: "post",
610
+ path: "/api/session-moves/prefix",
611
+ pattern: ["api", "session-moves", "prefix"],
612
+ summary: "Rename or delete a session group",
613
+ tags: ["Sessions"],
614
+ body: z.object({ from: AssetKeySchema, to: AssetKeySchema.nullable() }),
615
+ responses: {
616
+ 200: {
617
+ description: "Moved and skipped sessions and remaining movable roots",
618
+ schema: SessionMoveResultSchema.extend({ remaining: z.number().int().min(0) }),
619
+ },
620
+ ...sessionMoveResponses,
621
+ },
622
+ auth: { apiKey: true },
623
+ rbac: sessionMoveRbac,
624
+ });
625
+
626
+ async function withSessionMove(
627
+ req: IncomingMessage,
628
+ res: ServerResponse,
629
+ myAgentId: string | undefined,
630
+ destination: string | null,
631
+ run: (context: { actor: SessionActor; changedBy?: string; key: string | null }) => Promise<void>,
632
+ ): Promise<void> {
633
+ try {
634
+ const changedBy = await resolveHttpAuditUserId(req, myAgentId);
635
+ const actor = await resolveSessionActor(req, myAgentId);
636
+ const key = destination === null ? null : await authorizeAssetKeyWrite(destination, changedBy);
637
+ await assertAssetKeyMovesAllowed();
638
+ await run({ actor, changedBy: changedBy ?? undefined, key });
639
+ } catch (error) {
640
+ if (error instanceof AssetKeyAuthorizationError) {
641
+ jsonError(res, error.message, error.statusCode);
642
+ } else if (error instanceof AssetKeyMovesBlockedError) {
643
+ jsonError(res, error.message, 409);
644
+ } else {
645
+ throw error;
646
+ }
647
+ }
648
+ }
649
+
650
+ async function checkSessionMove(
651
+ rootTaskId: string,
652
+ actor: SessionActor,
653
+ req: IncomingMessage,
654
+ myAgentId: string | undefined,
655
+ options: { ownerOnly?: boolean } = {},
656
+ ): Promise<{ reason: string } | { taskIds: string[] }> {
657
+ const root = await getTaskById(rootTaskId);
658
+ if (!root) return { reason: SESSION_MOVE_UNAVAILABLE };
659
+ if (options.ownerOnly) {
660
+ if (actor.kind === "user" && (!actor.userId || root.requestedByUserId !== actor.userId)) {
661
+ return { reason: SESSION_MOVE_OWNER_ONLY };
662
+ }
663
+ } else {
664
+ const access = await sessionAccessFor(actor, root.id);
665
+ if (access === "none") return { reason: SESSION_MOVE_UNAVAILABLE };
666
+ if (!hasSessionAccess(access, "write")) return { reason: "Session write access required" };
667
+ }
668
+ if (root.parentTaskId) return { reason: "Task is not a session root" };
669
+ const tasks = await getRootTaskChain(root.id);
670
+ for (const task of tasks) {
671
+ if (!(await canMutateTaskNamespace(task, myAgentId, req))) {
672
+ return { reason: "Not authorized to move this task namespace" };
673
+ }
674
+ }
675
+ return { taskIds: tasks.map((task) => task.id) };
676
+ }
677
+
460
678
  const getSession = route({
461
679
  method: "get",
462
680
  path: "/api/sessions/{rootTaskId}",
@@ -474,6 +692,11 @@ const getSession = route({
474
692
  chain: z.array(TaskWithSteeringSchema),
475
693
  access: SessionAccessSchema,
476
694
  owner: SessionOwnerSchema.nullable(),
695
+ approvals: z.array(SessionApprovalSchema).optional(),
696
+ /** The viewer's read state, with the list's rule. Omitted for callers that act for no user. */
697
+ readState: z.enum(["unread", "read", "later", "done"]).optional(),
698
+ /** Newest chain activity: the read cursor that "mark read" writes. With `readState`. */
699
+ lastActivityAt: z.string().optional(),
477
700
  }),
478
701
  },
479
702
  401: { description: "Unauthorized" },
@@ -641,12 +864,13 @@ export async function handleSessions(
641
864
  .filter(Boolean)
642
865
  : undefined;
643
866
  const actor = await resolveSessionActor(req, myAgentId);
644
- if (actor.kind === "bypass" && parsed.query.scope) {
867
+ const limit = Math.min(200, Math.max(0, parsed.query.limit ?? 25));
868
+ if (actor.kind === "bypass" && parsed.query.scope && parsed.query.scope !== "tree") {
645
869
  // No user to own or receive a share.
646
870
  listSessions.respond(res, 200, {
647
871
  sessions: [],
648
872
  total: 0,
649
- limit: parsed.query.limit ?? 25,
873
+ limit,
650
874
  offset: parsed.query.offset ?? 0,
651
875
  });
652
876
  return true;
@@ -656,13 +880,26 @@ export async function handleSessions(
656
880
  q: parsed.query.q,
657
881
  requestedByUserId: parsed.query.requestedByUserId,
658
882
  contextKeyPrefix: parsed.query.contextKeyPrefix,
883
+ keyPrefix: parsed.query.keyPrefix,
884
+ groupKey: parsed.query.groupKey,
885
+ noGroup: parsed.query.noGroup,
886
+ triage: parsed.query.triage,
887
+ status: parsed.query.status,
888
+ withUserId: parsed.query.withUserId,
889
+ now: new Date().toISOString(),
890
+ tree: parsed.query.scope === "tree",
659
891
  ...(actor.kind === "user"
660
- ? { viewer: { userId: actor.userId, scope: parsed.query.scope ?? "all" } as const }
892
+ ? {
893
+ viewer: {
894
+ userId: actor.userId,
895
+ scope: parsed.query.scope === "tree" ? "all" : (parsed.query.scope ?? "all"),
896
+ } as const,
897
+ }
661
898
  : {}),
662
899
  };
663
900
  const baseOpts = {
664
901
  ...filterOpts,
665
- limit: parsed.query.limit,
902
+ limit,
666
903
  offset: parsed.query.offset,
667
904
  };
668
905
  // List responses default to slim (root is a task summary); `?fields=full` restores it.
@@ -676,12 +913,121 @@ export async function handleSessions(
676
913
  listSessions.respond(res, 200, {
677
914
  sessions,
678
915
  total,
679
- limit: parsed.query.limit ?? 25,
916
+ limit,
680
917
  offset: parsed.query.offset ?? 0,
681
918
  });
682
919
  return true;
683
920
  }
684
921
 
922
+ if (sessionTree.match(req.method, pathSegments)) {
923
+ const parsed = await sessionTree.parse(req, res, pathSegments, queryParams);
924
+ if (!parsed) return true;
925
+ sessionTree.respond(res, 200, await getSessionTree(await resolveSessionActor(req, myAgentId)));
926
+ return true;
927
+ }
928
+
929
+ if (moveSessions.match(req.method, pathSegments)) {
930
+ const parsed = await moveSessions.parse(req, res, pathSegments, queryParams);
931
+ if (!parsed) return true;
932
+ await withSessionMove(
933
+ req,
934
+ res,
935
+ myAgentId,
936
+ parsed.body.groupKey,
937
+ async ({ actor, changedBy, key }) => {
938
+ const skipped: { rootTaskId: string; reason: string }[] = [];
939
+ const moves: SessionKeyMove[] = [];
940
+ for (const rootId of new Set(parsed.body.rootTaskIds)) {
941
+ const checked = await checkSessionMove(rootId, actor, req, myAgentId);
942
+ if ("reason" in checked) {
943
+ skipped.push({ rootTaskId: rootId, reason: checked.reason });
944
+ } else {
945
+ moves.push({
946
+ rootId,
947
+ taskIds: checked.taskIds,
948
+ key: key ?? defaultAssetKey("task", rootId),
949
+ });
950
+ }
951
+ }
952
+ const moved = await moveSessionChains({ moves, changedBy });
953
+ moveSessions.respond(res, 200, { moved, skipped });
954
+ },
955
+ );
956
+ return true;
957
+ }
958
+
959
+ if (moveSessionPrefix.match(req.method, pathSegments)) {
960
+ const parsed = await moveSessionPrefix.parse(req, res, pathSegments, queryParams);
961
+ if (!parsed) return true;
962
+ const from = normalizeAssetKey(parsed.body.from);
963
+ if (parseAssetKey(from).relativeSegments.length === 0) {
964
+ jsonError(res, "Cannot move a namespace root", 400);
965
+ return true;
966
+ }
967
+ await withSessionMove(
968
+ req,
969
+ res,
970
+ myAgentId,
971
+ parsed.body.to,
972
+ async ({ actor, changedBy, key }) => {
973
+ if (key?.startsWith(from)) {
974
+ jsonError(res, "Destination must be outside the source prefix", 400);
975
+ return;
976
+ }
977
+ const skipped =
978
+ actor.kind === "user"
979
+ ? (await listSharedPrefixRoots({ from, userId: actor.userId })).map((root) => ({
980
+ rootTaskId: root.id,
981
+ reason: SESSION_MOVE_OWNER_ONLY,
982
+ }))
983
+ : [];
984
+ const moves: SessionKeyMove[] = [];
985
+ let remaining = 0;
986
+ // Scan before moving so offsets stay stable. Only owned roots need chain authorization.
987
+ for (let offset = 0; ; offset += SESSION_MOVE_LIMIT) {
988
+ const roots = await listPrefixRoots({
989
+ from,
990
+ userId: actor.kind === "user" ? actor.userId : undefined,
991
+ limit: SESSION_MOVE_LIMIT,
992
+ offset,
993
+ });
994
+ for (const root of roots) {
995
+ const nextKey =
996
+ key === null ? defaultAssetKey("task", root.id) : key + root.key.slice(from.length);
997
+ if (nextKey.startsWith(from)) {
998
+ skipped.push({
999
+ rootTaskId: root.id,
1000
+ reason: "The new key stays inside the source group",
1001
+ });
1002
+ continue;
1003
+ }
1004
+ if (nextKey.length > ASSET_KEY_MAX_LENGTH) {
1005
+ skipped.push({
1006
+ rootTaskId: root.id,
1007
+ reason: `Asset key cannot exceed ${ASSET_KEY_MAX_LENGTH} characters`,
1008
+ });
1009
+ continue;
1010
+ }
1011
+ const checked = await checkSessionMove(root.id, actor, req, myAgentId, {
1012
+ ownerOnly: true,
1013
+ });
1014
+ if ("reason" in checked) {
1015
+ skipped.push({ rootTaskId: root.id, reason: checked.reason });
1016
+ } else if (moves.length < SESSION_MOVE_LIMIT) {
1017
+ moves.push({ rootId: root.id, taskIds: checked.taskIds, key: nextKey });
1018
+ } else {
1019
+ remaining++;
1020
+ }
1021
+ }
1022
+ if (roots.length < SESSION_MOVE_LIMIT) break;
1023
+ }
1024
+ const moved = await moveSessionChains({ moves, changedBy });
1025
+ moveSessionPrefix.respond(res, 200, { moved, skipped, remaining });
1026
+ },
1027
+ );
1028
+ return true;
1029
+ }
1030
+
685
1031
  if (getSession.match(req.method, pathSegments)) {
686
1032
  const parsed = await getSession.parse(req, res, pathSegments, queryParams);
687
1033
  if (!parsed) return true;
@@ -695,7 +1041,8 @@ export async function handleSessions(
695
1041
  id: root.id,
696
1042
  requestedByUserId: root.requestedByUserId ?? null,
697
1043
  };
698
- const access = await accessForRoot(await resolveSessionActor(req, myAgentId), sessionRoot);
1044
+ const actor = await resolveSessionActor(req, myAgentId);
1045
+ const access = await accessForRoot(actor, sessionRoot);
699
1046
  if (access === "none") {
700
1047
  jsonError(res, "You don't have access to this session. Ask the owner to share it.", 403);
701
1048
  return true;
@@ -710,12 +1057,17 @@ export async function handleSessions(
710
1057
  }
711
1058
  : null;
712
1059
  const chain = await getRootTaskChain(parsed.params.rootTaskId);
713
- // Fixed query count regardless of chain length: one agents lookup and one
714
- // citations lookup for the whole chain (chunked past 500 ids).
1060
+ // Load steering, citations and pending approvals for the already loaded chain.
715
1061
  const tasks = [root, ...chain];
716
- const [steering, citations] = await Promise.all([
1062
+ const [steering, citations, approvals, read] = await Promise.all([
717
1063
  getTaskSteeringFieldsForTasks(tasks),
718
1064
  getTaskCitationsForTasks(tasks.map((task) => task.id)),
1065
+ getPendingApprovalsForTasks(tasks.map((task) => task.id)),
1066
+ // The viewer's read state, so a client marks a session read on open
1067
+ // without the list row (the same rule as `GET /api/sessions`).
1068
+ actor.kind === "user" && actor.userId
1069
+ ? getSessionReadState(sessionRoot.id, actor.userId)
1070
+ : Promise.resolve(null),
719
1071
  ]);
720
1072
  const decorate = (task: AgentTask) => ({
721
1073
  ...task,
@@ -727,6 +1079,8 @@ export async function handleSessions(
727
1079
  chain: chain.map(decorate),
728
1080
  access,
729
1081
  owner,
1082
+ ...(approvals.length ? { approvals } : {}),
1083
+ ...(read ?? {}),
730
1084
  });
731
1085
  return true;
732
1086
  }
@@ -7,6 +7,7 @@ import {
7
7
  getAgentSkills,
8
8
  getSkillById,
9
9
  getSkillFile,
10
+ getSkillFiles,
10
11
  installSkill,
11
12
  listSkillFileManifest,
12
13
  listSkills,
@@ -17,7 +18,11 @@ import {
17
18
  upsertSkillFiles,
18
19
  } from "../be/db";
19
20
  import { parseSkillContent } from "../be/skill-parser";
20
- import { computeAgentSkillsSignature, syncSkillsToFilesystem } from "../be/skill-sync";
21
+ import {
22
+ buildSkillsBundle,
23
+ computeAgentSkillsSignature,
24
+ syncSkillsToFilesystem,
25
+ } from "../be/skill-sync";
21
26
  import { getSkillUsageDetail, getSkillUsageSummary, SKILL_USAGE_WINDOWS } from "../be/skill-usage";
22
27
  import {
23
28
  AgentSkillSchema,
@@ -102,6 +107,21 @@ const agentSkillsListResponseSchema = z.object({
102
107
  signature: z.string(),
103
108
  });
104
109
 
110
+ /**
111
+ * Boot payload for workers: every installed-and-enabled skill together with
112
+ * the full content of its bundled files. Replaces the list call plus the
113
+ * per-skill, per-file fan-out a worker used to make while booting.
114
+ */
115
+ const agentSkillsBundleResponseSchema = z.object({
116
+ skills: z.array(
117
+ SkillWithInstallInfoSchema.extend({
118
+ files: z.array(SkillFileSchema),
119
+ }),
120
+ ),
121
+ total: z.number(),
122
+ signature: z.string(),
123
+ });
124
+
105
125
  const agentSkillsSignatureResponseSchema = z.object({
106
126
  hash: z.string(),
107
127
  count: z.number(),
@@ -543,6 +563,21 @@ const getAgentSkillsSignatureRoute = route({
543
563
  },
544
564
  });
545
565
 
566
+ const getAgentSkillsBundleRoute = route({
567
+ method: "get",
568
+ path: "/api/agents/{id}/skills/bundle",
569
+ pattern: ["api", "agents", null, "skills", "bundle"],
570
+ summary: "Get an agent's installed skills together with their bundled files",
571
+ description:
572
+ "Single-request boot payload: every installed-and-enabled skill with the full content of its bundled files, plus the same signature the cheap probe returns. Lets a worker sync its local skill tree without an N+1 file fan-out.",
573
+ tags: ["Skills"],
574
+ auth: { apiKey: true },
575
+ params: z.object({ id: z.string() }),
576
+ responses: {
577
+ 200: { description: "Agent skills bundle", schema: agentSkillsBundleResponseSchema },
578
+ },
579
+ });
580
+
546
581
  // ─── Handler ─────────────────────────────────────────────────────────────────
547
582
 
548
583
  export async function handleSkills(
@@ -552,6 +587,18 @@ export async function handleSkills(
552
587
  queryParams: URLSearchParams,
553
588
  myAgentId: string | undefined,
554
589
  ): Promise<boolean> {
590
+ // GET /api/agents/:id/skills/bundle (must come before the shorter patterns)
591
+ if (getAgentSkillsBundleRoute.match(req.method, pathSegments)) {
592
+ const parsed = await getAgentSkillsBundleRoute.parse(req, res, pathSegments, queryParams);
593
+ if (!parsed) return true;
594
+ const bundle = await buildSkillsBundle(
595
+ () => getAgentSkills(parsed.params.id),
596
+ (skillId) => getSkillFiles(skillId),
597
+ );
598
+ getAgentSkillsBundleRoute.respond(res, 200, bundle);
599
+ return true;
600
+ }
601
+
555
602
  // GET /api/agents/:id/skills/signature (must come before the shorter pattern)
556
603
  if (getAgentSkillsSignatureRoute.match(req.method, pathSegments)) {
557
604
  const parsed = await getAgentSkillsSignatureRoute.parse(req, res, pathSegments, queryParams);
package/src/http/tasks.ts CHANGED
@@ -744,7 +744,7 @@ const updateTaskVcsRoute = route({
744
744
  tags: ["Tasks"],
745
745
  params: z.object({ id: z.string() }),
746
746
  body: z.object({
747
- vcsProvider: z.enum(["github", "gitlab", "azure-devops"]),
747
+ vcsProvider: z.enum(["github", "gitlab", "azure-devops", "bitbucket"]),
748
748
  vcsRepo: z.string(),
749
749
  vcsNumber: z.number().int().positive(),
750
750
  vcsUrl: z.string().url(),
package/src/http/users.ts CHANGED
@@ -550,7 +550,19 @@ const deleteIdentityRoute = route({
550
550
 
551
551
  // ─── Helpers ─────────────────────────────────────────────────────────────────
552
552
 
553
- const UNMAPPED_KINDS = ["slack", "github", "gitlab", "azure-devops", "linear", "kapso"] as const;
553
+ const UNMAPPED_KINDS = [
554
+ "slack",
555
+ "github",
556
+ "gitlab",
557
+ "azure-devops",
558
+ "bitbucket",
559
+ "linear",
560
+ "kapso",
561
+ // Chat SDK adapters record unknown senders under the adapter name.
562
+ "telegram",
563
+ "discord",
564
+ "teams",
565
+ ] as const;
554
566
 
555
567
  /**
556
568
  * Group the two-key-per-identity kv entries (`<externalId>:meta` json +