@desplega.ai/agent-swarm 1.152.2 → 1.154.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 (284) hide show
  1. package/README.md +2 -2
  2. package/dist/{acp-adapter-pfeje2v5.js → acp-adapter-eb2aj8xy.js} +4 -4
  3. package/dist/{actions-0sttc7f5.js → actions-kpnwp1kz.js} +31 -27
  4. package/dist/{anthropic-messages-2y8nktar.js → anthropic-messages-9vqsbznt.js} +112 -84
  5. package/dist/{app-p8teva95.js → app-zk1apkf6.js} +10 -7
  6. package/dist/{assistant-r45jnsrt.js → assistant-kwq4y50q.js} +32 -28
  7. package/dist/{azure-openai-responses-f38x2avq.js → azure-openai-responses-04qwdwfq.js} +27 -13
  8. package/dist/{boot-reembed-je26h5y7.js → boot-reembed-22hd3jjn.js} +9 -7
  9. package/dist/{boot-reembed-1qmybsd4.js → boot-reembed-qe168424.js} +10 -8
  10. package/dist/{boot-scrub-logs-gte3h1mm.js → boot-scrub-logs-pkdpsrjd.js} +7 -5
  11. package/dist/{claude-adapter-csc1mzka.js → claude-adapter-3fx90nww.js} +18 -16
  12. package/dist/{claude-managed-adapter-dsmkcrjy.js → claude-managed-adapter-ea2aga6w.js} +3 -3
  13. package/dist/{claude-sdk-session-fcd6f078.js → claude-sdk-session-z7fahf1k.js} +18 -16
  14. package/dist/{cli-r2fpe7cx.js → cli-00w2c66p.js} +61 -55
  15. package/dist/{cli-wbyk465z.js → cli-09y3tjxz.js} +1 -1
  16. package/dist/{cli-ht5rs63n.js → cli-0e31nm4n.js} +16 -7
  17. package/dist/cli-0x5qzvkg.js +4279 -0
  18. package/dist/{cli-d3p82erb.js → cli-18amys3y.js} +9 -9
  19. package/dist/{cli-4ahpr4sw.js → cli-1jpjafw4.js} +5 -5
  20. package/dist/{cli-jbmc0r7r.js → cli-243tzws8.js} +1 -1
  21. package/dist/{cli-ch38q5z4.js → cli-24k806sc.js} +6789 -2542
  22. package/dist/cli-3bkz2p7j.js +2828 -0
  23. package/dist/cli-3er6gcsn.js +1849 -0
  24. package/dist/cli-4k7jazkq.js +3339 -0
  25. package/dist/{cli-c4eeb5q2.js → cli-5b6b62tz.js} +1 -1
  26. package/dist/{cli-qd65dvr6.js → cli-5vs0nh43.js} +65 -44
  27. package/dist/{cli-z3wkng35.js → cli-6r3cgpk2.js} +224 -156
  28. package/dist/{cli-pgj4a5fb.js → cli-71cj99hx.js} +4 -3
  29. package/dist/cli-8sea27q4.js +14075 -0
  30. package/dist/{cli-jvy9qgjs.js → cli-913yakhv.js} +22 -7
  31. package/dist/{cli-pf9q3696.js → cli-927erqqt.js} +5 -2
  32. package/dist/cli-95jnpnqg.js +230 -0
  33. package/dist/{cli-wpv5rgg2.js → cli-abp3g4ax.js} +15 -2
  34. package/dist/{cli-ckz5t2hb.js → cli-acy6bfhr.js} +10 -7
  35. package/dist/{cli-4yc5d8fk.js → cli-avkd6mqv.js} +50 -0
  36. package/dist/{cli-8wrympd7.js → cli-bzr0ftht.js} +26 -7
  37. package/dist/{cli-qn042eh7.js → cli-c1v10xb2.js} +194 -1913
  38. package/dist/cli-cgj4hc12.js +18902 -0
  39. package/dist/{cli-2nnd56zt.js → cli-e0pg4zeh.js} +3 -2
  40. package/dist/{cli-r84ctdxn.js → cli-e31ehqwy.js} +2 -2
  41. package/dist/{cli-gphgnvpf.js → cli-e3229na9.js} +3 -3
  42. package/dist/{cli-gpqcs935.js → cli-e3cyf59d.js} +2 -2
  43. package/dist/{cli-s25avqg9.js → cli-e781c6f9.js} +1 -0
  44. package/dist/{cli-s2qjdd6z.js → cli-eetrvv91.js} +1 -1
  45. package/dist/{cli-j4nv2wqy.js → cli-fdzg6jhf.js} +10 -4
  46. package/dist/{cli-fm4ytsdk.js → cli-g2h29qar.js} +2 -2
  47. package/dist/{cli-zvgj5zg8.js → cli-ge67p080.js} +302 -3497
  48. package/dist/cli-h38x0qhg.js +6912 -0
  49. package/dist/{cli-9xvdm3bx.js → cli-h60qfasa.js} +742 -278
  50. package/dist/{cli-jew3jda5.js → cli-hws0vn86.js} +2 -2
  51. package/dist/{cli-j8dggbcr.js → cli-k0g1sjzm.js} +23 -31
  52. package/dist/{cli-xw7vak96.js → cli-kp3qs53m.js} +5 -5
  53. package/dist/{cli-rgvp6r2x.js → cli-mcnhgnn1.js} +193 -4
  54. package/dist/{cli-zvkq5csn.js → cli-myaxtgh8.js} +7 -6
  55. package/dist/{cli-56h18ng2.js → cli-n62atwh9.js} +1 -1
  56. package/dist/cli-n7h1kqpf.js +29 -0
  57. package/dist/{cli-fmy65jdn.js → cli-pf1v8xhb.js} +7 -5
  58. package/dist/{cli-xy1cchaf.js → cli-q85qhckh.js} +238 -11
  59. package/dist/{cli-3bpzj8me.js → cli-r1d3dzt6.js} +2 -2
  60. package/dist/{pi-mono-adapter-jcqt71cg.js → cli-rxkqvngf.js} +67343 -101083
  61. package/dist/{cli-kj76p1ez.js → cli-scwq51gn.js} +13 -7
  62. package/dist/{cli-b9dd0h3k.js → cli-stwsmd6k.js} +3 -2
  63. package/dist/{cli-c2mk1kgf.js → cli-v4en75c5.js} +8 -1
  64. package/dist/{cli-z61ykvam.js → cli-wg04s9kr.js} +4 -3
  65. package/dist/cli-yn0a417a.js +4069 -0
  66. package/dist/{cli-zfzznn7c.js → cli-ytxs7mn1.js} +26 -2
  67. package/dist/{cli-4j8k1m4w.js → cli-zt1227k1.js} +1162 -192
  68. package/dist/cli.js +28 -35
  69. package/dist/{codex-adapter-fxmpsabw.js → codex-adapter-ttdm995v.js} +12 -11
  70. package/dist/{codex-hook-8jexc34x.js → codex-hook-8gx7bcz0.js} +3 -3
  71. package/dist/{codex-session-runner-rr1e47ea.js → codex-session-runner-3v8nh4c8.js} +12 -11
  72. package/dist/{commands-0f5rxz51.js → commands-1cxwbhhq.js} +7 -5
  73. package/dist/{db-r3jxf3wy.js → db-y9zwsr79.js} +25 -5
  74. package/dist/{dispatcher-06vph6q4.js → dispatcher-9m9zss36.js} +30 -26
  75. package/dist/dsh-adapter-gsb2qqf9.js +19 -0
  76. package/dist/{e2b-413b8t4v.js → e2b-93x1k0j5.js} +6 -7
  77. package/dist/{google-generative-ai-ndjmj5fa.js → google-generative-ai-gwwrnzqn.js} +33 -73
  78. package/dist/{google-vertex-fr665tqs.js → google-vertex-5wfawpwz.js} +38 -68
  79. package/dist/{handlers-0hgtc910.js → handlers-36fm1bwn.js} +33 -29
  80. package/dist/{hook-9kvrv1er.js → hook-pntw4r8h.js} +12 -10
  81. package/dist/{hook-fbfhkat3.js → hook-vwq8y26p.js} +13 -11
  82. package/dist/{http-xbyx8k5j.js → http-20jesakk.js} +357 -261
  83. package/dist/{index-jsw3aynb.js → index-6artrehg.js} +30 -26
  84. package/dist/{cli-pdz9qde4.js → index-9ch1n761.js} +10 -11
  85. package/dist/{index-rnyahfn5.js → index-9n5z2492.js} +30 -26
  86. package/dist/{index-66xz5e9j.js → index-h5gs261z.js} +521 -9
  87. package/dist/{index-tfsdxyv6.js → index-j5wrf1yw.js} +40 -25
  88. package/dist/{index-2sp0384z.js → index-kkeftv5h.js} +738 -21
  89. package/dist/{index-sgk50mbg.js → index-m2xqpz5r.js} +31 -27
  90. package/dist/{index-gpfn1741.js → index-m9ks8a7t.js} +9 -7
  91. package/dist/index-w097eg0x.js +5 -0
  92. package/dist/jiti-loader-b4837mtm.js +34 -0
  93. package/dist/jiti-static-loader-q2g5ds0y.js +31267 -0
  94. package/dist/{keepalive-s3xpr7sw.js → keepalive-pwxvxd2c.js} +9 -7
  95. package/dist/{lead-rq4n5r16.js → lead-m789wqa3.js} +46 -41
  96. package/dist/{maintenance-f0atj1ab.js → maintenance-bvg6w7e2.js} +10 -8
  97. package/dist/{mistral-conversations-qhr84ph3.js → mistral-conversations-zx0kn554.js} +24 -18
  98. package/dist/{node-x1g4v0pq.js → node-5qtycf6a.js} +16 -11
  99. package/dist/{oauth-refresh-sweep-q0t3bxre.js → oauth-refresh-sweep-6v4bd737.js} +8 -6
  100. package/dist/{onboard-br5j1kch.js → onboard-snckpcj1.js} +115 -35
  101. package/dist/{openai-codex-responses-wy6a43wd.js → openai-codex-responses-4vjqzvjp.js} +33 -27
  102. package/dist/{openai-completions-32rdwmze.js → openai-completions-pxsmym7g.js} +52 -64
  103. package/dist/{openai-responses-6xwf7bzg.js → openai-responses-tmn0rqp0.js} +23 -24
  104. package/dist/{opencode-adapter-28axqa09.js → opencode-adapter-qbmv4tpy.js} +6 -6
  105. package/dist/{otel-impl-vdqge2nt.js → otel-impl-e0ytens5.js} +4 -4
  106. package/dist/{pi-messages-b1mbr6gp.js → pi-messages-txpjj39q.js} +3 -3
  107. package/dist/pi-mono-adapter-c4ggemhz.js +1316 -0
  108. package/dist/{pricing-refresh-hb3691fd.js → pricing-refresh-52g2ywrw.js} +9 -7
  109. package/dist/{rbac-roles-t78xt39b.js → rbac-roles-5455kfdn.js} +8 -6
  110. package/dist/{rbac-roles-jh280k9g.js → rbac-roles-hbf0xa52.js} +9 -7
  111. package/dist/{render-v2-7p8r2ymg.js → render-v2-npm8xdzh.js} +16 -9
  112. package/dist/{seed-pricing-hf889enh.js → seed-pricing-650wbr1n.js} +8 -6
  113. package/dist/{setup-w6mb03jv.js → setup-k1mew5rf.js} +4 -5
  114. package/dist/virtual-modules-2q5b2pk4.js +54 -0
  115. package/dist/{work-objects-nt8gy1kj.js → work-objects-2ea3kvwg.js} +33 -29
  116. package/dist/{worker-dwy617t0.js → worker-ec6hvc4m.js} +46 -41
  117. package/openapi.json +109 -16
  118. package/package.json +7 -6
  119. package/plugin/opencode-plugins/lib/memory-raters.ts +7 -0
  120. package/src/be/automation-preflight.ts +6 -4
  121. package/src/be/db/agents.ts +75 -2
  122. package/src/be/db/tasks/read.ts +2 -0
  123. package/src/be/db/tasks/write.ts +7 -1
  124. package/src/be/db.ts +140 -18
  125. package/src/be/extensions/db.ts +35 -4
  126. package/src/be/memory/raters/llm.ts +2 -6
  127. package/src/be/memory/raters/noop.ts +2 -3
  128. package/src/be/memory/raters/registry.ts +4 -10
  129. package/src/be/migrations/157_defer_provenance_and_marker.sql +27 -0
  130. package/src/be/models-catalog.ts +2 -0
  131. package/src/be/modelsdev-cache.json +213 -0
  132. package/src/be/rbac-audit.ts +2 -0
  133. package/src/be/seed/agent-fs-provision.ts +23 -0
  134. package/src/be/seed-pricing.ts +3 -1
  135. package/src/be/swarm-config-guard.ts +1 -0
  136. package/src/commands/onboard/compose-generator.ts +16 -1
  137. package/src/commands/onboard/env-generator.ts +14 -1
  138. package/src/commands/onboard/manifest.ts +1 -0
  139. package/src/commands/onboard/steps/integration-slack.tsx +56 -16
  140. package/src/commands/onboard/steps/review.tsx +5 -0
  141. package/src/commands/onboard/types.ts +6 -0
  142. package/src/commands/onboard.tsx +5 -1
  143. package/src/commands/provider-credentials.ts +14 -2
  144. package/src/commands/runner.ts +18 -2
  145. package/src/extensions/identity.ts +15 -7
  146. package/src/extensions/lifecycle.ts +15 -21
  147. package/src/hooks/codex-hook.ts +7 -1
  148. package/src/http/agents.ts +12 -4
  149. package/src/http/config.ts +1 -1
  150. package/src/http/core.ts +5 -3
  151. package/src/http/extensions.ts +108 -34
  152. package/src/http/poll.ts +7 -0
  153. package/src/http/schedules.ts +10 -0
  154. package/src/http/script-runs.ts +35 -26
  155. package/src/http/status.ts +24 -11
  156. package/src/http/tasks.ts +6 -1
  157. package/src/prompts/base-prompt.ts +15 -7
  158. package/src/prompts/memories.ts +5 -7
  159. package/src/prompts/session-templates.ts +3 -2
  160. package/src/prompts/steering-delivery.ts +2 -0
  161. package/src/providers/claude-managed-models.ts +8 -0
  162. package/src/providers/codex-adapter.ts +3 -6
  163. package/src/providers/codex-models.ts +15 -1
  164. package/src/providers/dsh-adapter.ts +248 -0
  165. package/src/providers/index.ts +5 -1
  166. package/src/providers/modelsdev-reasoning.json +50 -0
  167. package/src/providers/pi-mono-extension.ts +2 -5
  168. package/src/rbac/legacy-policy.ts +15 -1
  169. package/src/rbac/types.ts +2 -0
  170. package/src/server.ts +7 -1
  171. package/src/slack/ack.ts +43 -8
  172. package/src/slack/app.ts +32 -10
  173. package/src/slack/config.ts +29 -0
  174. package/src/slack/handlers.ts +7 -1
  175. package/src/slack/render-v2.ts +186 -17
  176. package/src/slack/responses.ts +1 -3
  177. package/src/slack/task-output.ts +66 -8
  178. package/src/slack/thread-buffer.ts +27 -3
  179. package/src/telemetry.ts +2 -3
  180. package/src/tests/agent-fs-provision-seeder.test.ts +56 -2
  181. package/src/tests/agents-harness-provider.test.ts +19 -0
  182. package/src/tests/agents-list-model-display.test.ts +60 -1
  183. package/src/tests/base-prompt.test.ts +25 -0
  184. package/src/tests/claude-managed-adapter.test.ts +6 -0
  185. package/src/tests/codex-gpt6-release.test.ts +97 -0
  186. package/src/tests/codex-steering-hook.test.ts +2 -2
  187. package/src/tests/config-api-only-keys.test.ts +9 -0
  188. package/src/tests/context-window.test.ts +1 -0
  189. package/src/tests/db-task-read-contract.test.ts +9 -0
  190. package/src/tests/db-task-write-contract.test.ts +9 -0
  191. package/src/tests/defer-task.test.ts +115 -16
  192. package/src/tests/dsh-adapter.test.ts +276 -0
  193. package/src/tests/e2e-nightly-report.test.ts +3 -3
  194. package/src/tests/e2e-redact.test.ts +2 -0
  195. package/src/tests/env-flag.test.ts +6 -0
  196. package/src/tests/extension-agent-visibility.test.ts +474 -0
  197. package/src/tests/extensions-db.test.ts +41 -0
  198. package/src/tests/extensions-http.test.ts +148 -4
  199. package/src/tests/extensions-lifecycle.test.ts +21 -1
  200. package/src/tests/extensions-mcp-tools.test.ts +9 -5
  201. package/src/tests/fetch-resolved-env.test.ts +43 -0
  202. package/src/tests/internal-ai/complete-structured.test.ts +32 -9
  203. package/src/tests/internal-ai/credentials.test.ts +4 -4
  204. package/src/tests/internal-ai/models.test.ts +20 -0
  205. package/src/tests/memory-rate-tool.test.ts +7 -10
  206. package/src/tests/memory-rater-defaults.test.ts +23 -0
  207. package/src/tests/memory-rater-e2e.test.ts +2 -3
  208. package/src/tests/migration-046-budgets.test.ts +2 -0
  209. package/src/tests/model-control.test.ts +2 -2
  210. package/src/tests/onboard-compose.test.ts +14 -0
  211. package/src/tests/onboard-env.test.ts +17 -0
  212. package/src/tests/onboard-manifest.test.ts +12 -0
  213. package/src/tests/opus-5-5-registry.test.ts +48 -0
  214. package/src/tests/pricing-refresh.test.ts +1 -1
  215. package/src/tests/pricing-routes.test.ts +15 -3
  216. package/src/tests/provider-registration.test.ts +5 -2
  217. package/src/tests/rbac-admission-e2e.test.ts +54 -0
  218. package/src/tests/rbac-e2e-helpers.ts +2 -0
  219. package/src/tests/rbac-engine.test.ts +32 -0
  220. package/src/tests/rbac-wire-e2e.test.ts +33 -0
  221. package/src/tests/reasoning-effort.test.ts +8 -0
  222. package/src/tests/reload-config.test.ts +33 -0
  223. package/src/tests/script-connections.test.ts +3 -0
  224. package/src/tests/session-costs-model-key-normalize.test.ts +1 -0
  225. package/src/tests/slack-config.test.ts +136 -0
  226. package/src/tests/slack-mode-lifecycle.test.ts +69 -0
  227. package/src/tests/slack-render-v2.test.ts +446 -16
  228. package/src/tests/slack-steering.test.ts +69 -1
  229. package/src/tests/slack-task-output.test.ts +33 -12
  230. package/src/tests/status.test.ts +22 -2
  231. package/src/tests/steering-core.test.ts +35 -0
  232. package/src/tests/steering-delivery.test.ts +39 -0
  233. package/src/tests/steering-transport.test.ts +2 -2
  234. package/src/tests/task-tool-preload-mcp.test.ts +3 -1
  235. package/src/tests/telemetry-init.test.ts +18 -0
  236. package/src/tests/workflow-live-recovery.test.ts +126 -0
  237. package/src/tests/workflow-llm.test.ts +3 -3
  238. package/src/tools/defer-task.ts +64 -26
  239. package/src/tools/extension-common.ts +4 -0
  240. package/src/tools/extension-install.ts +1 -1
  241. package/src/tools/extension-list.ts +4 -3
  242. package/src/tools/join-swarm.ts +1 -1
  243. package/src/tools/schedules/create-schedule.ts +11 -1
  244. package/src/tools/schedules/patch-schedule.ts +5 -0
  245. package/src/tools/schedules/update-schedule.ts +5 -0
  246. package/src/tools/send-task.ts +6 -0
  247. package/src/tools/utils.ts +1 -1
  248. package/src/types.ts +30 -8
  249. package/src/utils/context-window.ts +1 -0
  250. package/src/utils/credentials.ts +8 -0
  251. package/src/utils/internal-ai/complete-structured.ts +8 -0
  252. package/src/utils/internal-ai/models.ts +5 -5
  253. package/src/utils/memory-raters.ts +3 -0
  254. package/src/workflows/engine.ts +28 -0
  255. package/src/workflows/recovery.ts +7 -4
  256. package/templates/schedules/daily-status-report/content.md +25 -12
  257. package/templates/schedules/daily-workflow-health-audit/content.md +1 -1
  258. package/templates/skills/swarm-extensions/content.md +9 -5
  259. package/dist/cli-4wppfkcm.js +0 -16384
  260. package/dist/cli-55qrx3q8.js +0 -6041
  261. package/dist/cli-cg4vnyh4.js +0 -305
  262. package/dist/cli-jyetr64j.js +0 -52
  263. package/dist/cli-pp9ax9pa.js +0 -520
  264. package/dist/cli-qa66qaf0.js +0 -6076
  265. package/dist/cli-qghpvca2.js +0 -590
  266. package/dist/cli-rembwgt8.js +0 -861
  267. package/dist/cli-tcvj7j2r.js +0 -1971
  268. package/dist/cli-vfcth11k.js +0 -36
  269. package/dist/cli-w1d1bgkk.js +0 -1422
  270. package/dist/cli-y6x0gqtg.js +0 -5977
  271. package/dist/es5-tj3tn4jj.js +0 -844
  272. package/dist/index-1kq5hw2p.js +0 -467
  273. package/dist/index-1p8wdstt.js +0 -7
  274. package/dist/index-8fdjzb2f.js +0 -100
  275. package/dist/index-bxmwzbgw.js +0 -365
  276. package/dist/index-nzerxd5z.js +0 -7
  277. package/dist/index-pyymnzat.js +0 -1168
  278. package/dist/index-qv6atbab.js +0 -619
  279. package/dist/index-rja32qa7.js +0 -801
  280. package/dist/index-tzg5srfp.js +0 -211
  281. package/dist/index-vhncw983.js +0 -90
  282. package/dist/index-xk036zsk.js +0 -859
  283. package/dist/index-zyazbzcb.js +0 -6
  284. package/dist/{claude-managed-setup-48dwg9bv.js → claude-managed-setup-f5pmsyrp.js} +3 -3
