@desplega.ai/agent-swarm 2.5.4 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/README.md +2 -2
  2. package/dist/{acp-adapter-ek38cwsv.js → acp-adapter-dgfakfpw.js} +9 -9
  3. package/dist/{actions-92mepq03.js → actions-fbqma8aa.js} +21 -21
  4. package/dist/{amp-adapter-9gaxg015.js → amp-adapter-en0jy6hs.js} +4 -4
  5. package/dist/{app-ctn8gsgx.js → app-k8a5cgpp.js} +3 -3
  6. package/dist/{assistant-zyr5kz47.js → assistant-a16433gr.js} +29 -25
  7. package/dist/{boot-reembed-dwzfttdr.js → boot-reembed-6c1sjfnr.js} +1 -1
  8. package/dist/{boot-reembed-w75maga2.js → boot-reembed-f4nn561n.js} +1 -1
  9. package/dist/{boot-scrub-sweep-e9kq43kf.js → boot-scrub-sweep-d4w9gajf.js} +4 -4
  10. package/dist/{boot-seeds-x28v3fhb.js → boot-seeds-9bmkspyw.js} +4 -4
  11. package/dist/{claude-adapter-f0afqnne.js → claude-adapter-nys1kjxa.js} +9 -9
  12. package/dist/{claude-managed-adapter-x81a7r65.js → claude-managed-adapter-55207973.js} +2 -2
  13. package/dist/{claude-managed-setup-bqzwjd9x.js → claude-managed-setup-3cm35gey.js} +10 -3
  14. package/dist/{claude-sdk-session-7b7whkaq.js → claude-sdk-session-nw5yd8cp.js} +11 -11
  15. package/dist/{cli-c61mbdds.js → cli-03gg9pd1.js} +4 -4
  16. package/dist/{cli-kxpfx2xr.js → cli-0z1xjzwa.js} +2 -2
  17. package/dist/cli-1tem6ns9.js +894 -0
  18. package/dist/{cli-jbnn4vxb.js → cli-24j1jeeb.js} +3 -3
  19. package/dist/{cli-pzatvw94.js → cli-2sjhpsns.js} +2 -2
  20. package/dist/{cli-dew6x255.js → cli-39aet655.js} +2 -2
  21. package/dist/{cli-g1cw7xtt.js → cli-39rrw8yp.js} +1 -1
  22. package/dist/{cli-x0f2v69c.js → cli-3aqd4jaa.js} +12 -7
  23. package/dist/{cli-gbfxp48k.js → cli-3fwqy71j.js} +15 -15
  24. package/dist/{cli-7p4jxgtt.js → cli-3gfvmzht.js} +6 -6
  25. package/dist/{cli-03vpq45j.js → cli-3ndmpxxv.js} +2 -2
  26. package/dist/{cli-asq5ydm4.js → cli-4hxrf0p4.js} +131 -50
  27. package/dist/{cli-mz9afxxf.js → cli-4rf0f5vg.js} +15 -3
  28. package/dist/{cli-k4tjsdx9.js → cli-5rj83bam.js} +272 -195
  29. package/dist/{cli-sbwsxvtd.js → cli-5sckpqcx.js} +2 -2
  30. package/dist/{cli-cz5nmdxt.js → cli-683y0b3a.js} +2 -2
  31. package/dist/{cli-ethgy7ha.js → cli-6y51ryva.js} +2 -2
  32. package/dist/{cli-n4c8e3s5.js → cli-708wdy32.js} +1 -1
  33. package/dist/{cli-pasyvjmm.js → cli-737pyqxr.js} +1 -1
  34. package/dist/{cli-gx4rm0qx.js → cli-7enjwya9.js} +1 -1
  35. package/dist/{cli-6x9ma5w5.js → cli-803188pb.js} +1 -1
  36. package/dist/{cli-hh5we352.js → cli-9abdgn5t.js} +7 -7
  37. package/dist/{cli-ff1tm9n9.js → cli-a2rr0fmg.js} +1 -1
  38. package/dist/{cli-c952c7ac.js → cli-a34pr3w1.js} +2 -2
  39. package/dist/{cli-s682zgqz.js → cli-abkm71vr.js} +1 -1
  40. package/dist/{cli-mmhj8vd7.js → cli-ag2yrnt5.js} +11 -2
  41. package/dist/{cli-bvpgr9ny.js → cli-aj8djyqt.js} +5 -5
  42. package/dist/{cli-gwhjmh8g.js → cli-art7c1zh.js} +5 -5
  43. package/dist/{cli-5bnsc0e1.js → cli-az25fn5f.js} +4 -4
  44. package/dist/{cli-9s05422w.js → cli-bqztfyve.js} +1 -1
  45. package/dist/{cli-bn0rewsy.js → cli-c081bj3b.js} +2 -2
  46. package/dist/{cli-1r5ad7j5.js → cli-c2habvsf.js} +12 -12
  47. package/dist/{cli-64mh9mf3.js → cli-ca6banwz.js} +69 -17
  48. package/dist/{cli-25n3mv5d.js → cli-cz37jqss.js} +5 -5
  49. package/dist/{cli-6n8axjv1.js → cli-d2dpf95n.js} +2 -2
  50. package/dist/{cli-gn5af0x5.js → cli-emhjcj2y.js} +11 -11
  51. package/dist/{cli-6a1xg1p7.js → cli-esakngh8.js} +2 -2
  52. package/dist/{cli-ha9rmwgq.js → cli-fb8m699c.js} +2 -2
  53. package/dist/{cli-4b2z6n0m.js → cli-ffvsj2qw.js} +17 -8
  54. package/dist/{cli-abs8ndtr.js → cli-g20jjwt4.js} +26 -26
  55. package/dist/{cli-jb30dzvk.js → cli-g8ka5j4j.js} +446 -164
  56. package/dist/{cli-19202kew.js → cli-gf95q3bm.js} +5 -5
  57. package/dist/{cli-eftzyh9y.js → cli-gnsb1zxa.js} +2 -2
  58. package/dist/{cli-nwyw3zhg.js → cli-gxee0bqa.js} +1 -1
  59. package/dist/{cli-e42vss7b.js → cli-hfeb68z4.js} +5 -5
  60. package/dist/{cli-g9vd9ece.js → cli-k21gx5ht.js} +14 -16
  61. package/dist/{cli-tsma7p8v.js → cli-kf4jree9.js} +2 -2
  62. package/dist/{cli-dabehxhp.js → cli-kt907da6.js} +1 -1
  63. package/dist/{cli-wx2nj19m.js → cli-m5a9m0ak.js} +6 -6
  64. package/dist/{cli-621ce7kw.js → cli-m70bajzr.js} +3 -3
  65. package/dist/{cli-9j129brq.js → cli-mtnj4fwa.js} +6 -6
  66. package/dist/{cli-xzfq4at3.js → cli-mvjz5an3.js} +15 -11
  67. package/dist/{cli-277c31dw.js → cli-n7asmce6.js} +225 -3
  68. package/dist/{cli-xtrz6bzs.js → cli-nn6vx2mq.js} +1 -1
  69. package/dist/{cli-2655k16q.js → cli-nq677csy.js} +1 -1
  70. package/dist/{cli-0shx9j93.js → cli-pfk92eqz.js} +2 -2
  71. package/dist/{cli-wghjkr0e.js → cli-sqma6vw1.js} +10 -7
  72. package/dist/{cli-nzfnt2gs.js → cli-tf1n0r0b.js} +1 -1
  73. package/dist/{cli-qqzmjxkf.js → cli-v49fkghp.js} +11 -6
  74. package/dist/{cli-x77gen2p.js → cli-w2656ka1.js} +7 -7
  75. package/dist/{cli-5kqnemen.js → cli-yvy48785.js} +1 -1
  76. package/dist/{cli-m4ecdxvz.js → cli-zqne1qcj.js} +7 -7
  77. package/dist/cli.js +17 -17
  78. package/dist/{codex-adapter-wch12q99.js → codex-adapter-d95q4qsn.js} +7 -7
  79. package/dist/{codex-login-rn7c5gxt.js → codex-login-8cg0vhf2.js} +1 -1
  80. package/dist/{codex-session-runner-41pfcr95.js → codex-session-runner-gdwd8h0c.js} +8 -8
  81. package/dist/{commands-9849dkj6.js → commands-jqmgk54b.js} +2 -2
  82. package/dist/{controller-ja0sd95p.js → controller-z3jq589f.js} +4 -4
  83. package/dist/{controllers-connect-xp05dj6x.js → controllers-connect-r2za39ef.js} +23 -23
  84. package/dist/{cursor-adapter-623qxqew.js → cursor-adapter-4mr4jgkw.js} +2 -2
  85. package/dist/{db-47vj40dy.js → db-ppv2tze7.js} +3 -3
  86. package/dist/{db-w759qmx0.js → db-w36wwb5g.js} +4 -2
  87. package/dist/{devin-adapter-4ycy4vrf.js → devin-adapter-byen2cxt.js} +1 -1
  88. package/dist/{dispatcher-7b8e4z2j.js → dispatcher-pv5h77vf.js} +20 -20
  89. package/dist/{docker-s84hjs91.js → docker-fdzyewpz.js} +2 -2
  90. package/dist/{dsh-adapter-2ezw3xea.js → dsh-adapter-d4pdpb3r.js} +2 -2
  91. package/dist/{e2b-9gkdhm3c.js → e2b-222hsajc.js} +2 -2
  92. package/dist/{e2b-cjtf8k1z.js → e2b-fn3zacvt.js} +2 -2
  93. package/dist/{easter-egg-3d-e4qv8dxj.js → easter-egg-3d-4sp4dvmc.js} +1 -1
  94. package/dist/{event-bus-3z78ep5n.js → event-bus-pghg8hqy.js} +1 -1
  95. package/dist/{execute-0xpz8950.js → execute-jev839y1.js} +1 -1
  96. package/dist/{external-channel-e2s028da.js → external-channel-y6nkzhnn.js} +2 -2
  97. package/dist/{grok-adapter-ys7gaqee.js → grok-adapter-t08b9ntm.js} +10 -10
  98. package/dist/handlers-eqsnmv95.js +41 -0
  99. package/dist/{heartbeat-w3mntghj.js → heartbeat-vn31xwdk.js} +23 -23
  100. package/dist/{hook-g7kvv6x9.js → hook-6d0zbvvt.js} +6 -6
  101. package/dist/{hook-3jjywnpb.js → hook-cpjs8q51.js} +8 -8
  102. package/dist/{http-tkp7d4kb.js → http-9c6medda.js} +856 -99
  103. package/dist/{index-xdaahthb.js → index-38t35h0n.js} +20 -20
  104. package/dist/{index-3n8m9ea2.js → index-g6k9tctj.js} +1 -1
  105. package/dist/{index-1ef1myq2.js → index-k0yt1tvq.js} +20 -20
  106. package/dist/{index-31ceyf42.js → index-pkma1yf8.js} +1 -1
  107. package/dist/{index-q7m26kvs.js → index-qhjf2htm.js} +13 -10
  108. package/dist/index-yqgbfgaj.js +43 -0
  109. package/dist/{keepalive-v4ta8wgg.js → keepalive-em0117bp.js} +4 -4
  110. package/dist/lead-23e7z59t.js +79 -0
  111. package/dist/{maintenance-84n1ex21.js → maintenance-kbme3qy9.js} +1 -1
  112. package/dist/{oauth-refresh-sweep-vjnnandw.js → oauth-refresh-sweep-xysj4yse.js} +3 -3
  113. package/dist/{onboard-sfmpe4np.js → onboard-p6q0vz97.js} +3 -3
  114. package/dist/{opencode-adapter-jmpst4q4.js → opencode-adapter-esv3k9yn.js} +7 -7
  115. package/dist/{otel-impl-v6cc0zve.js → otel-impl-54qhqh1z.js} +2 -2
  116. package/dist/{pi-mono-adapter-fk8fkcf6.js → pi-mono-adapter-mymbwhj0.js} +10 -10
  117. package/dist/{pricing-refresh-fsk1e1zc.js → pricing-refresh-yb75wv33.js} +3 -3
  118. package/dist/{rbac-roles-prmkdnkw.js → rbac-roles-1vp2mw4p.js} +2 -2
  119. package/dist/{rbac-roles-zxmxqa39.js → rbac-roles-dexhc9x8.js} +1 -1
  120. package/dist/render-v2-tjay5hhf.js +16 -0
  121. package/dist/{resume-frdtgfa0.js → resume-8ayf37g5.js} +9 -9
  122. package/dist/{runtime-bt5pkpm1.js → runtime-hqe5cqcd.js} +1 -1
  123. package/dist/{session-access-fpez4y5q.js → session-access-fdhp987s.js} +4 -4
  124. package/dist/session-source-backfill-qgtc86z1.js +110 -0
  125. package/dist/{setup-mq6m3m8q.js → setup-4rrmqcq3.js} +1 -1
  126. package/dist/{virtual-modules-mnfbh5mr.js → virtual-modules-52km00d6.js} +2 -2
  127. package/dist/{work-objects-kfahhjsq.js → work-objects-p28fgd70.js} +23 -23
  128. package/dist/worker-e3w7vye1.js +79 -0
  129. package/dist/{x-pbpezr0s.js → x-wxrzvzx8.js} +2 -2
  130. package/openapi.json +1274 -32
  131. package/package.json +1 -1
  132. package/src/assets/source-key.ts +95 -0
  133. package/src/be/db/api-keys.ts +80 -0
  134. package/src/be/db/session-labels.ts +28 -0
  135. package/src/be/db/session-moves.ts +5 -6
  136. package/src/be/db/session-tree.ts +153 -14
  137. package/src/be/db/tasks/read.ts +11 -0
  138. package/src/be/db/tasks/write.ts +6 -1
  139. package/src/be/db/workflow-runs.ts +183 -1
  140. package/src/be/db-retention.ts +61 -6
  141. package/src/be/db.ts +59 -114
  142. package/src/be/memory/providers/openai-embedding.ts +5 -1
  143. package/src/be/migrations/244_api_key_validation.sql +4 -0
  144. package/src/be/onboarding.ts +4 -0
  145. package/src/be/scripts/typecheck.ts +1 -1
  146. package/src/be/session-source-backfill.ts +163 -0
  147. package/src/be/swarm-config-guard.ts +1 -0
  148. package/src/comb/review-batch.ts +28 -1
  149. package/src/comb/templates.ts +7 -2
  150. package/src/commands/claude-managed-setup.ts +7 -1
  151. package/src/commands/credential-refresh.ts +76 -1
  152. package/src/commands/credential-wait.ts +2 -2
  153. package/src/commands/provider-credentials.ts +210 -170
  154. package/src/commands/runner.ts +25 -4
  155. package/src/extensions/apply-task-create.ts +8 -1
  156. package/src/extensions/contract-types.generated.ts +1 -1
  157. package/src/extensions/contract.ts +5 -0
  158. package/src/http/all-routes.ts +2 -0
  159. package/src/http/api-keys.ts +53 -0
  160. package/src/http/assets.ts +15 -0
  161. package/src/http/codex-oauth-accounts.ts +191 -0
  162. package/src/http/comb.ts +15 -2
  163. package/src/http/config.ts +1 -1
  164. package/src/http/core.ts +6 -0
  165. package/src/http/fleet-benchmark.ts +5 -2
  166. package/src/http/index.ts +11 -0
  167. package/src/http/integrations.ts +5 -1
  168. package/src/http/kv.ts +8 -3
  169. package/src/http/onboarding.ts +6 -1
  170. package/src/http/pool-keys.ts +354 -0
  171. package/src/http/sessions.ts +37 -12
  172. package/src/http/stats.ts +1 -0
  173. package/src/http/status.ts +26 -12
  174. package/src/http/tasks.ts +3 -0
  175. package/src/http/used-by.ts +7 -2
  176. package/src/http/workflows.ts +78 -9
  177. package/src/integrations/chat-sdk/inbound.ts +27 -0
  178. package/src/kv-reserved-namespaces.ts +19 -0
  179. package/src/providers/acp-targets.ts +1 -1
  180. package/src/providers/amp-adapter.ts +1 -1
  181. package/src/providers/claude-adapter.ts +5 -1
  182. package/src/providers/claude-managed-adapter.ts +16 -9
  183. package/src/providers/codex-adapter.ts +71 -11
  184. package/src/scheduler/scheduler.ts +3 -2
  185. package/src/scripts-runtime/swarm-sdk.ts +7 -1
  186. package/src/scripts-runtime/types/stdlib.d.ts +1 -1
  187. package/src/scripts-runtime/types/swarm-extension.d.ts +17 -8
  188. package/src/scripts-runtime/types/swarm-sdk.d.ts +1 -1
  189. package/src/slack/assistant.ts +7 -0
  190. package/src/slack/channel-labels.ts +74 -0
  191. package/src/slack/handlers.ts +8 -0
  192. package/src/slack/inbound-files.ts +5 -2
  193. package/src/slack/thread-buffer.ts +4 -0
  194. package/src/tasks/sibling-awareness.ts +12 -3
  195. package/src/telemetry-context.ts +5 -6
  196. package/src/telemetry-identity.ts +3 -3
  197. package/src/tests/api-keys-validation.test.ts +170 -0
  198. package/src/tests/claude-managed-adapter.test.ts +46 -0
  199. package/src/tests/claude-routes-e2e.test.ts +7 -2
  200. package/src/tests/codex-credential-pools.test.ts +120 -0
  201. package/src/tests/codex-gateway-route.test.ts +165 -0
  202. package/src/tests/codex-oauth-accounts.test.ts +357 -0
  203. package/src/tests/comb-review-batch.test.ts +63 -2
  204. package/src/tests/config-pool-keys.test.ts +533 -0
  205. package/src/tests/credential-check.test.ts +290 -2
  206. package/src/tests/credential-refresh.test.ts +172 -1
  207. package/src/tests/db-retention.test.ts +9 -3
  208. package/src/tests/db-task-read-contract.test.ts +1 -0
  209. package/src/tests/db-task-write-contract.test.ts +1 -3
  210. package/src/tests/docs-index-footprint.test.js +3 -3
  211. package/src/tests/extensions-pre-task.test.ts +53 -1
  212. package/src/tests/fixtures/extensions/record-task-create/hooks.ts +9 -0
  213. package/src/tests/fixtures/extensions/record-task-create/manifest.json +7 -0
  214. package/src/tests/fleet-benchmark.test.ts +8 -0
  215. package/src/tests/get-workflow-run-tool.test.ts +98 -0
  216. package/src/tests/integrations-http.test.ts +32 -2
  217. package/src/tests/internal-ai/complete-structured.test.ts +37 -1
  218. package/src/tests/internal-ai/credentials.test.ts +23 -0
  219. package/src/tests/kv-http.test.ts +32 -0
  220. package/src/tests/kv-tool.test.ts +25 -0
  221. package/src/tests/multi-runtime-registration.test.ts +12 -12
  222. package/src/tests/onboarding.test.ts +108 -1
  223. package/src/tests/openai-embedding-credentials.test.ts +35 -0
  224. package/src/tests/openrouter-attribution-call-sites.test.ts +2 -2
  225. package/src/tests/org-website.test.ts +36 -0
  226. package/src/tests/provider-key-probes.test.ts +181 -0
  227. package/src/tests/rbac-e2e-helpers.test.ts +88 -1
  228. package/src/tests/rbac-e2e-helpers.ts +36 -11
  229. package/src/tests/script-connections.test.ts +2 -0
  230. package/src/tests/script-runs-retention.test.ts +212 -0
  231. package/src/tests/sdk-allowlist.test.ts +39 -0
  232. package/src/tests/session-source-backfill.test.ts +429 -0
  233. package/src/tests/session-source-keys.test.ts +236 -0
  234. package/src/tests/session-source-labels.test.ts +419 -0
  235. package/src/tests/sessions-detail-batch.test.ts +2 -0
  236. package/src/tests/sessions-move.test.ts +131 -0
  237. package/src/tests/sessions-tree.test.ts +299 -2
  238. package/src/tests/slack-inbound-files.test.ts +103 -23
  239. package/src/tests/status.test.ts +101 -18
  240. package/src/tests/tasks-workflow-run-filter.test.ts +146 -0
  241. package/src/tests/test-net.ts +31 -0
  242. package/src/tests/transcription.test.ts +8 -0
  243. package/src/tests/used-by-route.test.ts +11 -0
  244. package/src/tests/workflow-cascade-cancel.test.ts +172 -0
  245. package/src/tests/workflow-http-v2.test.ts +131 -0
  246. package/src/tests/workflow-step-order.test.ts +92 -0
  247. package/src/tests/workflow-sub-workflow.test.ts +30 -0
  248. package/src/tools/get-tasks.ts +8 -0
  249. package/src/tools/kv/kv-read-auth.ts +4 -2
  250. package/src/tools/task-action.ts +1 -1
  251. package/src/tools/workflows/get-workflow-run.ts +14 -4
  252. package/src/types.ts +24 -0
  253. package/src/utils/company-profile.ts +51 -1
  254. package/src/utils/credentials.ts +5 -2
  255. package/src/utils/internal-ai/complete-structured.ts +5 -0
  256. package/src/utils/internal-ai/credentials.ts +4 -3
  257. package/src/utils/provider-key-probes.ts +328 -0
  258. package/src/utils/transcription.ts +4 -1
  259. package/src/workflows/executors/sub-workflow.ts +8 -1
  260. package/templates/ai-toolbox.manifest.json +2 -2
  261. package/templates/skills/researching/content.md +2 -0
  262. package/templates/skills/swarm-scripts/SKILL.md +1 -0
  263. package/templates/skills/swarm-scripts/content.md +1 -0
  264. package/dist/cli-7421jzsw.js +0 -84
  265. package/dist/cli-e45e4m6b.js +0 -413
  266. package/dist/cli-zw88zjba.js +0 -307
  267. package/dist/handlers-wy1dr6ja.js +0 -41
  268. package/dist/index-nmm79qgr.js +0 -43
  269. package/dist/lead-sawnyqxk.js +0 -79
  270. package/dist/render-v2-pjwxvnkw.js +0 -12
  271. package/dist/worker-v5e5rxj3.js +0 -79
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. */
@@ -349,22 +364,50 @@ const getWorkflowRunRoute = route({
349
364
  path: "/api/workflow-runs/{id}",
350
365
  pattern: ["api", "workflow-runs", null],
351
366
  summary: "Get a workflow run with steps (includes retry columns)",
367
+ description:
368
+ "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
369
  tags: ["Workflows"],
353
370
  params: z.object({ id: z.string() }),
371
+ query: z.object({
372
+ /** `slim` omits `run.context` and `steps[].input`; default is full. */
373
+ fields: z.enum(["full", "slim"]).optional(),
374
+ }),
354
375
  responses: {
355
376
  200: {
356
377
  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
- }),
378
+ schema: z.union([
379
+ z.object({
380
+ run: WorkflowRunSchema,
381
+ steps: z.array(WorkflowRunStepSchema),
382
+ /** Set on a child run: the run and `sub-workflow` step that started it. */
383
+ parent: WorkflowRunParentSchema.optional(),
384
+ }),
385
+ z.object({
386
+ run: WorkflowRunSummarySchema,
387
+ steps: z.array(WorkflowRunStepSlimSchema),
388
+ parent: WorkflowRunParentSchema.optional(),
389
+ }),
390
+ ]),
363
391
  },
364
392
  404: { description: "Run not found" },
365
393
  },
