@desplega.ai/agent-swarm 2.5.4 → 2.7.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 (354) hide show
  1. package/README.md +4 -2
  2. package/dist/{acp-adapter-ek38cwsv.js → acp-adapter-ma1js5n7.js} +9 -9
  3. package/dist/{actions-92mepq03.js → actions-1vhmtvg9.js} +21 -21
  4. package/dist/{amp-adapter-9gaxg015.js → amp-adapter-sxqqvmdk.js} +4 -4
  5. package/dist/{app-ctn8gsgx.js → app-mwx1d5a9.js} +3 -3
  6. package/dist/{assistant-zyr5kz47.js → assistant-yx1p6g82.js} +30 -26
  7. package/dist/{boot-reembed-w75maga2.js → boot-reembed-5h37a2c2.js} +1 -1
  8. package/dist/{boot-reembed-dwzfttdr.js → boot-reembed-7h78vmvv.js} +2 -1
  9. package/dist/{boot-scrub-sweep-e9kq43kf.js → boot-scrub-sweep-8mawd4gw.js} +4 -4
  10. package/dist/{boot-seeds-x28v3fhb.js → boot-seeds-ca84dhnn.js} +4 -4
  11. package/dist/{claude-adapter-f0afqnne.js → claude-adapter-wqt4hfkq.js} +9 -9
  12. package/dist/{claude-managed-adapter-x81a7r65.js → claude-managed-adapter-6fjqtr2z.js} +2 -2
  13. package/dist/{claude-managed-setup-bqzwjd9x.js → claude-managed-setup-rqv98sea.js} +10 -3
  14. package/dist/{claude-sdk-session-7b7whkaq.js → claude-sdk-session-f29f3b2x.js} +11 -11
  15. package/dist/{cli-sxgtf5fw.js → cli-07q03xm2.js} +1 -1
  16. package/dist/{cli-kxpfx2xr.js → cli-0z1xjzwa.js} +2 -2
  17. package/dist/{cli-g9vd9ece.js → cli-1hhbzkbb.js} +14 -16
  18. package/dist/{cli-qqzmjxkf.js → cli-1m9x8dr5.js} +11 -6
  19. package/dist/{cli-jbnn4vxb.js → cli-2pvzmas1.js} +3 -3
  20. package/dist/{cli-x77gen2p.js → cli-2y7v133v.js} +7 -7
  21. package/dist/{cli-dew6x255.js → cli-39aet655.js} +2 -2
  22. package/dist/{cli-g1cw7xtt.js → cli-39rrw8yp.js} +1 -1
  23. package/dist/{cli-jb30dzvk.js → cli-48gxjmcz.js} +530 -190
  24. package/dist/{cli-pzatvw94.js → cli-54sj64fc.js} +2 -2
  25. package/dist/{cli-bvpgr9ny.js → cli-60sty27p.js} +5 -5
  26. package/dist/{cli-asq5ydm4.js → cli-6a9qfa8b.js} +411 -69
  27. package/dist/{cli-277c31dw.js → cli-6mgdba42.js} +225 -3
  28. package/dist/{cli-19202kew.js → cli-6srv653c.js} +5 -5
  29. package/dist/{cli-n4c8e3s5.js → cli-708wdy32.js} +1 -1
  30. package/dist/{cli-pasyvjmm.js → cli-737pyqxr.js} +1 -1
  31. package/dist/{cli-6x9ma5w5.js → cli-803188pb.js} +1 -1
  32. package/dist/{cli-hh5we352.js → cli-86ehn4s5.js} +7 -7
  33. package/dist/{cli-9s05422w.js → cli-8mbc9za9.js} +1 -1
  34. package/dist/{cli-ff1tm9n9.js → cli-8wmj18cz.js} +1 -1
  35. package/dist/{cli-1r5ad7j5.js → cli-9gsm2rzp.js} +12 -12
  36. package/dist/{cli-mz9afxxf.js → cli-9pshx6rd.js} +17 -3
  37. package/dist/{cli-s682zgqz.js → cli-a6ds2epe.js} +1 -1
  38. package/dist/{cli-6a1xg1p7.js → cli-a89zdqz9.js} +2 -2
  39. package/dist/{cli-03vpq45j.js → cli-ay40dnj3.js} +2 -2
  40. package/dist/{cli-tsma7p8v.js → cli-aypd09nf.js} +2 -2
  41. package/dist/{cli-ethgy7ha.js → cli-b3yzyc57.js} +2 -2
  42. package/dist/{cli-bn0rewsy.js → cli-c081bj3b.js} +2 -2
  43. package/dist/{cli-mmhj8vd7.js → cli-c668vvcf.js} +11 -2
  44. package/dist/{cli-sbwsxvtd.js → cli-d20qvpr2.js} +2 -2
  45. package/dist/{cli-c952c7ac.js → cli-e1595bgx.js} +3 -3
  46. package/dist/{cli-621ce7kw.js → cli-f12mr20z.js} +3 -3
  47. package/dist/{cli-dqygeeh5.js → cli-f1e0ptjj.js} +10 -1
  48. package/dist/{cli-xzfq4at3.js → cli-f4dbh7a5.js} +16 -12
  49. package/dist/{cli-ha9rmwgq.js → cli-fb8m699c.js} +2 -2
  50. package/dist/{cli-5bnsc0e1.js → cli-gsfk58nw.js} +5 -5
  51. package/dist/cli-gt4h8cxw.js +895 -0
  52. package/dist/{cli-nwyw3zhg.js → cli-gxee0bqa.js} +1 -1
  53. package/dist/{cli-25n3mv5d.js → cli-h5jeg46p.js} +6 -6
  54. package/dist/{cli-gn5af0x5.js → cli-he9gbdx0.js} +62 -12
  55. package/dist/{cli-gbfxp48k.js → cli-j0ppywdr.js} +42 -35
  56. package/dist/{cli-k4tjsdx9.js → cli-j3y0x2n4.js} +277 -199
  57. package/dist/{cli-7p4jxgtt.js → cli-j9bpmykh.js} +75 -27
  58. package/dist/{cli-dabehxhp.js → cli-kt907da6.js} +1 -1
  59. package/dist/{cli-wx2nj19m.js → cli-kv7sbc2c.js} +6 -6
  60. package/dist/{cli-abs8ndtr.js → cli-m30k9777.js} +26 -26
  61. package/dist/{cli-cz5nmdxt.js → cli-m5e802r7.js} +2 -2
  62. package/dist/{cli-0shx9j93.js → cli-pfk92eqz.js} +2 -2
  63. package/dist/{cli-xtrz6bzs.js → cli-ptfq202x.js} +5 -3
  64. package/dist/{cli-6n8axjv1.js → cli-qj442fws.js} +2 -2
  65. package/dist/{cli-c61mbdds.js → cli-rd5m70gj.js} +4 -4
  66. package/dist/{cli-eftzyh9y.js → cli-s09j9vp5.js} +2 -2
  67. package/dist/{cli-m4ecdxvz.js → cli-s0mgx214.js} +7 -7
  68. package/dist/{cli-wghjkr0e.js → cli-sgqm2582.js} +10 -7
  69. package/dist/{cli-2655k16q.js → cli-sjctjr48.js} +2 -2
  70. package/dist/{cli-nzfnt2gs.js → cli-tf1n0r0b.js} +1 -1
  71. package/dist/{cli-x0f2v69c.js → cli-twctnkng.js} +12 -7
  72. package/dist/{cli-j6rktc91.js → cli-v6shg7na.js} +1 -1
  73. package/dist/{cli-gx4rm0qx.js → cli-vz78s5wm.js} +1 -1
  74. package/dist/{cli-gwhjmh8g.js → cli-wqr7cea4.js} +5 -5
  75. package/dist/{cli-e42vss7b.js → cli-x37r7qwf.js} +16 -8
  76. package/dist/{cli-4b2z6n0m.js → cli-yd2nreqw.js} +17 -8
  77. package/dist/{cli-64mh9mf3.js → cli-ymjpkxgn.js} +69 -17
  78. package/dist/{cli-5kqnemen.js → cli-yvy48785.js} +1 -1
  79. package/dist/{cli-9j129brq.js → cli-yxbsmx56.js} +6 -6
  80. package/dist/cli.js +22 -20
  81. package/dist/{codex-adapter-wch12q99.js → codex-adapter-zk59x2zh.js} +7 -7
  82. package/dist/{codex-hook-brpnycn5.js → codex-hook-xyjhvxwd.js} +1 -1
  83. package/dist/{codex-login-rn7c5gxt.js → codex-login-8cg0vhf2.js} +1 -1
  84. package/dist/{codex-session-runner-41pfcr95.js → codex-session-runner-kh6s4j3n.js} +8 -8
  85. package/dist/{commands-9849dkj6.js → commands-68et1qv2.js} +2 -2
  86. package/dist/{controller-ja0sd95p.js → controller-fyrbnzg4.js} +4 -4
  87. package/dist/{controllers-connect-xp05dj6x.js → controllers-connect-nep9h2xv.js} +23 -23
  88. package/dist/{cursor-adapter-623qxqew.js → cursor-adapter-yss3pg7t.js} +2 -2
  89. package/dist/{db-w759qmx0.js → db-28gjyvxj.js} +4 -2
  90. package/dist/{db-47vj40dy.js → db-8ftwvzba.js} +3 -3
  91. package/dist/{devin-adapter-4ycy4vrf.js → devin-adapter-byen2cxt.js} +1 -1
  92. package/dist/{dispatcher-7b8e4z2j.js → dispatcher-hayexmkr.js} +20 -20
  93. package/dist/{docker-s84hjs91.js → docker-fdzyewpz.js} +2 -2
  94. package/dist/{dsh-adapter-2ezw3xea.js → dsh-adapter-46wawff9.js} +2 -2
  95. package/dist/{e2b-9gkdhm3c.js → e2b-222hsajc.js} +2 -2
  96. package/dist/{e2b-cjtf8k1z.js → e2b-fn3zacvt.js} +2 -2
  97. package/dist/{easter-egg-3d-e4qv8dxj.js → easter-egg-3d-8d1e0h4m.js} +1 -1
  98. package/dist/{event-bus-3z78ep5n.js → event-bus-pghg8hqy.js} +1 -1
  99. package/dist/{execute-0xpz8950.js → execute-cjrfxfnm.js} +1 -1
  100. package/dist/{external-channel-e2s028da.js → external-channel-xbmxcg09.js} +2 -2
  101. package/dist/{grok-adapter-ys7gaqee.js → grok-adapter-ag4wks2a.js} +10 -10
  102. package/dist/handlers-tzmevqb5.js +41 -0
  103. package/dist/{heartbeat-w3mntghj.js → heartbeat-psfvcgwp.js} +23 -23
  104. package/dist/{hook-g7kvv6x9.js → hook-8pz04697.js} +6 -6
  105. package/dist/{hook-3jjywnpb.js → hook-cyq4mryn.js} +8 -8
  106. package/dist/{http-tkp7d4kb.js → http-haj1nd6k.js} +921 -169
  107. package/dist/{index-q7m26kvs.js → index-jwt2bj6f.js} +265 -97
  108. package/dist/{index-3n8m9ea2.js → index-q52f47pe.js} +1 -1
  109. package/dist/{index-31ceyf42.js → index-syvhnvtw.js} +1 -1
  110. package/dist/index-y1ajx04a.js +43 -0
  111. package/dist/{index-1ef1myq2.js → index-z5h22pes.js} +20 -20
  112. package/dist/{index-xdaahthb.js → index-zyq8ct52.js} +20 -20
  113. package/dist/{keepalive-v4ta8wgg.js → keepalive-xdvsamvj.js} +4 -4
  114. package/dist/lead-hqg57kdp.js +79 -0
  115. package/dist/{maintenance-84n1ex21.js → maintenance-4y2xzh3a.js} +1 -1
  116. package/dist/{oauth-refresh-sweep-vjnnandw.js → oauth-refresh-sweep-jd4r7zbs.js} +9 -4
  117. package/dist/{onboard-sfmpe4np.js → onboard-18447q9q.js} +6 -6
  118. package/dist/{opencode-adapter-jmpst4q4.js → opencode-adapter-26b2frdf.js} +7 -7
  119. package/dist/{otel-impl-v6cc0zve.js → otel-impl-f9tgr8yw.js} +2 -2
  120. package/dist/{pi-mono-adapter-fk8fkcf6.js → pi-mono-adapter-qn6qbga1.js} +38 -17
  121. package/dist/{pricing-refresh-fsk1e1zc.js → pricing-refresh-dw2tgb53.js} +3 -3
  122. package/dist/{rbac-roles-prmkdnkw.js → rbac-roles-aygm2760.js} +2 -2
  123. package/dist/{rbac-roles-zxmxqa39.js → rbac-roles-s4nwj2m3.js} +1 -1
  124. package/dist/render-v2-yfvdfr0x.js +16 -0
  125. package/dist/{resume-frdtgfa0.js → resume-1bmvjbf3.js} +9 -9
  126. package/dist/{runtime-bt5pkpm1.js → runtime-f7tpzedy.js} +1 -1
  127. package/dist/{session-access-fpez4y5q.js → session-access-r5garej6.js} +4 -4
  128. package/dist/session-source-backfill-424wzv0r.js +110 -0
  129. package/dist/{setup-mq6m3m8q.js → setup-8tygvxkq.js} +1 -1
  130. package/dist/{virtual-modules-mnfbh5mr.js → virtual-modules-xsw323b3.js} +2 -2
  131. package/dist/{work-objects-kfahhjsq.js → work-objects-ggnghpgf.js} +23 -23
  132. package/dist/worker-sqjd1nqf.js +79 -0
  133. package/dist/{x-pbpezr0s.js → x-wxrzvzx8.js} +2 -2
  134. package/openapi.json +1324 -14
  135. package/package.json +5 -3
  136. package/src/assets/source-key.ts +95 -0
  137. package/src/be/automation-preflight.ts +2 -0
  138. package/src/be/db/api-keys.ts +89 -0
  139. package/src/be/db/session-labels.ts +28 -0
  140. package/src/be/db/session-moves.ts +5 -6
  141. package/src/be/db/session-tree.ts +153 -14
  142. package/src/be/db/tasks/read.ts +11 -0
  143. package/src/be/db/tasks/write.ts +6 -1
  144. package/src/be/db/workflow-runs.ts +219 -6
  145. package/src/be/db-retention.ts +61 -6
  146. package/src/be/db.ts +135 -132
  147. package/src/be/memory/providers/openai-embedding.ts +5 -1
  148. package/src/be/memory/task-learning.ts +155 -0
  149. package/src/be/migrations/244_api_key_validation.sql +4 -0
  150. package/src/be/migrations/245_script_executor.sql +3 -0
  151. package/src/be/oauth-refresh-sweep.ts +2 -1
  152. package/src/be/onboarding.ts +4 -0
  153. package/src/be/scripts/boot-reembed.ts +2 -0
  154. package/src/be/scripts/db.ts +14 -4
  155. package/src/be/scripts/embeddings.ts +2 -0
  156. package/src/be/scripts/run-saved.ts +1 -0
  157. package/src/be/scripts/typecheck.ts +10 -4
  158. package/src/be/seed-skills/index.ts +7 -0
  159. package/src/be/session-source-backfill.ts +163 -0
  160. package/src/be/swarm-config-guard.ts +1 -0
  161. package/src/comb/review-batch.ts +28 -1
  162. package/src/comb/templates.ts +7 -2
  163. package/src/commands/claude-managed-setup.ts +7 -1
  164. package/src/commands/credential-refresh.ts +76 -1
  165. package/src/commands/credential-wait.ts +2 -2
  166. package/src/commands/onboard/compose-generator.ts +5 -3
  167. package/src/commands/provider-credentials.ts +212 -171
  168. package/src/commands/runner.ts +25 -4
  169. package/src/extensions/apply-task-create.ts +8 -1
  170. package/src/extensions/contract-types.generated.ts +1 -1
  171. package/src/extensions/contract.ts +5 -0
  172. package/src/heartbeat/heartbeat.ts +14 -1
  173. package/src/heartbeat/script-runner-probe.ts +47 -0
  174. package/src/http/all-routes.ts +2 -0
  175. package/src/http/api-keys.ts +77 -9
  176. package/src/http/assets.ts +15 -0
  177. package/src/http/codex-oauth-accounts.ts +191 -0
  178. package/src/http/comb.ts +15 -2
  179. package/src/http/config.ts +1 -1
  180. package/src/http/core.ts +6 -0
  181. package/src/http/fleet-benchmark.ts +5 -2
  182. package/src/http/idempotency-key.ts +1 -1
  183. package/src/http/index.ts +14 -3
  184. package/src/http/integrations.ts +5 -1
  185. package/src/http/kv.ts +8 -3
  186. package/src/http/onboarding.ts +6 -1
  187. package/src/http/pages-public.ts +38 -5
  188. package/src/http/pool-keys.ts +354 -0
  189. package/src/http/scripts.ts +27 -0
  190. package/src/http/sessions.ts +37 -12
  191. package/src/http/stats.ts +1 -0
  192. package/src/http/status.ts +26 -12
  193. package/src/http/tasks.ts +3 -0
  194. package/src/http/used-by.ts +7 -2
  195. package/src/http/workflows.ts +88 -9
  196. package/src/http/x.ts +5 -0
  197. package/src/integrations/chat-sdk/inbound.ts +27 -0
  198. package/src/kv-reserved-namespaces.ts +19 -0
  199. package/src/prompts/base-prompt.ts +21 -1
  200. package/src/prompts/session-templates.ts +17 -3
  201. package/src/providers/acp-targets.ts +1 -1
  202. package/src/providers/amp-adapter.ts +1 -1
  203. package/src/providers/claude-adapter.ts +5 -1
  204. package/src/providers/claude-managed-adapter.ts +16 -9
  205. package/src/providers/codex-adapter.ts +71 -11
  206. package/src/providers/dsh-adapter.ts +1 -1
  207. package/src/providers/pi-mono-adapter.ts +41 -11
  208. package/src/scheduler/scheduler.ts +5 -3
  209. package/src/scripts-runtime/eval-harness.ts +8 -1
  210. package/src/scripts-runtime/executors/native.ts +22 -16
  211. package/src/scripts-runtime/executors/quickjs-prelude.ts +103 -1
  212. package/src/scripts-runtime/executors/quickjs-runner.ts +2 -1
  213. package/src/scripts-runtime/executors/quickjs.ts +7 -0
  214. package/src/scripts-runtime/executors/registry.ts +32 -0
  215. package/src/scripts-runtime/executors/types.ts +7 -0
  216. package/src/scripts-runtime/host-validated-args.ts +133 -0
  217. package/src/scripts-runtime/loader.ts +30 -3
  218. package/src/scripts-runtime/swarm-sdk.ts +16 -3
  219. package/src/scripts-runtime/types/stdlib.d.ts +10 -2
  220. package/src/scripts-runtime/types/swarm-extension.d.ts +17 -8
  221. package/src/scripts-runtime/types/swarm-sdk.d.ts +10 -2
  222. package/src/slack/assistant.ts +7 -0
  223. package/src/slack/channel-labels.ts +74 -0
  224. package/src/slack/handlers.ts +8 -0
  225. package/src/slack/inbound-files.ts +5 -2
  226. package/src/slack/thread-buffer.ts +4 -0
  227. package/src/tasks/sibling-awareness.ts +12 -3
  228. package/src/telemetry-context.ts +5 -6
  229. package/src/telemetry-identity.ts +3 -3
  230. package/src/tests/api-key-status-by-suffix.test.ts +242 -0
  231. package/src/tests/api-keys-validation.test.ts +170 -0
  232. package/src/tests/base-prompt.test.ts +70 -2
  233. package/src/tests/claude-managed-adapter.test.ts +46 -0
  234. package/src/tests/claude-routes-e2e.test.ts +7 -2
  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/community-linear-delivery-loop.test.ts +2099 -0
  240. package/src/tests/config-pool-keys.test.ts +533 -0
  241. package/src/tests/credential-check.test.ts +294 -4
  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/docs-index.test.ts +135 -0
  248. package/src/tests/dsh-adapter.test.ts +1 -1
  249. package/src/tests/extensions-pre-task.test.ts +53 -1
  250. package/src/tests/fetch-resolved-env.test.ts +16 -2
  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/fleet-benchmark.test.ts +8 -0
  254. package/src/tests/get-workflow-run-tool.test.ts +98 -0
  255. package/src/tests/heartbeat-checklist.test.ts +89 -0
  256. package/src/tests/heartbeat-script-runner-probe.test.ts +83 -0
  257. package/src/tests/integrations-http.test.ts +32 -2
  258. package/src/tests/internal-ai/complete-structured.test.ts +37 -1
  259. package/src/tests/internal-ai/credentials.test.ts +23 -0
  260. package/src/tests/kv-http.test.ts +32 -0
  261. package/src/tests/kv-tool.test.ts +25 -0
  262. package/src/tests/multi-runtime-registration.test.ts +12 -12
  263. package/src/tests/oauth-refresh-sweep.test.ts +34 -1
  264. package/src/tests/onboard-compose.test.ts +20 -0
  265. package/src/tests/onboarding.test.ts +108 -1
  266. package/src/tests/openai-embedding-credentials.test.ts +35 -0
  267. package/src/tests/opencode-adapter.test.ts +2 -1
  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/pages-frame-ancestors.test.ts +162 -0
  271. package/src/tests/pi-codemode.test.ts +2 -1
  272. package/src/tests/pi-mono-adapter.test.ts +54 -0
  273. package/src/tests/pi-native-mcp.test.ts +13 -1
  274. package/src/tests/pi-tool-exposure.test.ts +8 -8
  275. package/src/tests/probe-tool-schema-tokens.test.ts +144 -0
  276. package/src/tests/prompt-template-session.test.ts +4 -3
  277. package/src/tests/provider-key-probes.test.ts +181 -0
  278. package/src/tests/rbac-e2e-helpers.test.ts +88 -1
  279. package/src/tests/rbac-e2e-helpers.ts +36 -11
  280. package/src/tests/request-composition-derive.test.ts +329 -0
  281. package/src/tests/script-connections.test.ts +4 -0
  282. package/src/tests/script-executor-conformance.test.ts +36 -1
  283. package/src/tests/script-executor-quickjs-pool.test.ts +50 -0
  284. package/src/tests/script-executor-quickjs.test.ts +140 -0
  285. package/src/tests/script-host-validated-args-native.test.ts +196 -0
  286. package/src/tests/script-per-script-executor.test.ts +415 -0
  287. package/src/tests/script-runs-retention.test.ts +212 -0
  288. package/src/tests/scripts-external-api.test.ts +45 -0
  289. package/src/tests/sdk-allowlist.test.ts +42 -2
  290. package/src/tests/self-improvement.test.ts +4 -4
  291. package/src/tests/session-source-backfill.test.ts +429 -0
  292. package/src/tests/session-source-keys.test.ts +236 -0
  293. package/src/tests/session-source-labels.test.ts +419 -0
  294. package/src/tests/sessions-detail-batch.test.ts +2 -0
  295. package/src/tests/sessions-move.test.ts +131 -0
  296. package/src/tests/sessions-tree.test.ts +299 -2
  297. package/src/tests/slack-inbound-files.test.ts +103 -23
  298. package/src/tests/status.test.ts +101 -18
  299. package/src/tests/store-progress-learning.test.ts +288 -0
  300. package/src/tests/tasks-workflow-run-filter.test.ts +146 -0
  301. package/src/tests/test-net.ts +31 -0
  302. package/src/tests/transcription.test.ts +8 -0
  303. package/src/tests/ui-logs-parser.test.ts +180 -0
  304. package/src/tests/used-by-route.test.ts +11 -0
  305. package/src/tests/workflow-cascade-cancel.test.ts +172 -0
  306. package/src/tests/workflow-engine-v2.test.ts +116 -1
  307. package/src/tests/workflow-http-v2.test.ts +131 -0
  308. package/src/tests/workflow-mcp-trigger-schema.test.ts +209 -0
  309. package/src/tests/workflow-run-finished-event.test.ts +263 -0
  310. package/src/tests/workflow-step-order.test.ts +92 -0
  311. package/src/tests/workflow-sub-workflow.test.ts +30 -0
  312. package/src/tests/workflow-swarm-script.test.ts +34 -0
  313. package/src/tools/get-tasks.ts +8 -0
  314. package/src/tools/kv/kv-read-auth.ts +4 -2
  315. package/src/tools/script-common.ts +1 -0
  316. package/src/tools/script-run.ts +7 -0
  317. package/src/tools/script-upsert.ts +7 -0
  318. package/src/tools/store-progress.ts +67 -1
  319. package/src/tools/task-action.ts +1 -1
  320. package/src/tools/workflows/get-workflow-run.ts +14 -4
  321. package/src/tools/workflows/trigger-workflow.ts +104 -10
  322. package/src/types.ts +36 -1
  323. package/src/utils/company-profile.ts +51 -1
  324. package/src/utils/credentials.ts +10 -4
  325. package/src/utils/internal-ai/complete-structured.ts +5 -0
  326. package/src/utils/internal-ai/credentials.ts +4 -3
  327. package/src/utils/provider-key-probes.ts +328 -0
  328. package/src/utils/transcription.ts +4 -1
  329. package/src/workflows/engine.ts +18 -16
  330. package/src/workflows/executors/sub-workflow.ts +8 -1
  331. package/src/workflows/executors/swarm-script.ts +5 -0
  332. package/src/workflows/foreach-driver.ts +5 -11
  333. package/src/workflows/node-timeout.ts +30 -0
  334. package/templates/ai-toolbox.manifest.json +2 -2
  335. package/templates/community/linear-delivery-loop/PLAYBOOK.md +323 -0
  336. package/templates/community/linear-delivery-loop/README.md +38 -0
  337. package/templates/community/linear-delivery-loop/config.example.json +31 -0
  338. package/templates/community/linear-delivery-loop/lead-prompt.md +34 -0
  339. package/templates/community/linear-delivery-loop/schedule.json +10 -0
  340. package/templates/community/linear-delivery-loop/scripts/linear-delivery-setup.ts +148 -0
  341. package/templates/community/linear-delivery-loop/scripts/linear-delivery-tick.ts +1781 -0
  342. package/templates/skills/researching/content.md +2 -0
  343. package/templates/skills/swarm-docs/config.json +14 -0
  344. package/templates/skills/swarm-docs/content.md +144 -0
  345. package/templates/skills/swarm-scripts/SKILL.md +1 -0
  346. package/templates/skills/swarm-scripts/content.md +1 -0
  347. package/dist/cli-7421jzsw.js +0 -84
  348. package/dist/cli-e45e4m6b.js +0 -413
  349. package/dist/cli-zw88zjba.js +0 -307
  350. package/dist/handlers-wy1dr6ja.js +0 -41
  351. package/dist/index-nmm79qgr.js +0 -43
  352. package/dist/lead-sawnyqxk.js +0 -79
  353. package/dist/render-v2-pjwxvnkw.js +0 -12
  354. package/dist/worker-v5e5rxj3.js +0 -79