@@ -1,6 +1,22 @@
1
1
  import type { AgentTask } from "../types";
2
2
 
3
- /** Keep the stored deferral ETA for task details, but omit it from Slack. */
3
+ /**
4
+ * Legacy deferral output, written before `defer-task` started storing the
5
+ * human-facing card directly:
6
+ * `Deferred until {date} {time} ([shortId](url)) -> {note excerpt}`
7
+ * Group 1 is the ETA; the schedule link and the note excerpt are dropped.
8
+ */
9
+ const LEGACY_DEFERRAL_NOTICE = /^Deferred until ([^\n(]+?) \([^\n]*?\) -> .*$/;
10
+
11
+ /**
12
+ * The Slack-facing form of a task's stored output.
13
+ *
14
+ * For a deferral this is the stored text verbatim — `defer-task` already
15
+ * writes the human card (`Checking back today at 18:38`, or
16
+ * `Waiting on Researcher — or today at 18:38 at the latest`). Rows written
17
+ * before that change carried the agent's internal handoff note plus a raw
18
+ * schedule UUID link instead, so they are rewritten to the ETA alone.
19
+ */
4
20
  export function slackTaskOutput(
5
21
  task: Pick<AgentTask, "output" | "tags" | "outputSchema" | "status">,
6
22
  ): string | undefined {
@@ -9,13 +25,55 @@ export function slackTaskOutput(
9
25
  return output;
10
26
  }
11
27
  // Only match the engine-authored line from defer-task, not continuation results.
12
- const notice = output?.match(/^Deferred until [^\n]+? \([^\n]+?\) -> (.*)$/);
13
- if (!notice) return output;
14
- const pending = notice[1]?.trim();
15
- return pending ? `Pending: ${pending}` : "";
28
+ const legacy = output?.match(LEGACY_DEFERRAL_NOTICE);
29
+ if (!legacy) return output;
30
+ const eta = legacy[1]?.trim();
31
+ return eta ? `Checking back ${eta}` : output;
32
+ }
33
+
34
+ /**
35
+ * A task that ENDED in a deferral. It is stored `completed` — `defer-task`
36
+ * ends the task and books a wake-up — but to a human reading the thread the
37
+ * work is still outstanding, so every glyph and card treats it as waiting.
38
+ *
39
+ * `deferredAt` is the exact signal. The `deferred` tag is not: `defer-task`
40
+ * puts it on both the deferring task and the wake-up schedule, so a wake-up
41
+ * child that simply finished is tag-identical to one that deferred again.
42
+ * The tag is still consulted for rows written before `deferredAt` existed,
43
+ * narrowed to tasks the scheduler did not create (`taskType !== "deferred"`),
44
+ * where it is unambiguous.
45
+ */
46
+ export function isDeferredTask(
47
+ task: Pick<AgentTask, "tags" | "status" | "deferredAt" | "taskType">,
48
+ ): boolean {
49
+ if (task.status !== "completed") return false;
50
+ if (task.deferredAt) return true;
51
+ return !!task.tags?.includes("deferred") && task.taskType !== "deferred";
52
+ }
53
+
54
+ /** The wake-up tasks `defer-task` booked to continue `task`. */
55
+ export function deferralWakes<T extends Pick<AgentTask, "parentTaskId" | "taskType">>(
56
+ task: Pick<AgentTask, "id">,
57
+ threadTasks: readonly T[],
58
+ ): T[] {
59
+ return threadTasks.filter(
60
+ (candidate) => candidate.parentTaskId === task.id && candidate.taskType === "deferred",
61
+ );
16
62
  }
17
63
 
18
- /** An ETA-only notice has no useful content left to post. */
19
- export function isSlackEtaOnlyNotice(task: AgentTask): boolean {
20
- return !!task.output && slackTaskOutput(task) === "";
64
+ /**
65
+ * A deferred task that is still parked: none of its wake-ups has settled.
66
+ * Once one has, the deferral is over — the answer (or the next deferral)
67
+ * lives on the wake-up, and the deferring task reads as the `completed` it
68
+ * is stored as.
69
+ */
70
+ export function isAwaitingWake(
71
+ task: Pick<AgentTask, "id" | "tags" | "status" | "deferredAt" | "taskType">,
72
+ threadTasks: readonly Pick<AgentTask, "parentTaskId" | "taskType" | "status">[],
73
+ ): boolean {
74
+ if (!isDeferredTask(task)) return false;
75
+ return !deferralWakes(task, threadTasks).some(
76
+ (wake) =>
77
+ wake.status === "completed" || wake.status === "failed" || wake.status === "cancelled",
78
+ );
21
79
  }
@@ -4,11 +4,12 @@ import {
4
4
  getLeadAgent,
5
5
  getMostRecentTaskInThread,
6
6
  } from "../be/db";
7
+ import { findUserByExternalId } from "../be/users";
7
8
  import { createAdditiveBuffer } from "../tasks/additive-buffer";
8
9
  import { slackContextKey } from "../tasks/context-key";
9
10
  import { getSlackApp } from "./app";
10
11
  import { buildBufferFlushBlocks } from "./blocks";
11
- import { rewriteSlackMentions } from "./enrich";
12
+ import { resolveSlackUserId, rewriteSlackMentions } from "./enrich";
12
13
  import type { SlackFile } from "./files";
13
14
  import {
14
15
  bufferedFileFailures,
@@ -204,6 +205,18 @@ async function slackFlush(
204
205
  );
205
206
  }
206
207
 
208
+ const requestedByUserId = app
209
+ ? await resolveSlackUserId(app.client, originalRequesterId, {
210
+ sampleEventType: "buffered_thread_reply",
211
+ sampleContext: items[0]!.text,
212
+ })
213
+ : (await findUserByExternalId("slack", originalRequesterId))?.id;
214
+
215
+ // A steering row has one sender. Keep consecutive messages from that sender
216
+ // together, then flush the remaining senders in order after successful delivery.
217
+ const nextSenderIndex = items.findIndex((item) => item.userId !== originalRequesterId);
218
+ const steeringItems = nextSenderIndex === -1 ? items : items.slice(0, nextSenderIndex);
219
+
207
220
  // Steering carries text only. As on the mention path, files need a follow-up
208
221
  // task so its attachments exist before a worker can claim it.
209
222
  const steering =
@@ -211,8 +224,15 @@ async function slackFlush(
211
224
  ? await requestSlackThreadSteering({
212
225
  channelId,
213
226
  threadTs,
214
- message: combinedText,
215
- messageTimestamps: items.map((item) => item.ts),
227
+ message:
228
+ nextSenderIndex === -1
229
+ ? combinedText
230
+ : await rewriteSlackMentions(
231
+ steeringItems.map((item) => item.text).join("\n---\n"),
232
+ botUserId,
233
+ ),
234
+ messageTimestamps: steeringItems.map((item) => item.ts),
235
+ requestedByUserId,
216
236
  })
217
237
  : null;
218
238
  if (steering) {
@@ -239,6 +259,9 @@ async function slackFlush(
239
259
  }
240
260
  }
241
261
  }
262
+ if (nextSenderIndex !== -1) {
263
+ await slackFlush(items.slice(nextSenderIndex), key, immediate);
264
+ }
242
265
  return;
243
266
  }
244
267
 
@@ -267,6 +290,7 @@ async function slackFlush(
267
290
  slackThreadTs: threadTs,
268
291
  slackTriggerMessageTs: items.at(-1)!.ts,
269
292
  slackUserId: originalRequesterId,
293
+ requestedByUserId,
270
294
  dependsOn,
271
295
  parentTaskId: mostRecentTask?.id,
272
296
  contextKey: slackContextKey({ channelId, threadTs }),
package/src/telemetry.ts CHANGED
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { randomUUID } from "node:crypto";
10
10
  import pkg from "../package.json";
11
+ import { isSlackConfigured } from "./slack/config";
11
12
  import { isEnvFlagEnabled } from "./utils/env-flag";
12
13
 
13
14
  const TELEMETRY_ENDPOINT = "https://proxy.desplega.sh/v1/events";
@@ -269,9 +270,7 @@ export function _hasEmbeddingKey(env: NodeJS.ProcessEnv = process.env): boolean
269
270
  * Exported for tests; not part of the public API.
270
271
  */
271
272
  export function _hasSlackChannel(env: NodeJS.ProcessEnv = process.env): boolean {
272
- return (
273
- !!(env.SLACK_BOT_TOKEN && env.SLACK_APP_TOKEN) && !isEnvFlagEnabled("SLACK_DISABLE", false, env)
274
- );
273
+ return isSlackConfigured(env);
275
274
  }
276
275
 
277
276
  /**
@@ -1,4 +1,4 @@
1
- import { afterAll, afterEach, beforeAll, describe, expect, test } from "bun:test";
1
+ import { afterAll, afterEach, beforeAll, describe, expect, spyOn, test } from "bun:test";
2
2
  import { unlink } from "node:fs/promises";
3
3
  import {
4
4
  closeDb,
@@ -110,6 +110,7 @@ function createFetchStub(
110
110
  orgId: "personal-org",
111
111
  });
112
112
  }
113
+ if (url.pathname === "/auth/profile" && method === "PATCH") return Response.json(body);
113
114
  if (url.pathname === "/auth/me" && method === "GET") {
114
115
  return Response.json({
115
116
  userId: "admin-user",
@@ -502,11 +503,64 @@ describe("agent-fs provisioning seeder", () => {
502
503
  role: "editor",
503
504
  });
504
505
 
506
+ expect(records.find((r) => r.path === "/auth/profile")).toMatchObject({
507
+ method: "PATCH",
508
+ body: { displayName: worker.name },
509
+ authorization: "Bearer example-afs-agent-key",
510
+ hasSignal: true,
511
+ });
505
512
  records.length = 0;
506
513
  const second = await ensureAgentFsCredentialsForAgent(worker.id);
507
514
 
508
515
  expect(second.created).toBe(false);
509
- expect(records).toEqual([]);
516
+ expect(records).toEqual([
517
+ {
518
+ method: "PATCH",
519
+ path: "/auth/profile",
520
+ body: { displayName: "Credential Worker" },
521
+ authorization: "Bearer example-afs-agent-key",
522
+ hasSignal: true,
523
+ },
524
+ ]);
525
+ });
526
+
527
+ test("profile sync failures leave existing credentials usable and retry later", async () => {
528
+ process.env.AGENT_FS_API_URL = "https://agent-fs.example.test";
529
+ const worker = await createAgent({
530
+ name: "Legacy Worker",
531
+ description: "Profile compatibility",
532
+ role: "worker",
533
+ isLead: false,
534
+ status: "idle",
535
+ maxTasks: 1,
536
+ capabilities: [],
537
+ });
538
+ await upsertSwarmConfig({
539
+ scope: "agent",
540
+ scopeId: worker.id,
541
+ key: "AGENT_FS_API_KEY",
542
+ value: "example-profile-key",
543
+ isSecret: true,
544
+ });
545
+ const warn = spyOn(console, "warn").mockImplementation(() => {});
546
+ try {
547
+ for (const status of [404, 405, 500, 0]) {
548
+ setAgentFsProvisionFetchForTests((async (_input, init) => {
549
+ expect(init?.signal).toBeInstanceOf(AbortSignal);
550
+ if (!status) throw new DOMException("Timed out", "TimeoutError");
551
+ return Response.json({ error: "unsupported" }, { status });
552
+ }) as typeof fetch);
553
+ expect((await ensureAgentFsCredentialsForAgent(worker.id)).created).toBe(false);
554
+ }
555
+ expect(warn).toHaveBeenCalledTimes(4);
556
+ const records: RequestRecord[] = [];
557
+ setAgentFsProvisionFetchForTests(createFetchStub(records));
558
+ await ensureAgentFsCredentialsForAgent(worker.id);
559
+ expect(records[0].body).toEqual({ displayName: "Legacy Worker" });
560
+ expect(records[0].authorization).toBe("Bearer example-profile-key");
561
+ } finally {
562
+ warn.mockRestore();
563
+ }
510
564
  });
511
565
 
512
566
  test("recovers a lost agent credential by registering an alias after a 409", async () => {
@@ -333,6 +333,25 @@ describe("PATCH /api/agents/:id/harness-provider", () => {
333
333
  });
334
334
 
335
335
  describe("PATCH /api/agents/:id/runtime", () => {
336
+ test("accepts dsh and persists its harness and model for the worker", async () => {
337
+ const agent = await createAgent({
338
+ name: "dsh-worker",
339
+ isLead: false,
340
+ status: "idle",
341
+ capabilities: [],
342
+ });
343
+ const res = await fetch(`${baseUrl}/api/agents/${agent.id}/runtime`, {
344
+ method: "PATCH",
345
+ headers: { "Content-Type": "application/json" },
346
+ body: JSON.stringify({ harness_provider: "dsh", model: "deepseek-flash" }),
347
+ });
348
+ expect(res.status).toBe(200);
349
+ expect((await getAgentById(agent.id))?.harnessProvider).toBe("dsh");
350
+ const rows = await getSwarmConfigs({ scope: "agent", scopeId: agent.id });
351
+ expect(rows.find((row) => row.key === "HARNESS_PROVIDER")?.value).toBe("dsh");
352
+ expect(rows.find((row) => row.key === "MODEL_OVERRIDE")?.value).toBe("deepseek-flash");
353
+ });
354
+
336
355
  test("updates harness_provider and agent-scoped runtime config rows", async () => {
337
356
  const a = await createAgent({
338
357
  name: "runtime-target-1",
@@ -1,9 +1,38 @@
1
1
  import { describe, expect, test } from "bun:test";
2
+ import type { LiveModelsCatalog } from "../../apps/ui/src/lib/agent-runtime-models";
2
3
  import {
3
4
  getAgentModelDisplay,
4
5
  getAgentModelPresentation,
5
6
  } from "../../apps/ui/src/lib/agents-list-model-display";
6
7
 
8
+ const liveCatalog: LiveModelsCatalog = {
9
+ openrouter: {
10
+ id: "openrouter",
11
+ name: "OpenRouter",
12
+ models: {
13
+ "deepseek/deepseek-v4-pro": { id: "deepseek/deepseek-v4-pro", name: "DeepSeek V4 Pro" },
14
+ "deepseek/deepseek-v4.1-flash": {
15
+ id: "deepseek/deepseek-v4.1-flash",
16
+ name: "DeepSeek V4.1 Flash",
17
+ },
18
+ },
19
+ },
20
+ opencode: {
21
+ id: "opencode",
22
+ name: "OpenCode Zen",
23
+ models: {
24
+ "glm-5.3-flash": { id: "glm-5.3-flash", name: "GLM-5.3-Flash" },
25
+ },
26
+ },
27
+ anthropic: {
28
+ id: "anthropic",
29
+ name: "Anthropic",
30
+ models: {
31
+ "claude-opus-5-5": { id: "claude-opus-5-5", name: "Claude Opus 5.5" },
32
+ },
33
+ },
34
+ };
35
+
7
36
  describe("agents list model display", () => {
8
37
  test("shows configured and last-used models when they diverge", () => {
9
38
  const display = getAgentModelDisplay("claude-opus-4-7", "claude-sonnet-4-6");
@@ -43,6 +72,31 @@ describe("agents list model display", () => {
43
72
  });
44
73
  });
45
74
 
75
+ test.each([
76
+ ["openrouter/deepseek/deepseek-v4-pro", "DeepSeek V4 Pro", "OpenRouter"],
77
+ ["openrouter/deepseek/deepseek-v4.1-flash", "DeepSeek V4.1 Flash", "OpenRouter"],
78
+ ["opencode/glm-5.3-flash", "GLM-5.3-Flash", "OpenCode Zen"],
79
+ ["claude-opus-5-5", "Claude Opus 5.5", "Anthropic"],
80
+ ])("resolves %s from the live catalog", (model, label, provider) => {
81
+ expect(getAgentModelPresentation(model, liveCatalog)).toMatchObject({
82
+ raw: model,
83
+ label,
84
+ provider,
85
+ });
86
+ });
87
+
88
+ test("title-cases an uncatalogued model id without a maintained label entry", () => {
89
+ expect(
90
+ getAgentModelPresentation("opencode/future-model-v8.2-preview", liveCatalog),
91
+ ).toMatchObject({
92
+ label: "Future Model V8.2 Preview",
93
+ provider: null,
94
+ });
95
+ expect(
96
+ getAgentModelPresentation("openrouter/deepseek/deepseek-v5.1-flash", liveCatalog)?.label,
97
+ ).toBe("Deepseek V5.1 Flash");
98
+ });
99
+
46
100
  test("presents latest Anthropic direct model ids as readable labels", () => {
47
101
  expect(getAgentModelPresentation("claude-fable-5-1")).toMatchObject({
48
102
  label: "Claude Fable 5.1",
@@ -54,6 +108,11 @@ describe("agents list model display", () => {
54
108
  provider: "Anthropic",
55
109
  providerId: "anthropic",
56
110
  });
111
+ expect(getAgentModelPresentation("claude-opus-5-5")).toMatchObject({
112
+ label: "Claude Opus 5.5",
113
+ provider: "Anthropic",
114
+ providerId: "anthropic",
115
+ });
57
116
  expect(getAgentModelPresentation("claude-opus-5")).toMatchObject({
58
117
  label: "Claude Opus 5",
59
118
  provider: "Anthropic",
@@ -75,7 +134,7 @@ describe("agents list model display", () => {
75
134
  providerId: "anthropic",
76
135
  });
77
136
  expect(getAgentModelPresentation("opus")).toMatchObject({
78
- label: "Claude Opus 5",
137
+ label: "Claude Opus 5.5",
79
138
  provider: "Anthropic",
80
139
  providerId: "anthropic",
81
140
  });
@@ -25,6 +25,8 @@ const ENV_KEYS = [
25
25
  "SLACK_DISABLE",
26
26
  "SLACK_BOT_TOKEN",
27
27
  "SLACK_APP_TOKEN",
28
+ "SLACK_MODE",
29
+ "SLACK_SIGNING_SECRET",
28
30
  "STEERING_ENABLED",
29
31
  "AGENT_FS_API_URL",
30
32
  "SCRIPTS_ONLY_MCP",
@@ -561,6 +563,29 @@ describe("getBasePrompt: slack section", () => {
561
563
  expect(result).toContain(SLACK_HEADER);
562
564
  });
563
565
 
566
+ test("a tool capability alone does not bypass the default socket credential gate", async () => {
567
+ const result = await getBasePrompt({
568
+ ...minimalArgs,
569
+ serverCapabilities: ["core", "slack"],
570
+ });
571
+ expect(result).not.toContain(SLACK_HEADER);
572
+ });
573
+
574
+ test("supports the HTTP prompt contract without distributing Slack secrets to the worker", async () => {
575
+ process.env.SLACK_MODE = "http";
576
+ const result = await getBasePrompt({
577
+ ...minimalArgs,
578
+ serverCapabilities: ["core", "slack"],
579
+ });
580
+ expect(result).toContain(SLACK_HEADER);
581
+
582
+ const withoutCapability = await getBasePrompt({
583
+ ...minimalArgs,
584
+ serverCapabilities: ["core"],
585
+ });
586
+ expect(withoutCapability).not.toContain(SLACK_HEADER);
587
+ });
588
+
564
589
  test("a scripts-only worker with a Slack task gets the scripts-only variant only", async () => {
565
590
  enableSlack();
566
591
  const result = await getBasePrompt({
@@ -898,6 +898,12 @@ describe("ClaudeManagedAdapter (Phase 4) — repo provisioning + cost data", ()
898
898
  cacheReadPerMillion: 0.25,
899
899
  cacheWritePerMillion: 12.5,
900
900
  });
901
+ expect(CLAUDE_MANAGED_MODEL_PRICING["claude-opus-5-5"]).toEqual({
902
+ inputPerMillion: 4.0,
903
+ outputPerMillion: 20.0,
904
+ cacheReadPerMillion: 0.2,
905
+ cacheWritePerMillion: 5.0,
906
+ });
901
907
  expect(CLAUDE_MANAGED_MODEL_PRICING["claude-opus-5"]).toEqual({
902
908
  inputPerMillion: 5.0,
903
909
  outputPerMillion: 25.0,
@@ -0,0 +1,97 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { modelGroupsForHarness } from "../../apps/ui/src/lib/agent-runtime-models";
3
+ import { buildModelsCatalog } from "../be/models-catalog";
4
+ import cache from "../be/modelsdev-cache.json";
5
+ import { buildModelsDevSeedRows } from "../be/seed-pricing";
6
+ import {
7
+ CODEX_DEFAULT_MODEL,
8
+ CODEX_MODELS,
9
+ computeCodexCostUsd,
10
+ FALLBACK_CODEX_MODEL_PRICING,
11
+ getCodexContextWindow,
12
+ resolveCodexModel,
13
+ } from "../providers/codex-models";
14
+ import { applyReasoningEffort, reasoningCapability } from "../providers/reasoning-effort";
15
+
16
+ const releases = [
17
+ { id: "gpt-6-sol", label: "GPT-6 Sol", input: 2, output: 10, read: 0.2, write: 2.5 },
18
+ { id: "gpt-6-luna", label: "GPT-6 Luna", input: 0.1, output: 0.5, read: 0.01, write: 0.125 },
19
+ ] as const;
20
+
21
+ for (const release of releases) {
22
+ describe(release.id, () => {
23
+ test("uses the exact bare CLI ID with verified context and base costs", () => {
24
+ expect(CODEX_MODELS).toContain(release.id);
25
+ expect(resolveCodexModel(release.id.toUpperCase())).toBe(release.id);
26
+ expect(getCodexContextWindow(release.id)).toBe(1_050_000);
27
+ expect(cache.openai.models[release.id].limit.output).toBe(128_000);
28
+ expect(FALLBACK_CODEX_MODEL_PRICING[release.id]).toEqual({
29
+ inputPerMillion: release.input,
30
+ cachedInputPerMillion: release.read,
31
+ outputPerMillion: release.output,
32
+ });
33
+ expect(computeCodexCostUsd(release.id, 1_000_000, 250_000, 100_000)).toBeCloseTo(
34
+ release.input * 0.75 + release.read * 0.25 + release.output * 0.1,
35
+ 8,
36
+ );
37
+ });
38
+
39
+ test("exposes the full reasoning range and transports none/max correctly", () => {
40
+ expect(reasoningCapability("codex", release.id)).toEqual({
41
+ supported: true,
42
+ levels: ["off", "low", "medium", "high", "xhigh", "max"],
43
+ default: "medium",
44
+ });
45
+ for (const [effort, value] of [
46
+ ["off", "none"],
47
+ ["max", "max"],
48
+ ] as const) {
49
+ expect(applyReasoningEffort("codex", release.id, effort)).toEqual({
50
+ kind: "codex-config",
51
+ config: { model_reasoning_effort: value },
52
+ });
53
+ }
54
+ });
55
+
56
+ test("seeds all four token classes under canonical Codex IDs", () => {
57
+ const rows = buildModelsDevSeedRows(cache).filter(
58
+ (row) => row.provider === "codex" && row.model === release.id,
59
+ );
60
+ expect(
61
+ Object.fromEntries(rows.map((row) => [row.tokenClass, row.pricePerMillionUsd])),
62
+ ).toEqual({
63
+ input: release.input,
64
+ output: release.output,
65
+ cached_input: release.read,
66
+ cache_write: release.write,
67
+ });
68
+ expect(cache.openai.models[release.id].cost.tiers).toEqual([
69
+ {
70
+ input: release.input * 2,
71
+ output: release.output * 1.5,
72
+ cache_read: release.read * 2,
73
+ cache_write: release.write * 2,
74
+ tier: { type: "context", size: 272000 },
75
+ },
76
+ ]);
77
+ });
78
+
79
+ test("is available through the catalog and direct UI selector", () => {
80
+ const model = buildModelsCatalog(cache).openai?.models[release.id];
81
+ expect(model?.limit?.context).toBe(1_050_000);
82
+ expect(model?.cost).toEqual({ input: release.input, output: release.output });
83
+ const option = modelGroupsForHarness("codex", undefined, undefined)
84
+ .flatMap((group) => group.models)
85
+ .find((entry) => entry.id === release.id);
86
+ expect(option?.label).toBe(release.label);
87
+ expect(option?.reasoningLevels).toEqual(["off", "low", "medium", "high", "xhigh", "max"]);
88
+ });
89
+ });
90
+ }
91
+
92
+ test("release additions preserve rollout-safe defaults and aliases", () => {
93
+ expect(CODEX_DEFAULT_MODEL).toBe("gpt-5.6-terra");
94
+ expect(resolveCodexModel("sonnet")).toBe("gpt-5.6-terra");
95
+ expect(resolveCodexModel("haiku")).toBe("gpt-5.6-luna");
96
+ expect(resolveCodexModel("opus")).toBe("gpt-5.6-sol");
97
+ });
@@ -101,7 +101,7 @@ describe("codex steering hook", () => {
101
101
  );
102
102
 
103
103
  test("PostToolUse injects the envelope and marks the row delivered first", async () => {
104
- const pending = message();
104
+ const pending = message({ createdByKind: "user", senderLabel: "Taras (user)" });
105
105
  const deliveredCalls: string[] = [];
106
106
  const output = await handleCodexHookEvent(
107
107
  { hook_event_name: "PostToolUse" },
@@ -116,7 +116,7 @@ describe("codex steering hook", () => {
116
116
  additionalContext: string;
117
117
  };
118
118
  expect(hookOutput.hookEventName).toBe("PostToolUse");
119
- expect(hookOutput.additionalContext).toContain(pending.body);
119
+ expect(hookOutput.additionalContext).toContain(`From Taras (user): ${pending.body}`);
120
120
  expect(hookOutput.additionalContext).toContain(`[steering ${pending.id}]`);
121
121
  expect(hookOutput.additionalContext).toContain("accept-steer");
122
122
  });
@@ -50,6 +50,12 @@ beforeAll(async () => {
50
50
  value: BOOTSTRAP_VALUE,
51
51
  isSecret: true,
52
52
  });
53
+ await upsertSwarmConfig({
54
+ scope: "global",
55
+ key: "SLACK_SIGNING_SECRET",
56
+ value: "synthetic-api-only-signing-secret",
57
+ isSecret: true,
58
+ });
53
59
  await upsertSwarmConfig({
54
60
  scope: "global",
55
61
  key: "SOME_WORKER_SECRET",
@@ -86,6 +92,7 @@ describe("API-only config keys are never served over HTTP", () => {
86
92
  test("GET /api/config/resolved WITHOUT agentId strips the bootstrap key", async () => {
87
93
  const { configs } = await getJson("/api/config/resolved?includeSecrets=true");
88
94
  expect(configs.some((c) => c.key === BOOTSTRAP_KEY)).toBe(false);
95
+ expect(configs.some((c) => c.key === "SLACK_SIGNING_SECRET")).toBe(false);
89
96
  // A legitimate worker secret still comes through.
90
97
  expect(configs.some((c) => c.key === "SOME_WORKER_SECRET")).toBe(true);
91
98
  });
@@ -95,11 +102,13 @@ describe("API-only config keys are never served over HTTP", () => {
95
102
  `/api/config/resolved?includeSecrets=true&agentId=${agentId}`,
96
103
  );
97
104
  expect(configs.some((c) => c.key === BOOTSTRAP_KEY)).toBe(false);
105
+ expect(configs.some((c) => c.key === "SLACK_SIGNING_SECRET")).toBe(false);
98
106
  });
99
107
 
100
108
  test("GET /api/config?scope=global strips the bootstrap key", async () => {
101
109
  const { configs } = await getJson("/api/config?scope=global&includeSecrets=true");
102
110
  expect(configs.some((c) => c.key === BOOTSTRAP_KEY)).toBe(false);
111
+ expect(configs.some((c) => c.key === "SLACK_SIGNING_SECRET")).toBe(false);
103
112
  expect(configs.some((c) => c.key === "SOME_WORKER_SECRET")).toBe(true);
104
113
  });
105
114
  });
@@ -18,6 +18,7 @@ describe("getContextWindowSize", () => {
18
18
  });
19
19
 
20
20
  test("returns 1M for opus models", () => {
21
+ expect(getContextWindowSize("claude-opus-5-5")).toBe(1_000_000);
21
22
  expect(getContextWindowSize("claude-opus-5")).toBe(1_000_000);
22
23
  expect(getContextWindowSize("claude-opus-4-8")).toBe(1_000_000);
23
24
  expect(getContextWindowSize("claude-opus-4-7")).toBe(1_000_000);
@@ -94,8 +94,12 @@ type:UpsertAssetKeyMappingInput
94
94
  type:WorkflowFilters
95
95
  type:WorkflowRunListOptions
96
96
  type:WorkflowRunPage
97
+ value:EXTENSION_AGENT_ROLE
98
+ value:ExtensionAgentAssignmentError
97
99
  value:KvTypeCollisionError
98
100
  value:MAX_EMPTY_POLLS
101
+ value:NOT_EXTENSION_AGENT_SQL
102
+ value:ReservedAgentRoleError
99
103
  value:SKILL_FILE_LIMITS
100
104
  value:UNATTRIBUTED_USER_ID
101
105
  value:__resetSqliteVecExtensionPathCacheForTests
@@ -195,6 +199,7 @@ value:emitBuiltInIntegrationConnectedOnce
195
199
  value:emitMcpServerConnectedTelemetry
196
200
  value:ensureSlackDelegationActivation
197
201
  value:ensureSlackRenderV2Activation
202
+ value:extensionAgentAssignmentError
198
203
  value:failPendingResumeIfUnclaimed
199
204
  value:failTask
200
205
  value:findCompletedTaskInThread
@@ -326,6 +331,7 @@ value:getRecentFailedTasks
326
331
  value:getRecentlyCancelledTasksForAgent
327
332
  value:getRecentlyFinishedWorkerTasks
328
333
  value:getRemainingCapacity
334
+ value:getResolvableDeferralOutcomes
329
335
  value:getResolvedConfig
330
336
  value:getRetryableSteps
331
337
  value:getRootTaskChain
@@ -418,6 +424,7 @@ value:installMcpServer
418
424
  value:installSkill
419
425
  value:installSystemDefaultSkillsForAgent
420
426
  value:isAgentEligibleForTask
427
+ value:isExtensionAgent
421
428
  value:isPendingSlackMessage
422
429
  value:isPoolAffinityEnforcementEnabled
423
430
  value:isSqliteVecAvailable
@@ -449,6 +456,7 @@ value:markInboxMessageDelegated
449
456
  value:markInboxMessageRead
450
457
  value:markInboxMessageResponded
451
458
  value:markKeyRateLimited
459
+ value:markSlackDeferralResolved
452
460
  value:markSlackRelayAttempted
453
461
  value:markSlackRelayDelivered
454
462
  value:markSlackTreeRendered
@@ -485,6 +493,7 @@ value:releaseStaleReviewingTasks
485
493
  value:releaseTask
486
494
  value:replaceTaskAttachment
487
495
  value:reserveSlackMessage
496
+ value:reservedRoleViolation
488
497
  value:resetEmptyPollCount
489
498
  value:resetOrphanedInProgressTasksForAgent
490
499
  value:resetPromptTemplateToDefault