@desplega.ai/agent-swarm 1.158.0 → 1.160.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 (378) hide show
  1. package/README.md +1 -1
  2. package/dist/{acp-adapter-xaq4n6pc.js → acp-adapter-2mjwcsfh.js} +124 -27
  3. package/dist/{actions-p9hz5yat.js → actions-e43rxskr.js} +35 -30
  4. package/dist/{anthropic-messages-51pz26qe.js → anthropic-messages-5qx80j9k.js} +106 -16
  5. package/dist/{app-g26wdcz6.js → app-mc7xfagn.js} +12 -10
  6. package/dist/{assistant-wydfqkb3.js → assistant-sedkzv3b.js} +42 -31
  7. package/dist/{azure-openai-responses-8gksqtmz.js → azure-openai-responses-nfb21z9a.js} +13 -14
  8. package/dist/{boot-reembed-se8zmdcp.js → boot-reembed-cqz9swn5.js} +13 -12
  9. package/dist/{boot-reembed-g5ckg9yb.js → boot-reembed-m3656j2n.js} +14 -13
  10. package/dist/{boot-scrub-logs-wfxqan61.js → boot-scrub-logs-xbxf5d7w.js} +11 -9
  11. package/dist/boot-seeds-gsetbs5y.js +28 -0
  12. package/dist/{claude-adapter-tv2x4f36.js → claude-adapter-dc6289mc.js} +28 -23
  13. package/dist/{claude-managed-adapter-41b2s7fd.js → claude-managed-adapter-naaw51me.js} +4 -3
  14. package/dist/{claude-managed-setup-60g8aazx.js → claude-managed-setup-eacvchx0.js} +12 -3
  15. package/dist/claude-sdk-session-bqp2xvym.js +29722 -0
  16. package/dist/{cli-987hg0aa.js → cli-0jwe9034.js} +2 -2
  17. package/dist/{cli-vj6h9kc2.js → cli-0m7wct0d.js} +59 -1
  18. package/dist/{cli-c07r9a2m.js → cli-0q3avq3s.js} +12 -11
  19. package/dist/{cli-19jenss3.js → cli-0wgxmwf6.js} +31 -10
  20. package/dist/cli-0x01kwrd.js +491 -0
  21. package/dist/cli-0xqde53b.js +176 -0
  22. package/dist/cli-10nhj6xq.js +8355 -0
  23. package/dist/{cli-tt9k8r35.js → cli-12tszpk9.js} +479 -910
  24. package/dist/{cli-5b6b62tz.js → cli-13hw4z91.js} +1 -1
  25. package/dist/{cli-cqt19wg6.js → cli-1a2j50m0.js} +11 -2
  26. package/dist/{cli-xberxa1r.js → cli-25fcq4b4.js} +2 -2
  27. package/dist/{cli-npssxgaz.js → cli-2pdaysab.js} +52 -2
  28. package/dist/cli-3qhyh31j.js +166 -0
  29. package/dist/cli-447dt1h3.js +3828 -0
  30. package/dist/cli-4derpgyk.js +115 -0
  31. package/dist/{cli-00w2c66p.js → cli-523679eh.js} +13 -2
  32. package/dist/{cli-y95wp3tg.js → cli-58e8yr0d.js} +1 -1
  33. package/dist/{cli-24k806sc.js → cli-5nbmpy8w.js} +3 -3
  34. package/dist/{cli-53sz8gkz.js → cli-6cs6wzxt.js} +1 -1
  35. package/dist/cli-741yb0xx.js +1027 -0
  36. package/dist/cli-74h9hscv.js +69 -0
  37. package/dist/cli-7cxw8b60.js +14910 -0
  38. package/dist/{cli-xhaeg2nc.js → cli-7e398j1h.js} +1 -1
  39. package/dist/cli-7kyk9jr3.js +22 -0
  40. package/dist/cli-89s4ngw3.js +183 -0
  41. package/dist/{cli-9b13ggd1.js → cli-89v6kyq7.js} +1 -0
  42. package/dist/{cli-ftfvggzx.js → cli-9hsgwz4w.js} +1045 -581
  43. package/dist/{cli-rqz8qbw7.js → cli-a8dvhzrt.js} +59 -20
  44. package/dist/{cli-j57qkrpq.js → cli-azhgzetg.js} +395 -56
  45. package/dist/cli-br0qdrnp.js +2150 -0
  46. package/dist/{cli-rj19hc34.js → cli-btfv8y88.js} +1 -1
  47. package/dist/{cli-8qmme6vd.js → cli-c8a36vcc.js} +2 -2
  48. package/dist/cli-cpvsaqsw.js +333 -0
  49. package/dist/cli-d5czme6m.js +17805 -0
  50. package/dist/{cli-h38x0qhg.js → cli-et8m9xsv.js} +4 -3
  51. package/dist/{cli-x24pshtq.js → cli-evvtk35y.js} +6 -6
  52. package/dist/{cli-7s3syk22.js → cli-f8gy6r2r.js} +5 -5
  53. package/dist/{cli-zt1227k1.js → cli-fkqhbg8g.js} +5 -3
  54. package/dist/{cli-9httdpyq.js → cli-g2whhdrf.js} +1 -1
  55. package/dist/cli-gt4hxb8p.js +99 -0
  56. package/dist/{cli-s8eet254.js → cli-jajnsm8q.js} +37184 -53838
  57. package/dist/{cli-7gtvvpbh.js → cli-jayjpjmb.js} +21 -12
  58. package/dist/{cli-68w09sjw.js → cli-jyk0ynw2.js} +27 -89
  59. package/dist/{cli-aehm1rbq.js → cli-jz4w45qt.js} +1152 -61
  60. package/dist/cli-kmcpfwn2.js +333856 -0
  61. package/dist/{cli-4198qc9v.js → cli-ktmyrtpn.js} +19 -3
  62. package/dist/{cli-j55n64b6.js → cli-mfe5btp9.js} +505 -189
  63. package/dist/{cli-q67y4wzm.js → cli-phsaj4xq.js} +24 -2
  64. package/dist/{cli-1hkrch3k.js → cli-pm1rst0h.js} +46 -7
  65. package/dist/{cli-pw9rj5t9.js → cli-pqetb83f.js} +1 -176
  66. package/dist/{cli-ggm92wcn.js → cli-ptr8h7kp.js} +20 -1
  67. package/dist/{cli-s8s499xy.js → cli-qbksc292.js} +3 -2
  68. package/dist/{cli-47m6dkd3.js → cli-qydf22qq.js} +597 -346
  69. package/dist/{cli-3gmcwfyx.js → cli-r1mxd1xh.js} +13 -6
  70. package/dist/cli-rb9m6rpe.js +4980 -0
  71. package/dist/cli-rykfmgwg.js +102 -0
  72. package/dist/{cli-5d10vd2m.js → cli-shkxm1vn.js} +6 -5
  73. package/dist/{cli-eetrvv91.js → cli-t3p77qk1.js} +1 -1
  74. package/dist/cli-tfky2838.js +756 -0
  75. package/dist/{cli-g8djp6qe.js → cli-tmqt7dpj.js} +40 -0
  76. package/dist/{cli-fgqxwrpv.js → cli-tpq83z89.js} +1631 -127
  77. package/dist/cli-trc24wk2.js +143 -0
  78. package/dist/{cli-aky8tctm.js → cli-tzx2ke1v.js} +3 -3
  79. package/dist/cli-v7fn5gms.js +1425 -0
  80. package/dist/{cli-wscdvcqz.js → cli-vctdqs5h.js} +1 -1
  81. package/dist/{cli-cw9zck8j.js → cli-xjcywfsf.js} +8 -4
  82. package/dist/{cli-k0g1sjzm.js → cli-ybgv4kw2.js} +18 -11
  83. package/dist/{cli-rjehvrts.js → cli-ydmcgve1.js} +8 -8
  84. package/dist/{cli-vhjwh6cz.js → cli-ygwm91nt.js} +1 -1
  85. package/dist/{cli-mcnhgnn1.js → cli-ykk5jaax.js} +184 -26
  86. package/dist/{cli-de1396j2.js → cli-ywybs5as.js} +7 -1
  87. package/dist/cli-zpep9q5e.js +491 -0
  88. package/dist/cli.js +41 -32
  89. package/dist/cloudflare-workers-ai-system-one-k6fbxw9d.js +46 -0
  90. package/dist/{codex-adapter-cnm8wk11.js → codex-adapter-nmxcatww.js} +26 -17
  91. package/dist/{codex-hook-9hptmdhj.js → codex-hook-20me6byw.js} +2 -2
  92. package/dist/{codex-login-x3jk4etn.js → codex-login-w6fz4xbz.js} +50 -5
  93. package/dist/{codex-session-runner-gfg547zj.js → codex-session-runner-90aa80ev.js} +24 -17
  94. package/dist/{commands-e3bn0p75.js → commands-8wcq1kn7.js} +11 -9
  95. package/dist/{db-03v9kzr2.js → db-j40kc4se.js} +25 -11
  96. package/dist/{devin-adapter-8vgkwh29.js → devin-adapter-dpsv6hd9.js} +1 -1
  97. package/dist/{dispatcher-nrmg8yht.js → dispatcher-4zqy768d.js} +34 -29
  98. package/dist/{dsh-adapter-k03nfqa6.js → dsh-adapter-mpnzgztj.js} +3 -3
  99. package/dist/{e2b-93x1k0j5.js → e2b-ez07znam.js} +4 -3
  100. package/dist/execute-6e7qeyk4.js +1017 -0
  101. package/dist/{google-generative-ai-bxmv50nc.js → google-generative-ai-k6n5nbpp.js} +6 -5
  102. package/dist/{google-vertex-809af514.js → google-vertex-44xxtcvp.js} +6 -5
  103. package/dist/{handlers-aqcedbyb.js → handlers-kjdce1w5.js} +37 -32
  104. package/dist/{hook-ybvmrmhq.js → hook-czx13n12.js} +19 -15
  105. package/dist/{hook-7vkskkre.js → hook-r2t4bqfg.js} +18 -14
  106. package/dist/{http-2eqa8t24.js → http-3bbb19be.js} +900 -145
  107. package/dist/{index-f6cryxwe.js → index-4a5njnm1.js} +2 -59
  108. package/dist/{index-x71bvq0p.js → index-668me3f0.js} +35 -30
  109. package/dist/{index-c6zsv1bd.js → index-7aj368tw.js} +13 -12
  110. package/dist/{index-1yks3qcr.js → index-fg0dsp6c.js} +34 -29
  111. package/dist/{index-chgdkq36.js → index-kkv84jq9.js} +34 -29
  112. package/dist/{index-a3vn96n8.js → index-zg7vp46x.js} +198 -54
  113. package/dist/{keepalive-vtkf5n99.js → keepalive-yra8dy57.js} +13 -11
  114. package/dist/{lead-h3sp357x.js → lead-va0pad2c.js} +54 -49
  115. package/dist/llama-cpp-classify-tgj35k8d.js +335 -0
  116. package/dist/{maintenance-jzeqtk3q.js → maintenance-hqewtmyg.js} +14 -13
  117. package/dist/{mistral-conversations-zx0kn554.js → mistral-conversations-pc3ntnxt.js} +14 -17
  118. package/dist/{oauth-refresh-sweep-qp3dzmdp.js → oauth-refresh-sweep-7rdt1m9n.js} +12 -10
  119. package/dist/{onboard-tmsp0qbz.js → onboard-j6ms6d0x.js} +6 -5
  120. package/dist/{openai-codex-responses-4vjqzvjp.js → openai-codex-responses-bbancrr0.js} +21 -8
  121. package/dist/{openai-completions-9hjrqbw8.js → openai-completions-jpaj5g2b.js} +9 -12
  122. package/dist/{openai-responses-7b1pn0xt.js → openai-responses-200k1wx4.js} +23 -18
  123. package/dist/{opencode-adapter-x6x2da1z.js → opencode-adapter-89rabkw2.js} +8 -7
  124. package/dist/{openrouter-images-9f9ek4rw.js → openrouter-images-8yx3487g.js} +5 -7
  125. package/dist/{otel-impl-9mz73q65.js → otel-impl-a792rjz0.js} +1 -1
  126. package/dist/{pi-messages-txpjj39q.js → pi-messages-vxdt8v7j.js} +2 -1
  127. package/dist/{pi-mono-adapter-m6dvv0dz.js → pi-mono-adapter-g7qzvr3v.js} +295 -70
  128. package/dist/pricing-refresh-wx0tp7cb.js +39 -0
  129. package/dist/quickjs-r1pq2107.wasm +0 -0
  130. package/dist/{rbac-roles-c9wqz83x.js → rbac-roles-8tzawacv.js} +12 -10
  131. package/dist/{render-v2-gryrh9h8.js → render-v2-mm7zswb8.js} +12 -10
  132. package/dist/runtime-zyssgse0.js +24 -0
  133. package/dist/{setup-q9720bt6.js → setup-8y70ys6t.js} +6 -5
  134. package/dist/typesafe-system-one-2ef30pec.js +27 -0
  135. package/dist/{virtual-modules-3htpgn8x.js → virtual-modules-6nmk7h3n.js} +22 -13
  136. package/dist/{work-objects-b76x7deb.js → work-objects-fg6xkb0n.js} +37 -32
  137. package/dist/{worker-6vrxbv41.js → worker-wmwhxsph.js} +54 -49
  138. package/openapi.json +1314 -91
  139. package/package.json +12 -11
  140. package/src/be/db/agents.ts +20 -0
  141. package/src/be/db/api-keys.ts +70 -0
  142. package/src/be/db/tasks/human-free.ts +442 -0
  143. package/src/be/db/tasks/read.ts +56 -1
  144. package/src/be/db/tasks/write.ts +107 -11
  145. package/src/be/db.ts +274 -188
  146. package/src/be/harness-model-support.ts +182 -0
  147. package/src/be/human-free-drain.ts +67 -0
  148. package/src/be/memory/providers/sqlite-store.ts +51 -15
  149. package/src/be/migrations/182_task_human_free_flag.sql +63 -0
  150. package/src/be/migrations/183_human_free_reclassify_queue.sql +25 -0
  151. package/src/be/migrations/184_model_catalog.sql +120 -0
  152. package/src/be/migrations/185_api_key_auth_failures.sql +6 -0
  153. package/src/be/migrations/186_api_key_seat_mismatch.sql +8 -0
  154. package/src/be/model-catalog-store.ts +644 -0
  155. package/src/be/model-tier-keys.ts +46 -0
  156. package/src/be/model-tier-resolution.ts +534 -0
  157. package/src/be/model-validation.ts +188 -0
  158. package/src/be/models-catalog.ts +61 -7
  159. package/src/be/modelsdev-cache.json +82 -0
  160. package/src/be/modelsdev-cache.ts +2 -0
  161. package/src/be/pricing-refresh.ts +154 -12
  162. package/src/be/scripts/typecheck.ts +15 -0
  163. package/src/be/seed/runner.ts +18 -12
  164. package/src/be/seed/types.ts +1 -0
  165. package/src/be/seed-scripts/catalog/task-failure-audit.ts +61 -9
  166. package/src/be/swarm-config-guard.ts +8 -0
  167. package/src/cli.tsx +10 -0
  168. package/src/commands/claude-managed-setup.ts +3 -1
  169. package/src/commands/codex-login.ts +67 -0
  170. package/src/commands/credential-outcome-report.ts +191 -0
  171. package/src/commands/rate-limit-outcome.ts +34 -4
  172. package/src/commands/runner.ts +357 -186
  173. package/src/commands/seat-mismatch-report.ts +46 -0
  174. package/src/extensions/apply-task-create.ts +27 -1
  175. package/src/extensions/contract-types.generated.ts +1 -1
  176. package/src/heartbeat/heartbeat.ts +79 -17
  177. package/src/heartbeat/templates.ts +2 -2
  178. package/src/http/agents.ts +62 -1
  179. package/src/http/api-keys.ts +140 -2
  180. package/src/http/codex-oauth-keep-warm.ts +3 -5
  181. package/src/http/index.ts +8 -0
  182. package/src/http/memory.ts +12 -5
  183. package/src/http/models-catalog.ts +357 -8
  184. package/src/http/poll.ts +54 -3
  185. package/src/http/schedules.ts +51 -6
  186. package/src/http/script-runs.ts +12 -0
  187. package/src/http/session-data.ts +2 -5
  188. package/src/http/stats.ts +6 -8
  189. package/src/http/status.ts +15 -1
  190. package/src/http/tasks.ts +90 -7
  191. package/src/http/usage-cache.ts +56 -15
  192. package/src/http/workflows.ts +46 -8
  193. package/src/mcp-client/http-client.ts +5 -0
  194. package/src/memory/recall-query.ts +74 -0
  195. package/src/prompts/base-prompt.ts +15 -4
  196. package/src/prompts/session-templates.ts +23 -1
  197. package/src/providers/acp-adapter.ts +1 -0
  198. package/src/providers/claude-adapter.ts +14 -5
  199. package/src/providers/claude-managed-adapter.ts +1 -0
  200. package/src/providers/claude-managed-models.ts +47 -161
  201. package/src/providers/claude-sdk-session.ts +3 -1
  202. package/src/providers/codex-adapter.ts +14 -14
  203. package/src/providers/codex-models.ts +65 -198
  204. package/src/providers/devin-adapter.ts +1 -0
  205. package/src/providers/dsh-adapter.ts +2 -1
  206. package/src/providers/harness-version.ts +83 -7
  207. package/src/providers/opencode-adapter.ts +53 -1
  208. package/src/providers/pi-binary-root/src/extensions/codemode/worker.ts +11 -0
  209. package/src/providers/pi-codemode-runtime.ts +17 -0
  210. package/src/providers/pi-mono-adapter.ts +361 -83
  211. package/src/providers/pi-mono-extension.ts +16 -1
  212. package/src/providers/reasoning-effort.ts +44 -140
  213. package/src/providers/types.ts +8 -0
  214. package/src/rbac/legacy-policy.ts +43 -0
  215. package/src/rbac/permissions.ts +15 -0
  216. package/src/scripts-runtime/sdk-allowlist.ts +4 -0
  217. package/src/scripts-runtime/types/stdlib.d.ts +15 -0
  218. package/src/scripts-runtime/types/swarm-extension.d.ts +3 -0
  219. package/src/scripts-runtime/types/swarm-sdk.d.ts +15 -0
  220. package/src/server.ts +6 -0
  221. package/src/slack/assistant.ts +13 -0
  222. package/src/slack/message-text.ts +56 -2
  223. package/src/slack/render-v2.ts +87 -37
  224. package/src/slack/session-status.ts +541 -0
  225. package/src/slack/thread-buffer.ts +10 -2
  226. package/src/tasks/sibling-awareness.ts +3 -2
  227. package/src/telemetry.ts +5 -4
  228. package/src/templates.d.ts +6 -0
  229. package/src/tests/agents-list-model-display.test.ts +4 -6
  230. package/src/tests/api-key-auth-failure-route.test.ts +142 -0
  231. package/src/tests/api-key-tracking.test.ts +368 -1
  232. package/src/tests/base-prompt.test.ts +43 -2
  233. package/src/tests/claude-adapter.test.ts +68 -0
  234. package/src/tests/claude-managed-adapter.test.ts +12 -18
  235. package/src/tests/codex-adapter.test.ts +2 -1
  236. package/src/tests/codex-auth-failure.test.ts +52 -0
  237. package/src/tests/codex-gpt6-release.test.ts +66 -5
  238. package/src/tests/codex-login.test.ts +116 -0
  239. package/src/tests/context-window.test.ts +7 -5
  240. package/src/tests/credential-exhaustion-policy.test.ts +118 -3
  241. package/src/tests/credential-outcome-report.test.ts +171 -0
  242. package/src/tests/credential-status-api.test.ts +103 -2
  243. package/src/tests/db-extraction-contract.test.ts +1 -0
  244. package/src/tests/db-task-read-contract.test.ts +10 -1
  245. package/src/tests/db-task-write-contract.test.ts +535 -2
  246. package/src/tests/docs-index-footprint.test.js +3 -3
  247. package/src/tests/dokploy-deploy.test.ts +315 -0
  248. package/src/tests/dsh-adapter.test.ts +1 -1
  249. package/src/tests/explicit-model-validation.test.ts +583 -0
  250. package/src/tests/extensions-pre-task.test.ts +73 -0
  251. package/src/tests/fetch-resolved-env.test.ts +46 -0
  252. package/src/tests/hang-watchdog-worker.ts +47 -0
  253. package/src/tests/hang-watchdog.test.ts +45 -0
  254. package/src/tests/hang-watchdog.ts +30 -0
  255. package/src/tests/harness-model-guard.test.ts +123 -0
  256. package/src/tests/harness-model-support.test.ts +374 -0
  257. package/src/tests/harness-version.test.ts +70 -3
  258. package/src/tests/heartbeat-checklist-config.test.ts +71 -0
  259. package/src/tests/heartbeat-checklist.test.ts +4 -2
  260. package/src/tests/heartbeat-orphan-resume.test.ts +48 -0
  261. package/src/tests/http-task-write-ownership.test.ts +172 -0
  262. package/src/tests/human-free-flag.test.ts +279 -0
  263. package/src/tests/human-free-oracle.ts +57 -0
  264. package/src/tests/human-free-reclassify.test.ts +669 -0
  265. package/src/tests/memory-fts-populate.test.ts +139 -0
  266. package/src/tests/memory-http-recall-gating.test.ts +125 -1
  267. package/src/tests/migration-046-budgets.test.ts +3 -3
  268. package/src/tests/model-catalog-migration.test.ts +134 -0
  269. package/src/tests/model-catalog-table.test.ts +249 -0
  270. package/src/tests/model-catalog-write-access.test.ts +387 -0
  271. package/src/tests/model-rate-limit-windows.test.ts +19 -0
  272. package/src/tests/model-tier-resolution.test.ts +611 -0
  273. package/src/tests/models-catalog.test.ts +1 -1
  274. package/src/tests/multi-runtime-enabled-stats.test.ts +65 -1
  275. package/src/tests/opencode-adapter.test.ts +157 -2
  276. package/src/tests/package-publish.test.ts +137 -36
  277. package/src/tests/pi-codemode.test.ts +246 -0
  278. package/src/tests/pi-mono-adapter.test.ts +3 -0
  279. package/src/tests/pi-native-mcp.test.ts +193 -0
  280. package/src/tests/pi-tool-exposure.test.ts +374 -0
  281. package/src/tests/preload.ts +3 -0
  282. package/src/tests/pricing-routes.test.ts +2 -2
  283. package/src/tests/prompt-template-session.test.ts +5 -3
  284. package/src/tests/rate-limit-event.test.ts +65 -0
  285. package/src/tests/rate-limit-outcome.test.ts +90 -0
  286. package/src/tests/rbac-engine.test.ts +77 -3
  287. package/src/tests/reasoning-effort.test.ts +109 -0
  288. package/src/tests/refresh-modelsdev-carryforward.test.ts +5 -5
  289. package/src/tests/runner-key-completion-outcome.test.ts +112 -0
  290. package/src/tests/runner-key-outcome-late-reset.test.ts +224 -0
  291. package/src/tests/runner-registration-retry.test.ts +149 -0
  292. package/src/tests/runtime-model-catalog.test.ts +86 -0
  293. package/src/tests/script-connections.test.ts +9 -0
  294. package/src/tests/seat-mismatch-report.test.ts +85 -0
  295. package/src/tests/seed-scripts.test.ts +138 -0
  296. package/src/tests/seed.test.ts +25 -0
  297. package/src/tests/session-costs.test.ts +14 -11
  298. package/src/tests/slack-message-text.test.ts +101 -0
  299. package/src/tests/slack-render-v2.test.ts +96 -0
  300. package/src/tests/slack-session-status.test.ts +626 -0
  301. package/src/tests/slack-thread-buffer-context.test.ts +93 -0
  302. package/src/tests/status.test.ts +29 -0
  303. package/src/tests/steering-promotion.test.ts +51 -0
  304. package/src/tests/store-progress-ownership.test.ts +136 -0
  305. package/src/tests/subscription-plans.test.ts +28 -0
  306. package/src/tests/task-cascade-fail.test.ts +148 -2
  307. package/src/tests/task-citations.test.ts +3 -2
  308. package/src/tests/tool-annotations.test.ts +1 -1
  309. package/src/tests/usage-cache.test.ts +166 -0
  310. package/src/tests/workflow-agent-task.test.ts +30 -0
  311. package/src/tests/workflow-executors.test.ts +5 -2
  312. package/src/tests/workflow-system-one-decision-review.test.ts +1021 -0
  313. package/src/tests/workflow-system-one-decision-save-warnings.test.ts +365 -0
  314. package/src/tests/workflow-system-one-decision.test.ts +2063 -0
  315. package/src/tests/workflow-triggers-v2.test.ts +320 -0
  316. package/src/tools/model-catalog/index.ts +137 -0
  317. package/src/tools/schedules/create-schedule.ts +17 -2
  318. package/src/tools/schedules/patch-schedule.ts +31 -1
  319. package/src/tools/schedules/update-schedule.ts +31 -1
  320. package/src/tools/send-task.ts +18 -1
  321. package/src/tools/store-progress.ts +49 -4
  322. package/src/tools/task-action.ts +14 -1
  323. package/src/tools/tool-config.ts +3 -1
  324. package/src/tools/workflows/create-workflow.ts +17 -4
  325. package/src/tools/workflows/patch-workflow-node.ts +17 -7
  326. package/src/tools/workflows/patch-workflow.ts +9 -2
  327. package/src/tools/workflows/update-workflow.ts +17 -4
  328. package/src/types.ts +41 -1
  329. package/src/utils/codex-auth-failure.ts +34 -0
  330. package/src/utils/context-window.ts +47 -53
  331. package/src/utils/credentials.ts +72 -30
  332. package/src/utils/error-tracker.ts +80 -0
  333. package/src/utils/harness-cli-version.ts +115 -0
  334. package/src/utils/harness-model-error.ts +33 -0
  335. package/src/utils/model-rate-limit-windows.ts +12 -0
  336. package/src/utils/runtime-model-catalog.ts +152 -0
  337. package/src/utils/spawn-model-guard.ts +43 -0
  338. package/src/utils/subscription-plans.ts +22 -0
  339. package/src/workflows/approval-resolution.ts +39 -0
  340. package/src/workflows/definition.ts +7 -0
  341. package/src/workflows/engine.ts +69 -1
  342. package/src/workflows/executors/agent-task.ts +16 -6
  343. package/src/workflows/executors/base.ts +53 -0
  344. package/src/workflows/executors/human-in-the-loop.ts +8 -8
  345. package/src/workflows/executors/index.ts +1 -0
  346. package/src/workflows/executors/registry.ts +2 -0
  347. package/src/workflows/executors/system-one-decision.ts +1232 -0
  348. package/src/workflows/executors/system-one-providers.ts +301 -0
  349. package/src/workflows/executors/workflow-llm.ts +13 -1
  350. package/src/workflows/model-validation.ts +45 -0
  351. package/src/workflows/patch-definition.ts +5 -0
  352. package/src/workflows/readiness.ts +86 -0
  353. package/src/workflows/recovery.ts +12 -12
  354. package/src/workflows/resume.ts +23 -9
  355. package/src/workflows/triggers.ts +85 -0
  356. package/templates/schedules/daily-blocker-digest/content.md +4 -5
  357. package/templates/skills/agent-browser/SKILL.md +6 -5
  358. package/templates/skills/agent-browser/content.md +6 -5
  359. package/templates/skills/heartbeat-runbook/config.json +1 -1
  360. package/templates/skills/heartbeat-runbook/content.md +2 -2
  361. package/templates/skills/swarm-scripts/SKILL.md +1 -1
  362. package/templates/skills/swarm-scripts/content.md +1 -1
  363. package/templates/skills/workflow-iterate/content.md +82 -1
  364. package/templates/workflows/claude-code-changelog-watch/content.md +2 -0
  365. package/templates/workflows/pr-review-status-sweep/content.md +2 -0
  366. package/dist/boot-seeds-s3q01xj3.js +0 -26
  367. package/dist/claude-sdk-session-t442qz6f.js +0 -39108
  368. package/dist/cli-0xjkj1j7.js +0 -61
  369. package/dist/cli-20cj37vt.js +0 -335561
  370. package/dist/cli-2djtr51b.js +0 -48
  371. package/dist/cli-65hjmev5.js +0 -1141
  372. package/dist/cli-aw1jwp1r.js +0 -14075
  373. package/dist/cli-h967nb6r.js +0 -6603
  374. package/dist/cli-vapyk8sw.js +0 -20
  375. package/dist/cli-ws6rqycm.js +0 -247
  376. package/dist/pricing-refresh-v1cbr9te.js +0 -27
  377. package/src/providers/modelsdev-reasoning.json +0 -6453
  378. package/dist/{index-f24115ge.js → index-2wsdsnmm.js} +3 -3