366
394
  });
367
395
 
396
+ const getWorkflowRunTreeRoute = route({
397
+ method: "get",
398
+ path: "/api/workflow-runs/{id}/tree",
399
+ pattern: ["api", "workflow-runs", null, "tree"],
400
+ summary: "Get a workflow run and every sub-workflow run below it",
401
+ 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.`,
402
+ tags: ["Workflows"],
403
+ params: z.object({ id: z.string() }),
404
+ responses: {
405
+ 200: { description: "The run tree", schema: WorkflowRunTreeSchema },
406
+ 404: { description: "Run not found" },
407
+ 409: { description: "Stored parent links form a cycle or nest past the depth bound" },
408
+ },
409
+ });
410
+
368
411
  const retryWorkflowRunRoute = route({
369
412
  method: "post",
370
413
  path: "/api/workflow-runs/{id}/retry",
@@ -1014,18 +1057,44 @@ export async function handleWorkflows(
1014
1057
  const parsed = await getWorkflowRunRoute.parse(req, res, pathSegments, queryParams);
1015
1058
  if (!parsed) return true;
1016
1059
  // Display view: the scrubbed context and step outputs, never replay state.
1017
- const run = await getWorkflowRun(parsed.params.id, "display");
1060
+ // `fields=slim` skips the context and step-input columns in SQL too.
1061
+ const slim = parsed.query.fields === "slim";
1062
+ const run = slim
1063
+ ? await getWorkflowRunSummary(parsed.params.id)
1064
+ : await getWorkflowRun(parsed.params.id, "display");
1018
1065
  if (!run) {
1019
1066
  res.writeHead(404);
1020
1067
  res.end();
1021
1068
  return true;
1022
1069
  }
1023
- const steps = await getWorkflowRunStepsByRunId(parsed.params.id, "display");
1070
+ const steps = slim
1071
+ ? await getWorkflowRunStepsSlimByRunId(parsed.params.id)
1072
+ : await getWorkflowRunStepsByRunId(parsed.params.id, "display");
1024
1073
  const parent = run.parentStepId ? await getWorkflowRunParent(run.parentStepId) : null;
1025
1074
  getWorkflowRunRoute.respond(res, 200, { run, steps, ...(parent ? { parent } : {}) });
1026
1075
  return true;
1027
1076
  }
1028
1077
 
1078
+ if (getWorkflowRunTreeRoute.match(req.method, pathSegments)) {
1079
+ const parsed = await getWorkflowRunTreeRoute.parse(req, res, pathSegments, queryParams);
1080
+ if (!parsed) return true;
1081
+ let tree: Awaited<ReturnType<typeof getWorkflowRunTree>>;
1082
+ try {
1083
+ tree = await getWorkflowRunTree(parsed.params.id);
1084
+ } catch (err) {
1085
+ if (!(err instanceof WorkflowRunTreeError)) throw err;
1086
+ jsonError(res, err.message, 409);
1087
+ return true;
1088
+ }
1089
+ if (!tree) {
1090
+ res.writeHead(404);
1091
+ res.end();
1092
+ return true;
1093
+ }
1094
+ getWorkflowRunTreeRoute.respond(res, 200, tree);
1095
+ return true;
1096
+ }
1097
+
1029
1098
  if (retryWorkflowRunRoute.match(req.method, pathSegments)) {
1030
1099
  const parsed = await retryWorkflowRunRoute.parse(req, res, pathSegments, queryParams);
1031
1100
  if (!parsed) return true;
@@ -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"
@@ -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.1792929650-g000512";
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
  /**
@@ -30,6 +30,13 @@
30
30
 
31
31
  import { existsSync as nodeExistsSync } from "node:fs";
32
32
  import { join } from "node:path";
33
+ import {
34
+ CODEX_GATEWAY_API_KEY,
35
+ deriveDefaultRoute,
36
+ type ModelRoute,
37
+ routeCredentialStatus,
38
+ routeUnsetEnv,
39
+ } from "@desplega/model-routing";
33
40
  import { buildRatingsFromLlm, fetchRetrievalsForTask, postRatings } from "../be/memory/raters/llm";
34
41
  import { contextBoundNoteThreshold, createContextBoundNote } from "../hooks/context-bound-note";
35
42
  import { getApiKey } from "../utils/api-key";
@@ -38,6 +45,7 @@ import {
38
45
  clampContextPercent,
39
46
  computeContextUsedUnified,
40
47
  } from "../utils/context-window";
48
+ import { selectCredential } from "../utils/credentials";
41
49
  import { isEnvFlagEnabled, isSessionSummarySkipped } from "../utils/env-flag";
42
50
  import { SessionErrorTracker } from "../utils/error-tracker";
43
51
  import { summarizeSession as runSummarize } from "../utils/internal-ai";
@@ -82,6 +90,32 @@ import type {
82
90
 
83
91
  type CodexConfig = Record<string, CodexConfigValue | undefined>;
84
92
 
93
+ /** `model_provider` id of the generated gateway block. */
94
+ const CODEX_GATEWAY_PROVIDER_ID = "swarm-gateway";
95
+
96
+ /**
97
+ * Config overrides that send Codex through the gateway route. Codex 0.164
98
+ * ignores `OPENAI_BASE_URL` and env keys for its built-in `openai` provider, so
99
+ * the gateway needs its own provider block. Verified on 0.164 against a local
100
+ * gateway: Codex sends `Authorization: Bearer $CODEX_GATEWAY_API_KEY` to
101
+ * `<base_url>/responses` and never the auth.json OAuth token. `wire_api`
102
+ * defaults to `responses` (`chat` is rejected); it is set to state intent.
103
+ */
104
+ export function codexGatewayConfig(route: ModelRoute): CodexConfig {
105
+ return {
106
+ model_provider: CODEX_GATEWAY_PROVIDER_ID,
107
+ model_providers: {
108
+ [CODEX_GATEWAY_PROVIDER_ID]: {
109
+ name: "Swarm gateway",
110
+ base_url: route.baseUrl,
111
+ env_key: CODEX_GATEWAY_API_KEY,
112
+ wire_api: "responses",
113
+ requires_openai_auth: false,
114
+ },
115
+ },
116
+ };
117
+ }
118
+
85
119
  /** Plugin id the worker image installs (`codex plugin add context-mode@context-mode`). */
86
120
  const CONTEXT_MODE_CODEX_PLUGIN = "context-mode@context-mode";
87
121
 
@@ -187,7 +221,10 @@ export type CodexThreadFactory = (options: {
187
221
  }) => Promise<CodexThread> | CodexThread;
188
222
 
189
223
  /**
190
- * Codex satisfies its credential requirement by ANY of:
224
+ * A gateway route (`CODEX_GATEWAY_BASE_URL` + `CODEX_GATEWAY_API_KEY`) wins
225
+ * and alone decides readiness: a gateway URL without its key is not ready,
226
+ * whatever OAuth or auth.json is present. Otherwise Codex satisfies its
227
+ * credential requirement by ANY of:
191
228
  * 1. `~/.codex/auth.json` already exists on disk (the canonical state once
192
229
  * `codex login` has run).
193
230
  * 2. `OPENAI_API_KEY` is set — the entrypoint will run
@@ -204,6 +241,13 @@ export function checkCodexCredentials(
204
241
  env: Record<string, string | undefined>,
205
242
  opts: CredCheckOptions = {},
206
243
  ): CredStatus {
244
+ const gateway = deriveDefaultRoute("codex", env);
245
+ if (gateway) {
246
+ const status = routeCredentialStatus(gateway, env);
247
+ return status.ready
248
+ ? { ready: true, missing: [], satisfiedBy: "env", hint: status.hint }
249
+ : { ready: false, missing: status.missing, hint: status.hint };
250
+ }
207
251
  const homeDir = opts.homeDir ?? env.HOME ?? "/root";
208
252
  const existsSync = opts.fs?.existsSync ?? nodeExistsSync;
209
253
  const authFile = `${homeDir}/.codex/auth.json`;
@@ -1965,14 +2009,21 @@ export async function createInProcessCodexSession(
1965
2009
  preSessionEvents.push(event);
1966
2010
  };
1967
2011
 
1968
- const mergedConfig = await buildCodexConfig(config, resolvedModel, bufferedEmit);
2012
+ const routeEnv = config.env ?? process.env;
2013
+ const gateway = deriveDefaultRoute("codex", routeEnv);
2014
+ const mergedConfig = {
2015
+ ...(await buildCodexConfig(config, resolvedModel, bufferedEmit)),
2016
+ ...(gateway ? codexGatewayConfig(gateway) : {}),
2017
+ };
1969
2018
 
1970
2019
  // Auth resolution. `codex_oauth` (in the swarm config store) wins over
1971
2020
  // `OPENAI_API_KEY` so users can keep an OpenAI key set for embeddings
1972
2021
  // without it shadowing their ChatGPT login. The entrypoint already runs
1973
2022
  // this same precedence at boot — this block handles local dev (where
1974
2023
  // the entrypoint didn't run) and any case where auth.json is stale.
1975
- const authMode = await resolveCodexAuthMode(config, bufferedEmit);
2024
+ // A gateway route skips it: no OAuth is restored for a session whose traffic
2025
+ // goes to the gateway.
2026
+ const authMode = gateway ? null : await resolveCodexAuthMode(config, bufferedEmit);
1976
2027
 
1977
2028
  // The app-server receives an explicit environment. Construct a
1978
2029
  // minimal env explicitly so the spawned Codex CLI can find its binary
@@ -1999,7 +2050,14 @@ export async function createInProcessCodexSession(
1999
2050
  ...buildOtelTraceparentEnv(config.env ?? process.env),
2000
2051
  SWARM_CODEX_APP_SERVER: "1",
2001
2052
  };
2002
- if (authMode === "chatgpt") delete env.OPENAI_API_KEY;
2053
+ if (gateway) {
2054
+ for (const key of routeUnsetEnv("codex", gateway)) delete env[key];
2055
+ const gatewayKey = routeEnv[CODEX_GATEWAY_API_KEY];
2056
+ if (gatewayKey) env[CODEX_GATEWAY_API_KEY] = gatewayKey;
2057
+ } else if (authMode === "chatgpt") delete env.OPENAI_API_KEY;
2058
+ else if (env.OPENAI_API_KEY) {
2059
+ env.OPENAI_API_KEY = selectCredential(env.OPENAI_API_KEY, undefined, "OPENAI_API_KEY").selected;
2060
+ }
2003
2061
 
2004
2062
  const codexPathOverride = process.env.CODEX_PATH_OVERRIDE;
2005
2063
 
@@ -2151,6 +2209,11 @@ class CodexSubprocessSession implements ProviderSession {
2151
2209
  };
2152
2210
 
2153
2211
  const apiKey = getApiKey();
2212
+ const providerKeys: Record<string, string> = {};
2213
+ for (const keyVar of ["OPENAI_API_KEY", "OPENROUTER_API_KEY", "ANTHROPIC_API_KEY"]) {
2214
+ const value = config.env?.[keyVar] ?? process.env[keyVar];
2215
+ if (value) providerKeys[keyVar] = selectCredential(value, undefined, keyVar).selected;
2216
+ }
2154
2217
 
2155
2218
  this.proc = registerProcessGroup(
2156
2219
  Bun.spawn(argv, {
@@ -2168,13 +2231,7 @@ class CodexSubprocessSession implements ProviderSession {
2168
2231
  ...(process.env.MCP_BASE_URL ? { MCP_BASE_URL: process.env.MCP_BASE_URL } : {}),
2169
2232
  ...(apiKey ? { AGENT_SWARM_API_KEY: apiKey, API_KEY: apiKey } : {}),
2170
2233
  // Embedding / summarization paths read these:
2171
- ...(process.env.OPENAI_API_KEY ? { OPENAI_API_KEY: process.env.OPENAI_API_KEY } : {}),
2172
- ...(process.env.OPENROUTER_API_KEY
2173
- ? { OPENROUTER_API_KEY: process.env.OPENROUTER_API_KEY }
2174
- : {}),
2175
- ...(process.env.ANTHROPIC_API_KEY
2176
- ? { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY }
2177
- : {}),
2234
+ ...providerKeys,
2178
2235
  ...(process.env.CODEX_PATH_OVERRIDE
2179
2236
  ? { CODEX_PATH_OVERRIDE: process.env.CODEX_PATH_OVERRIDE }
2180
2237
  : {}),
@@ -2475,6 +2532,9 @@ export class CodexAdapter implements ProviderAdapter {
2475
2532
  }
2476
2533
 
2477
2534
  async createSession(config: ProviderSessionConfig): Promise<ProviderSession> {
2535
+ if (deriveDefaultRoute("codex", config.env ?? process.env)) {
2536
+ console.log("\x1b[2m[codex]\x1b[0m Using credential: gateway");
2537
+ }
2478
2538
  if (this.bypassSubprocess) {
2479
2539
  return createInProcessCodexSession(config, {
2480
2540
  skillsDir: this.skillsDir,
@@ -91,8 +91,9 @@ async function executeScheduleScript(schedule: ScheduledTask): Promise<void> {
91
91
  agentId,
92
92
  scriptName: schedule.scriptName,
93
93
  // A pointer, not a copy: script schedules fire as often as every minute and
94
- // script_runs has no retention sweep, so copying the source on every fire
95
- // would grow the table by tens of MB a day. script_versions keeps the text.
94
+ // script_runs retention (SCRIPT_RUNS_RETENTION_DAYS) is off by default, so
95
+ // copying the source on every fire would grow the table by tens of MB a
96
+ // day. script_versions keeps the text.
96
97
  source: `// Scheduled run of '${script.name}' v${script.version} (global, contentHash ${script.contentHash}) by schedule '${schedule.name}'.\n// Full source: script_versions row for this version.\n`,
97
98
  args,
98
99
  startedAt: new Date().toISOString(),
@@ -319,7 +319,13 @@ function bridgeRequestFor(name: string, args: unknown): BridgeRequest | null {
319
319
  case "workflow_getRun": {
320
320
  const id = typeof body.id === "string" ? body.id : undefined;
321
321
  if (!id) throw new Error("workflow_getRun requires string `id`");
322
- return { method: "GET", path: `/api/workflow-runs/${encodeURIComponent(id)}` };
322
+ if (body.fields !== undefined && body.fields !== "full" && body.fields !== "slim") {
323
+ throw new Error('workflow_getRun `fields` must be "full" or "slim"');
324
+ }
325
+ return {
326
+ method: "GET",
327
+ path: appendQuery(`/api/workflow-runs/${encodeURIComponent(id)}`, { fields: body.fields }),
328
+ };
323
329
  }
324
330
  case "workflow_delete": {
325
331
  const id = typeof body.id === "string" ? body.id : undefined;
@@ -378,7 +378,7 @@ declare module "swarm-sdk" {
378
378
  limit?: number;
379
379
  offset?: number;
380
380
  }): Promise<unknown>;
381
- workflow_getRun(args: { id: string }): Promise<unknown>;
381
+ workflow_getRun(args: { id: string; fields?: "full" | "slim" }): Promise<unknown>;
382
382
  // --- prompt templates ---
383
383
  prompt_list(args?: {
384
384
  eventType?: string;
@@ -9,16 +9,16 @@ declare module "swarm-extension" {
9
9
  | "schedule"
10
10
  | "slack"
11
11
  | "agentmail"
12
- | "mcp"
13
- | "api"
14
- | "ui"
15
12
  | "github"
16
13
  | "gitlab"
17
14
  | "azure-devops"
18
15
  | "bitbucket"
19
- | "system"
20
16
  | "linear"
21
17
  | "jira"
18
+ | "mcp"
19
+ | "api"
20
+ | "ui"
21
+ | "system"
22
22
  | "comb"
23
23
  | "chat"
24
24
  | undefined;
@@ -127,16 +127,16 @@ declare module "swarm-extension" {
127
127
  | "schedule"
128
128
  | "slack"
129
129
  | "agentmail"
130
- | "mcp"
131
- | "api"
132
- | "ui"
133
130
  | "github"
134
131
  | "gitlab"
135
132
  | "azure-devops"
136
133
  | "bitbucket"
137
- | "system"
138
134
  | "linear"
139
135
  | "jira"
136
+ | "mcp"
137
+ | "api"
138
+ | "ui"
139
+ | "system"
140
140
  | "comb"
141
141
  | "chat";
142
142
  tags: string[];
@@ -401,6 +401,15 @@ declare module "swarm-extension" {
401
401
  description: string;
402
402
  origin: TaskCreateOrigin;
403
403
  requestInfo?: RequestInfo;
404
+ /** Slack ingress metadata; not persisted or writable through modify. */
405
+ slack?: {
406
+ trigger: "mention" | "thread-buffer";
407
+ messages: {
408
+ ts: string;
409
+ userId: string;
410
+ text: string;
411
+ }[];
412
+ };
404
413
  }
405
414
  /** A pre.task.create hook blocked a task before it was created. */
406
415
  export interface TaskBlockedEvent {
@@ -360,7 +360,7 @@ declare module "swarm-sdk" {
360
360
  limit?: number;
361
361
  offset?: number;
362
362
  }): Promise<unknown>;
363
- workflow_getRun(args: { id: string }): Promise<unknown>;
363
+ workflow_getRun(args: { id: string; fields?: "full" | "slim" }): Promise<unknown>;
364
364
  // --- prompt templates ---
365
365
  prompt_list(args?: {
366
366
  eventType?: string;