@desplega.ai/agent-swarm 1.158.0 → 1.159.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/dist/{acp-adapter-xaq4n6pc.js → acp-adapter-qhch0n2v.js} +1 -1
- package/dist/{actions-p9hz5yat.js → actions-dtsh1mfb.js} +33 -29
- package/dist/{anthropic-messages-51pz26qe.js → anthropic-messages-f2h06k64.js} +8 -3
- package/dist/{app-g26wdcz6.js → app-y1b9srva.js} +10 -9
- package/dist/{assistant-wydfqkb3.js → assistant-31hwy5xj.js} +34 -30
- package/dist/{azure-openai-responses-8gksqtmz.js → azure-openai-responses-0k1mfax5.js} +12 -13
- package/dist/{boot-reembed-se8zmdcp.js → boot-reembed-48d2qpkt.js} +11 -11
- package/dist/{boot-reembed-g5ckg9yb.js → boot-reembed-w0bryhkr.js} +12 -12
- package/dist/{boot-scrub-logs-wfxqan61.js → boot-scrub-logs-t65wbrn7.js} +9 -8
- package/dist/{boot-seeds-s3q01xj3.js → boot-seeds-h7c32hvm.js} +12 -11
- package/dist/{claude-adapter-tv2x4f36.js → claude-adapter-1t6611ne.js} +26 -21
- package/dist/{claude-managed-adapter-41b2s7fd.js → claude-managed-adapter-werp659j.js} +4 -2
- package/dist/{claude-managed-setup-60g8aazx.js → claude-managed-setup-y6rdgbc1.js} +11 -2
- package/dist/claude-sdk-session-zhkyk8yw.js +29319 -0
- package/dist/{cli-vj6h9kc2.js → cli-0m7wct0d.js} +59 -1
- package/dist/{cli-wscdvcqz.js → cli-0n2e4x49.js} +1 -1
- package/dist/cli-0xqde53b.js +176 -0
- package/dist/{cli-xberxa1r.js → cli-10ym38qc.js} +2 -2
- package/dist/{cli-5b6b62tz.js → cli-13hw4z91.js} +1 -1
- package/dist/{cli-rqz8qbw7.js → cli-20035trh.js} +50 -18
- package/dist/{cli-k0g1sjzm.js → cli-31nkw3g3.js} +3 -3
- package/dist/cli-3gvrr8vj.js +1422 -0
- package/dist/{cli-c07r9a2m.js → cli-48527fpw.js} +9 -8
- package/dist/{cli-24k806sc.js → cli-4aj25szq.js} +2 -2
- package/dist/{cli-7gtvvpbh.js → cli-4tvgz99m.js} +7 -7
- package/dist/cli-503ya656.js +455 -0
- package/dist/{cli-cw9zck8j.js → cli-5f2hq128.js} +8 -4
- package/dist/{cli-s8eet254.js → cli-68bmme5p.js} +11078 -27833
- package/dist/{cli-ftfvggzx.js → cli-79aq9cgb.js} +794 -563
- package/dist/cli-7cxw8b60.js +14910 -0
- package/dist/cli-7kyk9jr3.js +22 -0
- package/dist/cli-7zttzetj.js +324 -0
- package/dist/{cli-y95wp3tg.js → cli-96qd5psa.js} +1 -1
- package/dist/{cli-g8djp6qe.js → cli-9yg65c1j.js} +22 -0
- package/dist/{cli-3gmcwfyx.js → cli-ardpzhjm.js} +5 -5
- package/dist/{cli-7s3syk22.js → cli-b6thtzac.js} +5 -5
- package/dist/{cli-00w2c66p.js → cli-bgv70kym.js} +13 -2
- package/dist/{cli-j55n64b6.js → cli-bv0a38v9.js} +163 -44
- package/dist/cli-ca2w7zv7.js +115 -0
- package/dist/cli-cpvsaqsw.js +333 -0
- package/dist/cli-cygyqj3b.js +754 -0
- package/dist/{cli-x24pshtq.js → cli-dvjpkkjd.js} +6 -6
- package/dist/{cli-5d10vd2m.js → cli-e0ndxhtk.js} +4 -4
- package/dist/{cli-4198qc9v.js → cli-e4xnc0qp.js} +15 -3
- package/dist/{cli-rjehvrts.js → cli-eg7cdg68.js} +8 -8
- package/dist/cli-ejpygnqz.js +3815 -0
- package/dist/{cli-fgqxwrpv.js → cli-fjtp3463.js} +1631 -127
- package/dist/{cli-zt1227k1.js → cli-fkqhbg8g.js} +5 -3
- package/dist/{cli-47m6dkd3.js → cli-g9df2m37.js} +409 -168
- package/dist/cli-gzyh0j0g.js +8455 -0
- package/dist/cli-hgqfttfc.js +183 -0
- package/dist/cli-hj65wsts.js +17755 -0
- package/dist/cli-j9z8mfx3.js +476 -0
- package/dist/{cli-aehm1rbq.js → cli-kc3dp84d.js} +1135 -61
- package/dist/{cli-h38x0qhg.js → cli-kdnehe0t.js} +4 -3
- package/dist/{cli-20cj37vt.js → cli-m4excttz.js} +136 -2216
- package/dist/{cli-8qmme6vd.js → cli-mg24hkt4.js} +2 -2
- package/dist/{cli-xhaeg2nc.js → cli-mzkqpxee.js} +1 -1
- package/dist/{cli-q67y4wzm.js → cli-phsaj4xq.js} +24 -2
- package/dist/{cli-pw9rj5t9.js → cli-pqetb83f.js} +1 -176
- package/dist/{cli-rj19hc34.js → cli-qjxy52w7.js} +1 -1
- package/dist/{cli-53sz8gkz.js → cli-qv4jv8bx.js} +1 -1
- package/dist/cli-qvnnx4ct.js +1051 -0
- package/dist/cli-rb9m6rpe.js +4980 -0
- package/dist/{cli-tt9k8r35.js → cli-rfezr056.js} +476 -909
- package/dist/{cli-ggm92wcn.js → cli-rgjprct3.js} +1 -0
- package/dist/{cli-9httdpyq.js → cli-rrqmqr1s.js} +1 -1
- package/dist/cli-rykfmgwg.js +102 -0
- package/dist/{cli-eetrvv91.js → cli-t3p77qk1.js} +1 -1
- package/dist/{cli-68w09sjw.js → cli-vn0qv9cj.js} +25 -88
- package/dist/{cli-987hg0aa.js → cli-vwbbaecp.js} +2 -2
- package/dist/cli-wd9hz40d.js +69 -0
- package/dist/{cli-j57qkrpq.js → cli-wgjx0q33.js} +13 -13
- package/dist/cli-xgpf00sk.js +2149 -0
- package/dist/{cli-mcnhgnn1.js → cli-ykk5jaax.js} +184 -26
- package/dist/{cli-aky8tctm.js → cli-z9x0sh1t.js} +3 -3
- package/dist/{cli-1hkrch3k.js → cli-zw65mab6.js} +5 -5
- package/dist/cli.js +38 -29
- package/dist/cloudflare-workers-ai-system-one-1sg586qv.js +46 -0
- package/dist/{codex-adapter-cnm8wk11.js → codex-adapter-jqfqqa21.js} +21 -14
- package/dist/{codex-hook-9hptmdhj.js → codex-hook-5eys3dya.js} +2 -2
- package/dist/{codex-session-runner-gfg547zj.js → codex-session-runner-zd341dsm.js} +21 -14
- package/dist/{commands-e3bn0p75.js → commands-vfsysz4s.js} +9 -8
- package/dist/{db-03v9kzr2.js → db-s3znhvbn.js} +15 -10
- package/dist/{dispatcher-nrmg8yht.js → dispatcher-5xn0c7rf.js} +32 -28
- package/dist/{dsh-adapter-k03nfqa6.js → dsh-adapter-894mgfvs.js} +3 -3
- package/dist/{e2b-93x1k0j5.js → e2b-ez07znam.js} +4 -3
- package/dist/execute-71ysgv35.js +965 -0
- package/dist/{google-generative-ai-bxmv50nc.js → google-generative-ai-1nfcdphp.js} +5 -4
- package/dist/{google-vertex-809af514.js → google-vertex-1tqcd7b1.js} +5 -4
- package/dist/{handlers-aqcedbyb.js → handlers-5w4jwycr.js} +35 -31
- package/dist/{hook-7vkskkre.js → hook-853bxrr6.js} +16 -13
- package/dist/{hook-ybvmrmhq.js → hook-z0hbxj85.js} +16 -13
- package/dist/{http-2eqa8t24.js → http-akc3wy3m.js} +600 -123
- package/dist/{index-f6cryxwe.js → index-4a5njnm1.js} +2 -59
- package/dist/{index-a3vn96n8.js → index-5sz16ksh.js} +187 -47
- package/dist/{index-c6zsv1bd.js → index-bjmjh9h4.js} +11 -11
- package/dist/{index-chgdkq36.js → index-cd151qkq.js} +32 -28
- package/dist/{index-x71bvq0p.js → index-k661dzzr.js} +33 -29
- package/dist/{index-1yks3qcr.js → index-ttpbhnjg.js} +32 -28
- package/dist/{keepalive-vtkf5n99.js → keepalive-8p9rpvz5.js} +11 -10
- package/dist/{lead-h3sp357x.js → lead-jp69z5h3.js} +49 -45
- package/dist/llama-cpp-classify-t2tccams.js +335 -0
- package/dist/{maintenance-jzeqtk3q.js → maintenance-fwx4ev0k.js} +12 -12
- package/dist/{mistral-conversations-zx0kn554.js → mistral-conversations-bhvwqcnk.js} +14 -17
- package/dist/{oauth-refresh-sweep-qp3dzmdp.js → oauth-refresh-sweep-cwcq1ctx.js} +10 -9
- package/dist/{onboard-tmsp0qbz.js → onboard-2b4q0t84.js} +6 -5
- package/dist/{openai-codex-responses-4vjqzvjp.js → openai-codex-responses-nekthgks.js} +21 -8
- package/dist/{openai-completions-9hjrqbw8.js → openai-completions-hxtzwj0s.js} +8 -11
- package/dist/{openai-responses-7b1pn0xt.js → openai-responses-w4r118w7.js} +22 -17
- package/dist/{opencode-adapter-x6x2da1z.js → opencode-adapter-zz7xvjj6.js} +6 -4
- package/dist/{openrouter-images-9f9ek4rw.js → openrouter-images-b6sqqjgn.js} +4 -6
- package/dist/{otel-impl-9mz73q65.js → otel-impl-g8rs4vwr.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-n0me28wt.js} +29 -17
- package/dist/pricing-refresh-0v77385v.js +39 -0
- package/dist/{rbac-roles-c9wqz83x.js → rbac-roles-krjyazbj.js} +10 -9
- package/dist/{render-v2-gryrh9h8.js → render-v2-3wjsp9aw.js} +10 -9
- package/dist/runtime-ktjysacx.js +24 -0
- package/dist/{setup-q9720bt6.js → setup-yeqryke6.js} +6 -5
- package/dist/typesafe-system-one-96960jxp.js +27 -0
- package/dist/{virtual-modules-3htpgn8x.js → virtual-modules-dzdeq1sf.js} +21 -13
- package/dist/{work-objects-b76x7deb.js → work-objects-3pakpkw0.js} +35 -31
- package/dist/{worker-6vrxbv41.js → worker-85testgj.js} +49 -45
- package/openapi.json +863 -14
- package/package.json +9 -8
- package/src/be/db/agents.ts +20 -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 +89 -165
- package/src/be/harness-model-support.ts +182 -0
- package/src/be/human-free-drain.ts +67 -0
- 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/model-catalog-store.ts +644 -0
- package/src/be/model-tier-keys.ts +31 -0
- package/src/be/model-tier-resolution.ts +489 -0
- package/src/be/model-validation.ts +112 -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-scripts/catalog/task-failure-audit.ts +61 -9
- package/src/be/swarm-config-guard.ts +6 -0
- package/src/cli.tsx +10 -0
- package/src/commands/claude-managed-setup.ts +3 -1
- package/src/commands/runner.ts +76 -2
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/heartbeat/heartbeat.ts +64 -15
- package/src/heartbeat/templates.ts +2 -2
- package/src/http/agents.ts +49 -1
- package/src/http/index.ts +8 -0
- package/src/http/models-catalog.ts +357 -8
- package/src/http/poll.ts +45 -2
- package/src/http/schedules.ts +36 -6
- package/src/http/session-data.ts +2 -5
- package/src/http/stats.ts +6 -8
- package/src/http/tasks.ts +26 -1
- package/src/http/usage-cache.ts +56 -15
- package/src/http/workflows.ts +35 -8
- package/src/prompts/session-templates.ts +1 -0
- package/src/providers/claude-managed-models.ts +47 -161
- package/src/providers/codex-adapter.ts +4 -4
- package/src/providers/codex-models.ts +65 -198
- package/src/providers/reasoning-effort.ts +44 -140
- package/src/rbac/legacy-policy.ts +25 -0
- package/src/rbac/permissions.ts +10 -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/message-text.ts +56 -2
- package/src/slack/thread-buffer.ts +10 -2
- package/src/tests/agents-list-model-display.test.ts +4 -6
- package/src/tests/base-prompt.test.ts +3 -0
- package/src/tests/claude-adapter.test.ts +10 -0
- package/src/tests/claude-managed-adapter.test.ts +12 -18
- package/src/tests/codex-adapter.test.ts +2 -1
- package/src/tests/codex-gpt6-release.test.ts +66 -5
- package/src/tests/context-window.test.ts +7 -5
- package/src/tests/db-extraction-contract.test.ts +1 -0
- package/src/tests/db-task-read-contract.test.ts +5 -1
- package/src/tests/db-task-write-contract.test.ts +4 -1
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/explicit-model-validation.test.ts +332 -0
- package/src/tests/harness-model-support.test.ts +374 -0
- 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/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/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-tier-resolution.test.ts +519 -0
- package/src/tests/models-catalog.test.ts +1 -1
- package/src/tests/multi-runtime-enabled-stats.test.ts +65 -1
- package/src/tests/package-publish.test.ts +137 -36
- package/src/tests/pricing-routes.test.ts +2 -2
- package/src/tests/rbac-engine.test.ts +42 -2
- package/src/tests/reasoning-effort.test.ts +109 -0
- package/src/tests/refresh-modelsdev-carryforward.test.ts +5 -5
- package/src/tests/runtime-model-catalog.test.ts +86 -0
- package/src/tests/script-connections.test.ts +5 -0
- package/src/tests/seed-scripts.test.ts +89 -0
- package/src/tests/session-costs.test.ts +14 -11
- package/src/tests/slack-message-text.test.ts +101 -0
- package/src/tests/slack-thread-buffer-context.test.ts +93 -0
- package/src/tests/task-cascade-fail.test.ts +148 -2
- package/src/tests/tool-annotations.test.ts +1 -1
- package/src/tests/usage-cache.test.ts +166 -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/tools/model-catalog/index.ts +137 -0
- package/src/tools/schedules/create-schedule.ts +16 -1
- package/src/tools/schedules/patch-schedule.ts +17 -0
- package/src/tools/schedules/update-schedule.ts +17 -0
- package/src/tools/send-task.ts +14 -0
- package/src/tools/task-action.ts +12 -0
- package/src/tools/tool-config.ts +3 -1
- package/src/tools/workflows/create-workflow.ts +9 -2
- 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 +9 -2
- package/src/types.ts +30 -1
- package/src/utils/context-window.ts +47 -53
- package/src/utils/harness-cli-version.ts +115 -0
- package/src/utils/harness-model-error.ts +33 -0
- package/src/utils/runtime-model-catalog.ts +137 -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 +9 -0
- 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/readiness.ts +86 -0
- package/src/workflows/recovery.ts +12 -12
- package/src/workflows/resume.ts +23 -9
- 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 +81 -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/claude-sdk-session-t442qz6f.js +0 -39108
- package/dist/cli-0xjkj1j7.js +0 -61
- 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/{codex-login-x3jk4etn.js → codex-login-he9vrn90.js} +3 -3
- package/dist/{index-f24115ge.js → index-2wsdsnmm.js} +3 -3
package/src/be/models-catalog.ts
CHANGED
|
@@ -60,8 +60,12 @@ export interface CatalogReasoningOption {
|
|
|
60
60
|
export interface CatalogModel {
|
|
61
61
|
id: string;
|
|
62
62
|
name?: string;
|
|
63
|
-
cost?: { input?: number; output?: number };
|
|
63
|
+
cost?: { input?: number; output?: number; cache_read?: number; cache_write?: number };
|
|
64
64
|
limit?: { context?: number };
|
|
65
|
+
/** models.dev `release_date` (ISO date). Drives newest-first ordering in harness lists. */
|
|
66
|
+
release_date?: string;
|
|
67
|
+
/** models.dev `status` (e.g. "deprecated"). Deprecated models drop out of harness lists. */
|
|
68
|
+
status?: string;
|
|
65
69
|
reasoning?: boolean;
|
|
66
70
|
reasoning_options?: CatalogReasoningOption[];
|
|
67
71
|
}
|
|
@@ -94,9 +98,20 @@ function slimModel(modelKey: string, model: ModelsDevModel): CatalogModel {
|
|
|
94
98
|
id: model.id ?? modelKey,
|
|
95
99
|
...(model.name !== undefined ? { name: model.name } : {}),
|
|
96
100
|
...(model.cost !== undefined
|
|
97
|
-
? {
|
|
101
|
+
? {
|
|
102
|
+
cost: {
|
|
103
|
+
input: model.cost.input,
|
|
104
|
+
output: model.cost.output,
|
|
105
|
+
...(model.cost.cache_read !== undefined ? { cache_read: model.cost.cache_read } : {}),
|
|
106
|
+
...(model.cost.cache_write !== undefined
|
|
107
|
+
? { cache_write: model.cost.cache_write }
|
|
108
|
+
: {}),
|
|
109
|
+
},
|
|
110
|
+
}
|
|
98
111
|
: {}),
|
|
99
112
|
...(model.limit?.context !== undefined ? { limit: { context: model.limit.context } } : {}),
|
|
113
|
+
...(typeof model.release_date === "string" ? { release_date: model.release_date } : {}),
|
|
114
|
+
...(typeof model.status === "string" ? { status: model.status } : {}),
|
|
100
115
|
...(model.reasoning !== undefined ? { reasoning: model.reasoning } : {}),
|
|
101
116
|
...(reasoningOptions.length > 0 ? { reasoning_options: reasoningOptions } : {}),
|
|
102
117
|
};
|
|
@@ -148,22 +163,61 @@ function mergePinnedEntries(catalog: ModelsCatalog): void {
|
|
|
148
163
|
}
|
|
149
164
|
}
|
|
150
165
|
|
|
151
|
-
/**
|
|
152
|
-
|
|
153
|
-
|
|
166
|
+
/**
|
|
167
|
+
* Install a freshly built live catalog (already merged with the persistent
|
|
168
|
+
* table + overlay by `src/be/model-catalog-store.ts`). Pinned snapshot entries
|
|
169
|
+
* are re-merged so they never drop out of the picker.
|
|
170
|
+
*/
|
|
171
|
+
export function setLiveModelsCatalog(catalog: ModelsCatalog, updatedAt: number | null): void {
|
|
154
172
|
mergePinnedEntries(catalog);
|
|
155
173
|
liveCatalog = catalog;
|
|
156
|
-
liveUpdatedAt =
|
|
174
|
+
liveUpdatedAt = updatedAt;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Legacy in-memory update from a raw models.dev payload. The refresh path now
|
|
179
|
+
* persists to `model_catalog` and reloads via `reloadModelsCatalog()`; this
|
|
180
|
+
* stays for callers/tests that only have a payload in hand.
|
|
181
|
+
*/
|
|
182
|
+
export function updateLiveModelsCatalog(cache: ModelsDevCache, now = Date.now()): void {
|
|
183
|
+
setLiveModelsCatalog(buildModelsCatalog(cache), now);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Drop the in-memory catalog so the next async read rebuilds it from the DB. */
|
|
187
|
+
export function invalidateModelsCatalog(): void {
|
|
188
|
+
liveCatalog = null;
|
|
189
|
+
liveUpdatedAt = null;
|
|
190
|
+
snapshotOverride = null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function isModelsCatalogLoaded(): boolean {
|
|
194
|
+
return liveCatalog !== null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Deep copy of the slimmed vendored snapshot (safe to mutate). */
|
|
198
|
+
export function snapshotModelsCatalog(): ModelsCatalog {
|
|
199
|
+
return structuredClone(bundledSnapshotCatalog());
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** Snapshot with overlay rows layered on; served while the table is empty. */
|
|
203
|
+
let snapshotOverride: ModelsCatalog | null = null;
|
|
204
|
+
export function setSnapshotOverlayCatalog(catalog: ModelsCatalog | null): void {
|
|
205
|
+
snapshotOverride = catalog;
|
|
157
206
|
}
|
|
158
207
|
|
|
159
208
|
export function getModelsCatalog(): ModelsCatalogResult {
|
|
160
209
|
if (liveCatalog) {
|
|
161
210
|
return { source: "live", updatedAt: liveUpdatedAt, providers: liveCatalog };
|
|
162
211
|
}
|
|
163
|
-
return {
|
|
212
|
+
return {
|
|
213
|
+
source: "snapshot",
|
|
214
|
+
updatedAt: null,
|
|
215
|
+
providers: snapshotOverride ?? bundledSnapshotCatalog(),
|
|
216
|
+
};
|
|
164
217
|
}
|
|
165
218
|
|
|
166
219
|
export function resetModelsCatalogForTests(): void {
|
|
167
220
|
liveCatalog = null;
|
|
168
221
|
liveUpdatedAt = null;
|
|
222
|
+
snapshotOverride = null;
|
|
169
223
|
}
|
|
@@ -287104,6 +287104,88 @@
|
|
|
287104
287104
|
"cache_read": 2.5
|
|
287105
287105
|
}
|
|
287106
287106
|
},
|
|
287107
|
+
"gpt-6.1-sol": {
|
|
287108
|
+
"id": "gpt-6.1-sol",
|
|
287109
|
+
"name": "GPT-6.1 Sol",
|
|
287110
|
+
"description": "Near-Astra performance for complex work at a lower cost",
|
|
287111
|
+
"family": "gpt-sol",
|
|
287112
|
+
"attachment": true,
|
|
287113
|
+
"reasoning": true,
|
|
287114
|
+
"reasoning_options": [
|
|
287115
|
+
{
|
|
287116
|
+
"type": "effort",
|
|
287117
|
+
"values": [
|
|
287118
|
+
"low",
|
|
287119
|
+
"medium",
|
|
287120
|
+
"high",
|
|
287121
|
+
"xhigh",
|
|
287122
|
+
"max"
|
|
287123
|
+
]
|
|
287124
|
+
}
|
|
287125
|
+
],
|
|
287126
|
+
"tool_call": true,
|
|
287127
|
+
"structured_output": true,
|
|
287128
|
+
"temperature": false,
|
|
287129
|
+
"knowledge": "2026-04-30",
|
|
287130
|
+
"release_date": "2026-09-29",
|
|
287131
|
+
"last_updated": "2026-09-29",
|
|
287132
|
+
"modalities": {
|
|
287133
|
+
"input": [
|
|
287134
|
+
"text",
|
|
287135
|
+
"image"
|
|
287136
|
+
],
|
|
287137
|
+
"output": [
|
|
287138
|
+
"text"
|
|
287139
|
+
]
|
|
287140
|
+
},
|
|
287141
|
+
"open_weights": false,
|
|
287142
|
+
"limit": {
|
|
287143
|
+
"context": 1050000,
|
|
287144
|
+
"input": 922000,
|
|
287145
|
+
"output": 128000
|
|
287146
|
+
},
|
|
287147
|
+
"experimental": {
|
|
287148
|
+
"modes": {
|
|
287149
|
+
"fast": {
|
|
287150
|
+
"cost": {
|
|
287151
|
+
"input": 4,
|
|
287152
|
+
"output": 20,
|
|
287153
|
+
"cache_read": 0.2,
|
|
287154
|
+
"cache_write": 5
|
|
287155
|
+
},
|
|
287156
|
+
"provider": {
|
|
287157
|
+
"body": {
|
|
287158
|
+
"service_tier": "priority"
|
|
287159
|
+
}
|
|
287160
|
+
}
|
|
287161
|
+
}
|
|
287162
|
+
}
|
|
287163
|
+
},
|
|
287164
|
+
"cost": {
|
|
287165
|
+
"input": 2,
|
|
287166
|
+
"output": 10,
|
|
287167
|
+
"cache_read": 0.1,
|
|
287168
|
+
"cache_write": 2.5,
|
|
287169
|
+
"tiers": [
|
|
287170
|
+
{
|
|
287171
|
+
"input": 4,
|
|
287172
|
+
"output": 15,
|
|
287173
|
+
"cache_read": 0.2,
|
|
287174
|
+
"cache_write": 5,
|
|
287175
|
+
"tier": {
|
|
287176
|
+
"type": "context",
|
|
287177
|
+
"size": 272000
|
|
287178
|
+
}
|
|
287179
|
+
}
|
|
287180
|
+
],
|
|
287181
|
+
"context_over_200k": {
|
|
287182
|
+
"input": 4,
|
|
287183
|
+
"output": 15,
|
|
287184
|
+
"cache_read": 0.2,
|
|
287185
|
+
"cache_write": 5
|
|
287186
|
+
}
|
|
287187
|
+
}
|
|
287188
|
+
},
|
|
287107
287189
|
"gpt-6-sol": {
|
|
287108
287190
|
"id": "gpt-6-sol",
|
|
287109
287191
|
"name": "GPT-6 Sol",
|
|
@@ -6,14 +6,25 @@ import {
|
|
|
6
6
|
type InsertPricingRowInput,
|
|
7
7
|
insertPricingRow,
|
|
8
8
|
} from "./db";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
applyOverlayPricingRows,
|
|
11
|
+
buildCatalogEntries,
|
|
12
|
+
countModelCatalog,
|
|
13
|
+
expireMatchedOverlays,
|
|
14
|
+
getModelCatalogMeta,
|
|
15
|
+
reloadModelsCatalog,
|
|
16
|
+
replaceModelCatalog,
|
|
17
|
+
touchModelCatalogCheckedAt,
|
|
18
|
+
updateModelCatalogMeta,
|
|
19
|
+
} from "./model-catalog-store";
|
|
10
20
|
import type { ModelsDevCache } from "./modelsdev-cache";
|
|
11
21
|
import { buildModelsDevSeedRows, type PricingSeedRow } from "./seed-pricing";
|
|
12
22
|
|
|
13
23
|
const MODELSDEV_API_URL = "https://models.dev/api.json";
|
|
14
24
|
export const PRICING_REFRESH_INTERVAL_MS = 12 * 60 * 60 * 1000;
|
|
25
|
+
/** Unforced `refreshModelCatalog()` calls skip the network when the last models.dev check (200 or 304) is younger than this. */
|
|
26
|
+
export const MODEL_CATALOG_FRESH_MS = 4 * 60 * 60 * 1000;
|
|
15
27
|
|
|
16
|
-
let lastETag: string | null = null;
|
|
17
28
|
let refreshLoopStarted = false;
|
|
18
29
|
|
|
19
30
|
interface RefreshPricingOptions {
|
|
@@ -28,6 +39,11 @@ export interface PricingRefreshResult {
|
|
|
28
39
|
unchanged: number;
|
|
29
40
|
pruned: number;
|
|
30
41
|
etag?: string;
|
|
42
|
+
/** Catalog keys ("provider/modelId") new since the previous fetch. */
|
|
43
|
+
added?: string[];
|
|
44
|
+
/** Overlay rows auto-expired because upstream now matches. */
|
|
45
|
+
expiredOverlays?: string[];
|
|
46
|
+
catalogModels?: number;
|
|
31
47
|
}
|
|
32
48
|
|
|
33
49
|
function logPricingRefresh(message: string): void {
|
|
@@ -129,17 +145,29 @@ export async function refreshPricingFromModelsDev(
|
|
|
129
145
|
): Promise<PricingRefreshResult> {
|
|
130
146
|
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
131
147
|
const now = opts.now ?? Date.now();
|
|
132
|
-
const
|
|
148
|
+
const meta = await getModelCatalogMeta();
|
|
149
|
+
// Only send validators when the table is populated — a 304 against an empty
|
|
150
|
+
// table (fresh DB, same ETag) would leave the catalog empty.
|
|
151
|
+
const catalogCount = await countModelCatalog();
|
|
152
|
+
const headers: Record<string, string> = {};
|
|
153
|
+
if (catalogCount > 0 && meta.etag) headers["If-None-Match"] = meta.etag;
|
|
154
|
+
if (catalogCount > 0 && meta.lastModified) headers["If-Modified-Since"] = meta.lastModified;
|
|
133
155
|
|
|
134
156
|
const response = await fetchImpl(MODELSDEV_API_URL, { headers });
|
|
135
157
|
if (response.status === 304) {
|
|
158
|
+
await touchModelCatalogCheckedAt(now);
|
|
159
|
+
await updateModelCatalogMeta({ lastCheckedAt: now });
|
|
160
|
+
await reloadModelsCatalog();
|
|
136
161
|
const result: PricingRefreshResult = {
|
|
137
162
|
status: "not_modified",
|
|
138
163
|
candidateRows: 0,
|
|
139
164
|
inserted: 0,
|
|
140
165
|
unchanged: 0,
|
|
141
166
|
pruned: 0,
|
|
142
|
-
etag:
|
|
167
|
+
etag: meta.etag ?? undefined,
|
|
168
|
+
added: [],
|
|
169
|
+
expiredOverlays: [],
|
|
170
|
+
catalogModels: catalogCount,
|
|
143
171
|
};
|
|
144
172
|
await auditPricingRefresh(result);
|
|
145
173
|
logPricingRefresh("models.dev returned 304; pricing rows unchanged");
|
|
@@ -151,36 +179,150 @@ export async function refreshPricingFromModelsDev(
|
|
|
151
179
|
|
|
152
180
|
const cache = (await response.json()) as ModelsDevCache;
|
|
153
181
|
const etag = response.headers.get("etag");
|
|
154
|
-
|
|
182
|
+
const lastModified = response.headers.get("last-modified");
|
|
183
|
+
const entries = buildCatalogEntries(cache, now);
|
|
184
|
+
const added = await replaceModelCatalog(entries);
|
|
155
185
|
const rows = buildModelsDevSeedRows(cache);
|
|
156
186
|
const { inserted, unchanged } = await insertChangedPricingRows(rows, now);
|
|
187
|
+
const expiredOverlays = await expireMatchedOverlays();
|
|
188
|
+
// Overlay prices only fill (provider, model, tokenClass) gaps upstream left.
|
|
189
|
+
const overlayInserted = await applyOverlayPricingRows(now);
|
|
157
190
|
const pruned = await prunePricingHistory(2);
|
|
158
|
-
|
|
191
|
+
await updateModelCatalogMeta({ etag, lastModified, lastFetchAt: now, lastCheckedAt: now });
|
|
192
|
+
await reloadModelsCatalog();
|
|
159
193
|
|
|
160
194
|
const result: PricingRefreshResult = {
|
|
161
195
|
status: "refreshed",
|
|
162
196
|
candidateRows: rows.length,
|
|
163
|
-
inserted,
|
|
197
|
+
inserted: inserted + overlayInserted,
|
|
164
198
|
unchanged,
|
|
165
199
|
pruned,
|
|
166
|
-
etag:
|
|
200
|
+
etag: etag ?? undefined,
|
|
201
|
+
added,
|
|
202
|
+
expiredOverlays,
|
|
203
|
+
catalogModels: entries.length,
|
|
167
204
|
};
|
|
168
205
|
await auditPricingRefresh(result);
|
|
169
206
|
logPricingRefresh(
|
|
170
|
-
`refreshed ${rows.length} candidate row(s); inserted=${inserted}; unchanged=${unchanged}; pruned=${pruned}`,
|
|
207
|
+
`refreshed ${rows.length} candidate row(s); inserted=${inserted}; unchanged=${unchanged}; pruned=${pruned}; catalog=${entries.length}; added=${added.length}; expiredOverlays=${expiredOverlays.length}`,
|
|
171
208
|
);
|
|
172
209
|
return result;
|
|
173
210
|
}
|
|
174
211
|
|
|
175
|
-
|
|
212
|
+
export interface ModelCatalogRefreshResult {
|
|
213
|
+
status: "updated" | "not-modified" | "skipped-fresh" | "skipped-cooldown" | "error";
|
|
214
|
+
models: number;
|
|
215
|
+
added: string[];
|
|
216
|
+
checkedAt: number | null;
|
|
217
|
+
/** Set with `skipped-cooldown`: milliseconds until a forced refresh is accepted again. */
|
|
218
|
+
retryAfterMs?: number;
|
|
219
|
+
error?: string;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** Minimum gap between forced refreshes accepted from callers (HTTP route, MCP tool). */
|
|
223
|
+
export const MODEL_CATALOG_FORCE_COOLDOWN_MS = 60 * 1000;
|
|
224
|
+
|
|
225
|
+
let refreshInFlight: Promise<ModelCatalogRefreshResult> | null = null;
|
|
226
|
+
let lastForcedRefreshAt: number | null = null;
|
|
227
|
+
|
|
228
|
+
/** Test-only: forget the in-flight refresh and the forced-refresh cooldown. */
|
|
229
|
+
export function resetModelCatalogRefreshGuardForTests(): void {
|
|
230
|
+
refreshInFlight = null;
|
|
231
|
+
lastForcedRefreshAt = null;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* `pi update --models` for the swarm. Unforced calls skip the network when the
|
|
236
|
+
* last fetch is younger than MODEL_CATALOG_FRESH_MS; `force` always fetches
|
|
237
|
+
* (still conditional on the stored ETag). Never throws — failures come back as
|
|
238
|
+
* `status: "error"` and the catalog keeps serving the last good table (or the
|
|
239
|
+
* vendored snapshot).
|
|
240
|
+
*/
|
|
241
|
+
export function refreshModelCatalog(
|
|
242
|
+
opts: { force?: boolean } & RefreshPricingOptions = {},
|
|
243
|
+
): Promise<ModelCatalogRefreshResult> {
|
|
244
|
+
// Single flight: a refresh that is already running serves every concurrent caller, so a burst of
|
|
245
|
+
// requests (or the boot loop racing a forced call) costs one models.dev fetch.
|
|
246
|
+
if (refreshInFlight) return refreshInFlight;
|
|
247
|
+
const run = runModelCatalogRefresh(opts).finally(() => {
|
|
248
|
+
if (refreshInFlight === run) refreshInFlight = null;
|
|
249
|
+
});
|
|
250
|
+
refreshInFlight = run;
|
|
251
|
+
return run;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Caller-facing entry for the HTTP route and the MCP tool: `refreshModelCatalog` plus a cooldown
|
|
256
|
+
* on `force`. A forced call within MODEL_CATALOG_FORCE_COOLDOWN_MS of the previous one does not
|
|
257
|
+
* fetch; it reports `skipped-cooldown` and how long to wait. Unforced calls pass straight through
|
|
258
|
+
* (they already skip the network while the table is fresh).
|
|
259
|
+
*/
|
|
260
|
+
export async function requestModelCatalogRefresh(
|
|
261
|
+
opts: { force?: boolean } & RefreshPricingOptions = {},
|
|
262
|
+
): Promise<ModelCatalogRefreshResult> {
|
|
263
|
+
const now = opts.now ?? Date.now();
|
|
264
|
+
if (opts.force) {
|
|
265
|
+
const waitMs =
|
|
266
|
+
lastForcedRefreshAt === null
|
|
267
|
+
? 0
|
|
268
|
+
: lastForcedRefreshAt + MODEL_CATALOG_FORCE_COOLDOWN_MS - now;
|
|
269
|
+
if (waitMs > 0) {
|
|
270
|
+
const meta = await getModelCatalogMeta().catch(() => null);
|
|
271
|
+
return {
|
|
272
|
+
status: "skipped-cooldown",
|
|
273
|
+
models: await countModelCatalog().catch(() => 0),
|
|
274
|
+
added: [],
|
|
275
|
+
checkedAt: meta?.lastCheckedAt ?? null,
|
|
276
|
+
retryAfterMs: waitMs,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
lastForcedRefreshAt = now;
|
|
280
|
+
}
|
|
281
|
+
return refreshModelCatalog({ ...opts, now });
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async function runModelCatalogRefresh(
|
|
285
|
+
opts: { force?: boolean } & RefreshPricingOptions = {},
|
|
286
|
+
): Promise<ModelCatalogRefreshResult> {
|
|
287
|
+
const now = opts.now ?? Date.now();
|
|
176
288
|
try {
|
|
177
|
-
await
|
|
289
|
+
const meta = await getModelCatalogMeta();
|
|
290
|
+
const count = await countModelCatalog();
|
|
291
|
+
if (
|
|
292
|
+
!opts.force &&
|
|
293
|
+
count > 0 &&
|
|
294
|
+
meta.lastCheckedAt !== null &&
|
|
295
|
+
now - meta.lastCheckedAt < MODEL_CATALOG_FRESH_MS
|
|
296
|
+
) {
|
|
297
|
+
await reloadModelsCatalog();
|
|
298
|
+
return { status: "skipped-fresh", models: count, added: [], checkedAt: meta.lastCheckedAt };
|
|
299
|
+
}
|
|
300
|
+
const result = await refreshPricingFromModelsDev({ fetchImpl: opts.fetchImpl, now });
|
|
301
|
+
return {
|
|
302
|
+
status: result.status === "refreshed" ? "updated" : "not-modified",
|
|
303
|
+
models: result.catalogModels ?? (await countModelCatalog()),
|
|
304
|
+
added: result.added ?? [],
|
|
305
|
+
checkedAt: now,
|
|
306
|
+
};
|
|
178
307
|
} catch (err) {
|
|
179
|
-
logPricingRefreshError("refresh failed", err);
|
|
308
|
+
logPricingRefreshError("model catalog refresh failed", err);
|
|
180
309
|
await auditPricingRefreshFailure(err);
|
|
310
|
+
const meta = await getModelCatalogMeta().catch(() => null);
|
|
311
|
+
return {
|
|
312
|
+
status: "error",
|
|
313
|
+
models: await countModelCatalog().catch(() => 0),
|
|
314
|
+
added: [],
|
|
315
|
+
checkedAt: meta?.lastCheckedAt ?? null,
|
|
316
|
+
error: scrubSecrets(err instanceof Error ? err.message : String(err)),
|
|
317
|
+
};
|
|
181
318
|
}
|
|
182
319
|
}
|
|
183
320
|
|
|
321
|
+
async function runPricingRefreshSafely(): Promise<void> {
|
|
322
|
+
// refreshModelCatalog never throws; errors are logged + audited inside.
|
|
323
|
+
await refreshModelCatalog();
|
|
324
|
+
}
|
|
325
|
+
|
|
184
326
|
export function startPricingRefreshLoop(): void {
|
|
185
327
|
if (refreshLoopStarted) return;
|
|
186
328
|
refreshLoopStarted = true;
|
|
@@ -262,6 +262,21 @@ export interface SwarmSdk {
|
|
|
262
262
|
config_set(args: { key: string; value: unknown; scope?: "global" | "agent" | "repo"; scopeId?: string; isSecret?: boolean }): Promise<unknown>;
|
|
263
263
|
config_delete(args: { id: string }): Promise<unknown>;
|
|
264
264
|
|
|
265
|
+
// --- write: model catalog ---
|
|
266
|
+
modelCatalog_refresh(args?: { force?: boolean }): Promise<unknown>;
|
|
267
|
+
modelCatalog_overlayUpsert(args: {
|
|
268
|
+
provider: string;
|
|
269
|
+
modelId: string;
|
|
270
|
+
name?: string;
|
|
271
|
+
releaseDate?: string;
|
|
272
|
+
contextWindow?: number;
|
|
273
|
+
maxOutput?: number;
|
|
274
|
+
reasoningOptions?: Array<{ type: string; values?: string[] }>;
|
|
275
|
+
pricing?: { input?: number; output?: number; cache_read?: number; cache_write?: number };
|
|
276
|
+
reason: string;
|
|
277
|
+
verifiedBy?: string;
|
|
278
|
+
}): Promise<unknown>;
|
|
279
|
+
|
|
265
280
|
// --- write: slack ---
|
|
266
281
|
slack_post(args: { channelId: string; message: string; blocks?: unknown }): Promise<unknown>;
|
|
267
282
|
slack_reply(args: { channelId?: string; threadTs?: string; message: string; taskId?: string; blocks?: unknown }): Promise<unknown>;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { publishCatalogReportPage } from "./catalog-report";
|
|
3
3
|
|
|
4
|
+
const MAX_TASK_SCAN_LIMIT = 500;
|
|
5
|
+
|
|
4
6
|
export const argsSchema = z.object({
|
|
5
7
|
days: z
|
|
6
8
|
.number()
|
|
@@ -16,8 +18,9 @@ export const argsSchema = z.object({
|
|
|
16
18
|
.number()
|
|
17
19
|
.int()
|
|
18
20
|
.positive()
|
|
21
|
+
.max(MAX_TASK_SCAN_LIMIT)
|
|
19
22
|
.optional()
|
|
20
|
-
.describe("Max failed tasks to scan (default 500)"),
|
|
23
|
+
.describe("Max failed tasks to scan (default 500, max 500)"),
|
|
21
24
|
publishPage: z.boolean().optional().describe("Publish an authed HTML page (default true)"),
|
|
22
25
|
});
|
|
23
26
|
|
|
@@ -25,20 +28,37 @@ const REASON_PATTERNS: any[] = [
|
|
|
25
28
|
{ key: "sigterm/killed", re: /sigterm|sigkill|killed|143|137/i },
|
|
26
29
|
{ key: "timeout", re: /time?d?\s*out|timeout|deadline/i },
|
|
27
30
|
{ key: "context-window", re: /context (window|limit|saturat)|peakcontext|compact/i },
|
|
31
|
+
{ key: "reboot-sweep", re: /reboot sweep/i },
|
|
28
32
|
{ key: "not-found", re: /not found|404|missing|no such/i },
|
|
29
33
|
{ key: "auth/credentials", re: /unauthorized|401|403|credential|token|forbidden/i },
|
|
30
|
-
{ key: "ci/checks-failed", re:
|
|
34
|
+
{ key: "ci/checks-failed", re: /\bci\b|check.?s? fail|lint|tsc|test.?s? fail/i },
|
|
31
35
|
{ key: "network", re: /network|econn|fetch failed|socket|dns|502|503|504/i },
|
|
32
36
|
{ key: "cancelled", re: /cancel|aborted/i },
|
|
33
37
|
];
|
|
34
38
|
|
|
39
|
+
function sanitizeUntrustedReasonSample(reason: unknown): string {
|
|
40
|
+
return String(reason)
|
|
41
|
+
.replace(/[\u0000-\u001f\u007f-\u009f]/g, " ")
|
|
42
|
+
.replace(/\s+/g, " ")
|
|
43
|
+
.trim()
|
|
44
|
+
.slice(0, 200);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function rowsToObjects(result: any): any[] {
|
|
48
|
+
const payload = result?.data ?? result;
|
|
49
|
+
const columns: string[] = payload?.columns ?? [];
|
|
50
|
+
return (payload?.rows ?? []).map((row: any) =>
|
|
51
|
+
Array.isArray(row) ? Object.fromEntries(columns.map((column, i) => [column, row[i]])) : row,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
35
55
|
function reasonCluster(reason: string): string {
|
|
36
56
|
const r = (reason || "").trim();
|
|
37
57
|
if (!r) return "(no reason given)";
|
|
38
58
|
for (const p of REASON_PATTERNS) {
|
|
39
59
|
if (p.re.test(r)) return p.key;
|
|
40
60
|
}
|
|
41
|
-
return
|
|
61
|
+
return "other";
|
|
42
62
|
}
|
|
43
63
|
|
|
44
64
|
/** Cluster recently failed swarm tasks by reason, agent, or schedule. */
|
|
@@ -47,7 +67,7 @@ export default async function taskFailureAudit(args: any, ctx: any) {
|
|
|
47
67
|
if (!parsed.success) return { error: "invalid args: " + parsed.error.message };
|
|
48
68
|
const days = parsed.data.days || 7;
|
|
49
69
|
const groupBy = parsed.data.groupBy || "reason";
|
|
50
|
-
const limit = parsed.data.limit
|
|
70
|
+
const limit = Math.min(parsed.data.limit ?? MAX_TASK_SCAN_LIMIT, MAX_TASK_SCAN_LIMIT);
|
|
51
71
|
const publishPage = parsed.data.publishPage !== false;
|
|
52
72
|
|
|
53
73
|
const since = new Date(Date.now() - days * 86400000).toISOString();
|
|
@@ -62,17 +82,49 @@ export default async function taskFailureAudit(args: any, ctx: any) {
|
|
|
62
82
|
const payload: any = res && res.data ? res.data : res;
|
|
63
83
|
const tasks: any = payload && Array.isArray(payload.tasks) ? payload.tasks : [];
|
|
64
84
|
|
|
85
|
+
const failureReasons = new Map<string, string>();
|
|
86
|
+
if (groupBy === "reason") {
|
|
87
|
+
const tasksNeedingReason = tasks.filter((task: any) => !task.failureReason && task.id);
|
|
88
|
+
const taskIds = tasksNeedingReason.map((task: any) => task.id as string);
|
|
89
|
+
if (taskIds.length > 0) {
|
|
90
|
+
const placeholders = taskIds.map(() => "?").join(", ");
|
|
91
|
+
const reasonResult: any = await ctx.swarm.db_query({
|
|
92
|
+
sql: `SELECT id, failureReason FROM agent_tasks WHERE id IN (${placeholders})`,
|
|
93
|
+
params: taskIds,
|
|
94
|
+
});
|
|
95
|
+
const reasonPayload: any = reasonResult?.data ?? reasonResult;
|
|
96
|
+
if (
|
|
97
|
+
reasonResult?.success === false ||
|
|
98
|
+
reasonPayload?.success === false ||
|
|
99
|
+
reasonPayload?.error
|
|
100
|
+
) {
|
|
101
|
+
return { error: "failure reason projection failed with status " + reasonResult?.status };
|
|
102
|
+
}
|
|
103
|
+
if (reasonPayload?.truncated) {
|
|
104
|
+
return {
|
|
105
|
+
error: `failure reason projection truncated (${reasonPayload.rows?.length ?? 0} of ${reasonPayload.total ?? "unknown"} rows)`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
for (const row of rowsToObjects(reasonResult)) {
|
|
109
|
+
if (typeof row?.id === "string" && typeof row.failureReason === "string") {
|
|
110
|
+
failureReasons.set(row.id, row.failureReason);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
65
116
|
const groups: any = {};
|
|
66
117
|
for (const t of tasks) {
|
|
67
118
|
let key: string;
|
|
68
119
|
if (groupBy === "agent") key = t.agentId || "(unassigned)";
|
|
69
120
|
else if (groupBy === "schedule") key = t.scheduleId || "(not scheduled)";
|
|
70
|
-
else key = reasonCluster(t.failureReason || "");
|
|
71
|
-
if (!groups[key]) groups[key] = { key, count: 0, taskIds: [],
|
|
121
|
+
else key = reasonCluster(failureReasons.get(t.id) || t.failureReason || "");
|
|
122
|
+
if (!groups[key]) groups[key] = { key, count: 0, taskIds: [], untrustedWorkerTextSample: "" };
|
|
72
123
|
groups[key].count++;
|
|
73
124
|
if (groups[key].taskIds.length < 5) groups[key].taskIds.push(t.id);
|
|
74
|
-
|
|
75
|
-
|
|
125
|
+
const failureReason = failureReasons.get(t.id) || t.failureReason;
|
|
126
|
+
if (!groups[key].untrustedWorkerTextSample && failureReason) {
|
|
127
|
+
groups[key].untrustedWorkerTextSample = sanitizeUntrustedReasonSample(failureReason);
|
|
76
128
|
}
|
|
77
129
|
}
|
|
78
130
|
|
|
@@ -118,7 +170,7 @@ export default async function taskFailureAudit(args: any, ctx: any) {
|
|
|
118
170
|
key: group.key,
|
|
119
171
|
count: group.count,
|
|
120
172
|
taskIds: group.taskIds,
|
|
121
|
-
|
|
173
|
+
untrustedWorkerTextSample: group.untrustedWorkerTextSample,
|
|
122
174
|
},
|
|
123
175
|
],
|
|
124
176
|
})),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { normalizeSlackReactionShortcode } from "../slack/reaction-shortcode";
|
|
2
2
|
import { ProviderNameSchema } from "../types";
|
|
3
3
|
import { parseTaskToolManifest } from "../utils/task-tool-manifest";
|
|
4
|
+
import { isTierConfigKey, validateTierConfigValue } from "./model-tier-keys";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Guards against storing reserved keys in the swarm_config table.
|
|
@@ -274,6 +275,7 @@ const VALIDATED_KEYS: Record<string, ConfigValidator> = {
|
|
|
274
275
|
return null;
|
|
275
276
|
},
|
|
276
277
|
...booleanValidators([
|
|
278
|
+
"SWARM_DEV_MODE",
|
|
277
279
|
"MULTI_RUNTIME_ENABLED",
|
|
278
280
|
"STEERING_ENABLED",
|
|
279
281
|
"MEMORY_HYBRID_SEARCH",
|
|
@@ -304,6 +306,7 @@ const VALIDATED_KEYS: Record<string, ConfigValidator> = {
|
|
|
304
306
|
"SWARM_HIDE_CLOUD_PROMO",
|
|
305
307
|
"DB_QUERY_BOUNDED_ENABLED",
|
|
306
308
|
"DB_RETENTION_DRY_RUN",
|
|
309
|
+
"MODEL_AUTO_UPGRADE",
|
|
307
310
|
]),
|
|
308
311
|
...enumValidator("SLACK_MODE", ["socket", "http"]),
|
|
309
312
|
...enumValidator("SLACK_THREAD_STEERING", ["off", "lead", "all"]),
|
|
@@ -359,6 +362,8 @@ const VALIDATED_KEYS: Record<string, ConfigValidator> = {
|
|
|
359
362
|
),
|
|
360
363
|
// 0 is meaningful here: "auto-assign nothing this sweep".
|
|
361
364
|
...integerValidators(["HEARTBEAT_MAX_AUTO_ASSIGN"], 0),
|
|
365
|
+
// 0 disables the `latest:...@stable` soak window.
|
|
366
|
+
...integerValidators(["MODEL_LATEST_SOAK_DAYS"], 0),
|
|
362
367
|
// 0 turns approval auto-cancellation off.
|
|
363
368
|
...integerValidators(["APPROVAL_REQUEST_AUTO_CANCELLATION_DAYS"], 0),
|
|
364
369
|
// Below ~100 tokens the preamble can't fit a useful summary; above 20000
|
|
@@ -378,6 +383,7 @@ const VALIDATED_KEYS: Record<string, ConfigValidator> = {
|
|
|
378
383
|
};
|
|
379
384
|
|
|
380
385
|
export function validateConfigValue(key: string, value: unknown): string | null {
|
|
386
|
+
if (isTierConfigKey(key)) return validateTierConfigValue(key.toUpperCase(), value);
|
|
381
387
|
const validator = VALIDATED_KEYS[key.toUpperCase()];
|
|
382
388
|
return validator ? validator(value) : null;
|
|
383
389
|
}
|
package/src/cli.tsx
CHANGED
|
@@ -717,6 +717,16 @@ if (args.showHelp || args.command === "help" || args.command === undefined) {
|
|
|
717
717
|
} else if (args.command === "version") {
|
|
718
718
|
console.log(`${binName} v${pkg.version}`);
|
|
719
719
|
process.exit(0);
|
|
720
|
+
} else if (args.command === "onboard" && !process.versions.bun) {
|
|
721
|
+
// The published bin uses Node, but the onboarding wizard requires Bun APIs.
|
|
722
|
+
const { spawnSync } = await import("node:child_process");
|
|
723
|
+
const result = spawnSync("bun", process.argv.slice(1), { stdio: "inherit" });
|
|
724
|
+
if (result.error) {
|
|
725
|
+
console.error(
|
|
726
|
+
"Onboarding requires Bun. Install Bun from https://bun.sh, then retry this command.",
|
|
727
|
+
);
|
|
728
|
+
}
|
|
729
|
+
process.exit(result.status ?? 1);
|
|
720
730
|
} else if (args.command === "docs") {
|
|
721
731
|
const docsUrl = "https://docs.agent-swarm.dev";
|
|
722
732
|
console.log(`\n${binName} docs — v${pkg.version}\n`);
|
|
@@ -34,6 +34,7 @@ import type { SkillCreateResponse } from "@anthropic-ai/sdk/resources/beta/skill
|
|
|
34
34
|
import { toFile } from "@anthropic-ai/sdk/uploads";
|
|
35
35
|
|
|
36
36
|
import { buildSkillContent, type SkillTemplateConfig } from "../be/seed-skills/render";
|
|
37
|
+
import { DEFAULT_MODEL_TIER_MAP } from "../types";
|
|
37
38
|
import { getApiKey } from "../utils/api-key";
|
|
38
39
|
import { promptHiddenInput } from "./codex-login.js";
|
|
39
40
|
|
|
@@ -65,7 +66,8 @@ export type ClaudeManagedSetupResult = {
|
|
|
65
66
|
|
|
66
67
|
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
67
68
|
|
|
68
|
-
|
|
69
|
+
/** The claude-managed `regular` tier default — no per-model constant here. */
|
|
70
|
+
const DEFAULT_AGENT_MODEL = DEFAULT_MODEL_TIER_MAP["claude-managed"].regular;
|
|
69
71
|
const SKILLS_DIR_RELATIVE = "plugin/commands";
|
|
70
72
|
const SEEDED_SKILLS_DIR_RELATIVE = "templates/skills";
|
|
71
73
|
const SKILLS_BETA_HEADER = "skills-2025-10-02";
|