@@ -2,21 +2,28 @@
2
2
  * Normalized per-agent reasoning/effort control, shared across the four
3
3
  * local harnesses (`claude`, `codex`, `pi`, `opencode`).
4
4
  *
5
- * Pure module — no DB import, no network I/O at runtime. Capability data is
6
- * read from the slim, checked-in `modelsdev-reasoning.json` snapshot (derived
7
- * from the canonical `src/be/modelsdev-cache.json` by
8
- * `scripts/refresh-modelsdev-pricing.ts`), layered with a small
9
- * harness-specific override table for quirks the cache can't encode. See
5
+ * Pure module — no DB import, no network I/O of its own. Capability data is
6
+ * read from the model catalog (`src/utils/runtime-model-catalog.ts`: live
7
+ * `model_catalog` + overlay rows, vendored models.dev snapshot offline),
8
+ * layered with a small harness-specific override table for quirks the
9
+ * catalog can't encode. A new model's reasoning levels arrive with its
10
+ * catalog row (model-catalog phase 4). See
10
11
  * `thoughts/taras/plans/2026-07-01-agent-reasoning-effort-runtime-control.md`
11
12
  * (Phase 1) and `thoughts/taras/research/2026-05-26-agent-reasoning-effort-runtime-control.md`
12
13
  * for the design rationale.