@@ -29,6 +29,7 @@ import {
29
29
  getDbClient,
30
30
  getInstanceActivity,
31
31
  getLiveAgentCounts,
32
+ getReadyAgentCounts,
32
33
  hasFirstCompletedTask,
33
34
  listAgentsWithCredStatusByProvider,
34
35
  NOT_EXTENSION_AGENT_SQL,
@@ -121,6 +122,12 @@ export type StatusIdentity = z.infer<typeof StatusIdentitySchema>;
121
122
  export const StatusActivitySchema = z.object({
122
123
  agents_online: z.number().int().nonnegative(),
123
124
  leads_online: z.number().int().nonnegative(),
125
+ /**
126
+ * Leads that can take work now (status `idle` or `busy`). Unlike
127
+ * `leads_online`, this does not depend on a recent heartbeat: an idle lead
128
+ * stops updating its activity time.
129
+ */
130
+ leads_ready: z.number().int().nonnegative(),
124
131
  recent_tasks_count: z.number().int().nonnegative(),
125
132
  });
126
133
  export type StatusActivity = z.infer<typeof StatusActivitySchema>;
@@ -194,12 +201,13 @@ export type AutomationStatus = z.infer<typeof AutomationStatusSchema>;
194
201
  /**
195
202
  * Phase 2: Aggregate health derived from setup milestones.
196
203
  *
197
- * - `broken` — harness or workers blocking (creds missing, no live workers ever).
198
- * - `degraded` — at least one optional integration `unverified`/`configured`
199
- * while another is configured, OR harness creds present but
200
- * the latest live test failed or never ran (`configured`).
201
- * - `ok` — harness + workers `verified`; no integration left in
202
- * `configured` state.
204
+ * - `broken`: harness or workers blocking (creds missing, no live workers ever).
205
+ * - `degraded`: no lead can take work (none is `idle` or `busy`), OR at
206
+ * least one optional integration `unverified`/`configured` while another
207
+ * is configured, OR harness creds present but the latest live test failed
208
+ * or never ran (`configured`).
209
+ * - `ok`: harness + workers `verified`, a lead can take work, and no
210
+ * integration left in `configured` state.
203
211
  */
204
212
  export const StatusHealthSchema = z.enum(["ok", "degraded", "broken"]);
205
213
  export type StatusHealth = z.infer<typeof StatusHealthSchema>;
@@ -692,19 +700,23 @@ export async function buildSetup(
692
700
  * Decision matrix:
693
701
  * - Harness `unverified` (no creds at all) → `broken` — the swarm cannot run a task.
694
702
  * - Workers `unverified` (no agents ever) → `broken` — same reason.
703
+ * - No lead can take work (`readyLeads` is 0) → `degraded`. Tasks wait until a lead is online.
695
704
  * - Harness `configured` (creds present, never live-tested) → `degraded`.
696
705
  * - Any of {slack, github, linear, jira} `configured` (i.e. half-set-up) → `degraded`.
697
706
  * - Otherwise → `ok`.
698
707
  *
699
708
  * Notes:
709
+ * - `readyLeads` counts leads with status `idle` or `busy`
710
+ * (`getReadyAgentCounts`). It does not use the heartbeat time: an agent
711
+ * updates `lastActivityAt` only while a session runs, so an idle, healthy
712
+ * lead looks stale.
700
713
  * - Worker `configured` (fleet exists but missed recent heartbeats) does NOT
701
- * degrade the rollup. Heartbeat drift is a runtime concern surfaced on the
702
- * /agents page and the dashboard canvas — not a setup health signal.
714
+ * degrade the rollup on its own, for the same reason.
703
715
  * - Integrations in `unverified` are NOT degrading on their own — most
704
716
  * deployments don't connect every integration. They only nudge the rollup if
705
717
  * paired with another integration in `configured` (the brainstorm contract).
706
718
  */
707
- export function computeHealth(setup: SetupMilestone[]): StatusHealth {
719
+ export function computeHealth(setup: SetupMilestone[], readyLeads: number): StatusHealth {
708
720
  const byId = new Map(setup.map((m) => [m.id, m] as const));
709
721
  const harness = byId.get("harness");
710
722
  const workers = byId.get("workers");
@@ -714,6 +726,7 @@ export function computeHealth(setup: SetupMilestone[]): StatusHealth {
714
726
  if (!workers || workers.state === "unverified") return "broken";
715
727
 
716
728
  // `degraded` rules.
729
+ if (readyLeads === 0) return "degraded";
717
730
  if (harness.state === "configured") return "degraded";
718
731
 
719
732
  for (const id of ["slack", "github", "linear", "jira"] as const) {
@@ -740,12 +753,13 @@ export async function buildStatusPayload(): Promise<StatusResponse> {
740
753
  .map((automation) => toStatus(preflightAutomation(automation, automationSetup)))
741
754
  .sort((a, b) => a.name.localeCompare(b.name) || a.kind.localeCompare(b.kind));
742
755
  const activity = await getInstanceActivity();
756
+ const readyLeads = (await getReadyAgentCounts()).leads;
743
757
  const instance = await getInstanceFeedbackSignals();
744
758
  return {
745
759
  runtime_pools: { enabled: isRuntimePoolsEnabled(), supervisor: runtimeSupervisorStatus() },
746
760
  identity: buildIdentity(),
747
761
  setup,
748
- activity,
762
+ activity: { ...activity, leads_ready: readyLeads },
749
763
  agent_fs: {
750
764
  configured: !!process.env.AGENT_FS_API_URL,
751
765
  base_url: process.env.AGENT_FS_API_URL ?? null,
@@ -753,7 +767,7 @@ export async function buildStatusPayload(): Promise<StatusResponse> {
753
767
  comb: getCombStatus(await combServiceUserId),
754
768
  },
755
769
  automations,
756
- health: computeHealth(setup),
770
+ health: computeHealth(setup, readyLeads),
757
771
  telemetry: { enabled: isTelemetryEnabled() },
758
772
  instance,
759
773
  };
@@ -797,7 +811,7 @@ const getStatus = route({
797
811
  pattern: ["status"],
798
812
  summary: "Identity + setup readiness + live activity for the swarm dashboard",
799
813
  description:
800
- "Single source of truth consumed by the UI home page. Identity comes from SWARM_* envs; setup milestones each emit `unverified | configured | verified`; automations report `running | needs_setup` from the same runtime preflight used at dispatch; activity counts agents alive in the last 5 min and tasks created in the last 24h; agent_fs reports whether AGENT_FS_API_URL is set, plus the Comb settings (COMB_ENABLED, the browser-facing agent-fs URL, the shared org and drive ids, and the agent-fs user id of the swarm service account); telemetry reports the effective ANONYMIZED_TELEMETRY opt-out state.",
814
+ "Single source of truth consumed by the UI home page. Identity comes from SWARM_* envs; setup milestones each emit `unverified | configured | verified`; automations report `running | needs_setup` from the same runtime preflight used at dispatch; activity counts agents alive in the last 5 min, leads that can take work (status idle or busy), and tasks created in the last 24h; agent_fs reports whether AGENT_FS_API_URL is set, plus the Comb settings (COMB_ENABLED, the browser-facing agent-fs URL, the shared org and drive ids, and the agent-fs user id of the swarm service account); telemetry reports the effective ANONYMIZED_TELEMETRY opt-out state.",
801
815
  tags: ["Status"],
802
816
  responses: {
803
817
  200: { description: "Status payload", schema: StatusResponseSchema },
package/src/http/tasks.ts CHANGED
@@ -227,6 +227,8 @@ const listTasks = route({
227
227
  status: z.string().optional(),
228
228
  agentId: z.string().optional(),
229
229
  scheduleId: z.string().optional(),
230
+ /** Only tasks of this workflow run (indexed; independent of fleet size). */
231
+ workflowRunId: z.string().uuid().optional(),
230
232
  key: AssetKeySchema.optional(),
231
233
  keyPrefix: AssetKeySchema.optional(),
232
234
  search: z.string().optional(),
@@ -976,6 +978,7 @@ export async function handleTasks(
976
978
  status,
977
979
  agentId: parsed.query.agentId || undefined,
978
980
  scheduleId: parsed.query.scheduleId || undefined,
981
+ workflowRunId: parsed.query.workflowRunId,
979
982
  key: parsed.query.key,
980
983
  keyPrefix: parsed.query.keyPrefix,
981
984
  search: parsed.query.search || undefined,
@@ -2,7 +2,7 @@ import type { IncomingMessage, ServerResponse } from "node:http";
2
2
  import { z } from "zod";
3
3
  import { getDbClient, upsertSwarmConfig } from "../be/db";
4
4
  import { isTelemetryEnabled, trackRelayed } from "../telemetry";
5
- import { USER_FUNCTIONS } from "../utils/company-profile";
5
+ import { reportedUserFunction, USER_FUNCTIONS } from "../utils/company-profile";
6
6
  import { orgIdentityStatus } from "../utils/org-identity";
7
7
  import { normalizeOrgWebsite, TELEMETRY_PROXY_ORIGIN } from "../utils/org-website";
8
8
  import { ensureConfigAdmin } from "./config";
@@ -129,7 +129,12 @@ export async function handleUsedBy(
129
129
  ...(orgDomain ? { org_domain: orgDomain } : {}),
130
130
  granted,
131
131
  ...(granted
132
- ? { contact_email: contactEmail, contact_function: contactFunction, quote }
132
+ ? {
133
+ contact_email: contactEmail,
134
+ // The proxy rejects a job function its catalog does not list yet.
135
+ contact_function: reportedUserFunction(contactFunction),
136
+ quote,
137
+ }
133
138
  : {}),
134
139
  changed_at: changedAt,
135
140
  }),
@@ -23,7 +23,14 @@ import {
23
23
  type WorkflowIdempotencyClaim,
24
24
  workflowRequestFingerprint,
25
25
  } from "../be/db/workflow-idempotency";
26
- import { getWorkflowRunParent } from "../be/db/workflow-runs";
26
+ import {
27
+ getWorkflowRunParent,
28
+ getWorkflowRunStepsSlimByRunId,
29
+ getWorkflowRunSummary,
30
+ getWorkflowRunTree,
31
+ WORKFLOW_RUN_TREE_MAX_DEPTH,
32
+ WorkflowRunTreeError,
33
+ } from "../be/db/workflow-runs";
27
34
  import {
28
35
  AssetKeySchema,
29
36
  AutomationIntegrationIdSchema,
@@ -39,6 +46,7 @@ import {
39
46
  WorkflowRunSchema,
40
47
  WorkflowRunStatusSchema,
41
48
  WorkflowRunStepSchema,
49
+ WorkflowRunStepSlimSchema,
42
50
  WorkflowRunSummarySchema,
43
51
  WorkflowSchema,
44
52
  WorkflowVersionSchema,
@@ -109,6 +117,13 @@ const WorkflowRunPageSchema = z.object({
109
117
  }),
110
118
  });
111
119
 
120
+ /** Mirrors `WorkflowRunTree` in src/be/db/workflow-runs.ts. */
121
+ const WorkflowRunTreeSchema = z.object({
122
+ rootRunId: z.string(),
123
+ runs: z.array(WorkflowRunSummarySchema),
124
+ hasOpenWork: z.boolean(),
125
+ });
126
+
112
127
  const SuccessResponseSchema = z.object({ success: z.literal(true) });
113
128
 
114
129
  /** Adds `warnings` to a just-saved workflow when a node in it cannot run yet. */
@@ -292,6 +307,15 @@ const triggerWorkflowRoute = route({
292
307
  "With an `Idempotency-Key`, a retry returns the accepted run's `runId` and `skipped` without starting, resuming or waiting for anything, even after the workflow was disabled or edited and whatever state the run is in now.",
293
308
  tags: ["Workflows"],
294
309
  params: z.object({ id: z.string() }),
310
+ query: z.object({
311
+ wait: z
312
+ .enum(["true", "false"])
313
+ .optional()
314
+ .describe(
315
+ "Default true: respond after the run's instant steps ran, when it finishes or parks at its first async step. " +
316
+ "false: respond as soon as the run is committed and walk it in the background.",
317
+ ),
318
+ }),
295
319
  headers: idempotencyKeyHeader("starts one run"),
296
320
  responses: {
297
321
  201: {
@@ -349,22 +373,50 @@ const getWorkflowRunRoute = route({
349
373
  path: "/api/workflow-runs/{id}",
350
374
  pattern: ["api", "workflow-runs", null],
351
375
  summary: "Get a workflow run with steps (includes retry columns)",
376
+ description:
377
+ "Returns the run with its `context` and every step with its `input` by default. A step's `input` is a snapshot of the context when the step started, so in runs with loops (polling, `wait`, iterations) the response grows with steps × context size. Pass `fields=slim` to drop `run.context` and `steps[].input`; outputs, errors, diagnostics, retry fields and `parent` stay.",
352
378
  tags: ["Workflows"],
353
379
  params: z.object({ id: z.string() }),
380
+ query: z.object({
381
+ /** `slim` omits `run.context` and `steps[].input`; default is full. */
382
+ fields: z.enum(["full", "slim"]).optional(),
383
+ }),
354
384
  responses: {
355
385
  200: {
356
386
  description: "Workflow run details with steps including retry info",
357
- schema: z.object({
358
- run: WorkflowRunSchema,
359
- steps: z.array(WorkflowRunStepSchema),
360
- /** Set on a child run: the run and `sub-workflow` step that started it. */
361
- parent: WorkflowRunParentSchema.optional(),
362
- }),
387
+ schema: z.union([
388
+ z.object({
389
+ run: WorkflowRunSchema,
390
+ steps: z.array(WorkflowRunStepSchema),
391
+ /** Set on a child run: the run and `sub-workflow` step that started it. */
392
+ parent: WorkflowRunParentSchema.optional(),
393
+ }),
394
+ z.object({
395
+ run: WorkflowRunSummarySchema,
396
+ steps: z.array(WorkflowRunStepSlimSchema),
397
+ parent: WorkflowRunParentSchema.optional(),
398
+ }),
399
+ ]),
363
400
  },
364
401
  404: { description: "Run not found" },
365
402
  },
366
403
  });
367
404
 
405
+ const getWorkflowRunTreeRoute = route({
406
+ method: "get",
407
+ path: "/api/workflow-runs/{id}/tree",
408
+ pattern: ["api", "workflow-runs", null, "tree"],
409
+ summary: "Get a workflow run and every sub-workflow run below it",
410
+ description: `Follows stored parent links (\`parentStepId\` → step → run) through every attempt of every step, terminal runs included. \`runs\` holds list rows without \`context\`, root first. \`hasOpenWork\` is true while any run in the tree is live, has a pending, running or waiting step, a queued retry, an open task or a pending approval; after a cancel it turns false once the tree has settled. Both come from one read snapshot. The tree is not paginated. Links that form a cycle or nest deeper than ${WORKFLOW_RUN_TREE_MAX_DEPTH} levels return 409.`,
411
+ tags: ["Workflows"],
412
+ params: z.object({ id: z.string() }),
413
+ responses: {
414
+ 200: { description: "The run tree", schema: WorkflowRunTreeSchema },
415
+ 404: { description: "Run not found" },
416
+ 409: { description: "Stored parent links form a cycle or nest past the depth bound" },
417
+ },
418
+ });
419
+
368
420
  const retryWorkflowRunRoute = route({
369
421
  method: "post",
370
422
  path: "/api/workflow-runs/{id}/retry",
@@ -959,6 +1011,7 @@ export async function handleWorkflows(
959
1011
  runId = await startWorkflowExecution(workflow, body, getExecutorRegistry(), {
960
1012
  triggerType: "api",
961
1013
  requestedByUserId: (await resolveHttpAuditUserId(req, myAgentId)) ?? undefined,
1014
+ detach: parsed.query.wait === "false",
962
1015
  // Recorded in the run's own transaction, before the walk starts.
963
1016
  accept: claim
964
1017
  ? async (run) => {
@@ -1014,18 +1067,44 @@ export async function handleWorkflows(
1014
1067
  const parsed = await getWorkflowRunRoute.parse(req, res, pathSegments, queryParams);
1015
1068
  if (!parsed) return true;
1016
1069
  // Display view: the scrubbed context and step outputs, never replay state.
1017
- const run = await getWorkflowRun(parsed.params.id, "display");
1070
+ // `fields=slim` skips the context and step-input columns in SQL too.
1071
+ const slim = parsed.query.fields === "slim";
1072
+ const run = slim
1073
+ ? await getWorkflowRunSummary(parsed.params.id)
1074
+ : await getWorkflowRun(parsed.params.id, "display");
1018
1075
  if (!run) {
1019
1076
  res.writeHead(404);
1020
1077
  res.end();
1021
1078
  return true;
1022
1079
  }
1023
- const steps = await getWorkflowRunStepsByRunId(parsed.params.id, "display");
1080
+ const steps = slim
1081
+ ? await getWorkflowRunStepsSlimByRunId(parsed.params.id)
1082
+ : await getWorkflowRunStepsByRunId(parsed.params.id, "display");
1024
1083
  const parent = run.parentStepId ? await getWorkflowRunParent(run.parentStepId) : null;
1025
1084
  getWorkflowRunRoute.respond(res, 200, { run, steps, ...(parent ? { parent } : {}) });
1026
1085
  return true;
1027
1086
  }
1028
1087
 
1088
+ if (getWorkflowRunTreeRoute.match(req.method, pathSegments)) {
1089
+ const parsed = await getWorkflowRunTreeRoute.parse(req, res, pathSegments, queryParams);
1090
+ if (!parsed) return true;
1091
+ let tree: Awaited<ReturnType<typeof getWorkflowRunTree>>;
1092
+ try {
1093
+ tree = await getWorkflowRunTree(parsed.params.id);
1094
+ } catch (err) {
1095
+ if (!(err instanceof WorkflowRunTreeError)) throw err;
1096
+ jsonError(res, err.message, 409);
1097
+ return true;
1098
+ }
1099
+ if (!tree) {
1100
+ res.writeHead(404);
1101
+ res.end();
1102
+ return true;
1103
+ }
1104
+ getWorkflowRunTreeRoute.respond(res, 200, tree);
1105
+ return true;
1106
+ }
1107
+
1029
1108
  if (retryWorkflowRunRoute.match(req.method, pathSegments)) {
1030
1109
  const parsed = await retryWorkflowRunRoute.parse(req, res, pathSegments, queryParams);
1031
1110
  if (!parsed) return true;
package/src/http/x.ts CHANGED
@@ -20,6 +20,7 @@ import {
20
20
  MAX_SCRIPT_WALL_CLOCK_MS,
21
21
  MIN_SCRIPT_WALL_CLOCK_MS,
22
22
  } from "../scripts-runtime/executors/types";
23
+ import { HOST_VALIDATED_ARGS } from "../scripts-runtime/host-validated-args";
23
24
  import type { RunScriptOutput } from "../scripts-runtime/loader";
24
25
  import { scrubObject, scrubSecrets } from "../utils/secret-scrubber";
25
26
  import { validateJsonSchema } from "../workflows/json-schema-validator";
@@ -214,6 +215,7 @@ export async function handleX(
214
215
  // Scripts predating the schema column, or where extraction failed, have a
215
216
  // null schema — we skip and let the in-subprocess Zod check surface as a
216
217
  // runtime_error instead.
218
+ let hostValidatedArgs = false;
217
219
  if (script.argsJsonSchema) {
218
220
  try {
219
221
  const schema = JSON.parse(script.argsJsonSchema) as Record<string, unknown>;
@@ -227,6 +229,7 @@ export async function handleX(
227
229
  });
228
230
  return true;
229
231
  }
232
+ hostValidatedArgs = true;
230
233
  } catch {
231
234
  // Malformed stored schema — don't block execution on it.
232
235
  }
@@ -245,7 +248,9 @@ export async function handleX(
245
248
  const output = await runScriptWithRunCredential({
246
249
  source: script.source,
247
250
  args: args ?? null,
251
+ [HOST_VALIDATED_ARGS]: hostValidatedArgs,
248
252
  fsMode: "none",
253
+ executor: script.executor,
249
254
  agentId: endpoint.agentId,
250
255
  // timeoutMs only raises the wall-clock limit (network-bound scripts can use
251
256
  // the full window); the CPU ulimit (`ulimit -t`) is intentionally left at
@@ -1,5 +1,7 @@
1
1
  import type { Adapter, Message, Thread } from "chat";
2
2
  import { z } from "zod";
3
+ import { sessionGroupKey } from "@/assets/key";
4
+ import { sessionSourceKey } from "@/assets/source-key";
3
5
  import {
4
6
  getAgentById,
5
7
  getAgentWorkingOnContextKey,
@@ -9,6 +11,7 @@ import {
9
11
  getLeadAgent,
10
12
  isExtensionAgent,
11
13
  } from "@/be/db";
14
+ import { rememberSessionLabel } from "@/be/db/session-labels";
12
15
  import { resolveIdentity } from "@/be/identity";
13
16
  import { recordUnmappedIdentity } from "@/be/unmapped-identities";
14
17
  import { dispatchPre } from "@/extensions/dispatcher";
@@ -373,6 +376,20 @@ async function targetFromExtension(
373
376
  * Runs outside any transaction, so `pre.chat.route` is dispatched here, after
374
377
  * the flush and before any task exists.
375
378
  */
379
+ /**
380
+ * The source group label a Telegram or Discord message gives: the DM sender,
381
+ * or a Telegram group title. Slack labels come from the Slack lookup instead.
382
+ */
383
+ function chatSourceLabel(adapter: string, item: ChatBufferedMessage): string | undefined {
384
+ if (adapter !== "telegram" && adapter !== "discord") return undefined;
385
+ const { facts } = item;
386
+ if (facts.chatType === "dm" || item.isDirectMessage)
387
+ return facts.from ? `DM · ${facts.from}` : undefined;
388
+ if (adapter === "telegram" && (facts.chatType === "group" || facts.chatType === "supergroup"))
389
+ return facts.title || undefined;
390
+ return undefined;
391
+ }
392
+
376
393
  export async function flushChatThread(
377
394
  threadId: string,
378
395
  items: ChatBufferedMessage[],
@@ -538,6 +555,7 @@ export async function flushChatThread(
538
555
  const latestActive = await getLatestActiveTaskByContextKey(contextKey, "chat");
539
556
  const dependsOn = !immediate && latestActive ? [latestActive.id] : undefined;
540
557
  let created = 0;
558
+ let sourceKey: string | null = null;
541
559
  for (const match of target.matches) {
542
560
  const options: CreateTaskOptions = {
543
561
  agentId: match.agent?.id ?? null,
@@ -564,6 +582,7 @@ export async function flushChatThread(
564
582
  };
565
583
  try {
566
584
  const { task, unattached } = await createChatTaskWithFiles(description, options, inbound);
585
+ sourceKey ??= sessionSourceKey(task);
567
586
  created++;
568
587
  console.log(
569
588
  `[chat-sdk] ${threadId}: task ${task.id} for ${match.agent ? `agent ${match.agent.id}` : "no agent (queued)"} (${match.matchedText}, dependsOn: ${dependsOn?.join(", ") ?? "none"}${unattached.length ? `, ${unattached.length} file(s) not attached` : ""})`,
@@ -578,6 +597,14 @@ export async function flushChatThread(
578
597
  }
579
598
  }
580
599
  }
600
+ const label = chatSourceLabel(adapter, requester);
601
+ if (sourceKey && label) {
602
+ // Every message refreshes the label, so a renamed group shows its new title.
603
+ // The error may echo the label, which can hold a person's name: do not log it.
604
+ await rememberSessionLabel(sessionGroupKey(sourceKey), label).catch(() => {
605
+ console.warn("[chat-sdk] Could not save a session source label");
606
+ });
607
+ }
581
608
  if (created > 0) {
582
609
  await rememberChatThreadRequester(threadId, requester.userId);
583
610
  await subscribe(thread);
@@ -12,8 +12,20 @@ const RESERVED_FAMILIES = new Map([
12
12
  ["worker", "namespace is reserved for worker nudges"],
13
13
  // Durable dedupe for SLACK_BLOCK_EXTERNAL_CHANNELS alerts (src/slack/external-channel.ts).
14
14
  ["slack-external-alert", "namespace is reserved for Slack external channel alerts"],
15
+ // Source group labels (src/be/db/session-labels.ts). Private, see PRIVATE_FAMILIES.
16
+ [
17
+ "session-labels",
18
+ "namespace is reserved for session source labels; GET /api/session-tree returns them",
19
+ ],
15
20
  ]);
16
21
 
22
+ /**
23
+ * Reserved families that generic KV callers may not read either. Their rows
24
+ * hold data that another route returns only to callers with access (a
25
+ * source label names a private channel or a DM partner).
26
+ */
27
+ const PRIVATE_FAMILIES = new Set(["session-labels"]);
28
+
17
29
  export function isReservedNamespace(namespace: string): boolean {
18
30
  return reservedNamespaceError(namespace) !== null;
19
31
  }
@@ -22,6 +34,13 @@ export function reservedNamespaceError(namespace: string): string | null {
22
34
  return RESERVED_FAMILIES.get(namespace.split(":", 1)[0] ?? "") ?? null;
23
35
  }
24
36
 
37
+ /** The error a generic KV read of a private family gets, or null. */
38
+ export function privateNamespaceError(namespace: string): string | null {
39
+ return PRIVATE_FAMILIES.has(namespace.split(":", 1)[0] ?? "")
40
+ ? reservedNamespaceError(namespace)
41
+ : null;
42
+ }
43
+
25
44
  export function reservedRoomKeyError(key: string): string | null {
26
45
  return key.startsWith("_room/")
27
46
  ? "room snapshot keys are reserved; use the room endpoints"
@@ -271,12 +271,22 @@ export const getBasePrompt = async (args: BasePromptArgs): Promise<string> => {
271
271
  renderToolsAndSkillsVars({
272
272
  skillsSummary: args.skillsSummary,
273
273
  mcpServers: args.mcpServers,
274
+ // pi defers installed MCP servers together with swarm tools.
275
+ mcpToolsDeferred: provider === "pi" && hasToolSearch,
274
276
  nativeSkillDiscovery,
275
277
  toolDiscovery: discoveryResult.text,
276
278
  hasLocalEnv,
277
279
  }),
278
280
  );
279
281
  prompt += toolsResult.text;
282
+
283
+ // Lead docs pointer: only when the lead has the `swarm-docs` skill. The
284
+ // skill list is unknown on claude-managed (the cloud sandbox owns skill
285
+ // delivery) and before the first skill load, so render it then.
286
+ if (role === "lead" && hasSwarmDocsSkill(args.skillsSummary)) {
287
+ const docsResult = await resolveTemplateAsync("system.agent.lead.docs", {});
288
+ prompt += docsResult.text;
289
+ }
280
290
  }
281
291
 
282
292
  // J. Agent notes (CLAUDE.md), only when it differs from the generated
@@ -339,6 +349,11 @@ export const getTaskContextPrompt = async (args: TaskContextPromptArgs): Promise
339
349
  return prompt.replace(/\n{3,}/g, "\n\n").trim();
340
350
  };
341
351
 
352
+ /** True when the skill list includes `swarm-docs`, or when the list is unknown. */
353
+ function hasSwarmDocsSkill(skills: { name: string }[] | undefined): boolean {
354
+ return skills === undefined || skills.some((skill) => skill.name === "swarm-docs");
355
+ }
356
+
342
357
  /**
343
358
  * Dynamic lines for `system.agent.tools_skills`. The static text lives in the
344
359
  * template so operators can override or skip the section; only the lists that
@@ -347,6 +362,7 @@ export const getTaskContextPrompt = async (args: TaskContextPromptArgs): Promise
347
362
  function renderToolsAndSkillsVars(input: {
348
363
  skillsSummary?: { name: string; description: string }[];
349
364
  mcpServers?: string[];
365
+ mcpToolsDeferred?: boolean;
350
366
  nativeSkillDiscovery: boolean;
351
367
  hasLocalEnv: boolean;
352
368
  toolDiscovery: string;
@@ -382,7 +398,11 @@ function renderToolsAndSkillsVars(input: {
382
398
  const servers = (input.mcpServers ?? []).filter((name) => name.trim().length > 0);
383
399
  const mcpLine =
384
400
  servers.length > 0
385
- ? `Connected MCP servers: ${servers.join(", ")}. Their tools are in your tool list.\n`
401
+ ? `Connected MCP servers: ${servers.join(", ")}. ${
402
+ input.mcpToolsDeferred
403
+ ? "Load their tools with your harness tool search."
404
+ : "Their tools are in your tool list."
405
+ }\n`
386
406
  : "";
387
407
 
388
408
  return {
@@ -15,8 +15,9 @@
15
15
  * G secrets system.agent.secrets
16
16
  *
17
17
  * Deployment-gated blocks (E outputs, H slack, steering) and the per-agent and
18
- * per-task sections (tools and skills, agent notes, repository, requester
19
- * profile) are appended by base-prompt.ts after the composite.
18
+ * per-task sections (tools and skills, the lead docs pointer, agent notes,
19
+ * repository, requester profile) are appended by base-prompt.ts after the
20
+ * composite.
20
21
  *
21
22
  * Writing rules for every body: sentence case headings, one idea per sentence,
22
23
  * active voice, plain words, no em dashes, a prohibition only where no positive
@@ -109,6 +110,19 @@ Your heartbeat runbook is the \`heartbeatMd\` profile field. Edit it with \`upda
109
110
  category: "system",
110
111
  });
111
112
 
113
+ // Lead docs pointer. base-prompt.ts appends it after the tools and skills
114
+ // section when the lead has the `swarm-docs` skill (or when the skill list is
115
+ // unknown). An operator override with an empty body switches it off.
116
+ registerTemplate({
117
+ eventType: "system.agent.lead.docs",
118
+ header: "",
119
+ defaultBody: `
120
+ Load the \`swarm-docs\` skill only when a person asks for something no skill, workflow, schedule or memory of yours covers, or asks where something is documented. Then follow its offer rules.
121
+ `,
122
+ variables: [],
123
+ category: "system",
124
+ });
125
+
112
126
  // Remote providers (no MCP, no container): the task arrives in the session
113
127
  // prompt and the final message is the output. No tools to name.
114
128
  registerTemplate({
@@ -168,7 +182,7 @@ registerTemplate({
168
182
  ## Memory
169
183
 
170
184
  Memories from past sessions are in your task message. Read them before you start. For a wider search, run the \`task-context-gathering\` script with the task ID and two to four queries.
171
- Store a learning with \`memory-store\` when you solve something that will come back: a fix, a pattern, a gotcha. Completed task outputs are stored for you.
185
+ When you learn something that would change how the next agent works (a fix, a pattern, a gotcha), pass it as \`learning\` on a \`store-progress\` call before the task ends. Completed task outputs are stored for you.
172
186
  You MUST use the \`memory\` skill before you store, edit, or delete a memory.
173
187
  `,
174
188
  variables: [],
@@ -359,7 +359,7 @@ const copilotTargetProfile: AcpTargetProfile = {
359
359
  },
360
360
  };
361
361
 
362
- /** Qwen Code's `--auth-type` choices (0.26.0). `qwen-oauth` needs a browser, so it is left out. */
362
+ /** Qwen Code's `--auth-type` choices (0.26.1). `qwen-oauth` needs a browser, so it is left out. */
363
363
  const QWEN_AUTH_TYPES = ["openai", "openai-responses", "anthropic", "gemini", "vertex-ai"] as const;
364
364
 
365
365
  // Qwen Code is a Gemini CLI fork: QWEN_SYSTEM_MD replaces its built-in prompt
@@ -45,7 +45,7 @@ import type {
45
45
  * default; the image installs exactly this version and the per-task settings
46
46
  * set `amp.updates.mode: "disabled"`. Bump it deliberately, with a live run.
47
47
  */
48
- export const AMP_PACKAGE = "@ampcode/cli@0.0.1792844927-g360c96";
48
+ export const AMP_PACKAGE = "@ampcode/cli@0.0.1793025586-g9211b5";
49
49
 
50
50
  /** MCP server name. Amp exposes its tools as `agent_swarm` / `store_progress` (dashes become underscores). */
51
51
  const AMP_MCP_SERVER_NAME = "agent-swarm";
@@ -1445,7 +1445,11 @@ export class ClaudeAdapter implements ProviderAdapter {
1445
1445
  // (headless) or block on the trust dialog (tmux), so seed trust for every session.
1446
1446
  if (isEnvFlagEnabled("CLAUDE_TRUST_PRESEED", true, sourceEnv)) {
1447
1447
  try {
1448
- await preseedClaudeTrustDialog(await resolveClaudeTrustDirs(config.cwd));
1448
+ // Seed the ~/.claude.json the CLI reads: its HOME comes from the session env.
1449
+ await preseedClaudeTrustDialog(
1450
+ await resolveClaudeTrustDirs(config.cwd),
1451
+ sourceEnv.HOME || undefined,
1452
+ );
1449
1453
  } catch (err) {
1450
1454
  console.warn(
1451
1455
  `\x1b[33m[claude]\x1b[0m ${scrubSecrets(`Failed to pre-seed trust for ${config.cwd}: ${err}`)}`,
@@ -64,6 +64,7 @@ import {
64
64
  computeContextUsedUnified,
65
65
  getContextWindowSize,
66
66
  } from "../utils/context-window";
67
+ import { selectCredential } from "../utils/credentials";
67
68
  import { scrubSecrets } from "../utils/secret-scrubber";
68
69
  import { computeClaudeManagedCostUsd } from "./claude-managed-models";
69
70
  import { getRuntimeFeePerHour } from "./claude-managed-pricing";
@@ -862,7 +863,11 @@ export class ClaudeManagedAdapter implements ProviderAdapter {
862
863
  );
863
864
  }
864
865
 
865
- this.apiKey = process.env.ANTHROPIC_API_KEY as string;
866
+ this.apiKey = selectCredential(
867
+ process.env.ANTHROPIC_API_KEY as string,
868
+ undefined,
869
+ "ANTHROPIC_API_KEY",
870
+ ).selected;
866
871
  this.agentId = process.env.MANAGED_AGENT_ID as string;
867
872
  this.environmentId = process.env.MANAGED_ENVIRONMENT_ID as string;
868
873
 
@@ -879,6 +884,14 @@ export class ClaudeManagedAdapter implements ProviderAdapter {
879
884
  }
880
885
 
881
886
  async createSession(config: ProviderSessionConfig): Promise<ProviderSession> {
887
+ // Keep the runner's task selection instead of the container's pool.
888
+ const taskKey = config.env?.ANTHROPIC_API_KEY;
889
+ const client =
890
+ taskKey && this.client instanceof Anthropic
891
+ ? this.client.withOptions({
892
+ apiKey: selectCredential(taskKey, undefined, "ANTHROPIC_API_KEY").selected,
893
+ })
894
+ : this.client;
882
895
  // Native resume is deprecated. Follow-up continuity is delivered via the
883
896
  // context preamble (see src/commands/context-preamble.ts). Any stray
884
897
  // resumeSessionId is logged and ignored — we always create a fresh session.
@@ -942,16 +955,10 @@ export class ClaudeManagedAdapter implements ProviderAdapter {
942
955
  if (vaultIds.length > 0) {
943
956
  createParams.vault_ids = Array.from(new Set(vaultIds));
944
957
  }
945
- const created = await Promise.resolve(this.client.beta.sessions.create(createParams));
958
+ const created = await Promise.resolve(client.beta.sessions.create(createParams));
946
959
  const sessionId = created.id;
947
960
 
948
- return new ClaudeManagedSession(
949
- this.client,
950
- sessionId,
951
- config,
952
- userMessageContent,
953
- seenEventIds,
954
- );
961
+ return new ClaudeManagedSession(client, sessionId, config, userMessageContent, seenEventIds);
955
962
  }
956
963
 
957
964
  /**