@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
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
* This module is safe for both API and worker code — it has NO database imports.
|
|
5
5
|
*
|
|
6
6
|
* Phase 4 + Phase 9 of the cost-tracking plan:
|
|
7
|
-
* -
|
|
7
|
+
* - Model-catalog phase 4: windows come from the model catalog (no
|
|
8
|
+
* per-model table). A new model gets the right window as soon as it
|
|
9
|
+
* lands in `model_catalog`.
|
|
10
|
+
* - `getContextWindowSize` resolves shortnames, family-versioned ids
|
|
8
11
|
* (`claude-sonnet-4-6`), AND dated full ids (`claude-sonnet-4-6-20251004`)
|
|
9
12
|
* by stripping the trailing date suffix. Previously the dated form fell
|
|
10
13
|
* to the 200k default — wildly wrong for sonnet/opus 4.x.
|
|
@@ -17,6 +20,9 @@
|
|
|
17
20
|
* deprecated; new code should use `computeContextUsedUnified`.
|
|
18
21
|
*/
|
|
19
22
|
|
|
23
|
+
import { buildClaudeShortnameMap } from "@desplega/model-catalog";
|
|
24
|
+
import { runtimeCatalogModel, runtimeCatalogSection } from "./runtime-model-catalog";
|
|
25
|
+
|
|
20
26
|
/**
|
|
21
27
|
* Phase 9: stamp this onto every `context_usage` event the adapter emits.
|
|
22
28
|
* Callers that compute their own number for legacy reasons (e.g. pi-mono
|
|
@@ -25,43 +31,6 @@
|
|
|
25
31
|
*/
|
|
26
32
|
export const CONTEXT_FORMULA = "input-cache-output" as const;
|
|
27
33
|
|
|
28
|
-
const CONTEXT_WINDOW_DEFAULTS: Record<string, number> = {
|
|
29
|
-
// Anthropic Fable / Mythos tier
|
|
30
|
-
"claude-fable-5-1": 1_000_000,
|
|
31
|
-
"claude-mythos-5-1": 1_000_000,
|
|
32
|
-
"claude-opus-5-5": 1_000_000,
|
|
33
|
-
"claude-opus-5": 1_000_000,
|
|
34
|
-
"claude-fable-5": 1_000_000,
|
|
35
|
-
"claude-mythos-5": 1_000_000,
|
|
36
|
-
"claude-sonnet-5-5": 1_000_000,
|
|
37
|
-
"claude-sonnet-5": 1_000_000,
|
|
38
|
-
// Anthropic 4.x family
|
|
39
|
-
"claude-opus-4-8": 1_000_000,
|
|
40
|
-
"claude-opus-4-7": 1_000_000,
|
|
41
|
-
"claude-opus-4-6": 1_000_000,
|
|
42
|
-
"claude-opus-4-5": 1_000_000,
|
|
43
|
-
"claude-opus-4-1": 200_000,
|
|
44
|
-
"claude-opus-4-0": 200_000,
|
|
45
|
-
"claude-sonnet-4-6": 1_000_000,
|
|
46
|
-
"claude-sonnet-4-5": 1_000_000,
|
|
47
|
-
"claude-sonnet-4-0": 200_000,
|
|
48
|
-
"claude-haiku-4-5": 200_000,
|
|
49
|
-
// Anthropic 3.x family (legacy)
|
|
50
|
-
"claude-3-7-sonnet": 200_000,
|
|
51
|
-
"claude-3-5-sonnet": 200_000,
|
|
52
|
-
"claude-3-5-haiku": 200_000,
|
|
53
|
-
"claude-3-opus": 200_000,
|
|
54
|
-
"claude-3-sonnet": 200_000,
|
|
55
|
-
"claude-3-haiku": 200_000,
|
|
56
|
-
// Shortnames used by the local-CLI adapter and pi-mono OpenRouter mirror.
|
|
57
|
-
fable: 1_000_000,
|
|
58
|
-
mythos: 1_000_000,
|
|
59
|
-
opus: 1_000_000,
|
|
60
|
-
sonnet: 1_000_000,
|
|
61
|
-
haiku: 200_000,
|
|
62
|
-
default: 200_000,
|
|
63
|
-
};
|
|
64
|
-
|
|
65
34
|
const DEFAULT_CONTEXT_WINDOW = 200_000;
|
|
66
35
|
|
|
67
36
|
/**
|
|
@@ -69,30 +38,55 @@ const DEFAULT_CONTEXT_WINDOW = 200_000;
|
|
|
69
38
|
* resolve to the same window as the family-versioned id.
|
|
70
39
|
*
|
|
71
40
|
* `claude-sonnet-4-6-20251004` → `claude-sonnet-4-6`
|
|
72
|
-
* `claude-haiku-4-5-20251001` → `claude-haiku-4-5`
|
|
73
|
-
*
|
|
74
|
-
* Anthropic's dated full ids are always `${family}-${major}-${minor}-${YYYYMMDD}`,
|
|
75
|
-
* so an 8-digit trailing date is a reliable signal.
|
|
76
41
|
*/
|
|
77
42
|
function stripAnthropicDateSuffix(model: string): string {
|
|
78
43
|
return model.replace(/-(\d{8})$/, "");
|
|
79
44
|
}
|
|
80
45
|
|
|
46
|
+
let shortnameMap: { section: Record<string, unknown>; map: Record<string, string> } | null = null;
|
|
47
|
+
|
|
48
|
+
/** Claude CLI shortnames (`opus`, `sonnet`, ...) → newest catalog id. */
|
|
49
|
+
function resolveClaudeShortname(model: string): string | undefined {
|
|
50
|
+
const section = runtimeCatalogSection("anthropic");
|
|
51
|
+
if (!shortnameMap || shortnameMap.section !== section) {
|
|
52
|
+
shortnameMap = { section, map: buildClaudeShortnameMap(section) };
|
|
53
|
+
}
|
|
54
|
+
return shortnameMap.map[model.trim().toLowerCase()];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function catalogContext(provider: string, model: string): number | undefined {
|
|
58
|
+
const context = runtimeCatalogModel(provider, model)?.limit?.context;
|
|
59
|
+
return typeof context === "number" && context > 0 ? context : undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Context window (tokens) for a model id, read from the model catalog
|
|
64
|
+
* (`src/utils/runtime-model-catalog.ts`: live `model_catalog` + overlay,
|
|
65
|
+
* vendored models.dev snapshot offline). Accepts Anthropic ids (dated or
|
|
66
|
+
* not), Claude CLI shortnames, OpenAI/Codex ids and `provider/model` ids.
|
|
67
|
+
* Unknown models get a conservative 200k so percent math stays finite.
|
|
68
|
+
*/
|
|
81
69
|
export function getContextWindowSize(model: string): number {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
70
|
+
if (!model) return DEFAULT_CONTEXT_WINDOW;
|
|
71
|
+
const slash = model.indexOf("/");
|
|
72
|
+
if (slash > 0) {
|
|
73
|
+
const scoped = catalogContext(model.slice(0, slash), model.slice(slash + 1));
|
|
74
|
+
if (scoped) return scoped;
|
|
75
|
+
}
|
|
76
|
+
for (const provider of ["anthropic", "openai"]) {
|
|
77
|
+
const exact = catalogContext(provider, model);
|
|
78
|
+
if (exact) return exact;
|
|
85
79
|
}
|
|
86
|
-
// Dated full id → strip suffix and retry.
|
|
87
80
|
const stripped = stripAnthropicDateSuffix(model);
|
|
88
|
-
if (stripped !== model
|
|
89
|
-
|
|
81
|
+
if (stripped !== model) {
|
|
82
|
+
const dated = catalogContext("anthropic", stripped);
|
|
83
|
+
if (dated) return dated;
|
|
84
|
+
}
|
|
85
|
+
const shortname = resolveClaudeShortname(model);
|
|
86
|
+
if (shortname) {
|
|
87
|
+
const resolved = catalogContext("anthropic", shortname);
|
|
88
|
+
if (resolved) return resolved;
|
|
90
89
|
}
|
|
91
|
-
// OpenAI / GPT family — most reasoning models have 200k+; we keep this
|
|
92
|
-
// conservative and let callers override via models.dev rates if they want.
|
|
93
|
-
// Specific gpt-5.x context windows are >1M but the local-CLI adapter
|
|
94
|
-
// generally doesn't surface those; the API recompute path uses the rate
|
|
95
|
-
// table, not the window. The 200k default keeps the math safe.
|
|
96
90
|
return DEFAULT_CONTEXT_WINDOW;
|
|
97
91
|
}
|
|
98
92
|
|
package/src/utils/credentials.ts
CHANGED
|
@@ -113,8 +113,12 @@ export interface CredentialSelection {
|
|
|
113
113
|
modelBlockedIndices?: number[];
|
|
114
114
|
/** ISO of the earliest reset among modelBlockedIndices, or null/undefined when none. */
|
|
115
115
|
earliestModelResetAt?: string | null;
|
|
116
|
+
/** Indices excluded because the key's subscription plan cannot run the requested model. */
|
|
117
|
+
seatBlockedIndices?: number[];
|
|
116
118
|
/** Subscription plan id detected on the credential (`SUBSCRIPTION_PLANS`), reported with its usage. */
|
|
117
119
|
plan?: string | null;
|
|
120
|
+
/** `authFailureFence` from `GET /api/keys/available`, read before the task (Codex pool only). */
|
|
121
|
+
authFailureFence?: number;
|
|
118
122
|
}
|
|
119
123
|
|
|
120
124
|
const MODEL_LABELS: Record<ModelFamily, string> = {
|
|
@@ -126,33 +130,49 @@ const MODEL_LABELS: Record<ModelFamily, string> = {
|
|
|
126
130
|
|
|
127
131
|
/**
|
|
128
132
|
* Thrown by `resolveCredentialPools` at task admission (`enforceModelCapacity`)
|
|
129
|
-
* when
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
* `
|
|
133
|
-
*
|
|
133
|
+
* when no key for a pool can run the requested model: every key is key-wide
|
|
134
|
+
* rate-limited, blocked by the model's weekly window, or on a subscription
|
|
135
|
+
* seat that cannot run the model (`seatBlockedCount`). A window block throws
|
|
136
|
+
* only when `MODEL_WINDOW_EXHAUSTED_POLICY` is `fail` (the default); a seat
|
|
137
|
+
* block never resets, so it throws under every policy. The caller must not
|
|
138
|
+
* spawn the CLI on this error — see `spawnProviderProcess` in
|
|
139
|
+
* `src/commands/runner.ts`. Taskless configuration loads never throw it.
|
|
134
140
|
*/
|
|
135
141
|
export class ModelWindowExhaustedError extends Error {
|
|
136
142
|
readonly model: ModelFamily;
|
|
137
143
|
readonly window: string;
|
|
138
144
|
readonly earliestResetAt: string | null;
|
|
139
145
|
readonly keyType: string;
|
|
146
|
+
readonly seatBlockedCount: number;
|
|
147
|
+
readonly modelBlockedCount: number;
|
|
140
148
|
|
|
141
149
|
constructor(opts: {
|
|
142
150
|
model: ModelFamily;
|
|
143
151
|
window: string;
|
|
144
152
|
earliestResetAt: string | null;
|
|
145
153
|
keyType: string;
|
|
154
|
+
seatBlockedCount?: number;
|
|
155
|
+
modelBlockedCount?: number;
|
|
146
156
|
}) {
|
|
147
157
|
const modelLabel = MODEL_LABELS[opts.model];
|
|
158
|
+
const seatBlockedCount = opts.seatBlockedCount ?? 0;
|
|
159
|
+
const modelBlockedCount = opts.modelBlockedCount ?? 0;
|
|
160
|
+
const windowClause =
|
|
161
|
+
modelBlockedCount > 0 && opts.earliestResetAt
|
|
162
|
+
? `${modelBlockedCount} keys have the ${modelLabel} window exhausted until ${opts.earliestResetAt}`
|
|
163
|
+
: `${modelBlockedCount} keys have the ${modelLabel} window exhausted`;
|
|
148
164
|
super(
|
|
149
|
-
|
|
165
|
+
seatBlockedCount > 0
|
|
166
|
+
? `No ${opts.keyType} key can run ${modelLabel}: ${seatBlockedCount} keys are on a seat without ${modelLabel}, ${windowClause}. Re-dispatch with another model or modelTier.`
|
|
167
|
+
: `No ${opts.keyType} key has ${modelLabel} capacity until ${opts.earliestResetAt ?? "unknown"}. Re-dispatch with another model or modelTier.`,
|
|
150
168
|
);
|
|
151
169
|
this.name = "ModelWindowExhaustedError";
|
|
152
170
|
this.model = opts.model;
|
|
153
171
|
this.window = opts.window;
|
|
154
172
|
this.earliestResetAt = opts.earliestResetAt;
|
|
155
173
|
this.keyType = opts.keyType;
|
|
174
|
+
this.seatBlockedCount = seatBlockedCount;
|
|
175
|
+
this.modelBlockedCount = modelBlockedCount;
|
|
156
176
|
}
|
|
157
177
|
}
|
|
158
178
|
|
|
@@ -173,6 +193,15 @@ function isJsonObject(value: string): boolean {
|
|
|
173
193
|
* Falls back to random selection from all credentials if no available indices match.
|
|
174
194
|
* A JSON object value (the CODEX_OAUTH blob) is always one credential.
|
|
175
195
|
*/
|
|
196
|
+
function splitCredentialPool(value: string): string[] {
|
|
197
|
+
return isJsonObject(value)
|
|
198
|
+
? [value.trim()]
|
|
199
|
+
: value
|
|
200
|
+
.split(",")
|
|
201
|
+
.map((s) => s.trim())
|
|
202
|
+
.filter(Boolean);
|
|
203
|
+
}
|
|
204
|
+
|
|
176
205
|
export function selectCredential(
|
|
177
206
|
value: string,
|
|
178
207
|
availableIndices?: number[],
|
|
@@ -183,12 +212,7 @@ export function selectCredential(
|
|
|
183
212
|
// handed out fragments such as `"accountId":"..."}` as the selected credential
|
|
184
213
|
// and stamped `965"}` as the key suffix on task records. Codex pools live in
|
|
185
214
|
// config-store slots (codex_oauth_<n>), resolved by the runner instead.
|
|
186
|
-
const credentials =
|
|
187
|
-
? [value.trim()]
|
|
188
|
-
: value
|
|
189
|
-
.split(",")
|
|
190
|
-
.map((s) => s.trim())
|
|
191
|
-
.filter(Boolean);
|
|
215
|
+
const credentials = splitCredentialPool(value);
|
|
192
216
|
if (credentials.length <= 1) {
|
|
193
217
|
const selected = value.trim();
|
|
194
218
|
const isRateLimitFallback = availableIndices !== undefined && availableIndices.length === 0;
|
|
@@ -283,6 +307,8 @@ export interface AvailabilityInfo {
|
|
|
283
307
|
availableIndices: number[];
|
|
284
308
|
modelBlockedIndices?: number[];
|
|
285
309
|
earliestModelResetAt?: string | null;
|
|
310
|
+
/** Indices excluded because the key's subscription plan cannot run the model. */
|
|
311
|
+
seatBlockedIndices?: number[];
|
|
286
312
|
}
|
|
287
313
|
|
|
288
314
|
/**
|
|
@@ -315,11 +341,13 @@ async function fetchAvailableIndices(
|
|
|
315
341
|
availableIndices: number[];
|
|
316
342
|
modelBlockedIndices?: number[];
|
|
317
343
|
earliestModelResetAt?: string | null;
|
|
344
|
+
seatBlockedIndices?: number[];
|
|
318
345
|
};
|
|
319
346
|
availableIndicesMap[envVar] = {
|
|
320
347
|
availableIndices: data.availableIndices,
|
|
321
348
|
modelBlockedIndices: data.modelBlockedIndices,
|
|
322
349
|
earliestModelResetAt: data.earliestModelResetAt,
|
|
350
|
+
seatBlockedIndices: data.seatBlockedIndices,
|
|
323
351
|
};
|
|
324
352
|
if (data.availableIndices.length < totalKeys) {
|
|
325
353
|
console.log(
|
|
@@ -374,7 +402,9 @@ export async function resolveCredentialPools(
|
|
|
374
402
|
localBlocks?: Map<string, number>;
|
|
375
403
|
/**
|
|
376
404
|
* Set only at task admission (`spawnProviderProcess`). When true, an
|
|
377
|
-
* exhausted model window
|
|
405
|
+
* exhausted model window, or a pool where every remaining key is on a
|
|
406
|
+
* seat that cannot run the model, fails fast with
|
|
407
|
+
* `ModelWindowExhaustedError`.
|
|
378
408
|
* Taskless configuration loads (worker boot, credential recovery,
|
|
379
409
|
* periodic reconciliation) leave it unset: they still pick a key, so an
|
|
380
410
|
* exhausted default model never blocks boot or config refresh, and the
|
|
@@ -413,32 +443,43 @@ export async function resolveCredentialPools(
|
|
|
413
443
|
modelBlockedCount += beforeCount - available.length;
|
|
414
444
|
}
|
|
415
445
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
//
|
|
421
|
-
//
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
) {
|
|
446
|
+
const seatBlockedIndices = info?.seatBlockedIndices ?? [];
|
|
447
|
+
const seatBlockedCount = seatBlockedIndices.length;
|
|
448
|
+
let seatFallbackIndices: number[] | undefined;
|
|
449
|
+
|
|
450
|
+
// Every key is key-wide rate-limited, blocked by this model's weekly
|
|
451
|
+
// window, or on a seat that cannot run the model, and at least one is
|
|
452
|
+
// blocked specifically for the model (not just legacy key-wide rate
|
|
453
|
+
// limiting) — the picker can't make progress for this model on this
|
|
454
|
+
// pool. Default policy fails fast instead of looping the worker
|
|
455
|
+
// through the same exhausted key every few minutes. A seat block is a
|
|
456
|
+
// plan fact with no window and no reset, so the fallback pick excludes
|
|
457
|
+
// seat-blocked keys: a pick from them is a guaranteed failure. It fails
|
|
458
|
+
// only when every key is seat-blocked.
|
|
459
|
+
if (opts?.enforceModelCapacity && modelFamily && available && available.length === 0) {
|
|
430
460
|
const policy = (env.MODEL_WINDOW_EXHAUSTED_POLICY ?? "fail").trim().toLowerCase();
|
|
431
|
-
|
|
461
|
+
const windowExhausted =
|
|
462
|
+
window !== undefined && modelBlockedCount > 0 && policy !== "fallback";
|
|
463
|
+
if (seatBlockedCount > 0) {
|
|
464
|
+
seatFallbackIndices = splitCredentialPool(val)
|
|
465
|
+
.map((_, i) => i)
|
|
466
|
+
.filter((i) => !seatBlockedIndices.includes(i));
|
|
467
|
+
}
|
|
468
|
+
if (windowExhausted || seatFallbackIndices?.length === 0) {
|
|
432
469
|
throw new ModelWindowExhaustedError({
|
|
433
470
|
model: modelFamily,
|
|
434
|
-
window,
|
|
471
|
+
window: window ?? "",
|
|
435
472
|
earliestResetAt: info?.earliestModelResetAt ?? null,
|
|
436
473
|
keyType: envVar,
|
|
474
|
+
seatBlockedCount,
|
|
475
|
+
modelBlockedCount,
|
|
437
476
|
});
|
|
438
477
|
}
|
|
439
478
|
}
|
|
440
479
|
|
|
441
|
-
const result =
|
|
480
|
+
const result = seatFallbackIndices
|
|
481
|
+
? { ...selectCredential(val, seatFallbackIndices, envVar), isRateLimitFallback: true }
|
|
482
|
+
: selectCredential(val, available, envVar);
|
|
442
483
|
env[envVar] = result.selected;
|
|
443
484
|
const availInfo = available ? ` (${available.length} available of ${result.total})` : "";
|
|
444
485
|
console.log(
|
|
@@ -448,6 +489,7 @@ export async function resolveCredentialPools(
|
|
|
448
489
|
...result,
|
|
449
490
|
modelBlockedIndices: info?.modelBlockedIndices,
|
|
450
491
|
earliestModelResetAt: info?.earliestModelResetAt,
|
|
492
|
+
seatBlockedIndices: info?.seatBlockedIndices,
|
|
451
493
|
});
|
|
452
494
|
}
|
|
453
495
|
}
|
|
@@ -95,6 +95,51 @@ export function parseRateLimitWindowTelemetry(
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
/**
|
|
99
|
+
* `rate_limit_info` fields that describe the seat's billing state (the
|
|
100
|
+
* `credits_required` payload). `scrubSecrets` does not match them, so they
|
|
101
|
+
* are dropped structurally before a log egress.
|
|
102
|
+
*/
|
|
103
|
+
const SEAT_PAYLOAD_FIELDS = [
|
|
104
|
+
"overageDisabledReason",
|
|
105
|
+
"canUserPurchaseCredits",
|
|
106
|
+
"hasChargeableSavedPaymentMethod",
|
|
107
|
+
] as const;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Returns a `rate_limit_event` message without the seat billing fields of
|
|
111
|
+
* its `rate_limit_info`. Any other message is returned unchanged. Error
|
|
112
|
+
* tracking reads the original message; only log egress uses the result.
|
|
113
|
+
*/
|
|
114
|
+
export function redactRateLimitEvent<T>(message: T): T {
|
|
115
|
+
if (!message || typeof message !== "object") return message;
|
|
116
|
+
const json = message as Record<string, unknown>;
|
|
117
|
+
if (json.type !== "rate_limit_event") return message;
|
|
118
|
+
const info = json.rate_limit_info;
|
|
119
|
+
if (!info || typeof info !== "object") return message;
|
|
120
|
+
if (!SEAT_PAYLOAD_FIELDS.some((field) => field in info)) return message;
|
|
121
|
+
const redactedInfo = { ...(info as Record<string, unknown>) };
|
|
122
|
+
for (const field of SEAT_PAYLOAD_FIELDS) delete redactedInfo[field];
|
|
123
|
+
return { ...json, rate_limit_info: redactedInfo } as T;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Line form of `redactRateLimitEvent` for the CLI's stream-json output. A
|
|
128
|
+
* line that is not a `rate_limit_event` with seat billing fields is returned
|
|
129
|
+
* byte for byte.
|
|
130
|
+
*/
|
|
131
|
+
export function redactRateLimitEventLine(line: string): string {
|
|
132
|
+
if (!line.includes('"rate_limit_event"')) return line;
|
|
133
|
+
let parsed: unknown;
|
|
134
|
+
try {
|
|
135
|
+
parsed = JSON.parse(line);
|
|
136
|
+
} catch {
|
|
137
|
+
return line;
|
|
138
|
+
}
|
|
139
|
+
const redacted = redactRateLimitEvent(parsed);
|
|
140
|
+
return redacted === parsed ? line : JSON.stringify(redacted);
|
|
141
|
+
}
|
|
142
|
+
|
|
98
143
|
/**
|
|
99
144
|
* Maximum cooldown horizon for a rate-limit reset. A weekly OAuth limit resets
|
|
100
145
|
* up to ~7 days out, so the cap must be at least that or a weekly-limited key
|
|
@@ -186,6 +231,14 @@ export class SessionErrorTracker {
|
|
|
186
231
|
private modelRateLimit:
|
|
187
232
|
| { window: string; model: ModelFamily; resetAtMs: number; observedAt: string }
|
|
188
233
|
| undefined;
|
|
234
|
+
/**
|
|
235
|
+
* Stashed `credits_required` rejection: the key's seat cannot run the
|
|
236
|
+
* session's model (e.g. "Fable 5.1 requires usage credits"). A seat fact,
|
|
237
|
+
* not a rate limit, so it never lands in `keyWideRejections`.
|
|
238
|
+
*/
|
|
239
|
+
private creditsRequired:
|
|
240
|
+
| { observedAt: string; resetsAtMs?: number; overageDisabledReason?: string }
|
|
241
|
+
| undefined;
|
|
189
242
|
private rateLimitWindows: RateLimitWindowTelemetry = {};
|
|
190
243
|
|
|
191
244
|
/** Record an error from an assistant message with message.error field */
|
|
@@ -265,6 +318,21 @@ export class SessionErrorTracker {
|
|
|
265
318
|
return;
|
|
266
319
|
}
|
|
267
320
|
|
|
321
|
+
// Checked before the resetsAt validation: the observed event carries a
|
|
322
|
+
// resetsAt, but this branch must not depend on it.
|
|
323
|
+
if (info.errorCode === "credits_required") {
|
|
324
|
+
this.creditsRequired = {
|
|
325
|
+
observedAt,
|
|
326
|
+
resetsAtMs:
|
|
327
|
+
typeof info.resetsAt === "number" && Number.isFinite(info.resetsAt)
|
|
328
|
+
? info.resetsAt * 1000
|
|
329
|
+
: undefined,
|
|
330
|
+
overageDisabledReason:
|
|
331
|
+
typeof info.overageDisabledReason === "string" ? info.overageDisabledReason : undefined,
|
|
332
|
+
};
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
268
336
|
const resetsAtSec = info.resetsAt;
|
|
269
337
|
if (typeof resetsAtSec !== "number" || !Number.isFinite(resetsAtSec) || resetsAtSec <= 0) {
|
|
270
338
|
console.warn(
|
|
@@ -344,6 +412,18 @@ export class SessionErrorTracker {
|
|
|
344
412
|
};
|
|
345
413
|
}
|
|
346
414
|
|
|
415
|
+
/**
|
|
416
|
+
* Returns the stashed `credits_required` rejection, or undefined if none was
|
|
417
|
+
* seen in this session. It never sets the key-wide reset time.
|
|
418
|
+
*/
|
|
419
|
+
getCreditsRequired(): { observedAt: string; overageDisabledReason?: string } | undefined {
|
|
420
|
+
if (!this.creditsRequired) return undefined;
|
|
421
|
+
return {
|
|
422
|
+
observedAt: this.creditsRequired.observedAt,
|
|
423
|
+
overageDisabledReason: this.creditsRequired.overageDisabledReason,
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
|
|
347
427
|
hasErrors(): boolean {
|
|
348
428
|
return this.errors.length > 0;
|
|
349
429
|
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The worker's harness CLI version (`claude --version` / `codex --version`),
|
|
3
|
+
* reported on register and used to key harness_model_support rows. Worker-safe
|
|
4
|
+
* (no DB). Probed once per process; null when the CLI is missing or silent.
|
|
5
|
+
*/
|
|
6
|
+
import { isUnknownModelError } from "./harness-model-error";
|
|
7
|
+
import { scrubSecrets } from "./secret-scrubber";
|
|
8
|
+
|
|
9
|
+
const CLI_BINARY: Record<string, string> = { claude: "claude", codex: "codex" };
|
|
10
|
+
const PROBE_TIMEOUT_MS = 10_000;
|
|
11
|
+
const VERSION_RE = /\d+\.\d+\.\d+(?:[-+][\w.]+)?/;
|
|
12
|
+
|
|
13
|
+
const probed = new Map<string, Promise<string | null>>();
|
|
14
|
+
|
|
15
|
+
export function parseCliVersion(output: string): string | null {
|
|
16
|
+
return VERSION_RE.exec(output)?.[0] ?? null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function probeHarnessCliVersion(harness: string): Promise<string | null> {
|
|
20
|
+
const binary = CLI_BINARY[harness];
|
|
21
|
+
if (!binary) return Promise.resolve(null);
|
|
22
|
+
let pending = probed.get(harness);
|
|
23
|
+
if (!pending) {
|
|
24
|
+
pending = (async () => {
|
|
25
|
+
try {
|
|
26
|
+
const proc = Bun.spawn([binary, "--version"], {
|
|
27
|
+
stdin: "ignore",
|
|
28
|
+
stdout: "pipe",
|
|
29
|
+
stderr: "pipe",
|
|
30
|
+
timeout: PROBE_TIMEOUT_MS,
|
|
31
|
+
killSignal: "SIGKILL",
|
|
32
|
+
});
|
|
33
|
+
const [stdout, exitCode] = await Promise.all([
|
|
34
|
+
new Response(proc.stdout).text(),
|
|
35
|
+
proc.exited,
|
|
36
|
+
]);
|
|
37
|
+
return exitCode === 0 ? parseCliVersion(stdout) : null;
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
})();
|
|
42
|
+
probed.set(harness, pending);
|
|
43
|
+
}
|
|
44
|
+
return pending;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Last probed version, without waiting; null until the probe finished. */
|
|
48
|
+
export async function cachedHarnessCliVersion(harness: string): Promise<string | null> {
|
|
49
|
+
return (await probed.get(harness)) ?? null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const reported = new Set<string>();
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Record a model's run outcome on this worker's CLI. `ok` is sent once per
|
|
56
|
+
* model per process; `unsupported` whenever the CLI rejects the id. Other
|
|
57
|
+
* failures say nothing about support and are not sent. Never throws.
|
|
58
|
+
*/
|
|
59
|
+
export async function reportHarnessModelOutcome(opts: {
|
|
60
|
+
apiUrl: string;
|
|
61
|
+
apiKey?: string;
|
|
62
|
+
agentId: string;
|
|
63
|
+
harness: string;
|
|
64
|
+
model: string | undefined;
|
|
65
|
+
exitCode: number;
|
|
66
|
+
failureReason?: string;
|
|
67
|
+
fetchImpl?: typeof fetch;
|
|
68
|
+
}): Promise<void> {
|
|
69
|
+
if (!opts.model || !CLI_BINARY[opts.harness]) return;
|
|
70
|
+
const cliVersion = await cachedHarnessCliVersion(opts.harness);
|
|
71
|
+
if (!cliVersion) return;
|
|
72
|
+
const status =
|
|
73
|
+
opts.exitCode === 0
|
|
74
|
+
? "ok"
|
|
75
|
+
: isUnknownModelError(opts.failureReason, opts.model)
|
|
76
|
+
? "unsupported"
|
|
77
|
+
: null;
|
|
78
|
+
if (!status) return;
|
|
79
|
+
const key = `${opts.harness}|${cliVersion}|${opts.model}`;
|
|
80
|
+
if (status === "ok" && reported.has(key)) return;
|
|
81
|
+
try {
|
|
82
|
+
const headers: Record<string, string> = {
|
|
83
|
+
"Content-Type": "application/json",
|
|
84
|
+
"X-Agent-ID": opts.agentId,
|
|
85
|
+
};
|
|
86
|
+
if (opts.apiKey) headers.Authorization = `Bearer ${opts.apiKey}`;
|
|
87
|
+
const res = await (opts.fetchImpl ?? fetch)(
|
|
88
|
+
`${opts.apiUrl}/api/models-catalog/harness-support`,
|
|
89
|
+
{
|
|
90
|
+
method: "PUT",
|
|
91
|
+
headers,
|
|
92
|
+
body: JSON.stringify({
|
|
93
|
+
harness: opts.harness,
|
|
94
|
+
cliVersion,
|
|
95
|
+
modelId: opts.model,
|
|
96
|
+
status,
|
|
97
|
+
...(status === "unsupported" && opts.failureReason
|
|
98
|
+
? { error: scrubSecrets(opts.failureReason).slice(0, 2000) }
|
|
99
|
+
: {}),
|
|
100
|
+
}),
|
|
101
|
+
},
|
|
102
|
+
);
|
|
103
|
+
if (res.ok && status === "ok") reported.add(key);
|
|
104
|
+
} catch {
|
|
105
|
+
// Best effort: the next run reports again.
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function resetHarnessCliVersionForTests(seed?: Record<string, string | null>): void {
|
|
110
|
+
probed.clear();
|
|
111
|
+
reported.clear();
|
|
112
|
+
for (const [harness, version] of Object.entries(seed ?? {})) {
|
|
113
|
+
probed.set(harness, Promise.resolve(version));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recognizes a harness CLI rejecting a model id. Worker-safe (no DB).
|
|
3
|
+
*
|
|
4
|
+
* Claude Code: "There's an issue with the selected model (x). It may not exist
|
|
5
|
+
* or you may not have access to it", or the API's `not_found_error` "model: x".
|
|
6
|
+
* Codex: "The model `x` does not exist or you do not have access to it",
|
|
7
|
+
* "model is not supported", "unknown model".
|
|
8
|
+
*
|
|
9
|
+
* A model-rejection phrase can also appear in an unrelated failure ("invalid model output"),
|
|
10
|
+
* and a false positive marks the model unsupported for the whole (harness, CLI version). So
|
|
11
|
+
* when `modelId` is given, the rejection line must name that model.
|
|
12
|
+
*/
|
|
13
|
+
const UNKNOWN_MODEL_PATTERNS: RegExp[] = [
|
|
14
|
+
/issue with the selected model/i,
|
|
15
|
+
/model[`'" ]*[\w.:/-]*[`'" ]*(?:does not exist|not found)/i,
|
|
16
|
+
/not_found_error[^\n]*model/i,
|
|
17
|
+
/model[^\n]{0,80}is not supported/i,
|
|
18
|
+
/unsupported model/i,
|
|
19
|
+
/unknown model/i,
|
|
20
|
+
/invalid model/i,
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
export function isUnknownModelError(
|
|
24
|
+
text: string | null | undefined,
|
|
25
|
+
modelId?: string | null,
|
|
26
|
+
): boolean {
|
|
27
|
+
if (!text) return false;
|
|
28
|
+
const id = modelId?.trim().toLowerCase();
|
|
29
|
+
return text.split("\n").some((line) => {
|
|
30
|
+
if (!UNKNOWN_MODEL_PATTERNS.some((re) => re.test(line))) return false;
|
|
31
|
+
return !id || line.toLowerCase().includes(id);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -73,6 +73,18 @@ export function parseModelLimitMessage(text: string): ModelFamily | undefined {
|
|
|
73
73
|
return match[1]!.toLowerCase() as ModelFamily;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Text signal for a seat that cannot run the model. Matches the CLI's failure
|
|
78
|
+
* text for a `credits_required` rejection, e.g. "Fable 5.1 requires usage
|
|
79
|
+
* credits. Switch to another model to continue." A dot followed by a digit
|
|
80
|
+
* is allowed so a version number ("5.1") does not end the match.
|
|
81
|
+
*/
|
|
82
|
+
export function parseCreditsRequiredMessage(text: string): ModelFamily | undefined {
|
|
83
|
+
const match = text.match(/(Fable|Opus|Sonnet)(?:[^.]|\.(?=\d)){0,40}requires usage credits/i);
|
|
84
|
+
if (!match) return undefined;
|
|
85
|
+
return match[1]!.toLowerCase() as ModelFamily;
|
|
86
|
+
}
|
|
87
|
+
|
|
76
88
|
interface RateLimitWindowLike {
|
|
77
89
|
status: string;
|
|
78
90
|
resetsAt?: number;
|