13
14
  */
14
15
 
15
- import reasoningSnapshotJson from "./modelsdev-reasoning.json";
16
+ import {
17
+ claudeCatalogModelId,
18
+ REASONING_EFFORT_LEVELS,
19
+ type ReasoningEffortLevel,
20
+ reasoningLevelsFor,
21
+ } from "@desplega/model-catalog";
22
+ import { runtimeCatalogModel, runtimeCatalogSection } from "../utils/runtime-model-catalog";
16
23
 
17
24
  /** Closed, normalized enum. `minimal` remains out of scope; GPT-5.6 Codex adds `max`. */
18
- export const REASONING_EFFORT_LEVELS = ["off", "low", "medium", "high", "xhigh", "max"] as const;
19
- export type ReasoningEffort = (typeof REASONING_EFFORT_LEVELS)[number];
25
+ export { REASONING_EFFORT_LEVELS };
26
+ export type ReasoningEffort = ReasoningEffortLevel;
20
27
 
21
28
  /** The four local harnesses this feature covers (Devin / claude-managed are out of scope). */
22
29
  export type ReasoningHarness = "claude" | "codex" | "pi" | "opencode";
@@ -45,44 +52,7 @@ export type ReasoningEffortApplication =
45
52
  }
46
53
  | { kind: "noop" };
