@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
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Process-wide model catalog for code that runs on BOTH sides of the
3
+ * API/worker boundary (provider adapters, context-window math, reasoning
4
+ * gating). Model-catalog phase 4: this replaces the hand-maintained per-model
5
+ * tables (codex allowlist/windows/pricing, claude-managed allowlist/pricing,
6
+ * per-model context windows, the slim reasoning snapshot).
7
+ *
8
+ * Sources, highest first:
9
+ * 1. the live catalog: the API server's `model_catalog` + overlay rows.
10
+ * Workers pull it over HTTP (`GET /api/models-catalog`, see
11
+ * `refreshRuntimeModelCatalog`); the API process sets it directly after
12
+ * every catalog reload.
13
+ * 2. the vendored models.dev snapshot (`src/be/modelsdev-cache.json`) —
14
+ * offline fallback, and the only source for ids the live catalog has
15
+ * dropped (legacy models still named by old tasks and pricing rows).
16
+ *
17
+ * NO database imports (worker-safe). Every lookup is synchronous; the HTTP
18
+ * refresh is TTL-cached and never throws, so hot paths stay cheap.
19
+ */
20
+ import modelsDevSnapshot from "../be/modelsdev-cache.json";
21
+
22
+ export interface RuntimeCatalogModel {
23
+ id?: string;
24
+ name?: string;
25
+ cost?: { input?: number; output?: number; cache_read?: number; cache_write?: number };
26
+ limit?: { context?: number };
27
+ release_date?: string;
28
+ status?: string;
29
+ reasoning?: boolean;
30
+ reasoning_options?: { type?: string; values?: string[] }[];
31
+ }
32
+
33
+ export interface RuntimeCatalogProvider {
34
+ id?: string;
35
+ name?: string;
36
+ models?: Record<string, RuntimeCatalogModel>;
37
+ }
38
+
39
+ export type RuntimeCatalog = Record<string, RuntimeCatalogProvider | undefined>;
40
+
41
+ const SNAPSHOT = modelsDevSnapshot as unknown as RuntimeCatalog;
42
+
43
+ /** Default freshness window for the worker-side HTTP pull. */
44
+ export const RUNTIME_CATALOG_TTL_MS = 10 * 60_000;
45
+ const FETCH_TIMEOUT_MS = 5_000;
46
+
47
+ let live: RuntimeCatalog | null = null;
48
+ let liveAt = 0;
49
+ let liveSource: "live" | "snapshot" | null = null;
50
+ const mergedSections = new Map<string, Record<string, RuntimeCatalogModel>>();
51
+ let inFlight: Promise<boolean> | null = null;
52
+
53
+ /** Replace the live layer (API process after a reload; worker after a pull). */
54
+ export function setRuntimeModelCatalog(
55
+ providers: RuntimeCatalog,
56
+ at: number = Date.now(),
57
+ source: "live" | "snapshot" = "live",
58
+ ): void {
59
+ live = providers;
60
+ liveAt = at;
61
+ liveSource = source;
62
+ mergedSections.clear();
63
+ }
64
+
65
+ export function resetRuntimeModelCatalogForTests(): void {
66
+ live = null;
67
+ liveAt = 0;
68
+ liveSource = null;
69
+ mergedSections.clear();
70
+ inFlight = null;
71
+ }
72
+
73
+ /** Where the current live layer came from; null = snapshot only. */
74
+ export function runtimeModelCatalogSource(): "live" | "snapshot" | null {
75
+ return liveSource;
76
+ }
77
+
78
+ /** One provider section: snapshot models overlaid by the live catalog (live wins per id). */
79
+ export function runtimeCatalogSection(provider: string): Record<string, RuntimeCatalogModel> {
80
+ const cached = mergedSections.get(provider);
81
+ if (cached) return cached;
82
+ const merged: Record<string, RuntimeCatalogModel> = {
83
+ ...(SNAPSHOT[provider]?.models ?? {}),
84
+ ...(live?.[provider]?.models ?? {}),
85
+ };
86
+ mergedSections.set(provider, merged);
87
+ return merged;
88
+ }
89
+
90
+ /**
91
+ * The sections the harness-compatibility guard (`harnessModelMismatch`) reads: the two
92
+ * sections the pinned harnesses talk to. Sync, so worker spawn and config validation
93
+ * can judge a model without a round trip.
94
+ */
95
+ export function runtimeHarnessSections(): Record<
96
+ "anthropic" | "openai",
97
+ { models: Record<string, RuntimeCatalogModel> }
98
+ > {
99
+ return {
100
+ anthropic: { models: runtimeCatalogSection("anthropic") },
101
+ openai: { models: runtimeCatalogSection("openai") },
102
+ };
103
+ }
104
+
105
+ export function runtimeCatalogModel(
106
+ provider: string,
107
+ modelId: string,
108
+ ): RuntimeCatalogModel | undefined {
109
+ return runtimeCatalogSection(provider)[modelId];
110
+ }
111
+
112
+ /**
113
+ * Pull the live catalog from the API server. TTL-cached (`maxAgeMs`),
114
+ * single-flight, never throws: returns false and keeps the previous layer
115
+ * when the server is unreachable or answers non-2xx.
116
+ */
117
+ export async function refreshRuntimeModelCatalog(opts: {
118
+ apiUrl: string;
119
+ apiKey?: string;
120
+ agentId?: string;
121
+ maxAgeMs?: number;
122
+ fetchImpl?: typeof fetch;
123
+ }): Promise<boolean> {
124
+ const maxAge = opts.maxAgeMs ?? RUNTIME_CATALOG_TTL_MS;
125
+ if (live && Date.now() - liveAt < maxAge) return true;
126
+ if (inFlight) return inFlight;
127
+ const doFetch = opts.fetchImpl ?? fetch;
128
+ inFlight = (async () => {
129
+ try {
130
+ const headers: Record<string, string> = {};
131
+ if (opts.apiKey) headers.Authorization = `Bearer ${opts.apiKey}`;
132
+ if (opts.agentId) headers["X-Agent-ID"] = opts.agentId;
133
+ const res = await doFetch(`${opts.apiUrl}/api/models-catalog`, {
134
+ headers,
135
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
136
+ });
137
+ if (!res.ok) return false;
138
+ const body = (await res.json()) as {
139
+ source?: "live" | "snapshot";
140
+ providers?: RuntimeCatalog;
141
+ };
142
+ if (!body || typeof body.providers !== "object" || body.providers === null) return false;
143
+ setRuntimeModelCatalog(body.providers, Date.now(), body.source ?? "live");
144
+ return true;
145
+ } catch {
146
+ return false;
147
+ } finally {
148
+ inFlight = null;
149
+ }
150
+ })();
151
+ return inFlight;
152
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Worker-side harness guard, run right before a provider CLI starts. The server
3
+ * already refuses cross-harness models at create and at claim; this is the last
4
+ * line for what the server cannot see: the agent-scope `MODEL_OVERRIDE` fallback
5
+ * (a worker whose harness changed while the override still names the old family),
6
+ * and a task model from an API that predates the claim-time backstop.
7
+ *
8
+ * Worker-safe: no `src/be` imports; judges against the runtime catalog.
9
+ */
10
+ import { harnessModelMismatch } from "@desplega/model-catalog";
11
+ import { runtimeHarnessSections } from "./runtime-model-catalog";
12
+
13
+ export type SpawnModelDecision =
14
+ | { kind: "ok"; model: string; warning?: string }
15
+ | { kind: "mismatch"; reason: string };
16
+
17
+ /** Same literal as the server's claim-time reason (`harnessMismatchReason`). */
18
+ export function spawnHarnessMismatchReason(model: string, harness: string): string {
19
+ return `[model-harness-mismatch] Model "${model}" does not run on the ${harness} harness of this worker. The task pinned a model from another harness family. Re-create the task with modelTier or with a ${harness} model.`;
20
+ }
21
+
22
+ export function guardSpawnModel(input: {
23
+ taskModel: string;
24
+ configModel: string;
25
+ harness: string;
26
+ role: string;
27
+ }): SpawnModelDecision {
28
+ const { taskModel, configModel, harness, role } = input;
29
+ const sections = runtimeHarnessSections();
30
+ if (taskModel) {
31
+ return harnessModelMismatch(taskModel, harness, sections)
32
+ ? { kind: "mismatch", reason: spawnHarnessMismatchReason(taskModel, harness) }
33
+ : { kind: "ok", model: taskModel };
34
+ }
35
+ if (configModel && harnessModelMismatch(configModel, harness, sections)) {
36
+ return {
37
+ kind: "ok",
38
+ model: "",
39
+ warning: `[${role}] MODEL_OVERRIDE ${configModel} does not run on the ${harness} harness; using the adapter default`,
40
+ };
41
+ }
42
+ return { kind: "ok", model: configModel };
43
+ }
@@ -11,6 +11,8 @@
11
11
  * the operator can always pick it on the dashboard.
12
12
  */
13
13
 
14
+ import type { ModelFamily } from "./model-rate-limit-windows";
15
+
14
16
  export interface SubscriptionPlan {
15
17
  id: string;
16
18
  label: string;
@@ -18,6 +20,8 @@ export interface SubscriptionPlan {
18
20
  keyType: "CLAUDE_CODE_OAUTH_TOKEN" | "CODEX_OAUTH";
19
21
  /** Monthly list price in USD, billed monthly. */
20
22
  monthlyUsd: number;
23
+ /** Model families the plan cannot run. Absent means the plan runs every family. */
24
+ excludedModelFamilies?: readonly ModelFamily[];
21
25
  }
22
26
 
23
27
  /** List prices from claude.com/pricing and chatgpt.com/pricing, checked 2026-09-25. */
@@ -42,6 +46,9 @@ export const SUBSCRIPTION_PLANS: readonly SubscriptionPlan[] = [
42
46
  label: "Claude Team, standard seat",
43
47
  keyType: "CLAUDE_CODE_OAUTH_TOKEN",
44
48
  monthlyUsd: 25,
49
+ // The CLI rejects Fable on this seat with `errorCode: "credits_required"`
50
+ // ("Fable 5.1 requires usage credits"), observed 2026-09-30.
51
+ excludedModelFamilies: ["fable"],
45
52
  },
46
53
  {
47
54
  id: "claude_team_premium",
@@ -74,6 +81,21 @@ export function isSubscriptionPlanId(id: string): boolean {
74
81
  return SUBSCRIPTION_PLANS.some((plan) => plan.id === id);
75
82
  }
76
83
 
84
+ /**
85
+ * Whether a key on `plan` can run `family`. Fails open: a null plan, a plan
86
+ * id the map does not know, or an undefined family returns true. Returns
87
+ * false only when the plan record lists the family in `excludedModelFamilies`.
88
+ */
89
+ export function planAllowsModelFamily(
90
+ plan: string | null | undefined,
91
+ family: ModelFamily | undefined,
92
+ ): boolean {
93
+ if (!plan || !family) return true;
94
+ const record = SUBSCRIPTION_PLANS.find((p) => p.id === plan);
95
+ if (!record?.excludedModelFamilies) return true;
96
+ return !record.excludedModelFamilies.includes(family);
97
+ }
98
+
77
99
  /**
78
100
  * Map the `chatgpt_plan_type` JWT claim to a plan id. The claim says `pro`
79
101
  * for both Pro tiers, so `pro` maps to the 20x plan (the operator can change
@@ -0,0 +1,39 @@
1
+ import type { WorkflowDefinition } from "../types";
2
+ import type { ApprovalOutcome, ResolvedApproval } from "./executors/base";
3
+ import type { ExecutorRegistry } from "./executors/registry";
4
+
5
+ /**
6
+ * Step output and output port for a step whose approval request resolved.
7
+ *
8
+ * Both resume paths (the live `approval.resolved` event and the recovery sweep)
9
+ * go through here so they cannot disagree. A `human-in-the-loop` step gets its
10
+ * own output on the port named after the status. An executor that parked on the
11
+ * same approval machinery (`system-one-decision` with `humanReview`) shapes its
12
+ * own output from what it stored before it parked.
13
+ */
14
+ export function shapeApprovalResolution(
15
+ registry: ExecutorRegistry,
16
+ definition: Pick<WorkflowDefinition, "nodes">,
17
+ nodeId: string,
18
+ parkedOutput: unknown,
19
+ approval: ResolvedApproval,
20
+ ): ApprovalOutcome {
21
+ const node = definition.nodes.find((candidate) => candidate.id === nodeId);
22
+ if (node && registry.has(node.type)) {
23
+ const shaped = registry.get(node.type).resolveApproval(parkedOutput, approval);
24
+ if (shaped) return shaped;
25
+ }
26
+ return {
27
+ output: {
28
+ requestId: approval.requestId,
29
+ status: approval.status,
30
+ responses: approval.responses,
31
+ },
32
+ nextPort:
33
+ approval.status === "timeout"
34
+ ? "timeout"
35
+ : approval.status === "rejected"
36
+ ? "rejected"
37
+ : "approved",
38
+ };
39
+ }
@@ -6,6 +6,10 @@ import type {
6
6
  WorkflowPatch,
7
7
  } from "../types";
8
8
  import type { ExecutorRegistry } from "./executors/registry";
9
+ import {
10
+ systemOneRetryViolations,
11
+ systemOneStaticShapeViolations,
12
+ } from "./executors/system-one-decision";
9
13
 
10
14
  /** Extract all target node IDs from a node's `next` field */
11
15
  export function getNextTargets(next: string | string[] | Record<string, string>): string[] {
@@ -206,6 +210,9 @@ export function validateDefinition(
206
210
  `Node "${node.id}" human-in-the-loop config.questions must be an array or one exact {{interpolation}} token`,
207
211
  );
208
212
  }
213
+ // A system-one-decision node's question ids and return types are static, and it never carries
214
+ // an engine retry policy (it retries transient transport errors itself).
215
+ errors.push(...systemOneStaticShapeViolations(node), ...systemOneRetryViolations(node));
209
216
  if (node.type === "foreach") {
210
217
  validateForeachNode(node, errors);
211
218
  // A legacy `#` id may stay editable as a NORMAL node, but never as a foreach
@@ -28,10 +28,16 @@ import { shouldSkipCooldown } from "./cooldown";
28
28
  import { findEntryNodes, getNextTargets, getSuccessors, resolveValidationPort } from "./definition";
29
29
  import type { AsyncExecutorResult } from "./executors/base";
30
30
  import type { ExecutorRegistry } from "./executors/registry";
31
+ import {
32
+ SYSTEM_ONE_DECISION_NODE_TYPE,
33
+ systemOneRetryViolations,
34
+ systemOneUnresolvedError,
35
+ } from "./executors/system-one-decision";
31
36
  import { FOREACH_TERMINAL_STEP_STATUSES, resolveForeachParent } from "./foreach-join";
32
37
  import { getSecretInputKeys, redactSecretsForStorage, resolveInputs } from "./input";
33
38
  import { validateJsonSchema } from "./json-schema-validator";
34
39
  import { getMaxWorkflowStepsPerRun } from "./limits";
40
+ import { findWorkflowReadinessProblems, formatReadinessRunError } from "./readiness";
35
41
  import { deepInterpolate } from "./template";
36
42
  import { runStepValidation, type ValidationRunResult } from "./validation";
37
43
 
@@ -149,6 +155,18 @@ export async function startWorkflowExecution(
149
155
  triggerType: options.triggerType ?? "manual",
150
156
  });
151
157
 
158
+ // An executor that cannot run (for example a system-one-decision node with no API key) fails
159
+ // the run here, before any node has side effects, instead of partway through.
160
+ const notReady = await findWorkflowReadinessProblems(workflow.definition, registry);
161
+ if (notReady.length > 0) {
162
+ await updateWorkflowRun(runId, {
163
+ status: "failed",
164
+ error: formatReadinessRunError(notReady),
165
+ finishedAt: new Date().toISOString(),
166
+ });
167
+ return runId;
168
+ }
169
+
152
170
  // Resolve inputs and merge into initial context
153
171
  const ctx: Record<string, unknown> = { trigger: triggerData };
154
172
  if (options.requestedByUserId) {
@@ -744,6 +762,15 @@ async function runClaimedStep(
744
762
  // 3. Get executor
745
763
  const executor = registry.get(node.type);
746
764
 
765
+ // 3a. A system-one-decision node retries transient transport errors itself (config.maxRetries).
766
+ // Engine retries are not status-aware, so a policy here would re-send rejected
767
+ // requests and multiply paid attempts. Fail before any request is built.
768
+ const systemOneRetryProblems = systemOneRetryViolations(node);
769
+ if (systemOneRetryProblems.length > 0) {
770
+ await checkpointStepFailure(runId, stepId, systemOneRetryProblems.join("; "), 0);
771
+ return { outcome: "failed", successors: [] };
772
+ }
773
+
747
774
  // 3b. Build local interpolation context from explicit inputs mapping
748
775
  const interpolationCtx = buildNodeInterpolationCtx(node, ctx);
749
776
 
@@ -765,6 +792,7 @@ async function runClaimedStep(
765
792
  value: interpolatedValue,
766
793
  unresolved,
767
794
  scriptBodyUnresolved,
795
+ strictUnresolved,
768
796
  } = interpolateNodeConfig(node, interpolationCtx);
769
797
  const interpolatedConfig = interpolatedValue as Record<string, unknown>;
770
798
  const executionCtx: Record<string, unknown> = { ...ctx, ...interpolationCtx };
@@ -775,6 +803,17 @@ async function runClaimedStep(
775
803
  return { outcome: "failed", successors: [] };
776
804
  }
777
805
 
806
+ // A paid, non-idempotent call must not go out with a blanked field.
807
+ if (strictUnresolved && strictUnresolved.length > 0) {
808
+ await checkpointStepFailure(
809
+ runId,
810
+ stepId,
811
+ systemOneUnresolvedError(node.id, strictUnresolved),
812
+ 0,
813
+ );
814
+ return { outcome: "failed", successors: [] };
815
+ }
816
+
778
817
  if (unresolved.length > 0) {
779
818
  console.warn(
780
819
  `[workflow] Step ${node.id}: unresolved interpolation tokens: ${unresolved.join(", ")}`,
@@ -1063,7 +1102,36 @@ function buildScriptBodyCtx(
1063
1102
  export function interpolateNodeConfig(
1064
1103
  node: Pick<WorkflowNode, "type" | "config" | "inputs">,
1065
1104
  interpolationCtx: Record<string, unknown>,
1066
- ): { value: unknown; unresolved: string[]; scriptBodyUnresolved?: string[] } {
1105
+ ): {
1106
+ value: unknown;
1107
+ unresolved: string[];
1108
+ scriptBodyUnresolved?: string[];
1109
+ /** Tokens that must resolve before dispatch; the caller fails the step when non-empty. */
1110
+ strictUnresolved?: string[];
1111
+ } {
1112
+ if (node.type === SYSTEM_ONE_DECISION_NODE_TYPE) {
1113
+ // `state` and `questions` keep a whole-token reference's JSON type (an object
1114
+ // stays an object; string interpolation would flatten it to JSON text). The
1115
+ // resolved value is data, never re-interpolated, so a literal `{{` inside a
1116
+ // lead's text is not read as another template. Every other field, and every
1117
+ // unresolved token anywhere in the config, is strict: no blank reaches the API.
1118
+ const { state, questions, ...rest } = node.config;
1119
+ const restResult = deepInterpolate(rest, interpolationCtx);
1120
+ const dynamic: Record<string, unknown> = {};
1121
+ if (Object.hasOwn(node.config, "state")) dynamic.state = state;
1122
+ if (Object.hasOwn(node.config, "questions")) dynamic.questions = questions;
1123
+ const dynamicResult = deepInterpolate(dynamic, interpolationCtx, { preserveRawTokens: true });
1124
+ const unresolved = [...restResult.unresolved, ...dynamicResult.unresolved];
1125
+ return {
1126
+ value: {
1127
+ ...(restResult.value as Record<string, unknown>),
1128
+ ...(dynamicResult.value as Record<string, unknown>),
1129
+ },
1130
+ unresolved,
1131
+ strictUnresolved: unresolved,
1132
+ };
1133
+ }
1134
+
1067
1135
  if (node.type === "foreach" && Object.hasOwn(node.config, "over")) {
1068
1136
  const { over, body, ...configWithoutOverAndBody } = node.config;
1069
1137
  const configResult = deepInterpolate(configWithoutOverAndBody, interpolationCtx);
@@ -1,8 +1,8 @@
1
1
  import { z } from "zod";
2
+ import { explicitModelErrorForAgent } from "../../be/model-validation";
2
3
  import { applyPreTaskCreate } from "../../extensions/apply-task-create";
3
4
  import { workflowContextKey } from "../../tasks/context-key";
4
5
  import { TaskCreationBlockedError } from "../../tasks/errors";
5
- import { withSiblingAwareness } from "../../tasks/sibling-awareness";
6
6
  import type { ExecutorMeta } from "../../types";
7
7
  import {
8
8
  FollowUpConfigSchema,
@@ -30,6 +30,8 @@ export const AgentTaskConfigSchema = z.object({
30
30
  vcsRepo: z.string().min(1).optional(),
31
31
  model: z.string().min(1).optional(),
32
32
  modelTier: ModelTierSchema.optional(),
33
+ /** Accept a `model` the catalog does not list (a custom id). Without it an unknown id fails the node. */
34
+ allowCustomModel: z.boolean().optional(),
33
35
  effort: ReasoningEffortSchema.optional(),
34
36
  parentTaskId: z.string().uuid().optional(),
35
37
  requestedByUserId: z.string().optional(),
@@ -98,6 +100,12 @@ export class AgentTaskExecutor extends BaseExecutor<
98
100
  }
99
101
 
100
102
  // 3. Create the task (config is already deep-interpolated by the engine)
103
+ const modelError = await explicitModelErrorForAgent({
104
+ model: splitLegacyModelAlias({ model: config.model, modelTier: config.modelTier }).model,
105
+ allowCustomModel: config.allowCustomModel,
106
+ agentId: config.agentId,
107
+ });
108
+ if (modelError) return { status: "failed", error: modelError };
101
109
  const preCreate = await applyPreTaskCreate({
102
110
  description: config.template,
103
111
  options: {
@@ -128,15 +136,17 @@ export class AgentTaskExecutor extends BaseExecutor<
128
136
  contextKey: workflowContextKey({ workflowRunId: meta.runId }),
129
137
  },
130
138
  origin: "workflow",
139
+ allowCustomModel: config.allowCustomModel,
131
140
  });
132
141
  if (preCreate.kind === "blocked") {
133
142
  throw new TaskCreationBlockedError(preCreate.reason, preCreate.extension);
134
143
  }
135
- const { description: taskDescription, options: taskOptions } = await withSiblingAwareness(
136
- preCreate.description,
137
- preCreate.options,
138
- );
139
- const task = await db.createTaskExtended(taskDescription, taskOptions);
144
+ // No sibling awareness: every task of a run shares this contextKey, so its
145
+ // siblings are the engine's own parallel steps, not new user input. The
146
+ // block handed each child its siblings' task ids, and one child completed
147
+ // a sibling's task with its own report. It would also wire parentTaskId to
148
+ // a concurrent same-agent sibling's session.
149
+ const task = await db.createTaskExtended(preCreate.description, preCreate.options);
140
150
 
141
151
  // 4. Return async result — engine will pause the workflow
142
152
  return {
@@ -10,6 +10,34 @@ export interface ExecutorDependencies {
10
10
  interpolate: (template: string, ctx: Record<string, unknown>) => string;
11
11
  }
12
12
 
13
+ // ─── Readiness ─────────────────────────────────────────────
14
+
15
+ export interface ExecutorReadinessNode {
16
+ id: string;
17
+ config: Record<string, unknown>;
18
+ }
19
+
20
+ export interface ExecutorReadinessProblem {
21
+ /** Nodes that hit this problem. */
22
+ nodeIds: string[];
23
+ message: string;
24
+ }
25
+
26
+ // ─── Approval resolution ───────────────────────────────────
27
+
28
+ /** A resolved approval request, as the resume and recovery paths see it. */
29
+ export interface ResolvedApproval {
30
+ requestId: string;
31
+ status: "approved" | "rejected" | "timeout";
32
+ responses: Record<string, unknown> | null;
33
+ }
34
+
35
+ /** The step output and output port an executor chooses once its approval resolves. */
36
+ export interface ApprovalOutcome {
37
+ output: unknown;
38
+ nextPort: string;
39
+ }
40
+
13
41
  // ─── Input / Result Types ──────────────────────────────────
14
42
 
15
43
  export interface ExecutorInput {
@@ -47,6 +75,31 @@ export abstract class BaseExecutor<
47
75
 
48
76
  constructor(protected readonly deps: ExecutorDependencies) {}
49
77
 
78
+ /**
79
+ * Can this executor run the given nodes right now? Return one problem per
80
+ * distinct missing prerequisite (for example a credential), or an empty array.
81
+ * The engine asks before a run starts, and authoring surfaces ask after a
82
+ * save, so a missing prerequisite surfaces before any node has side effects.
83
+ * Reads raw (pre-interpolation) node config. Must not call external services
84
+ * and must not put secret values in a message.
85
+ */
86
+ async checkReadiness(
87
+ _nodes: readonly ExecutorReadinessNode[],
88
+ ): Promise<ExecutorReadinessProblem[]> {
89
+ return [];
90
+ }
91
+
92
+ /**
93
+ * An executor that parks its step on a human-in-the-loop approval decides the
94
+ * step's output and port once a person answers. `parkedOutput` is the step
95
+ * output it stored before it parked. Return null to keep the default: the
96
+ * human-in-the-loop output (`{ requestId, status, responses }`) on the port
97
+ * named after the status.
98
+ */
99
+ resolveApproval(_parkedOutput: unknown, _approval: ResolvedApproval): ApprovalOutcome | null {
100
+ return null;
101
+ }
102
+
50
103
  /**
51
104
  * Validate config, execute, validate output.
52
105
  * Catches Zod validation errors at both boundaries.
@@ -59,17 +59,22 @@ const QuestionSchema = z.discriminatedUnion("type", [
59
59
  }),
60
60
  ]);
61
61
 
62
- const ApproverConfigSchema = z.object({
62
+ export const ApproverConfigSchema = z.object({
63
63
  users: z.array(z.string()).optional(),
64
64
  roles: z.array(z.string()).optional(),
65
65
  policy: z.union([z.literal("any"), z.literal("all"), z.object({ min: z.number().int().min(1) })]),
66
66
  });
67
67
 
68
- const NotificationConfigSchema = z.object({
68
+ export const NotificationConfigSchema = z.object({
69
69
  channel: z.enum(["slack", "email"]),
70
70
  target: z.string(),
71
71
  });
72
72
 
73
+ export const HITLTimeoutSchema = z.object({
74
+ seconds: z.number().int().min(1),
75
+ action: z.literal("reject"),
76
+ });
77
+
73
78
  type HITLQuestion = z.infer<typeof QuestionSchema>;
74
79
 
75
80
  /** Exactly one `{{path}}` token: the dynamic-questions form. */
@@ -103,12 +108,7 @@ const HITLConfigSchema = z.object({
103
108
  ),
104
109
  ]),
105
110
  approvers: ApproverConfigSchema,
106
- timeout: z
107
- .object({
108
- seconds: z.number().int().min(1),
109
- action: z.literal("reject"),
110
- })
111
- .optional(),
111
+ timeout: HITLTimeoutSchema.optional(),
112
112
  notifications: z.array(NotificationConfigSchema).optional(),
113
113
  });
114
114
 
@@ -13,6 +13,7 @@ export { RawLlmExecutor } from "./raw-llm";
13
13
  export { createExecutorRegistry, ExecutorRegistry } from "./registry";
14
14
  export { ScriptExecutor } from "./script";
15
15
  export { SwarmScriptExecutor } from "./swarm-script";
16
+ export { SystemOneDecisionExecutor } from "./system-one-decision";
16
17
  export { ValidateExecutor } from "./validate";
17
18
  export { VcsExecutor } from "./vcs";
18
19
  export { WaitExecutor } from "./wait";
@@ -9,6 +9,7 @@ import { PropertyMatchExecutor } from "./property-match";
9
9
  import { RawLlmExecutor } from "./raw-llm";
10
10
  import { ScriptExecutor } from "./script";
11
11
  import { SwarmScriptExecutor } from "./swarm-script";
12
+ import { SystemOneDecisionExecutor } from "./system-one-decision";
12
13
  import { ValidateExecutor } from "./validate";
13
14
  import { VcsExecutor } from "./vcs";
14
15
  import { WaitExecutor } from "./wait";
@@ -69,6 +70,7 @@ export function createExecutorRegistry(deps: ExecutorDependencies): ExecutorRegi
69
70
  registry.register(new CodeMatchExecutor(deps));
70
71
  registry.register(new NotifyExecutor(deps));
71
72
  registry.register(new RawLlmExecutor(deps));
73
+ registry.register(new SystemOneDecisionExecutor(deps));
72
74
  registry.register(new ScriptExecutor(deps));
73
75
  registry.register(new SwarmScriptExecutor(deps));
74
76
  registry.register(new VcsExecutor(deps));