@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,756 @@
1
+ import {
2
+ buildModelsDevSeedRows,
3
+ loadModelsDevCache
4
+ } from "./cli-vctdqs5h.js";
5
+ import {
6
+ createLogEntry,
7
+ getActivePricingRow,
8
+ getDbClient,
9
+ init_db,
10
+ insertPricingRow
11
+ } from "./cli-qydf22qq.js";
12
+ import {
13
+ init_runtime_model_catalog,
14
+ setRuntimeModelCatalog
15
+ } from "./cli-kmcpfwn2.js";
16
+ import {
17
+ init_secret_scrubber,
18
+ scrubSecrets
19
+ } from "./cli-r2ap2czm.js";
20
+
21
+ // src/be/pricing-refresh.ts
22
+ init_secret_scrubber();
23
+ init_db();
24
+
25
+ // src/be/model-catalog-store.ts
26
+ init_runtime_model_catalog();
27
+ init_db();
28
+
29
+ // src/be/models-catalog.ts
30
+ var CATALOG_PROVIDER_IDS = [
31
+ "openrouter",
32
+ "anthropic",
33
+ "openai",
34
+ "amazon-bedrock",
35
+ "opencode"
36
+ ];
37
+ var PINNED_MODELSDEV_ENTRIES = [
38
+ "anthropic/claude-opus-5-5",
39
+ "anthropic/claude-sonnet-5-5",
40
+ "anthropic/claude-mythos-5",
41
+ "anthropic/claude-sonnet-5",
42
+ "amazon-bedrock/anthropic.claude-sonnet-5",
43
+ "openai/gpt-5-codex",
44
+ "openai/gpt-5.1-codex",
45
+ "openai/gpt-5.1-codex-max",
46
+ "openai/gpt-5.1-codex-mini",
47
+ "openai/gpt-5.2-codex"
48
+ ];
49
+ function slimModel(modelKey, model) {
50
+ const reasoningOptions = Array.isArray(model.reasoning_options) ? model.reasoning_options.filter((o) => typeof o.type === "string").map((o) => ({
51
+ type: o.type,
52
+ ...Array.isArray(o.values) ? { values: o.values } : {}
53
+ })) : [];
54
+ return {
55
+ id: model.id ?? modelKey,
56
+ ...model.name !== undefined ? { name: model.name } : {},
57
+ ...model.cost !== undefined ? {
58
+ cost: {
59
+ input: model.cost.input,
60
+ output: model.cost.output,
61
+ ...model.cost.cache_read !== undefined ? { cache_read: model.cost.cache_read } : {},
62
+ ...model.cost.cache_write !== undefined ? { cache_write: model.cost.cache_write } : {}
63
+ }
64
+ } : {},
65
+ ...model.limit?.context !== undefined ? { limit: { context: model.limit.context } } : {},
66
+ ...typeof model.release_date === "string" ? { release_date: model.release_date } : {},
67
+ ...typeof model.status === "string" ? { status: model.status } : {},
68
+ ...model.reasoning !== undefined ? { reasoning: model.reasoning } : {},
69
+ ...reasoningOptions.length > 0 ? { reasoning_options: reasoningOptions } : {}
70
+ };
71
+ }
72
+ function buildModelsCatalog(cache) {
73
+ const catalog = {};
74
+ for (const providerId of CATALOG_PROVIDER_IDS) {
75
+ const provider = cache[providerId];
76
+ if (!provider?.models)
77
+ continue;
78
+ const models = {};
79
+ for (const [modelKey, model] of Object.entries(provider.models)) {
80
+ models[modelKey] = slimModel(modelKey, model);
81
+ }
82
+ catalog[providerId] = {
83
+ id: provider.id ?? providerId,
84
+ ...provider.name !== undefined ? { name: provider.name } : {},
85
+ models
86
+ };
87
+ }
88
+ return catalog;
89
+ }
90
+ var liveCatalog = null;
91
+ var liveUpdatedAt = null;
92
+ var snapshotCatalog;
93
+ function bundledSnapshotCatalog() {
94
+ if (snapshotCatalog === undefined) {
95
+ const raw = loadModelsDevCache();
96
+ snapshotCatalog = raw ? buildModelsCatalog(raw) : null;
97
+ }
98
+ return snapshotCatalog ?? {};
99
+ }
100
+ function mergePinnedEntries(catalog) {
101
+ const snapshot = bundledSnapshotCatalog();
102
+ for (const entryPath of PINNED_MODELSDEV_ENTRIES) {
103
+ const slashIndex = entryPath.indexOf("/");
104
+ const providerId = entryPath.slice(0, slashIndex);
105
+ const modelId = entryPath.slice(slashIndex + 1);
106
+ if (catalog[providerId]?.models[modelId])
107
+ continue;
108
+ const pinned = snapshot[providerId]?.models[modelId];
109
+ if (!pinned)
110
+ continue;
111
+ catalog[providerId] ??= { id: providerId, models: {} };
112
+ catalog[providerId].models[modelId] = pinned;
113
+ }
114
+ }
115
+ function setLiveModelsCatalog(catalog, updatedAt) {
116
+ mergePinnedEntries(catalog);
117
+ liveCatalog = catalog;
118
+ liveUpdatedAt = updatedAt;
119
+ }
120
+ function invalidateModelsCatalog() {
121
+ liveCatalog = null;
122
+ liveUpdatedAt = null;
123
+ snapshotOverride = null;
124
+ }
125
+ function snapshotModelsCatalog() {
126
+ return structuredClone(bundledSnapshotCatalog());
127
+ }
128
+ var snapshotOverride = null;
129
+ function setSnapshotOverlayCatalog(catalog) {
130
+ snapshotOverride = catalog;
131
+ }
132
+ function getModelsCatalog() {
133
+ if (liveCatalog) {
134
+ return { source: "live", updatedAt: liveUpdatedAt, providers: liveCatalog };
135
+ }
136
+ return {
137
+ source: "snapshot",
138
+ updatedAt: null,
139
+ providers: snapshotOverride ?? bundledSnapshotCatalog()
140
+ };
141
+ }
142
+
143
+ // src/be/model-catalog-store.ts
144
+ var FACT_KEYS = [
145
+ "name",
146
+ "family",
147
+ "releaseDate",
148
+ "contextWindow",
149
+ "maxOutput",
150
+ "reasoning",
151
+ "reasoningOptions",
152
+ "pricing",
153
+ "status"
154
+ ];
155
+ function parseJson(value) {
156
+ if (value === null)
157
+ return null;
158
+ try {
159
+ return JSON.parse(value);
160
+ } catch {
161
+ return null;
162
+ }
163
+ }
164
+ function rowToFacts(row) {
165
+ return {
166
+ name: row.name,
167
+ family: row.family,
168
+ releaseDate: row.releaseDate,
169
+ contextWindow: row.contextWindow,
170
+ maxOutput: row.maxOutput,
171
+ reasoning: row.reasoning === null ? null : row.reasoning === 1,
172
+ reasoningOptions: parseJson(row.reasoningOptions),
173
+ pricing: parseJson(row.pricing),
174
+ status: row.status
175
+ };
176
+ }
177
+ function factParams(facts) {
178
+ return [
179
+ facts.name ?? null,
180
+ facts.family ?? null,
181
+ facts.releaseDate ?? null,
182
+ facts.contextWindow ?? null,
183
+ facts.maxOutput ?? null,
184
+ facts.reasoning === undefined || facts.reasoning === null ? null : facts.reasoning ? 1 : 0,
185
+ facts.reasoningOptions ? JSON.stringify(facts.reasoningOptions) : null,
186
+ facts.pricing ? JSON.stringify(facts.pricing) : null,
187
+ facts.status ?? null
188
+ ];
189
+ }
190
+ function pickNumber(value) {
191
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
192
+ }
193
+ function buildCatalogEntries(cache, checkedAt) {
194
+ const entries = [];
195
+ for (const providerId of CATALOG_PROVIDER_IDS) {
196
+ const provider = cache[providerId];
197
+ if (!provider?.models)
198
+ continue;
199
+ for (const [modelKey, rawModel] of Object.entries(provider.models)) {
200
+ const model = rawModel;
201
+ const reasoningOptions = Array.isArray(model.reasoning_options) ? model.reasoning_options.filter((o) => typeof o?.type === "string").map((o) => ({
202
+ type: o.type,
203
+ ...Array.isArray(o.values) ? { values: o.values } : {}
204
+ })) : [];
205
+ const pricing = {};
206
+ for (const key of ["input", "output", "cache_read", "cache_write"]) {
207
+ const value = pickNumber(model.cost?.[key]);
208
+ if (value !== undefined)
209
+ pricing[key] = value;
210
+ }
211
+ entries.push({
212
+ provider: providerId,
213
+ modelId: modelKey,
214
+ providerName: provider.name ?? null,
215
+ name: model.name ?? null,
216
+ family: model.family ?? null,
217
+ releaseDate: model.release_date ?? null,
218
+ contextWindow: pickNumber(model.limit?.context) ?? null,
219
+ maxOutput: pickNumber(model.limit?.output) ?? null,
220
+ reasoning: typeof model.reasoning === "boolean" ? model.reasoning : null,
221
+ reasoningOptions: reasoningOptions.length > 0 ? reasoningOptions : null,
222
+ pricing: Object.keys(pricing).length > 0 ? pricing : null,
223
+ status: model.status ?? null,
224
+ checkedAt
225
+ });
226
+ }
227
+ }
228
+ return entries;
229
+ }
230
+ async function replaceModelCatalog(entries) {
231
+ return await getDbClient().transaction(async (tx) => {
232
+ const before = await tx.query("SELECT provider, modelId FROM model_catalog");
233
+ const known = new Set(before.map((r) => `${r.provider}/${r.modelId}`));
234
+ await tx.run("DELETE FROM model_catalog");
235
+ const added = [];
236
+ for (const entry of entries) {
237
+ await tx.run(`INSERT INTO model_catalog
238
+ (provider, modelId, name, family, releaseDate, contextWindow, maxOutput, reasoning,
239
+ reasoningOptions, pricing, status, providerName, checkedAt)
240
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
241
+ entry.provider,
242
+ entry.modelId,
243
+ ...factParams(entry),
244
+ entry.providerName ?? null,
245
+ entry.checkedAt
246
+ ]);
247
+ const key = `${entry.provider}/${entry.modelId}`;
248
+ if (known.size > 0 && !known.has(key))
249
+ added.push(key);
250
+ }
251
+ return added;
252
+ });
253
+ }
254
+ async function touchModelCatalogCheckedAt(now) {
255
+ await getDbClient().run("UPDATE model_catalog SET checkedAt = ?", [now]);
256
+ }
257
+ async function listModelCatalog() {
258
+ const rows = await getDbClient().query("SELECT * FROM model_catalog ORDER BY provider, modelId");
259
+ return rows.map((row) => ({
260
+ provider: row.provider,
261
+ modelId: row.modelId,
262
+ providerName: row.providerName,
263
+ checkedAt: row.checkedAt,
264
+ ...rowToFacts(row)
265
+ }));
266
+ }
267
+ async function countModelCatalog() {
268
+ const row = await getDbClient().get("SELECT COUNT(*) AS n FROM model_catalog");
269
+ return row?.n ?? 0;
270
+ }
271
+ async function getModelCatalogMeta() {
272
+ const row = await getDbClient().get("SELECT etag, lastModified, lastFetchAt, lastCheckedAt FROM model_catalog_meta WHERE id = 1");
273
+ return row ?? { etag: null, lastModified: null, lastFetchAt: null, lastCheckedAt: null };
274
+ }
275
+ async function updateModelCatalogMeta(patch) {
276
+ const current = await getModelCatalogMeta();
277
+ const next = { ...current, ...patch };
278
+ await getDbClient().run(`INSERT INTO model_catalog_meta (id, etag, lastModified, lastFetchAt, lastCheckedAt)
279
+ VALUES (1, ?, ?, ?, ?)
280
+ ON CONFLICT(id) DO UPDATE SET
281
+ etag = excluded.etag,
282
+ lastModified = excluded.lastModified,
283
+ lastFetchAt = excluded.lastFetchAt,
284
+ lastCheckedAt = excluded.lastCheckedAt`, [next.etag, next.lastModified, next.lastFetchAt, next.lastCheckedAt]);
285
+ }
286
+ function overlayRowToEntry(row) {
287
+ return {
288
+ provider: row.provider,
289
+ modelId: row.modelId,
290
+ reason: row.reason,
291
+ verifiedBy: row.verifiedBy,
292
+ expiresWhenUpstreamMatches: row.expiresWhenUpstreamMatches === 1,
293
+ createdAt: row.createdAt,
294
+ updatedAt: row.updatedAt,
295
+ ...rowToFacts(row)
296
+ };
297
+ }
298
+ async function listModelCatalogOverlay() {
299
+ const rows = await getDbClient().query("SELECT * FROM model_catalog_overlay ORDER BY provider, modelId");
300
+ return rows.map(overlayRowToEntry);
301
+ }
302
+ async function upsertModelCatalogOverlay(input, opts = {}) {
303
+ const now = opts.now ?? Date.now();
304
+ const userId = opts.userId ?? null;
305
+ await getDbClient().run(`INSERT INTO model_catalog_overlay
306
+ (provider, modelId, name, family, releaseDate, contextWindow, maxOutput, reasoning,
307
+ reasoningOptions, pricing, status, reason, verifiedBy, expiresWhenUpstreamMatches,
308
+ createdAt, updatedAt, created_by, updated_by)
309
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
310
+ ON CONFLICT(provider, modelId) DO UPDATE SET
311
+ name = excluded.name,
312
+ family = excluded.family,
313
+ releaseDate = excluded.releaseDate,
314
+ contextWindow = excluded.contextWindow,
315
+ maxOutput = excluded.maxOutput,
316
+ reasoning = excluded.reasoning,
317
+ reasoningOptions = excluded.reasoningOptions,
318
+ pricing = excluded.pricing,
319
+ status = excluded.status,
320
+ reason = excluded.reason,
321
+ verifiedBy = excluded.verifiedBy,
322
+ expiresWhenUpstreamMatches = excluded.expiresWhenUpstreamMatches,
323
+ updatedAt = excluded.updatedAt,
324
+ updated_by = excluded.updated_by`, [
325
+ input.provider,
326
+ input.modelId,
327
+ ...factParams(input),
328
+ input.reason,
329
+ input.verifiedBy ?? null,
330
+ input.expiresWhenUpstreamMatches === false ? 0 : 1,
331
+ now,
332
+ now,
333
+ userId,
334
+ userId
335
+ ]);
336
+ await applyOverlayPricingRows(now);
337
+ markModelsCatalogStale();
338
+ const row = await getDbClient().get("SELECT * FROM model_catalog_overlay WHERE provider = ? AND modelId = ?", [input.provider, input.modelId]);
339
+ if (!row)
340
+ throw new Error("overlay row vanished after upsert");
341
+ return overlayRowToEntry(row);
342
+ }
343
+ async function deleteModelCatalogOverlay(provider, modelId) {
344
+ const result = await getDbClient().run("DELETE FROM model_catalog_overlay WHERE provider = ? AND modelId = ?", [provider, modelId]);
345
+ markModelsCatalogStale();
346
+ return result.changes > 0;
347
+ }
348
+ function factEquals(key, overlay, upstream) {
349
+ if (key === "pricing") {
350
+ const o = overlay;
351
+ const u = upstream ?? {};
352
+ return Object.keys(o).every((k) => o[k] === undefined || o[k] === null || o[k] === u[k]);
353
+ }
354
+ if (key === "reasoningOptions")
355
+ return JSON.stringify(overlay) === JSON.stringify(upstream);
356
+ return overlay === upstream;
357
+ }
358
+ async function expireMatchedOverlays() {
359
+ const overlays = await listModelCatalogOverlay();
360
+ if (overlays.length === 0)
361
+ return [];
362
+ const upstream = new Map((await listModelCatalog()).map((e) => [`${e.provider}/${e.modelId}`, e]));
363
+ const expired = [];
364
+ for (const overlay of overlays) {
365
+ if (!overlay.expiresWhenUpstreamMatches)
366
+ continue;
367
+ const key = `${overlay.provider}/${overlay.modelId}`;
368
+ const up = upstream.get(key);
369
+ if (!up)
370
+ continue;
371
+ const matches = FACT_KEYS.every((fact) => {
372
+ const value = overlay[fact];
373
+ if (value === null || value === undefined)
374
+ return true;
375
+ return factEquals(fact, value, up[fact]);
376
+ });
377
+ if (!matches)
378
+ continue;
379
+ await deleteModelCatalogOverlay(overlay.provider, overlay.modelId);
380
+ expired.push(key);
381
+ }
382
+ return expired;
383
+ }
384
+ async function applyOverlayPricingRows(now = Date.now()) {
385
+ const overlays = await listModelCatalogOverlay();
386
+ const upstream = new Set((await listModelCatalog()).map((e) => `${e.provider}/${e.modelId}`));
387
+ const synthetic = {};
388
+ const overlayOnly = {};
389
+ for (const overlay of overlays) {
390
+ if (!overlay.pricing || Object.keys(overlay.pricing).length === 0)
391
+ continue;
392
+ for (const target of upstream.has(`${overlay.provider}/${overlay.modelId}`) ? [synthetic] : [synthetic, overlayOnly]) {
393
+ target[overlay.provider] ??= { models: {} };
394
+ const models = target[overlay.provider]?.models;
395
+ if (models)
396
+ models[overlay.modelId] = { cost: overlay.pricing };
397
+ }
398
+ }
399
+ if (Object.keys(synthetic).length === 0)
400
+ return 0;
401
+ const rows = buildModelsDevSeedRows(synthetic);
402
+ const tripleKey = (r) => `${r.provider}|${r.model}|${r.tokenClass}`;
403
+ const emptyBlocks = Object.fromEntries(Object.keys(overlayOnly).map((provider) => [provider, { models: {} }]));
404
+ const fallbackKeys = new Set(buildModelsDevSeedRows(emptyBlocks).map(tripleKey));
405
+ const overlayOwned = new Set(buildModelsDevSeedRows(overlayOnly).map(tripleKey).filter((key) => !fallbackKeys.has(key)));
406
+ return await getDbClient().transaction(async () => {
407
+ let inserted = 0;
408
+ for (const row of rows) {
409
+ const existing = await getActivePricingRow(row.provider, row.model, row.tokenClass, now);
410
+ if (existing) {
411
+ if (!overlayOwned.has(tripleKey(row)))
412
+ continue;
413
+ if (existing.pricePerMillionUsd === row.pricePerMillionUsd)
414
+ continue;
415
+ if (existing.effectiveFrom === now) {
416
+ await getDbClient().run("UPDATE pricing SET price_per_million_usd = ?, lastUpdatedAt = ? WHERE provider = ? AND model = ? AND token_class = ? AND effective_from = ?", [row.pricePerMillionUsd, now, row.provider, row.model, row.tokenClass, now]);
417
+ inserted += 1;
418
+ continue;
419
+ }
420
+ }
421
+ const input = { ...row, effectiveFrom: now };
422
+ await insertPricingRow(input);
423
+ inserted += 1;
424
+ }
425
+ return inserted;
426
+ });
427
+ }
428
+ function toCatalogModel(modelId, facts) {
429
+ const cost = facts.pricing && (facts.pricing.input !== undefined || facts.pricing.output !== undefined) ? {
430
+ input: facts.pricing.input,
431
+ output: facts.pricing.output,
432
+ ...facts.pricing.cache_read !== undefined ? { cache_read: facts.pricing.cache_read } : {},
433
+ ...facts.pricing.cache_write !== undefined ? { cache_write: facts.pricing.cache_write } : {}
434
+ } : undefined;
435
+ const reasoning = facts.reasoning ?? (facts.reasoningOptions && facts.reasoningOptions.length > 0 ? true : null);
436
+ return {
437
+ id: modelId,
438
+ ...facts.name ? { name: facts.name } : {},
439
+ ...cost ? { cost } : {},
440
+ ...facts.contextWindow != null ? { limit: { context: facts.contextWindow } } : {},
441
+ ...facts.releaseDate ? { release_date: facts.releaseDate } : {},
442
+ ...facts.status ? { status: facts.status } : {},
443
+ ...reasoning != null ? { reasoning } : {},
444
+ ...facts.reasoningOptions && facts.reasoningOptions.length > 0 ? { reasoning_options: facts.reasoningOptions } : {}
445
+ };
446
+ }
447
+ function mergeFacts(base, overlay) {
448
+ const merged = { ...base };
449
+ for (const key of FACT_KEYS) {
450
+ const value = overlay[key];
451
+ if (value === null || value === undefined)
452
+ continue;
453
+ if (key === "pricing") {
454
+ merged.pricing = { ...base.pricing ?? {}, ...value };
455
+ } else {
456
+ merged[key] = value;
457
+ }
458
+ }
459
+ return merged;
460
+ }
461
+ function applyOverlays(catalog, baseFacts, overlays) {
462
+ for (const overlay of overlays) {
463
+ const providerId = overlay.provider;
464
+ const key = `${overlay.provider}/${overlay.modelId}`;
465
+ const base = baseFacts.get(key) ?? {};
466
+ catalog[providerId] ??= { id: providerId, models: {} };
467
+ catalog[providerId].models[overlay.modelId] = toCatalogModel(overlay.modelId, mergeFacts(base, overlay));
468
+ }
469
+ }
470
+ function catalogModelToFacts(model) {
471
+ return {
472
+ name: model.name ?? null,
473
+ contextWindow: model.limit?.context ?? null,
474
+ releaseDate: model.release_date ?? null,
475
+ status: model.status ?? null,
476
+ reasoning: model.reasoning ?? null,
477
+ reasoningOptions: model.reasoning_options ?? null,
478
+ pricing: model.cost ? { ...model.cost } : null
479
+ };
480
+ }
481
+ var catalogLoaded = false;
482
+ var catalogGeneration = 0;
483
+ function getCatalogGeneration() {
484
+ return catalogGeneration;
485
+ }
486
+ async function reloadModelsCatalog() {
487
+ catalogGeneration++;
488
+ invalidateModelsCatalog();
489
+ const [entries, overlays, meta] = await Promise.all([
490
+ listModelCatalog(),
491
+ listModelCatalogOverlay(),
492
+ getModelCatalogMeta()
493
+ ]);
494
+ if (entries.length === 0) {
495
+ if (overlays.length > 0) {
496
+ const snapshot = snapshotModelsCatalog();
497
+ const baseFacts2 = new Map;
498
+ for (const [providerId, provider] of Object.entries(snapshot)) {
499
+ for (const [modelId, model] of Object.entries(provider?.models ?? {})) {
500
+ baseFacts2.set(`${providerId}/${modelId}`, catalogModelToFacts(model));
501
+ }
502
+ }
503
+ applyOverlays(snapshot, baseFacts2, overlays);
504
+ setSnapshotOverlayCatalog(snapshot);
505
+ }
506
+ catalogLoaded = true;
507
+ return publishRuntimeCatalog(getModelsCatalog());
508
+ }
509
+ const catalog = {};
510
+ const baseFacts = new Map;
511
+ for (const entry of entries) {
512
+ const providerId = entry.provider;
513
+ catalog[providerId] ??= {
514
+ id: providerId,
515
+ ...entry.providerName ? { name: entry.providerName } : {},
516
+ models: {}
517
+ };
518
+ catalog[providerId].models[entry.modelId] = toCatalogModel(entry.modelId, entry);
519
+ baseFacts.set(`${entry.provider}/${entry.modelId}`, entry);
520
+ }
521
+ applyOverlays(catalog, baseFacts, overlays);
522
+ setLiveModelsCatalog(catalog, meta.lastFetchAt);
523
+ catalogLoaded = true;
524
+ return publishRuntimeCatalog(getModelsCatalog());
525
+ }
526
+ function publishRuntimeCatalog(result) {
527
+ setRuntimeModelCatalog(result.providers, Date.now(), result.source);
528
+ return result;
529
+ }
530
+ function markModelsCatalogStale() {
531
+ catalogLoaded = false;
532
+ catalogGeneration++;
533
+ invalidateModelsCatalog();
534
+ }
535
+ async function loadModelsCatalog() {
536
+ if (!catalogLoaded)
537
+ return await reloadModelsCatalog();
538
+ return getModelsCatalog();
539
+ }
540
+
541
+ // src/be/pricing-refresh.ts
542
+ var MODELSDEV_API_URL = "https://models.dev/api.json";
543
+ var PRICING_REFRESH_INTERVAL_MS = 12 * 60 * 60 * 1000;
544
+ var MODEL_CATALOG_FRESH_MS = 4 * 60 * 60 * 1000;
545
+ var refreshLoopStarted = false;
546
+ function logPricingRefresh(message) {
547
+ console.log(scrubSecrets(`[pricing-refresh] ${message}`));
548
+ }
549
+ function logPricingRefreshError(message, err) {
550
+ const detail = err instanceof Error ? err.message : String(err);
551
+ console.warn(scrubSecrets(`[pricing-refresh] ${message}: ${detail}`));
552
+ }
553
+ async function insertChangedPricingRows(rows, now) {
554
+ return await getDbClient().transaction(async () => {
555
+ let inserted = 0;
556
+ let unchanged = 0;
557
+ for (const row of rows) {
558
+ const existing = await getActivePricingRow(row.provider, row.model, row.tokenClass, now);
559
+ if (existing?.pricePerMillionUsd === row.pricePerMillionUsd) {
560
+ unchanged += 1;
561
+ continue;
562
+ }
563
+ const input = {
564
+ ...row,
565
+ effectiveFrom: now
566
+ };
567
+ await insertPricingRow(input);
568
+ inserted += 1;
569
+ }
570
+ return { inserted, unchanged };
571
+ });
572
+ }
573
+ async function prunePricingHistory(keepLatest = 2) {
574
+ const result = await getDbClient().run(`DELETE FROM pricing
575
+ WHERE rowid IN (
576
+ SELECT rowid
577
+ FROM (
578
+ SELECT
579
+ rowid,
580
+ ROW_NUMBER() OVER (
581
+ PARTITION BY provider, model, token_class
582
+ ORDER BY effective_from DESC
583
+ ) AS rn
584
+ FROM pricing
585
+ )
586
+ WHERE rn > ?
587
+ )`, [keepLatest]);
588
+ return result.changes;
589
+ }
590
+ async function auditPricingRefresh(result) {
591
+ try {
592
+ await createLogEntry({
593
+ eventType: "pricing.refresh",
594
+ newValue: `${result.status}: inserted=${result.inserted}; unchanged=${result.unchanged}; pruned=${result.pruned}`,
595
+ metadata: {
596
+ status: result.status,
597
+ candidateRows: result.candidateRows,
598
+ inserted: result.inserted,
599
+ unchanged: result.unchanged,
600
+ pruned: result.pruned,
601
+ etag: result.etag
602
+ }
603
+ });
604
+ } catch (err) {
605
+ logPricingRefreshError("audit log write failed", err);
606
+ }
607
+ }
608
+ async function auditPricingRefreshFailure(err) {
609
+ try {
610
+ await createLogEntry({
611
+ eventType: "pricing.refresh.failed",
612
+ newValue: scrubSecrets(err instanceof Error ? err.message : String(err))
613
+ });
614
+ } catch (auditErr) {
615
+ logPricingRefreshError("failure audit log write failed", auditErr);
616
+ }
617
+ }
618
+ async function refreshPricingFromModelsDev(opts = {}) {
619
+ const fetchImpl = opts.fetchImpl ?? fetch;
620
+ const now = opts.now ?? Date.now();
621
+ const meta = await getModelCatalogMeta();
622
+ const catalogCount = await countModelCatalog();
623
+ const headers = {};
624
+ if (catalogCount > 0 && meta.etag)
625
+ headers["If-None-Match"] = meta.etag;
626
+ if (catalogCount > 0 && meta.lastModified)
627
+ headers["If-Modified-Since"] = meta.lastModified;
628
+ const response = await fetchImpl(MODELSDEV_API_URL, { headers });
629
+ if (response.status === 304) {
630
+ await touchModelCatalogCheckedAt(now);
631
+ await updateModelCatalogMeta({ lastCheckedAt: now });
632
+ await reloadModelsCatalog();
633
+ const result2 = {
634
+ status: "not_modified",
635
+ candidateRows: 0,
636
+ inserted: 0,
637
+ unchanged: 0,
638
+ pruned: 0,
639
+ etag: meta.etag ?? undefined,
640
+ added: [],
641
+ expiredOverlays: [],
642
+ catalogModels: catalogCount
643
+ };
644
+ await auditPricingRefresh(result2);
645
+ logPricingRefresh("models.dev returned 304; pricing rows unchanged");
646
+ return result2;
647
+ }
648
+ if (!response.ok) {
649
+ throw new Error(`models.dev returned HTTP ${response.status}`);
650
+ }
651
+ const cache = await response.json();
652
+ const etag = response.headers.get("etag");
653
+ const lastModified = response.headers.get("last-modified");
654
+ const entries = buildCatalogEntries(cache, now);
655
+ const added = await replaceModelCatalog(entries);
656
+ const rows = buildModelsDevSeedRows(cache);
657
+ const { inserted, unchanged } = await insertChangedPricingRows(rows, now);
658
+ const expiredOverlays = await expireMatchedOverlays();
659
+ const overlayInserted = await applyOverlayPricingRows(now);
660
+ const pruned = await prunePricingHistory(2);
661
+ await updateModelCatalogMeta({ etag, lastModified, lastFetchAt: now, lastCheckedAt: now });
662
+ await reloadModelsCatalog();
663
+ const result = {
664
+ status: "refreshed",
665
+ candidateRows: rows.length,
666
+ inserted: inserted + overlayInserted,
667
+ unchanged,
668
+ pruned,
669
+ etag: etag ?? undefined,
670
+ added,
671
+ expiredOverlays,
672
+ catalogModels: entries.length
673
+ };
674
+ await auditPricingRefresh(result);
675
+ logPricingRefresh(`refreshed ${rows.length} candidate row(s); inserted=${inserted}; unchanged=${unchanged}; pruned=${pruned}; catalog=${entries.length}; added=${added.length}; expiredOverlays=${expiredOverlays.length}`);
676
+ return result;
677
+ }
678
+ var MODEL_CATALOG_FORCE_COOLDOWN_MS = 60 * 1000;
679
+ var refreshInFlight = null;
680
+ var lastForcedRefreshAt = null;
681
+ function resetModelCatalogRefreshGuardForTests() {
682
+ refreshInFlight = null;
683
+ lastForcedRefreshAt = null;
684
+ }
685
+ function refreshModelCatalog(opts = {}) {
686
+ if (refreshInFlight)
687
+ return refreshInFlight;
688
+ const run = runModelCatalogRefresh(opts).finally(() => {
689
+ if (refreshInFlight === run)
690
+ refreshInFlight = null;
691
+ });
692
+ refreshInFlight = run;
693
+ return run;
694
+ }
695
+ async function requestModelCatalogRefresh(opts = {}) {
696
+ const now = opts.now ?? Date.now();
697
+ if (opts.force) {
698
+ const waitMs = lastForcedRefreshAt === null ? 0 : lastForcedRefreshAt + MODEL_CATALOG_FORCE_COOLDOWN_MS - now;
699
+ if (waitMs > 0) {
700
+ const meta = await getModelCatalogMeta().catch(() => null);
701
+ return {
702
+ status: "skipped-cooldown",
703
+ models: await countModelCatalog().catch(() => 0),
704
+ added: [],
705
+ checkedAt: meta?.lastCheckedAt ?? null,
706
+ retryAfterMs: waitMs
707
+ };
708
+ }
709
+ lastForcedRefreshAt = now;
710
+ }
711
+ return refreshModelCatalog({ ...opts, now });
712
+ }
713
+ async function runModelCatalogRefresh(opts = {}) {
714
+ const now = opts.now ?? Date.now();
715
+ try {
716
+ const meta = await getModelCatalogMeta();
717
+ const count = await countModelCatalog();
718
+ if (!opts.force && count > 0 && meta.lastCheckedAt !== null && now - meta.lastCheckedAt < MODEL_CATALOG_FRESH_MS) {
719
+ await reloadModelsCatalog();
720
+ return { status: "skipped-fresh", models: count, added: [], checkedAt: meta.lastCheckedAt };
721
+ }
722
+ const result = await refreshPricingFromModelsDev({ fetchImpl: opts.fetchImpl, now });
723
+ return {
724
+ status: result.status === "refreshed" ? "updated" : "not-modified",
725
+ models: result.catalogModels ?? await countModelCatalog(),
726
+ added: result.added ?? [],
727
+ checkedAt: now
728
+ };
729
+ } catch (err) {
730
+ logPricingRefreshError("model catalog refresh failed", err);
731
+ await auditPricingRefreshFailure(err);
732
+ const meta = await getModelCatalogMeta().catch(() => null);
733
+ return {
734
+ status: "error",
735
+ models: await countModelCatalog().catch(() => 0),
736
+ added: [],
737
+ checkedAt: meta?.lastCheckedAt ?? null,
738
+ error: scrubSecrets(err instanceof Error ? err.message : String(err))
739
+ };
740
+ }
741
+ }
742
+ async function runPricingRefreshSafely() {
743
+ await refreshModelCatalog();
744
+ }
745
+ function startPricingRefreshLoop() {
746
+ if (refreshLoopStarted)
747
+ return;
748
+ refreshLoopStarted = true;
749
+ runPricingRefreshSafely();
750
+ const interval = setInterval(() => {
751
+ runPricingRefreshSafely();
752
+ }, PRICING_REFRESH_INTERVAL_MS);
753
+ interval.unref?.();
754
+ }
755
+
756
+ export { buildCatalogEntries, listModelCatalog, listModelCatalogOverlay, upsertModelCatalogOverlay, deleteModelCatalogOverlay, getCatalogGeneration, loadModelsCatalog, PRICING_REFRESH_INTERVAL_MS, MODEL_CATALOG_FRESH_MS, refreshPricingFromModelsDev, MODEL_CATALOG_FORCE_COOLDOWN_MS, resetModelCatalogRefreshGuardForTests, refreshModelCatalog, requestModelCatalogRefresh, startPricingRefreshLoop };