47
54
 
48
- // --- Slim capability snapshot ------------------------------------------------
49
-
50
- interface SlimReasoningOption {
51
- type: string;
52
- values?: string[];
53
- }
54
-
55
- interface SlimModelEntry {
56
- id: string;
57
- reasoning: boolean;
58
- reasoningOptions?: SlimReasoningOption[];
59
- }
60
-
61
- /**
62
- * Providers the snapshot covers — mirrors `SNAPSHOT_ORDER` +
63
- * `BEDROCK_SNAPSHOT_ID` in `apps/ui/src/lib/agent-runtime-models.ts`. Direct
64
- * `claude`/`codex` model strings (no provider prefix) resolve against
65
- * `anthropic`/`openai` respectively; `pi`/`opencode` model strings are always
66
- * `<providerId>/<model-id>` (see `splitProviderModel`).
67
- */
68
- type SnapshotProviderId = "anthropic" | "openai" | "openrouter" | "amazon-bedrock";
69
-
70
- type ReasoningSnapshot = Partial<Record<SnapshotProviderId, Record<string, SlimModelEntry>>>;
71
-
72
- const SNAPSHOT = reasoningSnapshotJson as ReasoningSnapshot;
73
-
74
- /** Shared-safe subset accepted by all four harnesses on at least their default models (see research doc). */
75
- const FALLBACK_LEVELS: ReasoningEffort[] = ["low", "medium", "high"];
76
-
77
- /** models.dev `reasoning_options[].type === "effort"` value → our normalized enum. `minimal` intentionally dropped. */
78
- const EFFORT_VALUE_MAP: Partial<Record<string, ReasoningEffort>> = {
79
- none: "off",
80
- low: "low",
81
- medium: "medium",
82
- high: "high",
83
- xhigh: "xhigh",
84
- max: "max",
85
- };
55
+ // --- Capability lookup --------------------------------------------------------
86
56
 
