@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.
- package/README.md +1 -1
- package/dist/{acp-adapter-xaq4n6pc.js → acp-adapter-2mjwcsfh.js} +124 -27
- package/dist/{actions-p9hz5yat.js → actions-e43rxskr.js} +35 -30
- package/dist/{anthropic-messages-51pz26qe.js → anthropic-messages-5qx80j9k.js} +106 -16
- package/dist/{app-g26wdcz6.js → app-mc7xfagn.js} +12 -10
- package/dist/{assistant-wydfqkb3.js → assistant-sedkzv3b.js} +42 -31
- package/dist/{azure-openai-responses-8gksqtmz.js → azure-openai-responses-nfb21z9a.js} +13 -14
- package/dist/{boot-reembed-se8zmdcp.js → boot-reembed-cqz9swn5.js} +13 -12
- package/dist/{boot-reembed-g5ckg9yb.js → boot-reembed-m3656j2n.js} +14 -13
- package/dist/{boot-scrub-logs-wfxqan61.js → boot-scrub-logs-xbxf5d7w.js} +11 -9
- package/dist/boot-seeds-gsetbs5y.js +28 -0
- package/dist/{claude-adapter-tv2x4f36.js → claude-adapter-dc6289mc.js} +28 -23
- package/dist/{claude-managed-adapter-41b2s7fd.js → claude-managed-adapter-naaw51me.js} +4 -3
- package/dist/{claude-managed-setup-60g8aazx.js → claude-managed-setup-eacvchx0.js} +12 -3
- package/dist/claude-sdk-session-bqp2xvym.js +29722 -0
- package/dist/{cli-987hg0aa.js → cli-0jwe9034.js} +2 -2
- package/dist/{cli-vj6h9kc2.js → cli-0m7wct0d.js} +59 -1
- package/dist/{cli-c07r9a2m.js → cli-0q3avq3s.js} +12 -11
- package/dist/{cli-19jenss3.js → cli-0wgxmwf6.js} +31 -10
- package/dist/cli-0x01kwrd.js +491 -0
- package/dist/cli-0xqde53b.js +176 -0
- package/dist/cli-10nhj6xq.js +8355 -0
- package/dist/{cli-tt9k8r35.js → cli-12tszpk9.js} +479 -910
- package/dist/{cli-5b6b62tz.js → cli-13hw4z91.js} +1 -1
- package/dist/{cli-cqt19wg6.js → cli-1a2j50m0.js} +11 -2
- package/dist/{cli-xberxa1r.js → cli-25fcq4b4.js} +2 -2
- package/dist/{cli-npssxgaz.js → cli-2pdaysab.js} +52 -2
- package/dist/cli-3qhyh31j.js +166 -0
- package/dist/cli-447dt1h3.js +3828 -0
- package/dist/cli-4derpgyk.js +115 -0
- package/dist/{cli-00w2c66p.js → cli-523679eh.js} +13 -2
- package/dist/{cli-y95wp3tg.js → cli-58e8yr0d.js} +1 -1
- package/dist/{cli-24k806sc.js → cli-5nbmpy8w.js} +3 -3
- package/dist/{cli-53sz8gkz.js → cli-6cs6wzxt.js} +1 -1
- package/dist/cli-741yb0xx.js +1027 -0
- package/dist/cli-74h9hscv.js +69 -0
- package/dist/cli-7cxw8b60.js +14910 -0
- package/dist/{cli-xhaeg2nc.js → cli-7e398j1h.js} +1 -1
- package/dist/cli-7kyk9jr3.js +22 -0
- package/dist/cli-89s4ngw3.js +183 -0
- package/dist/{cli-9b13ggd1.js → cli-89v6kyq7.js} +1 -0
- package/dist/{cli-ftfvggzx.js → cli-9hsgwz4w.js} +1045 -581
- package/dist/{cli-rqz8qbw7.js → cli-a8dvhzrt.js} +59 -20
- package/dist/{cli-j57qkrpq.js → cli-azhgzetg.js} +395 -56
- package/dist/cli-br0qdrnp.js +2150 -0
- package/dist/{cli-rj19hc34.js → cli-btfv8y88.js} +1 -1
- package/dist/{cli-8qmme6vd.js → cli-c8a36vcc.js} +2 -2
- package/dist/cli-cpvsaqsw.js +333 -0
- package/dist/cli-d5czme6m.js +17805 -0
- package/dist/{cli-h38x0qhg.js → cli-et8m9xsv.js} +4 -3
- package/dist/{cli-x24pshtq.js → cli-evvtk35y.js} +6 -6
- package/dist/{cli-7s3syk22.js → cli-f8gy6r2r.js} +5 -5
- package/dist/{cli-zt1227k1.js → cli-fkqhbg8g.js} +5 -3
- package/dist/{cli-9httdpyq.js → cli-g2whhdrf.js} +1 -1
- package/dist/cli-gt4hxb8p.js +99 -0
- package/dist/{cli-s8eet254.js → cli-jajnsm8q.js} +37184 -53838
- package/dist/{cli-7gtvvpbh.js → cli-jayjpjmb.js} +21 -12
- package/dist/{cli-68w09sjw.js → cli-jyk0ynw2.js} +27 -89
- package/dist/{cli-aehm1rbq.js → cli-jz4w45qt.js} +1152 -61
- package/dist/cli-kmcpfwn2.js +333856 -0
- package/dist/{cli-4198qc9v.js → cli-ktmyrtpn.js} +19 -3
- package/dist/{cli-j55n64b6.js → cli-mfe5btp9.js} +505 -189
- package/dist/{cli-q67y4wzm.js → cli-phsaj4xq.js} +24 -2
- package/dist/{cli-1hkrch3k.js → cli-pm1rst0h.js} +46 -7
- package/dist/{cli-pw9rj5t9.js → cli-pqetb83f.js} +1 -176
- package/dist/{cli-ggm92wcn.js → cli-ptr8h7kp.js} +20 -1
- package/dist/{cli-s8s499xy.js → cli-qbksc292.js} +3 -2
- package/dist/{cli-47m6dkd3.js → cli-qydf22qq.js} +597 -346
- package/dist/{cli-3gmcwfyx.js → cli-r1mxd1xh.js} +13 -6
- package/dist/cli-rb9m6rpe.js +4980 -0
- package/dist/cli-rykfmgwg.js +102 -0
- package/dist/{cli-5d10vd2m.js → cli-shkxm1vn.js} +6 -5
- package/dist/{cli-eetrvv91.js → cli-t3p77qk1.js} +1 -1
- package/dist/cli-tfky2838.js +756 -0
- package/dist/{cli-g8djp6qe.js → cli-tmqt7dpj.js} +40 -0
- package/dist/{cli-fgqxwrpv.js → cli-tpq83z89.js} +1631 -127
- package/dist/cli-trc24wk2.js +143 -0
- package/dist/{cli-aky8tctm.js → cli-tzx2ke1v.js} +3 -3
- package/dist/cli-v7fn5gms.js +1425 -0
- package/dist/{cli-wscdvcqz.js → cli-vctdqs5h.js} +1 -1
- package/dist/{cli-cw9zck8j.js → cli-xjcywfsf.js} +8 -4
- package/dist/{cli-k0g1sjzm.js → cli-ybgv4kw2.js} +18 -11
- package/dist/{cli-rjehvrts.js → cli-ydmcgve1.js} +8 -8
- package/dist/{cli-vhjwh6cz.js → cli-ygwm91nt.js} +1 -1
- package/dist/{cli-mcnhgnn1.js → cli-ykk5jaax.js} +184 -26
- package/dist/{cli-de1396j2.js → cli-ywybs5as.js} +7 -1
- package/dist/cli-zpep9q5e.js +491 -0
- package/dist/cli.js +41 -32
- package/dist/cloudflare-workers-ai-system-one-k6fbxw9d.js +46 -0
- package/dist/{codex-adapter-cnm8wk11.js → codex-adapter-nmxcatww.js} +26 -17
- package/dist/{codex-hook-9hptmdhj.js → codex-hook-20me6byw.js} +2 -2
- package/dist/{codex-login-x3jk4etn.js → codex-login-w6fz4xbz.js} +50 -5
- package/dist/{codex-session-runner-gfg547zj.js → codex-session-runner-90aa80ev.js} +24 -17
- package/dist/{commands-e3bn0p75.js → commands-8wcq1kn7.js} +11 -9
- package/dist/{db-03v9kzr2.js → db-j40kc4se.js} +25 -11
- package/dist/{devin-adapter-8vgkwh29.js → devin-adapter-dpsv6hd9.js} +1 -1
- package/dist/{dispatcher-nrmg8yht.js → dispatcher-4zqy768d.js} +34 -29
- package/dist/{dsh-adapter-k03nfqa6.js → dsh-adapter-mpnzgztj.js} +3 -3
- package/dist/{e2b-93x1k0j5.js → e2b-ez07znam.js} +4 -3
- package/dist/execute-6e7qeyk4.js +1017 -0
- package/dist/{google-generative-ai-bxmv50nc.js → google-generative-ai-k6n5nbpp.js} +6 -5
- package/dist/{google-vertex-809af514.js → google-vertex-44xxtcvp.js} +6 -5
- package/dist/{handlers-aqcedbyb.js → handlers-kjdce1w5.js} +37 -32
- package/dist/{hook-ybvmrmhq.js → hook-czx13n12.js} +19 -15
- package/dist/{hook-7vkskkre.js → hook-r2t4bqfg.js} +18 -14
- package/dist/{http-2eqa8t24.js → http-3bbb19be.js} +900 -145
- package/dist/{index-f6cryxwe.js → index-4a5njnm1.js} +2 -59
- package/dist/{index-x71bvq0p.js → index-668me3f0.js} +35 -30
- package/dist/{index-c6zsv1bd.js → index-7aj368tw.js} +13 -12
- package/dist/{index-1yks3qcr.js → index-fg0dsp6c.js} +34 -29
- package/dist/{index-chgdkq36.js → index-kkv84jq9.js} +34 -29
- package/dist/{index-a3vn96n8.js → index-zg7vp46x.js} +198 -54
- package/dist/{keepalive-vtkf5n99.js → keepalive-yra8dy57.js} +13 -11
- package/dist/{lead-h3sp357x.js → lead-va0pad2c.js} +54 -49
- package/dist/llama-cpp-classify-tgj35k8d.js +335 -0
- package/dist/{maintenance-jzeqtk3q.js → maintenance-hqewtmyg.js} +14 -13
- package/dist/{mistral-conversations-zx0kn554.js → mistral-conversations-pc3ntnxt.js} +14 -17
- package/dist/{oauth-refresh-sweep-qp3dzmdp.js → oauth-refresh-sweep-7rdt1m9n.js} +12 -10
- package/dist/{onboard-tmsp0qbz.js → onboard-j6ms6d0x.js} +6 -5
- package/dist/{openai-codex-responses-4vjqzvjp.js → openai-codex-responses-bbancrr0.js} +21 -8
- package/dist/{openai-completions-9hjrqbw8.js → openai-completions-jpaj5g2b.js} +9 -12
- package/dist/{openai-responses-7b1pn0xt.js → openai-responses-200k1wx4.js} +23 -18
- package/dist/{opencode-adapter-x6x2da1z.js → opencode-adapter-89rabkw2.js} +8 -7
- package/dist/{openrouter-images-9f9ek4rw.js → openrouter-images-8yx3487g.js} +5 -7
- package/dist/{otel-impl-9mz73q65.js → otel-impl-a792rjz0.js} +1 -1
- package/dist/{pi-messages-txpjj39q.js → pi-messages-vxdt8v7j.js} +2 -1
- package/dist/{pi-mono-adapter-m6dvv0dz.js → pi-mono-adapter-g7qzvr3v.js} +295 -70
- package/dist/pricing-refresh-wx0tp7cb.js +39 -0
- package/dist/quickjs-r1pq2107.wasm +0 -0
- package/dist/{rbac-roles-c9wqz83x.js → rbac-roles-8tzawacv.js} +12 -10
- package/dist/{render-v2-gryrh9h8.js → render-v2-mm7zswb8.js} +12 -10
- package/dist/runtime-zyssgse0.js +24 -0
- package/dist/{setup-q9720bt6.js → setup-8y70ys6t.js} +6 -5
- package/dist/typesafe-system-one-2ef30pec.js +27 -0
- package/dist/{virtual-modules-3htpgn8x.js → virtual-modules-6nmk7h3n.js} +22 -13
- package/dist/{work-objects-b76x7deb.js → work-objects-fg6xkb0n.js} +37 -32
- package/dist/{worker-6vrxbv41.js → worker-wmwhxsph.js} +54 -49
- package/openapi.json +1314 -91
- package/package.json +12 -11
- package/src/be/db/agents.ts +20 -0
- package/src/be/db/api-keys.ts +70 -0
- package/src/be/db/tasks/human-free.ts +442 -0
- package/src/be/db/tasks/read.ts +56 -1
- package/src/be/db/tasks/write.ts +107 -11
- package/src/be/db.ts +274 -188
- package/src/be/harness-model-support.ts +182 -0
- package/src/be/human-free-drain.ts +67 -0
- package/src/be/memory/providers/sqlite-store.ts +51 -15
- package/src/be/migrations/182_task_human_free_flag.sql +63 -0
- package/src/be/migrations/183_human_free_reclassify_queue.sql +25 -0
- package/src/be/migrations/184_model_catalog.sql +120 -0
- package/src/be/migrations/185_api_key_auth_failures.sql +6 -0
- package/src/be/migrations/186_api_key_seat_mismatch.sql +8 -0
- package/src/be/model-catalog-store.ts +644 -0
- package/src/be/model-tier-keys.ts +46 -0
- package/src/be/model-tier-resolution.ts +534 -0
- package/src/be/model-validation.ts +188 -0
- package/src/be/models-catalog.ts +61 -7
- package/src/be/modelsdev-cache.json +82 -0
- package/src/be/modelsdev-cache.ts +2 -0
- package/src/be/pricing-refresh.ts +154 -12
- package/src/be/scripts/typecheck.ts +15 -0
- package/src/be/seed/runner.ts +18 -12
- package/src/be/seed/types.ts +1 -0
- package/src/be/seed-scripts/catalog/task-failure-audit.ts +61 -9
- package/src/be/swarm-config-guard.ts +8 -0
- package/src/cli.tsx +10 -0
- package/src/commands/claude-managed-setup.ts +3 -1
- package/src/commands/codex-login.ts +67 -0
- package/src/commands/credential-outcome-report.ts +191 -0
- package/src/commands/rate-limit-outcome.ts +34 -4
- package/src/commands/runner.ts +357 -186
- package/src/commands/seat-mismatch-report.ts +46 -0
- package/src/extensions/apply-task-create.ts +27 -1
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/heartbeat/heartbeat.ts +79 -17
- package/src/heartbeat/templates.ts +2 -2
- package/src/http/agents.ts +62 -1
- package/src/http/api-keys.ts +140 -2
- package/src/http/codex-oauth-keep-warm.ts +3 -5
- package/src/http/index.ts +8 -0
- package/src/http/memory.ts +12 -5
- package/src/http/models-catalog.ts +357 -8
- package/src/http/poll.ts +54 -3
- package/src/http/schedules.ts +51 -6
- package/src/http/script-runs.ts +12 -0
- package/src/http/session-data.ts +2 -5
- package/src/http/stats.ts +6 -8
- package/src/http/status.ts +15 -1
- package/src/http/tasks.ts +90 -7
- package/src/http/usage-cache.ts +56 -15
- package/src/http/workflows.ts +46 -8
- package/src/mcp-client/http-client.ts +5 -0
- package/src/memory/recall-query.ts +74 -0
- package/src/prompts/base-prompt.ts +15 -4
- package/src/prompts/session-templates.ts +23 -1
- package/src/providers/acp-adapter.ts +1 -0
- package/src/providers/claude-adapter.ts +14 -5
- package/src/providers/claude-managed-adapter.ts +1 -0
- package/src/providers/claude-managed-models.ts +47 -161
- package/src/providers/claude-sdk-session.ts +3 -1
- package/src/providers/codex-adapter.ts +14 -14
- package/src/providers/codex-models.ts +65 -198
- package/src/providers/devin-adapter.ts +1 -0
- package/src/providers/dsh-adapter.ts +2 -1
- package/src/providers/harness-version.ts +83 -7
- package/src/providers/opencode-adapter.ts +53 -1
- package/src/providers/pi-binary-root/src/extensions/codemode/worker.ts +11 -0
- package/src/providers/pi-codemode-runtime.ts +17 -0
- package/src/providers/pi-mono-adapter.ts +361 -83
- package/src/providers/pi-mono-extension.ts +16 -1
- package/src/providers/reasoning-effort.ts +44 -140
- package/src/providers/types.ts +8 -0
- package/src/rbac/legacy-policy.ts +43 -0
- package/src/rbac/permissions.ts +15 -0
- package/src/scripts-runtime/sdk-allowlist.ts +4 -0
- package/src/scripts-runtime/types/stdlib.d.ts +15 -0
- package/src/scripts-runtime/types/swarm-extension.d.ts +3 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +15 -0
- package/src/server.ts +6 -0
- package/src/slack/assistant.ts +13 -0
- package/src/slack/message-text.ts +56 -2
- package/src/slack/render-v2.ts +87 -37
- package/src/slack/session-status.ts +541 -0
- package/src/slack/thread-buffer.ts +10 -2
- package/src/tasks/sibling-awareness.ts +3 -2
- package/src/telemetry.ts +5 -4
- package/src/templates.d.ts +6 -0
- package/src/tests/agents-list-model-display.test.ts +4 -6
- package/src/tests/api-key-auth-failure-route.test.ts +142 -0
- package/src/tests/api-key-tracking.test.ts +368 -1
- package/src/tests/base-prompt.test.ts +43 -2
- package/src/tests/claude-adapter.test.ts +68 -0
- package/src/tests/claude-managed-adapter.test.ts +12 -18
- package/src/tests/codex-adapter.test.ts +2 -1
- package/src/tests/codex-auth-failure.test.ts +52 -0
- package/src/tests/codex-gpt6-release.test.ts +66 -5
- package/src/tests/codex-login.test.ts +116 -0
- package/src/tests/context-window.test.ts +7 -5
- package/src/tests/credential-exhaustion-policy.test.ts +118 -3
- package/src/tests/credential-outcome-report.test.ts +171 -0
- package/src/tests/credential-status-api.test.ts +103 -2
- package/src/tests/db-extraction-contract.test.ts +1 -0
- package/src/tests/db-task-read-contract.test.ts +10 -1
- package/src/tests/db-task-write-contract.test.ts +535 -2
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/dokploy-deploy.test.ts +315 -0
- package/src/tests/dsh-adapter.test.ts +1 -1
- package/src/tests/explicit-model-validation.test.ts +583 -0
- package/src/tests/extensions-pre-task.test.ts +73 -0
- package/src/tests/fetch-resolved-env.test.ts +46 -0
- package/src/tests/hang-watchdog-worker.ts +47 -0
- package/src/tests/hang-watchdog.test.ts +45 -0
- package/src/tests/hang-watchdog.ts +30 -0
- package/src/tests/harness-model-guard.test.ts +123 -0
- package/src/tests/harness-model-support.test.ts +374 -0
- package/src/tests/harness-version.test.ts +70 -3
- package/src/tests/heartbeat-checklist-config.test.ts +71 -0
- package/src/tests/heartbeat-checklist.test.ts +4 -2
- package/src/tests/heartbeat-orphan-resume.test.ts +48 -0
- package/src/tests/http-task-write-ownership.test.ts +172 -0
- package/src/tests/human-free-flag.test.ts +279 -0
- package/src/tests/human-free-oracle.ts +57 -0
- package/src/tests/human-free-reclassify.test.ts +669 -0
- package/src/tests/memory-fts-populate.test.ts +139 -0
- package/src/tests/memory-http-recall-gating.test.ts +125 -1
- package/src/tests/migration-046-budgets.test.ts +3 -3
- package/src/tests/model-catalog-migration.test.ts +134 -0
- package/src/tests/model-catalog-table.test.ts +249 -0
- package/src/tests/model-catalog-write-access.test.ts +387 -0
- package/src/tests/model-rate-limit-windows.test.ts +19 -0
- package/src/tests/model-tier-resolution.test.ts +611 -0
- package/src/tests/models-catalog.test.ts +1 -1
- package/src/tests/multi-runtime-enabled-stats.test.ts +65 -1
- package/src/tests/opencode-adapter.test.ts +157 -2
- package/src/tests/package-publish.test.ts +137 -36
- package/src/tests/pi-codemode.test.ts +246 -0
- package/src/tests/pi-mono-adapter.test.ts +3 -0
- package/src/tests/pi-native-mcp.test.ts +193 -0
- package/src/tests/pi-tool-exposure.test.ts +374 -0
- package/src/tests/preload.ts +3 -0
- package/src/tests/pricing-routes.test.ts +2 -2
- package/src/tests/prompt-template-session.test.ts +5 -3
- package/src/tests/rate-limit-event.test.ts +65 -0
- package/src/tests/rate-limit-outcome.test.ts +90 -0
- package/src/tests/rbac-engine.test.ts +77 -3
- package/src/tests/reasoning-effort.test.ts +109 -0
- package/src/tests/refresh-modelsdev-carryforward.test.ts +5 -5
- package/src/tests/runner-key-completion-outcome.test.ts +112 -0
- package/src/tests/runner-key-outcome-late-reset.test.ts +224 -0
- package/src/tests/runner-registration-retry.test.ts +149 -0
- package/src/tests/runtime-model-catalog.test.ts +86 -0
- package/src/tests/script-connections.test.ts +9 -0
- package/src/tests/seat-mismatch-report.test.ts +85 -0
- package/src/tests/seed-scripts.test.ts +138 -0
- package/src/tests/seed.test.ts +25 -0
- package/src/tests/session-costs.test.ts +14 -11
- package/src/tests/slack-message-text.test.ts +101 -0
- package/src/tests/slack-render-v2.test.ts +96 -0
- package/src/tests/slack-session-status.test.ts +626 -0
- package/src/tests/slack-thread-buffer-context.test.ts +93 -0
- package/src/tests/status.test.ts +29 -0
- package/src/tests/steering-promotion.test.ts +51 -0
- package/src/tests/store-progress-ownership.test.ts +136 -0
- package/src/tests/subscription-plans.test.ts +28 -0
- package/src/tests/task-cascade-fail.test.ts +148 -2
- package/src/tests/task-citations.test.ts +3 -2
- package/src/tests/tool-annotations.test.ts +1 -1
- package/src/tests/usage-cache.test.ts +166 -0
- package/src/tests/workflow-agent-task.test.ts +30 -0
- package/src/tests/workflow-executors.test.ts +5 -2
- package/src/tests/workflow-system-one-decision-review.test.ts +1021 -0
- package/src/tests/workflow-system-one-decision-save-warnings.test.ts +365 -0
- package/src/tests/workflow-system-one-decision.test.ts +2063 -0
- package/src/tests/workflow-triggers-v2.test.ts +320 -0
- package/src/tools/model-catalog/index.ts +137 -0
- package/src/tools/schedules/create-schedule.ts +17 -2
- package/src/tools/schedules/patch-schedule.ts +31 -1
- package/src/tools/schedules/update-schedule.ts +31 -1
- package/src/tools/send-task.ts +18 -1
- package/src/tools/store-progress.ts +49 -4
- package/src/tools/task-action.ts +14 -1
- package/src/tools/tool-config.ts +3 -1
- package/src/tools/workflows/create-workflow.ts +17 -4
- package/src/tools/workflows/patch-workflow-node.ts +17 -7
- package/src/tools/workflows/patch-workflow.ts +9 -2
- package/src/tools/workflows/update-workflow.ts +17 -4
- package/src/types.ts +41 -1
- package/src/utils/codex-auth-failure.ts +34 -0
- package/src/utils/context-window.ts +47 -53
- package/src/utils/credentials.ts +72 -30
- package/src/utils/error-tracker.ts +80 -0
- package/src/utils/harness-cli-version.ts +115 -0
- package/src/utils/harness-model-error.ts +33 -0
- package/src/utils/model-rate-limit-windows.ts +12 -0
- package/src/utils/runtime-model-catalog.ts +152 -0
- package/src/utils/spawn-model-guard.ts +43 -0
- package/src/utils/subscription-plans.ts +22 -0
- package/src/workflows/approval-resolution.ts +39 -0
- package/src/workflows/definition.ts +7 -0
- package/src/workflows/engine.ts +69 -1
- package/src/workflows/executors/agent-task.ts +16 -6
- package/src/workflows/executors/base.ts +53 -0
- package/src/workflows/executors/human-in-the-loop.ts +8 -8
- package/src/workflows/executors/index.ts +1 -0
- package/src/workflows/executors/registry.ts +2 -0
- package/src/workflows/executors/system-one-decision.ts +1232 -0
- package/src/workflows/executors/system-one-providers.ts +301 -0
- package/src/workflows/executors/workflow-llm.ts +13 -1
- package/src/workflows/model-validation.ts +45 -0
- package/src/workflows/patch-definition.ts +5 -0
- package/src/workflows/readiness.ts +86 -0
- package/src/workflows/recovery.ts +12 -12
- package/src/workflows/resume.ts +23 -9
- package/src/workflows/triggers.ts +85 -0
- package/templates/schedules/daily-blocker-digest/content.md +4 -5
- package/templates/skills/agent-browser/SKILL.md +6 -5
- package/templates/skills/agent-browser/content.md +6 -5
- package/templates/skills/heartbeat-runbook/config.json +1 -1
- package/templates/skills/heartbeat-runbook/content.md +2 -2
- package/templates/skills/swarm-scripts/SKILL.md +1 -1
- package/templates/skills/swarm-scripts/content.md +1 -1
- package/templates/skills/workflow-iterate/content.md +82 -1
- package/templates/workflows/claude-code-changelog-watch/content.md +2 -0
- package/templates/workflows/pr-review-status-sweep/content.md +2 -0
- package/dist/boot-seeds-s3q01xj3.js +0 -26
- package/dist/claude-sdk-session-t442qz6f.js +0 -39108
- package/dist/cli-0xjkj1j7.js +0 -61
- package/dist/cli-20cj37vt.js +0 -335561
- package/dist/cli-2djtr51b.js +0 -48
- package/dist/cli-65hjmev5.js +0 -1141
- package/dist/cli-aw1jwp1r.js +0 -14075
- package/dist/cli-h967nb6r.js +0 -6603
- package/dist/cli-vapyk8sw.js +0 -20
- package/dist/cli-ws6rqycm.js +0 -247
- package/dist/pricing-refresh-v1cbr9te.js +0 -27
- package/src/providers/modelsdev-reasoning.json +0 -6453
- package/dist/{index-f24115ge.js → index-2wsdsnmm.js} +3 -3
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claim-time model resolution (model-catalog phase 3).
|
|
3
|
+
*
|
|
4
|
+
* The API server resolves which concrete model a task runs with at the moment
|
|
5
|
+
* a worker claims it, against the catalog it owns, and records the answer on
|
|
6
|
+
* the task row (`resolvedModel`, `modelSource`, `modelAlias`).
|
|
7
|
+
*
|
|
8
|
+
* Precedence (first hit wins):
|
|
9
|
+
* 1. task `model` → modelSource "model"
|
|
10
|
+
* 2. the claiming worker's MODEL_TIER_* → "worker-env" (agents.modelTierOverrides)
|
|
11
|
+
* 3. swarm_config MODEL_TIER_<PROV>_<TIER> → "tier-config"
|
|
12
|
+
* 4. DEFAULT_MODEL_TIER_MAP → "tier-default"
|
|
13
|
+
*
|
|
14
|
+
* Any layer may hold a `latest:` alias. An alias that resolves to nothing
|
|
15
|
+
* (empty catalog, everything filtered) falls through to the next layer.
|
|
16
|
+
*
|
|
17
|
+
* Guardrails for `latest:` aliases:
|
|
18
|
+
* - `@stable` (default) drops preview/experimental ids and models younger
|
|
19
|
+
* than MODEL_LATEST_SOAK_DAYS (default 2).
|
|
20
|
+
* - Unpriced catalog models are never picked.
|
|
21
|
+
* - MODEL_AUTO_UPGRADE=false freezes each alias at its last recorded
|
|
22
|
+
* resolution (resolves normally only the first time).
|
|
23
|
+
* - Every change of an alias's resolution writes one
|
|
24
|
+
* `model_alias_resolutions` row and logs one notice after commit.
|
|
25
|
+
*
|
|
26
|
+
* See runbooks/model-tiers.md.
|
|
27
|
+
*/
|
|
28
|
+
import {
|
|
29
|
+
type HarnessCatalogSections,
|
|
30
|
+
harnessCatalogSection,
|
|
31
|
+
harnessModelMismatch,
|
|
32
|
+
isAlias,
|
|
33
|
+
type ModelsDevCatalog,
|
|
34
|
+
type ModelsDevModel,
|
|
35
|
+
parseAlias,
|
|
36
|
+
resolveAlias,
|
|
37
|
+
} from "@desplega/model-catalog";
|
|
38
|
+
import { z } from "zod";
|
|
39
|
+
import {
|
|
40
|
+
DEFAULT_MODEL_TIER_MAP,
|
|
41
|
+
MODEL_TIERS,
|
|
42
|
+
type ModelTier,
|
|
43
|
+
type ModelTierOverrides,
|
|
44
|
+
type ProviderName,
|
|
45
|
+
ProviderNameSchema,
|
|
46
|
+
parseModelTier,
|
|
47
|
+
} from "../types";
|
|
48
|
+
import { getDbClient } from "./db";
|
|
49
|
+
import {
|
|
50
|
+
CLI_PINNED_HARNESSES,
|
|
51
|
+
fallbackForUnsupportedModel,
|
|
52
|
+
getAgentHarnessCliVersion,
|
|
53
|
+
getHarnessModelSupport,
|
|
54
|
+
noticeCliUnsupported,
|
|
55
|
+
unsupportedModelMessage,
|
|
56
|
+
} from "./harness-model-support";
|
|
57
|
+
import {
|
|
58
|
+
buildCatalogEntries,
|
|
59
|
+
getCatalogGeneration,
|
|
60
|
+
listModelCatalog,
|
|
61
|
+
listModelCatalogOverlay,
|
|
62
|
+
type ModelCatalogEntry,
|
|
63
|
+
type ModelCatalogFacts,
|
|
64
|
+
} from "./model-catalog-store";
|
|
65
|
+
import { tierConfigKey } from "./model-tier-keys";
|
|
66
|
+
import { loadModelsDevCache } from "./modelsdev-cache";
|
|
67
|
+
|
|
68
|
+
export { isTierConfigKey, tierConfigKey, validateTierConfigValue } from "./model-tier-keys";
|
|
69
|
+
|
|
70
|
+
export type ModelSource =
|
|
71
|
+
| "model"
|
|
72
|
+
| "worker-env"
|
|
73
|
+
| "tier-config"
|
|
74
|
+
| "tier-default"
|
|
75
|
+
| "fallback:cli-unsupported";
|
|
76
|
+
|
|
77
|
+
export interface TaskModelResolution {
|
|
78
|
+
resolvedModel: string;
|
|
79
|
+
modelSource: ModelSource;
|
|
80
|
+
modelAlias: string | null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const DEFAULT_MODEL_LATEST_SOAK_DAYS = 2;
|
|
84
|
+
|
|
85
|
+
// ─── Worker overrides (agents.modelTierOverrides) ────────────────────────────
|
|
86
|
+
|
|
87
|
+
const OverridesSchema = z.record(z.string(), z.record(z.string(), z.string()));
|
|
88
|
+
|
|
89
|
+
/** Keep only known providers/tiers and sane values. */
|
|
90
|
+
export function sanitizeModelTierOverrides(raw: unknown): ModelTierOverrides {
|
|
91
|
+
const parsed = OverridesSchema.safeParse(raw);
|
|
92
|
+
if (!parsed.success) return {};
|
|
93
|
+
const result: ModelTierOverrides = {};
|
|
94
|
+
for (const [provider, tiers] of Object.entries(parsed.data)) {
|
|
95
|
+
if (!ProviderNameSchema.safeParse(provider).success) continue;
|
|
96
|
+
const clean: Partial<Record<ModelTier, string>> = {};
|
|
97
|
+
for (const [tier, value] of Object.entries(tiers)) {
|
|
98
|
+
const t = parseModelTier(tier);
|
|
99
|
+
const v = value.trim();
|
|
100
|
+
if (t && t === tier && v && v.length <= 200 && !/\s/.test(v)) clean[t] = v;
|
|
101
|
+
}
|
|
102
|
+
if (Object.keys(clean).length > 0) result[provider] = clean;
|
|
103
|
+
}
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Parse the `X-Model-Tier-Overrides` poll header (URL-encoded JSON). */
|
|
108
|
+
export function parseModelTierOverridesHeader(
|
|
109
|
+
header: string | null | undefined,
|
|
110
|
+
): ModelTierOverrides | undefined {
|
|
111
|
+
if (header === null || header === undefined || header === "") return undefined;
|
|
112
|
+
try {
|
|
113
|
+
return sanitizeModelTierOverrides(JSON.parse(decodeURIComponent(header)));
|
|
114
|
+
} catch {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export async function getAgentModelTierOverrides(agentId: string): Promise<ModelTierOverrides> {
|
|
120
|
+
const row = await getDbClient().get<{ modelTierOverrides: string | null }>(
|
|
121
|
+
"SELECT modelTierOverrides FROM agents WHERE id = ?",
|
|
122
|
+
[agentId],
|
|
123
|
+
);
|
|
124
|
+
if (!row?.modelTierOverrides) return {};
|
|
125
|
+
try {
|
|
126
|
+
return sanitizeModelTierOverrides(JSON.parse(row.modelTierOverrides));
|
|
127
|
+
} catch {
|
|
128
|
+
return {};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Store the worker's overrides; no write when unchanged. Empty object clears. */
|
|
133
|
+
export async function setAgentModelTierOverrides(
|
|
134
|
+
agentId: string,
|
|
135
|
+
overrides: ModelTierOverrides,
|
|
136
|
+
): Promise<void> {
|
|
137
|
+
const next = Object.keys(overrides).length > 0 ? JSON.stringify(overrides) : null;
|
|
138
|
+
await getDbClient().run(
|
|
139
|
+
"UPDATE agents SET modelTierOverrides = ? WHERE id = ? AND modelTierOverrides IS NOT ?",
|
|
140
|
+
[next, agentId, next],
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ─── Catalog for alias resolution ────────────────────────────────────────────
|
|
145
|
+
|
|
146
|
+
const CATALOG_TTL_MS = 30_000;
|
|
147
|
+
let cachedCatalog: {
|
|
148
|
+
at: number;
|
|
149
|
+
/** `getCatalogGeneration()` read before the tables were, so a write mid-load still invalidates. */
|
|
150
|
+
generation: number;
|
|
151
|
+
catalog: ModelsDevCatalog;
|
|
152
|
+
priced: Set<string>;
|
|
153
|
+
} | null = null;
|
|
154
|
+
|
|
155
|
+
export function invalidateTierResolutionCatalog(): void {
|
|
156
|
+
cachedCatalog = null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function mergeFacts(base: ModelCatalogFacts, overlay: ModelCatalogFacts): ModelCatalogFacts {
|
|
160
|
+
const merged: ModelCatalogFacts = { ...base };
|
|
161
|
+
for (const [key, value] of Object.entries(overlay)) {
|
|
162
|
+
if (value !== null && value !== undefined) {
|
|
163
|
+
(merged as Record<string, unknown>)[key] = value;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return merged;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Catalog view for `resolveAlias`: `model_catalog` + overlay (overlay wins per
|
|
171
|
+
* non-null field). Empty table → the vendored models.dev snapshot.
|
|
172
|
+
*/
|
|
173
|
+
async function loadResolutionCatalog(): Promise<{
|
|
174
|
+
catalog: ModelsDevCatalog;
|
|
175
|
+
priced: Set<string>;
|
|
176
|
+
}> {
|
|
177
|
+
const now = Date.now();
|
|
178
|
+
const generation = getCatalogGeneration();
|
|
179
|
+
if (
|
|
180
|
+
cachedCatalog &&
|
|
181
|
+
cachedCatalog.generation === generation &&
|
|
182
|
+
now - cachedCatalog.at < CATALOG_TTL_MS
|
|
183
|
+
) {
|
|
184
|
+
return cachedCatalog;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
let entries: ModelCatalogEntry[] = await listModelCatalog();
|
|
188
|
+
if (entries.length === 0) {
|
|
189
|
+
const snapshot = loadModelsDevCache();
|
|
190
|
+
entries = snapshot ? buildCatalogEntries(snapshot, 0) : [];
|
|
191
|
+
}
|
|
192
|
+
const facts = new Map<string, { provider: string; modelId: string; facts: ModelCatalogFacts }>();
|
|
193
|
+
for (const entry of entries) {
|
|
194
|
+
facts.set(`${entry.provider}/${entry.modelId}`, {
|
|
195
|
+
provider: entry.provider,
|
|
196
|
+
modelId: entry.modelId,
|
|
197
|
+
facts: entry,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
for (const overlay of await listModelCatalogOverlay()) {
|
|
201
|
+
const key = `${overlay.provider}/${overlay.modelId}`;
|
|
202
|
+
const existing = facts.get(key);
|
|
203
|
+
facts.set(key, {
|
|
204
|
+
provider: overlay.provider,
|
|
205
|
+
modelId: overlay.modelId,
|
|
206
|
+
facts: existing ? mergeFacts(existing.facts, overlay) : overlay,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const catalog: ModelsDevCatalog = {};
|
|
211
|
+
const priced = new Set<string>();
|
|
212
|
+
for (const { provider, modelId, facts: f } of facts.values()) {
|
|
213
|
+
catalog[provider] ??= { id: provider, name: provider, models: {} };
|
|
214
|
+
const model: ModelsDevModel = {
|
|
215
|
+
...(f.name ? { name: f.name } : {}),
|
|
216
|
+
...(f.releaseDate ? { release_date: f.releaseDate } : {}),
|
|
217
|
+
...(f.contextWindow ? { limit: { context: f.contextWindow } } : {}),
|
|
218
|
+
...(f.pricing ? { cost: f.pricing } : {}),
|
|
219
|
+
};
|
|
220
|
+
catalog[provider].models[modelId] = model;
|
|
221
|
+
const p = f.pricing;
|
|
222
|
+
if (p && (typeof p.input === "number" || typeof p.output === "number")) {
|
|
223
|
+
priced.add(`${provider}/${modelId}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
cachedCatalog = { at: now, generation, catalog, priced };
|
|
227
|
+
return cachedCatalog;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ─── Alias resolution + change log ───────────────────────────────────────────
|
|
231
|
+
|
|
232
|
+
function soakDays(): number {
|
|
233
|
+
const raw = process.env.MODEL_LATEST_SOAK_DAYS?.trim();
|
|
234
|
+
if (!raw) return DEFAULT_MODEL_LATEST_SOAK_DAYS;
|
|
235
|
+
const n = Number(raw);
|
|
236
|
+
return Number.isInteger(n) && n >= 0 ? n : DEFAULT_MODEL_LATEST_SOAK_DAYS;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function autoUpgradeEnabled(): boolean {
|
|
240
|
+
const raw = process.env.MODEL_AUTO_UPGRADE?.trim().toLowerCase();
|
|
241
|
+
return !(raw === "false" || raw === "0");
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function lastAliasResolution(alias: string): Promise<string | null> {
|
|
245
|
+
const row = await getDbClient().get<{ newModel: string }>(
|
|
246
|
+
"SELECT newModel FROM model_alias_resolutions WHERE alias = ? ORDER BY id DESC LIMIT 1",
|
|
247
|
+
[alias],
|
|
248
|
+
);
|
|
249
|
+
return row?.newModel ?? null;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Resolve a `latest:` alias with the guardrails above. Returns null when the
|
|
254
|
+
* alias is invalid or nothing in the catalog qualifies.
|
|
255
|
+
*/
|
|
256
|
+
export async function resolveLatestAlias(
|
|
257
|
+
alias: string,
|
|
258
|
+
now: Date = new Date(),
|
|
259
|
+
opts: { record?: boolean } = {},
|
|
260
|
+
): Promise<string | null> {
|
|
261
|
+
const parsed = parseAlias(alias);
|
|
262
|
+
if (!parsed) return null;
|
|
263
|
+
const key = alias.trim().toLowerCase();
|
|
264
|
+
const previous = await lastAliasResolution(key);
|
|
265
|
+
if (previous && !autoUpgradeEnabled()) return previous;
|
|
266
|
+
|
|
267
|
+
const { catalog, priced } = await loadResolutionCatalog();
|
|
268
|
+
const section = parsed.kind;
|
|
269
|
+
const resolved = resolveAlias(key, catalog, {
|
|
270
|
+
soakDays: soakDays(),
|
|
271
|
+
now,
|
|
272
|
+
isSupported: (id) => priced.has(`${section}/${id}`),
|
|
273
|
+
});
|
|
274
|
+
if (!resolved) return null;
|
|
275
|
+
|
|
276
|
+
if (resolved !== previous && opts.record !== false) {
|
|
277
|
+
await getDbClient().run(
|
|
278
|
+
"INSERT INTO model_alias_resolutions (alias, previousModel, newModel, changedAt) VALUES (?, ?, ?, ?)",
|
|
279
|
+
[key, previous, resolved, now.getTime()],
|
|
280
|
+
);
|
|
281
|
+
getDbClient().afterCommit(() => {
|
|
282
|
+
console.info(
|
|
283
|
+
`[model-tiers] alias ${key} now resolves to ${resolved}${previous ? ` (was ${previous})` : ""}`,
|
|
284
|
+
);
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
return resolved;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// ─── Task resolution ─────────────────────────────────────────────────────────
|
|
291
|
+
|
|
292
|
+
export interface ResolveTaskModelInput {
|
|
293
|
+
model?: string | null;
|
|
294
|
+
modelTier?: string | null;
|
|
295
|
+
harnessProvider: ProviderName | null | undefined;
|
|
296
|
+
workerOverrides?: ModelTierOverrides;
|
|
297
|
+
/** Where tier-config values are read from. Default `process.env` (global swarm_config is hydrated into it). */
|
|
298
|
+
env?: Record<string, string | undefined>;
|
|
299
|
+
now?: Date;
|
|
300
|
+
/** Default true. False resolves `latest:` aliases without writing `model_alias_resolutions` (previews). */
|
|
301
|
+
record?: boolean;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/** Ordered candidates by precedence; no IO. */
|
|
305
|
+
export function taskModelCandidates(
|
|
306
|
+
input: ResolveTaskModelInput,
|
|
307
|
+
): { value: string; source: ModelSource }[] {
|
|
308
|
+
const candidates: { value: string; source: ModelSource }[] = [];
|
|
309
|
+
const model = input.model?.trim();
|
|
310
|
+
if (model) candidates.push({ value: model, source: "model" });
|
|
311
|
+
const tier = parseModelTier(input.modelTier);
|
|
312
|
+
const provider = input.harnessProvider;
|
|
313
|
+
if (tier && provider) {
|
|
314
|
+
const worker = input.workerOverrides?.[provider]?.[tier]?.trim();
|
|
315
|
+
if (worker) candidates.push({ value: worker, source: "worker-env" });
|
|
316
|
+
const env = input.env ?? process.env;
|
|
317
|
+
const configured = env[tierConfigKey(provider, tier)]?.trim();
|
|
318
|
+
if (configured) candidates.push({ value: configured, source: "tier-config" });
|
|
319
|
+
const fallback = DEFAULT_MODEL_TIER_MAP[provider]?.[tier];
|
|
320
|
+
if (fallback) candidates.push({ value: fallback, source: "tier-default" });
|
|
321
|
+
}
|
|
322
|
+
return candidates;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/** A task-pinned `model` from another harness family: the claim fails fast with this reason. */
|
|
326
|
+
export interface TaskModelHarnessMismatch {
|
|
327
|
+
harnessMismatch: string;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export function harnessMismatchReason(model: string, harness: string): string {
|
|
331
|
+
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.`;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Resolve a task's model at claim time. Null when the task names neither model nor tier.
|
|
336
|
+
* A tier layer (worker env, tier config, tier default) whose value does not run on the
|
|
337
|
+
* claiming harness is skipped; a task-pinned `model` that does not is a hard mismatch.
|
|
338
|
+
*/
|
|
339
|
+
export async function resolveTaskModel(
|
|
340
|
+
input: ResolveTaskModelInput,
|
|
341
|
+
): Promise<TaskModelResolution | TaskModelHarnessMismatch | null> {
|
|
342
|
+
const harness = input.harnessProvider ?? null;
|
|
343
|
+
const judged = harness !== null && harnessCatalogSection(harness) !== null;
|
|
344
|
+
const sections = judged
|
|
345
|
+
? ((await loadResolutionCatalog()).catalog as HarnessCatalogSections)
|
|
346
|
+
: {};
|
|
347
|
+
for (const candidate of taskModelCandidates(input)) {
|
|
348
|
+
let resolution: TaskModelResolution | null = null;
|
|
349
|
+
if (!isAlias(candidate.value)) {
|
|
350
|
+
resolution = {
|
|
351
|
+
resolvedModel: candidate.value,
|
|
352
|
+
modelSource: candidate.source,
|
|
353
|
+
modelAlias: null,
|
|
354
|
+
};
|
|
355
|
+
} else {
|
|
356
|
+
const resolved = await resolveLatestAlias(candidate.value, input.now, {
|
|
357
|
+
record: input.record,
|
|
358
|
+
});
|
|
359
|
+
if (resolved) {
|
|
360
|
+
resolution = {
|
|
361
|
+
resolvedModel: resolved,
|
|
362
|
+
modelSource: candidate.source,
|
|
363
|
+
modelAlias: candidate.value.trim().toLowerCase(),
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (!resolution) {
|
|
368
|
+
console.warn(
|
|
369
|
+
`[model-tiers] ${candidate.source} alias ${candidate.value} resolved to nothing; trying next layer`,
|
|
370
|
+
);
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
if (judged && harnessModelMismatch(resolution.resolvedModel, harness, sections)) {
|
|
374
|
+
if (candidate.source === "model") {
|
|
375
|
+
return { harnessMismatch: harnessMismatchReason(candidate.value, harness) };
|
|
376
|
+
}
|
|
377
|
+
console.warn(
|
|
378
|
+
`[model-tiers] ${candidate.source} value ${candidate.value} does not run on ${harness}; trying next layer`,
|
|
379
|
+
);
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
return resolution;
|
|
383
|
+
}
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// ─── Tier preview ────────────────────────────────────────────────────────────
|
|
388
|
+
|
|
389
|
+
export interface ModelTierPreview {
|
|
390
|
+
provider: ProviderName;
|
|
391
|
+
tier: ModelTier;
|
|
392
|
+
/** swarm_config / env key that overrides this tier globally. */
|
|
393
|
+
key: string;
|
|
394
|
+
/** The built-in DEFAULT_MODEL_TIER_MAP value. */
|
|
395
|
+
defaultValue: string;
|
|
396
|
+
/** The value stored for `key`, or null when unset. */
|
|
397
|
+
configured: string | null;
|
|
398
|
+
/** Layer that wins today: the configured value, else the built-in default. */
|
|
399
|
+
source: "tier-config" | "tier-default";
|
|
400
|
+
/** What that layer resolves to right now (`latest:` aliases resolved). Null when nothing qualifies. */
|
|
401
|
+
resolvedModel: string | null;
|
|
402
|
+
/** The `latest:` alias behind `resolvedModel`, when any. */
|
|
403
|
+
alias: string | null;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Every provider x tier with its default, configured value and the model it
|
|
408
|
+
* resolves to now. Read-only: `latest:` aliases resolve without writing
|
|
409
|
+
* `model_alias_resolutions`. Ignores per-worker overrides and per-task models,
|
|
410
|
+
* so it answers "what does a `modelTier=<tier>` task get on a fresh <provider>
|
|
411
|
+
* worker".
|
|
412
|
+
*/
|
|
413
|
+
export async function previewModelTiers(
|
|
414
|
+
opts: { env?: Record<string, string | undefined>; now?: Date } = {},
|
|
415
|
+
): Promise<ModelTierPreview[]> {
|
|
416
|
+
const env = opts.env ?? process.env;
|
|
417
|
+
const rows: ModelTierPreview[] = [];
|
|
418
|
+
for (const provider of ProviderNameSchema.options) {
|
|
419
|
+
// A provider with no portable tier mapping (acp) has nothing to preview.
|
|
420
|
+
if (MODEL_TIERS.every((tier) => !DEFAULT_MODEL_TIER_MAP[provider][tier])) continue;
|
|
421
|
+
for (const tier of MODEL_TIERS) {
|
|
422
|
+
const key = tierConfigKey(provider, tier);
|
|
423
|
+
const configured = env[key]?.trim() || null;
|
|
424
|
+
const resolution = await resolveTaskModel({
|
|
425
|
+
modelTier: tier,
|
|
426
|
+
harnessProvider: provider,
|
|
427
|
+
env,
|
|
428
|
+
now: opts.now,
|
|
429
|
+
record: false,
|
|
430
|
+
});
|
|
431
|
+
// No task `model` here, so a harness mismatch cannot occur; keep the type honest.
|
|
432
|
+
const previewed = resolution && "harnessMismatch" in resolution ? null : resolution;
|
|
433
|
+
rows.push({
|
|
434
|
+
provider,
|
|
435
|
+
tier,
|
|
436
|
+
key,
|
|
437
|
+
defaultValue: DEFAULT_MODEL_TIER_MAP[provider][tier],
|
|
438
|
+
configured,
|
|
439
|
+
source: previewed?.modelSource === "tier-config" ? "tier-config" : "tier-default",
|
|
440
|
+
resolvedModel: previewed?.resolvedModel ?? null,
|
|
441
|
+
alias: previewed?.modelAlias ?? null,
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return rows;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Apply the claiming worker's CLI support (harness_model_support). Returns the
|
|
450
|
+
* resolution to use, or `{ unsupported }` when a concrete pinned task `model`
|
|
451
|
+
* is rejected by this CLI and must fail fast.
|
|
452
|
+
*
|
|
453
|
+
* Anything that came from an alias or a tier means "the newest usable model",
|
|
454
|
+
* so it falls back to the newest model of the same family the CLI has not
|
|
455
|
+
* rejected: a `latest:` alias on the task itself, a worker or global tier
|
|
456
|
+
* value, or a tier default (including the Claude CLI shortnames `opus`,
|
|
457
|
+
* `sonnet`, ...). With no usable sibling left the resolution stays and the run
|
|
458
|
+
* reports the CLI's own error. Only a concrete id the task pinned fails fast,
|
|
459
|
+
* because that is the one case where silently running another model would
|
|
460
|
+
* betray the request.
|
|
461
|
+
*/
|
|
462
|
+
async function applyCliSupport(
|
|
463
|
+
resolution: TaskModelResolution,
|
|
464
|
+
harness: string | null,
|
|
465
|
+
cliVersion: string | null,
|
|
466
|
+
): Promise<TaskModelResolution | { unsupported: string }> {
|
|
467
|
+
if (!harness || !cliVersion || !CLI_PINNED_HARNESSES.has(harness)) return resolution;
|
|
468
|
+
const status = await getHarnessModelSupport(harness, cliVersion, resolution.resolvedModel);
|
|
469
|
+
if (status !== "unsupported") return resolution;
|
|
470
|
+
if (resolution.modelSource === "model" && !resolution.modelAlias) {
|
|
471
|
+
return { unsupported: unsupportedModelMessage(harness, cliVersion, resolution.resolvedModel) };
|
|
472
|
+
}
|
|
473
|
+
const section = harnessCatalogSection(harness);
|
|
474
|
+
const { catalog, priced } = await loadResolutionCatalog();
|
|
475
|
+
// Same rule as alias resolution: a model with no price never becomes the pick, or the run
|
|
476
|
+
// would bypass cost accounting.
|
|
477
|
+
const candidates = Object.fromEntries(
|
|
478
|
+
Object.entries((section && catalog[section]?.models) || {}).filter(([id]) =>
|
|
479
|
+
priced.has(`${section}/${id}`),
|
|
480
|
+
),
|
|
481
|
+
);
|
|
482
|
+
const fallback = await fallbackForUnsupportedModel(
|
|
483
|
+
harness,
|
|
484
|
+
cliVersion,
|
|
485
|
+
resolution.resolvedModel,
|
|
486
|
+
candidates,
|
|
487
|
+
);
|
|
488
|
+
if (!fallback) return resolution;
|
|
489
|
+
noticeCliUnsupported(harness, cliVersion, resolution.resolvedModel, fallback);
|
|
490
|
+
return { ...resolution, resolvedModel: fallback, modelSource: "fallback:cli-unsupported" };
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
export type ClaimModelFields = Partial<TaskModelResolution> & {
|
|
494
|
+
/**
|
|
495
|
+
* Set when the task's explicit model is rejected by this worker's CLI, or belongs to another
|
|
496
|
+
* harness family (`[model-harness-mismatch]`); the worker fails the task without a spawn.
|
|
497
|
+
*/
|
|
498
|
+
modelUnsupported?: string;
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Resolve and record the model for a task the given agent is claiming. Runs
|
|
503
|
+
* inside the claim transaction. Returns the fields to merge into the trigger.
|
|
504
|
+
*/
|
|
505
|
+
export async function recordClaimModelResolution(
|
|
506
|
+
task: { id: string; model?: string | null; modelTier?: string | null },
|
|
507
|
+
agent: { id: string; harnessProvider?: ProviderName | null; provider?: ProviderName | null },
|
|
508
|
+
): Promise<ClaimModelFields> {
|
|
509
|
+
const harnessProvider = agent.harnessProvider ?? agent.provider ?? null;
|
|
510
|
+
const resolved = await resolveTaskModel({
|
|
511
|
+
model: task.model,
|
|
512
|
+
modelTier: task.modelTier,
|
|
513
|
+
harnessProvider,
|
|
514
|
+
workerOverrides: await getAgentModelTierOverrides(agent.id),
|
|
515
|
+
});
|
|
516
|
+
if (!resolved) return {};
|
|
517
|
+
if ("harnessMismatch" in resolved) return { modelUnsupported: resolved.harnessMismatch };
|
|
518
|
+
const checked = await applyCliSupport(
|
|
519
|
+
resolved,
|
|
520
|
+
harnessProvider,
|
|
521
|
+
await getAgentHarnessCliVersion(agent.id),
|
|
522
|
+
);
|
|
523
|
+
if ("unsupported" in checked) {
|
|
524
|
+
return { modelUnsupported: checked.unsupported };
|
|
525
|
+
}
|
|
526
|
+
const resolution = checked;
|
|
527
|
+
await getDbClient().run(
|
|
528
|
+
"UPDATE agent_tasks SET resolvedModel = ?, modelSource = ?, modelAlias = ? WHERE id = ?",
|
|
529
|
+
[resolution.resolvedModel, resolution.modelSource, resolution.modelAlias, task.id],
|
|
530
|
+
);
|
|
531
|
+
return resolution.modelAlias
|
|
532
|
+
? resolution
|
|
533
|
+
: { resolvedModel: resolution.resolvedModel, modelSource: resolution.modelSource };
|
|
534
|
+
}
|