87
57
  function splitProviderModel(model: string): { providerId: string; modelId: string } {
88
58
  const slash = model.indexOf("/");
@@ -90,81 +60,33 @@ function splitProviderModel(model: string): { providerId: string; modelId: strin
90
60
  return { providerId: model.slice(0, slash), modelId: model.slice(slash + 1) };
91
61
  }
92
62
 
93
- function lookupModelEntry(harness: ReasoningHarness, model: string): SlimModelEntry | undefined {
94
- if (!model) return undefined;
95
- if (harness === "claude") return SNAPSHOT.anthropic?.[model];
96
- if (harness === "codex") return SNAPSHOT.openai?.[model];
97
- // pi / opencode model strings are always "<provider>/<model-id...>" — the
98
- // model id itself may contain further slashes (e.g. openrouter's
99
- // "google/gemini-3-flash-preview"), so split on the FIRST slash only.
100
- const { providerId, modelId } = splitProviderModel(model);
101
- if (!providerId) return undefined;
102
- return SNAPSHOT[providerId as SnapshotProviderId]?.[modelId];
103
- }
104
-
105
- function levelsFromReasoningOptions(options: SlimReasoningOption[] | undefined): ReasoningEffort[] {
106
- const effortEntry = options?.find((o) => o.type === "effort");
107
- if (!effortEntry?.values?.length) return [];
108
- const mapped = new Set(
109
- effortEntry.values
110
- .map((v) => EFFORT_VALUE_MAP[v])
111
- .filter((v): v is ReasoningEffort => v !== undefined),
112
- );
113
- // Preserve canonical ordering rather than whatever order models.dev lists them in.
114
- return REASONING_EFFORT_LEVELS.filter((level) => mapped.has(level));
115
- }
116
-
117
- function hasBudgetTokensOption(entry: SlimModelEntry): boolean {
118
- return Boolean(entry.reasoningOptions?.some((o) => o.type === "budget_tokens"));
119
- }
120
-
121
63
  /**
122
- * Harness-specific quirks the cache doesn't (fully) encode. Kept small — this
123
- * patches gaps, it does not duplicate the cache. Applied on top of whichever
124
- * levels resolution step 2/3 already produced.
64
+ * The (catalog id, catalog facts) a harness's model string names. Direct
65
+ * `claude`/`codex` strings (no provider prefix) resolve against
66
+ * `anthropic`/`openai`; a Claude CLI shortname (`opus`, the tier default)
67
+ * resolves to the newest model of its family. `pi`/`opencode` strings are
68
+ * always `<providerId>/<model-id>`, and the id may hold more slashes (openrouter's
69
+ * `google/gemini-3-flash-preview`), so split on the FIRST slash only.
125
70
  */
126
- function applyHarnessOverrides(
71
+ function lookupModel(
127
72
  harness: ReasoningHarness,
128
73
  model: string,
129
- entry: SlimModelEntry,
130
- levels: ReasoningEffort[],
131
- ): ReasoningEffort[] {
132
- let result = levels;
133
-
74
+ ): { id: string; facts: ReturnType<typeof runtimeCatalogModel> } | undefined {
75
+ if (!model) return undefined;
76
+ let providerId: string;
77
+ let modelId: string;
134
78
  if (harness === "claude") {
135
- // Claude's native vocabulary has no "off" (see research doc) — we
136
- // implement it as a synthetic level via `MAX_THINKING_TOKENS=0`, which
137
- // only exists on legacy (non-adaptive-only) models that still expose a
138
- // numeric thinking-budget knob (`reasoning_options` carries a
139
- // `budget_tokens` entry). Opus 4.7+ models are adaptive-only — effort
140
- // only, no `budget_tokens` — so `off` is naturally never added for them,
141
- // rather than hardcoding "Opus 4.7" by name.
142
- if (hasBudgetTokensOption(entry) && !result.includes("off")) {
143
- result = ["off", ...result];
144
- }
145
- }
146
-
147
- if (harness !== "codex") {
148
- result = result.filter((l) => l !== "max");
149
- }
150
-
151
- if (harness === "codex") {
152
- // The cache already tends to encode this correctly per model (verified:
153
- // `gpt-5.1-codex` excludes xhigh, `gpt-5.1-codex-max` includes it). This
154
- // rule is defense-in-depth for models missing from the snapshot (the
155
- // {low,medium,high} fallback path), where naming alone should still
156
- // decide xhigh eligibility for `*-codex` family models.
157
- const isCodexMax = /-codex-max$/.test(model);
158
- const isCodexNonMax = /-codex$/.test(model) && !isCodexMax;
159
- if (isCodexMax && !result.includes("xhigh")) {
160
- result = [...result, "xhigh"];
161
- }
162
- if (isCodexNonMax) {
163
- result = result.filter((l) => l !== "xhigh");
164
- }
79
+ providerId = "anthropic";
80
+ modelId = claudeCatalogModelId(model, runtimeCatalogSection("anthropic"));
81
+ } else if (harness === "codex") {
82
+ providerId = "openai";
83
+ modelId = model;
84
+ } else {
85
+ ({ providerId, modelId } = splitProviderModel(model));
86
+ if (!providerId) return undefined;
165
87
  }
166
-
167
- return REASONING_EFFORT_LEVELS.filter((level) => result.includes(level));
88
+ const facts = runtimeCatalogModel(providerId, modelId);
89
+ return facts ? { id: modelId, facts } : undefined;
168
90
  }
169
91
 
170
92
  function pickDefault(levels: ReasoningEffort[]): ReasoningEffort | null {
@@ -173,33 +95,15 @@ function pickDefault(levels: ReasoningEffort[]): ReasoningEffort | null {
173
95
  }
174
96
 
175
97
  /**
176
- * Resolution order:
177
- * 1. No capability data for (harness, model) at all (custom model strings,
178
- * models absent from the snapshot) → unsupported.
179
- * 2. `reasoning: false` → unsupported, regardless of the override table.
180
- * 3. `reasoning_options` has a usable `type: "effort"` entry → levels come
181
- * from its `values` (mapped/filtered — `none`→`off`, `minimal` dropped).
182
- * 4. Otherwise (`reasoning: true`, no usable effort entry) → the shared-safe
183
- * fallback subset `{low, medium, high}`.
184
- * 5. Harness-specific override table applied on top for known quirks.
98
+ * The levels `(harness, model)` accepts, from the catalog facts
99
+ * (`reasoningLevelsFor`, shared with the swarm app's effort pickers). Unknown
100
+ * models (custom strings, ids the catalog lacks) and non-reasoning models are
101
+ * unsupported.
185
102
  */
186
103
  export function reasoningCapability(harness: ReasoningHarness, model: string): ReasoningCapability {
187
- const entry = lookupModelEntry(harness, model);
188
- if (!entry || !entry.reasoning) {
189
- return { supported: false, levels: [], default: null };
190
- }
191
-
192
- let levels = levelsFromReasoningOptions(entry.reasoningOptions);
193
- if (levels.length === 0) {
194
- levels = [...FALLBACK_LEVELS];
195
- }
196
-
197
- levels = applyHarnessOverrides(harness, model, entry, levels);
198
-
199
- if (levels.length === 0) {
200
- return { supported: false, levels: [], default: null };
201
- }
202
-
104
+ const entry = lookupModel(harness, model);
105
+ const levels = entry ? reasoningLevelsFor(harness, entry.id, entry.facts) : [];
106
+ if (levels.length === 0) return { supported: false, levels: [], default: null };
203
107
  return { supported: true, levels, default: pickDefault(levels) };
204
108
  }
205
109
 
@@ -203,6 +203,12 @@ export interface ProviderResult {
203
203
  * rejection event arrived; it orders the report against other workers'.
204
204
  */
205
205
  modelRateLimit?: { window: string; model: ModelFamily; resetAt: string; observedAt?: string };
206
+ /**
207
+ * Set when the Claude CLI sent a rejected `rate_limit_event` with
208
+ * `errorCode: "credits_required"`: the key's seat cannot run the model. Not
209
+ * a rate limit, so `rateLimitResetAt` is never set from that event.
210
+ */
211
+ creditsRequired?: { observedAt: string; overageDisabledReason?: string };
206
212
  /**
207
213
  * Reasoning/effort level the adapter actually applied (Phase 4). `null`
208
214
  * means `applyReasoningEffort()` returned `noop` (capability rejected the
@@ -216,6 +222,8 @@ export interface ProviderResult {
216
222
  export interface ProviderTraits {
217
223
  /** Provider can call MCP tools (store-progress, task-action, skills, slack-reply, etc.) */
218
224
  hasMcp: boolean;
225
+ /** Provider hides MCP tools behind a native tool search. Defaults to false. */
226
+ hasToolSearch?: boolean;
219
227
  /**
220
228
  * Provider discovers installed skills on its own — it reads the local skills
221
229
  * tree and ambient-injects each skill's name + description into the session
@@ -56,6 +56,27 @@ const leadOrOperatorOrUser: LegacyRule = {
56
56
  principal.kind === "operator" || principal.kind === "user" || actsAsLead(principal),
57
57
  };
58
58
 
59
+ /**
60
+ * Lead agents and the operator (shared API key with no agent identity). Plain dashboard users are
61
+ * left out: nothing in the UI writes through a verb with this rule yet.
62
+ */
63
+ const leadOrOperator: LegacyRule = {
64
+ name: "lead-or-operator",
65
+ denyReason: "requires lead agent or operator authentication",
66
+ evaluate: (principal) => principal.kind === "operator" || actsAsLead(principal),
67
+ };
68
+
69
+ /**
70
+ * Registered agents and the operator. Dashboard users are left out: a support row is a worker's
71
+ * report about its own CLI (or an operator correction), and no UI writes one.
72
+ */
73
+ const agentOrOperator: LegacyRule = {
74
+ name: "agent-or-operator",
75
+ denyReason: "requires a registered agent or operator authentication",
76
+ evaluate: (principal) =>
77
+ principal.kind === "operator" || (principal.kind === "agent" && principal.agentId !== ""),
78
+ };
79
+
59
80
  const extensionWrite: LegacyRule = {
60
81
  name: "extension-owner-or-elevated",
61
82
  denyReason: "requires extension owner, lead agent, operator, or user authentication",
@@ -195,10 +216,29 @@ const taskFsMutate: LegacyRule = {
195
216
  },
196
217
  };
197
218
 
219
+ /**
220
+ * task.progress.write — operator OR user OR lead OR task-assignee OR unassigned
221
+ * task (store-progress, POST /api/tasks/{id}/progress). A worker once completed
222
+ * a workflow sibling's task with its own report; the creator gets no write
223
+ * here, unlike task.fs.mutate. Leads may write, unlike HTTP finish.
224
+ */
225
+ const taskProgressWrite: LegacyRule = {
226
+ name: "operator-or-user-or-lead-or-task-assignee",
227
+ denyReason: "requires operator, user, lead agent, or task assignee",
228
+ evaluate: (principal, resource) => {
229
+ if (principal.kind === "operator" || principal.kind === "user") return true;
230
+ if (actsAsLead(principal)) return true;
231
+ if (resource?.kind !== "task") return false;
232
+ return resource.agentId == null || resource.agentId === principal.agentId;
233
+ },
234
+ };
235
+
198
236
  /** All named (non-composite) rule kinds, keyed by identifier. */
199
237
  export const LEGACY_RULES = {
200
238
  "operator-or-user": operatorOrUser,
201
239
  "lead-or-operator-or-user": leadOrOperatorOrUser,
240
+ "lead-or-operator": leadOrOperator,
241
+ "agent-or-operator": agentOrOperator,
202
242
  "lead-only": leadOnly,
203
243
  "lead-or-task-creator": leadOrTaskCreator,
204
244
  "lead-or-resource-owner": leadOrResourceOwner,
@@ -223,6 +263,7 @@ export const LEGACY_POLICY = {
223
263
  "task.steer.own": requesterOwnsTask,
224
264
  "task.action.own": requesterOwnsTask,
225
265
  "task.fs.mutate": taskFsMutate,
266
+ "task.progress.write": taskProgressWrite,
226
267
  "favorite.write.own": anyAuthenticated,
227
268
  "memory.learning.inject": leadOnly,
228
269
  "memory.edit.any": leadOrResourceOwner,
@@ -246,6 +287,8 @@ export const LEGACY_POLICY = {
246
287
  "config.write.any": leadOnly,
247
288
  "config.delete.any": leadOnly,
248
289
  "config.read.secrets": leadOnly,
290
+ "models.catalog.write": leadOrOperator,
291
+ "models.harness-support.write": agentOrOperator,
249
292
  "skill.create.swarm": leadOnly,
250
293
  "skill.install.any": leadOnly,
251
294
  "skill.install.global": leadOnly,
@@ -65,6 +65,11 @@ export const PERMISSIONS = {
65
65
  description: "Mutate a task's filesystem artifacts and attachments.",
66
66
  namespace: "task",
67
67
  },
68
+ "task.progress.write": {
69
+ description:
70
+ "Write a task's progress, status, or output (its assignee, a lead, a human, or anyone while unassigned).",
71
+ namespace: "task",
72
+ },
68
73
  "favorite.write.own": {
69
74
  description: "Set a favorite the caller owns.",
70
75
  namespace: "favorite",
@@ -153,6 +158,16 @@ export const PERMISSIONS = {
153
158
  description: "Delete any swarm-config entry.",
154
159
  namespace: "config",
155
160
  },
161
+ "models.catalog.write": {
162
+ description:
163
+ "Force a model-catalog refresh from models.dev or write/delete model-catalog overlay rows.",
164
+ namespace: "models",
165
+ },
166
+ "models.harness-support.write": {
167
+ description:
168
+ "Record whether a pinned harness CLI version accepts a model (a worker reports its own probe results; the operator may correct any row).",
169
+ namespace: "models",
170
+ },
156
171
  "config.read.secrets": {
157
172
  description: "Read unmasked secret config values.",
158
173
  namespace: "config",
@@ -80,6 +80,10 @@ export const SDK_TOOL_NAME_MAP = {
80
80
  config_set: "set-config",
81
81
  config_delete: "delete-config", // destructive
82
82
 
83
+ // ── model catalog ──
84
+ modelCatalog_refresh: "model-catalog-refresh", // external: fetches models.dev
85
+ modelCatalog_overlayUpsert: "model-catalog-overlay-upsert",
86
+
83
87
  // ── slack ──
84
88
  slack_read: "slack-read",
85
89
  slack_listChannels: "slack-list-channels",
@@ -433,6 +433,21 @@ declare module "swarm-sdk" {
433
433
  }): Promise<unknown>;
434
434
  config_delete(args: { id: string }): Promise<unknown>;
435
435
 
436
+ // --- write: model catalog ---
437
+ modelCatalog_refresh(args?: { force?: boolean }): Promise<unknown>;
438
+ modelCatalog_overlayUpsert(args: {
439
+ provider: string;
440
+ modelId: string;
441
+ name?: string;
442
+ releaseDate?: string;
443
+ contextWindow?: number;
444
+ maxOutput?: number;
445
+ reasoningOptions?: Array<{ type: string; values?: string[] }>;
446
+ pricing?: { input?: number; output?: number; cache_read?: number; cache_write?: number };
447
+ reason: string;
448
+ verifiedBy?: string;
449
+ }): Promise<unknown>;
450
+
436
451
  // --- write: slack ---
437
452
  slack_post(args: { channelId: string; message: string; blocks?: unknown }): Promise<unknown>;
438
453
  slack_reply(args: {
@@ -180,6 +180,9 @@ declare module "swarm-extension" {
180
180
  model?: string | undefined;
181
181
  modelTier?: "smol" | "regular" | "smart" | "ultra" | undefined;
182
182
  effort?: "off" | "low" | "medium" | "high" | "xhigh" | "max" | undefined;
183
+ resolvedModel?: string | undefined;
184
+ modelSource?: string | undefined;
185
+ modelAlias?: string | undefined;
183
186
  scheduleId?: string | undefined;
184
187
  workflowRunId?: string | null | undefined;
185
188
  workflowRunStepId?: string | null | undefined;
@@ -415,6 +415,21 @@ declare module "swarm-sdk" {
415
415
  }): Promise<unknown>;
416
416
  config_delete(args: { id: string }): Promise<unknown>;
417
417
 
418
+ // --- write: model catalog ---
419
+ modelCatalog_refresh(args?: { force?: boolean }): Promise<unknown>;
420
+ modelCatalog_overlayUpsert(args: {
421
+ provider: string;
422
+ modelId: string;
423
+ name?: string;
424
+ releaseDate?: string;
425
+ contextWindow?: number;
426
+ maxOutput?: number;
427
+ reasoningOptions?: Array<{ type: string; values?: string[] }>;
428
+ pricing?: { input?: number; output?: number; cache_read?: number; cache_write?: number };
429
+ reason: string;
430
+ verifiedBy?: string;
431
+ }): Promise<unknown>;
432
+
418
433
  // --- write: slack ---
419
434
  slack_post(args: { channelId: string; message: string; blocks?: unknown }): Promise<unknown>;
420
435
  slack_reply(args: {
package/src/server.ts CHANGED
@@ -73,6 +73,10 @@ import { registerMemoryGetTool } from "./tools/memory-get";
73
73
  import { registerMemoryRateTool } from "./tools/memory-rate";
74
74
  import { registerMemorySearchTool } from "./tools/memory-search";
75
75
  import { registerMemoryStoreTool } from "./tools/memory-store";
76
+ import {
77
+ registerModelCatalogOverlayUpsertTool,
78
+ registerModelCatalogRefreshTool,
79
+ } from "./tools/model-catalog";
76
80
  import { registerMyAgentInfoTool } from "./tools/my-agent-info";
77
81
  import { registerGetOauthAccessTokenTool } from "./tools/oauth-access-token";
78
82
  import { registerPollTaskTool } from "./tools/poll-task";
@@ -413,6 +417,8 @@ export async function createServer(
413
417
  registerListConfigTool(server);
414
418
  registerDeleteConfigTool(server);
415
419
  registerCredentialBindingsTool(server);
420
+ registerModelCatalogRefreshTool(server);
421
+ registerModelCatalogOverlayUpsertTool(server);
416
422
  }
417
423
 
418
424
  // Scripts capability - reusable script catalog (HTTP MCP only in v1)
@@ -14,6 +14,7 @@ import {
14
14
  } from "./inbound-files";
15
15
  import { ensureSlackThreadTree, isSlackRenderV2Enabled } from "./render-v2";
16
16
  import { hasOtherUserMention } from "./router";
17
+ import { markSlackSessionProcessing } from "./session-status";
17
18
  import { bufferThreadMessage, getBufferMessageCount } from "./thread-buffer";
18
19
  // Side-effect import: registers all Slack event templates in the in-memory registry
19
20
  import "./templates";
@@ -65,7 +66,18 @@ export function createAssistant(): Assistant {
65
66
  // Wrap setStatus/setTitle to swallow all errors gracefully.
66
67
  // These calls can fail for various reasons (no_permission when the thread
67
68
  // wasn't started by the assistant, network errors, etc.), so we log and continue.
69
+ // With the v2 renderer Slack's native session status carries the "working"
70
+ // state for the whole ask (see session-status.ts), so the legacy free-text
71
+ // status below is only the fallback when the native call is unavailable.
72
+ let statusThread: { channelId: string; threadTs: string } | undefined;
68
73
  const safeSetStatus = async (status: string) => {
74
+ if (
75
+ statusThread &&
76
+ isSlackRenderV2Enabled() &&
77
+ (await markSlackSessionProcessing({ ...statusThread, client }))
78
+ ) {
79
+ return;
80
+ }
69
81
  try {
70
82
  await setStatus(status);
71
83
  } catch (error) {
@@ -85,6 +97,7 @@ export function createAssistant(): Assistant {
85
97
  const msg = message as unknown as Record<string, unknown>;
86
98
  const threadTs = (msg.thread_ts as string) || message.ts;
87
99
  const channelId = message.channel;
100
+ statusThread = { channelId, threadTs };
88
101
  const messageText = (msg.text as string) || "";
89
102
  // `file_share` messages land here too: an image sent with no caption
90
103
  // arrives with empty `text` and everything in `files`.
@@ -52,6 +52,34 @@ function collectRichTextParts(node: unknown, parts: string[]): void {
52
52
  }
53
53
  }
54
54
 
55
+ /**
56
+ * Turn top-level mrkdwn `text` into the plain text a rich_text block flattens
57
+ * to: drop `<…>` tokens (user/channel mentions, links, `<!here>`), which
58
+ * collectRichTextParts does not emit, then decode Slack's HTML escapes.
59
+ */
60
+ function decodeMrkdwn(text: string): string {
61
+ return text
62
+ .replace(/<[^<>]*>/g, "")
63
+ .replace(/&lt;/g, "<")
64
+ .replace(/&gt;/g, ">")
65
+ .replace(/&amp;/g, "&");
66
+ }
67
+
68
+ /**
69
+ * Normalize text for the text-vs-rich_text equality check only (never for
70
+ * output): drop emoji shortcodes, quote and list markers, and mrkdwn style
71
+ * characters, which rich_text carries as element types or style flags rather
72
+ * than characters, then all whitespace, since flattening joins styled runs
73
+ * with newlines.
74
+ */
75
+ function normalizeForDedupe(text: string): string {
76
+ return text
77
+ .replace(/:[a-z0-9_+'-]+:/gi, "")
78
+ .replace(/^[ \t]*(?:>|[•◦▪‣-]|\d+\.)[ \t]*/gm, "")
79
+ .replace(/[*_~`]/g, "")
80
+ .replace(/\s+/g, "");
81
+ }
82
+
55
83
  /**
56
84
  * Return displayable text for a Slack message, combining ALL content layers.
57
85
  *
@@ -64,7 +92,8 @@ function collectRichTextParts(node: unknown, parts: string[]): void {
64
92
  *
65
93
  * Dedup: if `msg.text` is already contained verbatim in the combined
66
94
  * attachments+blocks body (fallback echoed in a block), it is omitted to avoid
67
- * printing it twice.
95
+ * printing it twice. A rich_text block whose content equals `msg.text` after
96
+ * normalization is omitted instead (see below).
68
97
  *
69
98
  * Block types extracted: section (text + fields), rich_text, header, context,
70
99
  * actions (button label + url as mrkdwn link).
@@ -123,6 +152,9 @@ export function extractSlackMessageText(msg: SlackMessageLike): string {
123
152
 
124
153
  // Block Kit blocks
125
154
  const blockParts: string[] = [];
155
+ // Index range of each rich_text block's parts inside blockParts, so a
156
+ // rich_text block that merely re-renders `msg.text` can be dropped below.
157
+ const richTextRanges: Array<[number, number]> = [];
126
158
  if (Array.isArray(msg.blocks) && msg.blocks.length > 0) {
127
159
  for (const rawBlock of msg.blocks) {
128
160
  if (rawBlock == null || typeof rawBlock !== "object") continue;
@@ -137,9 +169,11 @@ export function extractSlackMessageText(msg: SlackMessageLike): string {
137
169
  }
138
170
  }
139
171
  } else if (block.type === "rich_text" && Array.isArray(block.elements)) {
172
+ const start = blockParts.length;
140
173
  for (const el of block.elements) {
141
174
  collectRichTextParts(el, blockParts);
142
175
  }
176
+ if (blockParts.length > start) richTextRanges.push([start, blockParts.length]);
143
177
  } else if (block.type === "header") {
144
178
  if (block.text?.text) blockParts.push(block.text.text);
145
179
  } else if (block.type === "context" && Array.isArray(block.elements)) {
@@ -162,7 +196,27 @@ export function extractSlackMessageText(msg: SlackMessageLike): string {
162
196
  }
163
197
  }
164
198
 
165
- const bodyText = [...attachmentParts, ...blockParts].filter(Boolean).join("\n");
199
+ // A human message carries the same content twice: `text` (mrkdwn) and a
200
+ // rich_text block. The block flattens to one part per styled run, so the
201
+ // per-line check below never matches a multi-line or formatted message and
202
+ // the whole message would render twice. When the rich_text content equals
203
+ // `text` after normalization, keep `text` (it also carries the mentions,
204
+ // links and emoji the rich_text flattening drops) and skip the blocks.
205
+ let bodyBlockParts = blockParts;
206
+ if (topText && richTextRanges.length > 0) {
207
+ const richText = richTextRanges
208
+ .map(([start, end]) => blockParts.slice(start, end).join("\n"))
209
+ .join("\n");
210
+ if (normalizeForDedupe(richText) === normalizeForDedupe(decodeMrkdwn(topText))) {
211
+ const dropped = new Set<number>();
212
+ for (const [start, end] of richTextRanges) {
213
+ for (let i = start; i < end; i++) dropped.add(i);
214
+ }
215
+ bodyBlockParts = blockParts.filter((_, i) => !dropped.has(i));
216
+ }
217
+ }
218
+
219
+ const bodyText = [...attachmentParts, ...bodyBlockParts].filter(Boolean).join("\n");
166
220
 
167
221
  // Include the top-level text unless it already appears as a complete line in the body.
168
222
  // Boundary-aware check: "hi".includes check would silently drop "hi" when the body