@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
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness CLI model support (model-catalog phase 4).
|
|
3
|
+
*
|
|
4
|
+
* A catalog model can exist before the pinned `claude` / `codex` CLI accepts
|
|
5
|
+
* it. Workers report their CLI version on register and record each model's
|
|
6
|
+
* first-run outcome here; claim-time resolution reads it:
|
|
7
|
+
*
|
|
8
|
+
* - `unknown` (no row) and `ok` are allowed at claim.
|
|
9
|
+
* - `unsupported` + an alias/tier resolution → fall back to the newest
|
|
10
|
+
* `ok`/`unknown` model in the same family (modelSource
|
|
11
|
+
* `fallback:cli-unsupported`), with one notice per (harness, CLI, model).
|
|
12
|
+
* - `unsupported` + an explicit task `model` → fail fast at claim.
|
|
13
|
+
*
|
|
14
|
+
* See runbooks/model-catalog.md.
|
|
15
|
+
*/
|
|
16
|
+
import {
|
|
17
|
+
claudeCatalogModelId,
|
|
18
|
+
harnessCatalogSection,
|
|
19
|
+
harnessModelIds,
|
|
20
|
+
modelFamilyKey,
|
|
21
|
+
} from "@desplega/model-catalog";
|
|
22
|
+
import { scrubSecrets } from "../utils/secret-scrubber";
|
|
23
|
+
import { getDbClient } from "./db";
|
|
24
|
+
|
|
25
|
+
export type HarnessModelSupportStatus = "ok" | "unsupported" | "unknown";
|
|
26
|
+
|
|
27
|
+
export interface HarnessModelSupportRow {
|
|
28
|
+
harness: string;
|
|
29
|
+
cliVersion: string;
|
|
30
|
+
modelId: string;
|
|
31
|
+
status: HarnessModelSupportStatus;
|
|
32
|
+
checkedAt: number;
|
|
33
|
+
error: string | null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Harnesses whose CLI pins the set of accepted model ids. */
|
|
37
|
+
export const CLI_PINNED_HARNESSES = new Set(["claude", "codex"]);
|
|
38
|
+
|
|
39
|
+
export async function getHarnessModelSupport(
|
|
40
|
+
harness: string,
|
|
41
|
+
cliVersion: string,
|
|
42
|
+
modelId: string,
|
|
43
|
+
): Promise<HarnessModelSupportStatus> {
|
|
44
|
+
const row = await getDbClient().get<{ status: HarnessModelSupportStatus }>(
|
|
45
|
+
"SELECT status FROM harness_model_support WHERE harness = ? AND cliVersion = ? AND modelId = ?",
|
|
46
|
+
[harness, cliVersion, modelId],
|
|
47
|
+
);
|
|
48
|
+
return row?.status ?? "unknown";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function listHarnessModelSupport(filter?: {
|
|
52
|
+
harness?: string;
|
|
53
|
+
cliVersion?: string;
|
|
54
|
+
}): Promise<HarnessModelSupportRow[]> {
|
|
55
|
+
const where: string[] = [];
|
|
56
|
+
const params: string[] = [];
|
|
57
|
+
if (filter?.harness) {
|
|
58
|
+
where.push("harness = ?");
|
|
59
|
+
params.push(filter.harness);
|
|
60
|
+
}
|
|
61
|
+
if (filter?.cliVersion) {
|
|
62
|
+
where.push("cliVersion = ?");
|
|
63
|
+
params.push(filter.cliVersion);
|
|
64
|
+
}
|
|
65
|
+
return await getDbClient().query<HarnessModelSupportRow>(
|
|
66
|
+
`SELECT harness, cliVersion, modelId, status, checkedAt, error FROM harness_model_support${
|
|
67
|
+
where.length > 0 ? ` WHERE ${where.join(" AND ")}` : ""
|
|
68
|
+
} ORDER BY checkedAt DESC`,
|
|
69
|
+
params,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Upsert one outcome. An `ok` never downgrades to `unknown`. */
|
|
74
|
+
export async function recordHarnessModelSupport(input: {
|
|
75
|
+
harness: string;
|
|
76
|
+
cliVersion: string;
|
|
77
|
+
modelId: string;
|
|
78
|
+
status: HarnessModelSupportStatus;
|
|
79
|
+
error?: string | null;
|
|
80
|
+
now?: number;
|
|
81
|
+
}): Promise<HarnessModelSupportRow> {
|
|
82
|
+
const checkedAt = input.now ?? Date.now();
|
|
83
|
+
const error = input.error ? scrubSecrets(input.error).slice(0, 2000) : null;
|
|
84
|
+
await getDbClient().run(
|
|
85
|
+
`INSERT INTO harness_model_support (harness, cliVersion, modelId, status, checkedAt, error)
|
|
86
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
87
|
+
ON CONFLICT (harness, cliVersion, modelId) DO UPDATE SET
|
|
88
|
+
status = CASE WHEN excluded.status = 'unknown' THEN harness_model_support.status ELSE excluded.status END,
|
|
89
|
+
checkedAt = excluded.checkedAt,
|
|
90
|
+
error = excluded.error`,
|
|
91
|
+
[input.harness, input.cliVersion, input.modelId, input.status, checkedAt, error],
|
|
92
|
+
);
|
|
93
|
+
const row = await getDbClient().get<HarnessModelSupportRow>(
|
|
94
|
+
"SELECT harness, cliVersion, modelId, status, checkedAt, error FROM harness_model_support WHERE harness = ? AND cliVersion = ? AND modelId = ?",
|
|
95
|
+
[input.harness, input.cliVersion, input.modelId],
|
|
96
|
+
);
|
|
97
|
+
if (!row) throw new Error("harness_model_support upsert returned no row");
|
|
98
|
+
return row;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* True when `agentId` was assigned a task that ran `modelId`: the server's claim-time resolution,
|
|
103
|
+
* or the task's own model when nothing was resolved. Binds an `unsupported` report to a model the
|
|
104
|
+
* worker actually ran, so it cannot mark an arbitrary model unsupported for its peers.
|
|
105
|
+
*/
|
|
106
|
+
export async function agentRanModel(agentId: string, modelId: string): Promise<boolean> {
|
|
107
|
+
const row = await getDbClient().get<{ id: string }>(
|
|
108
|
+
`SELECT id FROM agent_tasks
|
|
109
|
+
WHERE agentId = ? AND (resolvedModel = ? OR (resolvedModel IS NULL AND model = ?))
|
|
110
|
+
LIMIT 1`,
|
|
111
|
+
[agentId, modelId, modelId],
|
|
112
|
+
);
|
|
113
|
+
return row !== null && row !== undefined;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export async function getAgentHarnessCliVersion(agentId: string): Promise<string | null> {
|
|
117
|
+
const row = await getDbClient().get<{ harnessCliVersion: string | null }>(
|
|
118
|
+
"SELECT harnessCliVersion FROM agents WHERE id = ?",
|
|
119
|
+
[agentId],
|
|
120
|
+
);
|
|
121
|
+
return row?.harnessCliVersion ?? null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Store the worker's CLI version; no write when unchanged. */
|
|
125
|
+
export async function setAgentHarnessCliVersion(
|
|
126
|
+
agentId: string,
|
|
127
|
+
cliVersion: string | null,
|
|
128
|
+
): Promise<void> {
|
|
129
|
+
await getDbClient().run(
|
|
130
|
+
"UPDATE agents SET harnessCliVersion = ? WHERE id = ? AND harnessCliVersion IS NOT ?",
|
|
131
|
+
[cliVersion, agentId, cliVersion],
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function unsupportedModelMessage(harness: string, cliVersion: string, model: string) {
|
|
136
|
+
return `Model "${model}" is not supported by the ${harness} CLI ${cliVersion} on this worker. A CLI bump is needed before tasks can pin it.`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Newest model in the same family as `model` that this harness CLI has not
|
|
141
|
+
* rejected. Null when the family has nothing else. A Claude CLI shortname (the
|
|
142
|
+
* tier default `opus`) counts as the family of the model it stands for, so a
|
|
143
|
+
* rejected `opus` falls back to the newest usable `claude-opus-*`.
|
|
144
|
+
*/
|
|
145
|
+
export async function fallbackForUnsupportedModel(
|
|
146
|
+
harness: string,
|
|
147
|
+
cliVersion: string,
|
|
148
|
+
model: string,
|
|
149
|
+
catalogSection: Record<string, { release_date?: string; status?: string; reasoning?: boolean }>,
|
|
150
|
+
): Promise<string | null> {
|
|
151
|
+
if (!harnessCatalogSection(harness)) return null;
|
|
152
|
+
const family = modelFamilyKey(
|
|
153
|
+
harness === "claude" ? claudeCatalogModelId(model, catalogSection) : model,
|
|
154
|
+
);
|
|
155
|
+
const rejected = new Set(
|
|
156
|
+
(await listHarnessModelSupport({ harness, cliVersion }))
|
|
157
|
+
.filter((r) => r.status === "unsupported")
|
|
158
|
+
.map((r) => r.modelId),
|
|
159
|
+
);
|
|
160
|
+
for (const id of harnessModelIds(harness, catalogSection)) {
|
|
161
|
+
if (id === model || rejected.has(id)) continue;
|
|
162
|
+
if (modelFamilyKey(id) === family) return id;
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const noticed = new Set<string>();
|
|
168
|
+
|
|
169
|
+
/** One console notice per (harness, CLI, model) per process. */
|
|
170
|
+
export function noticeCliUnsupported(
|
|
171
|
+
harness: string,
|
|
172
|
+
cliVersion: string,
|
|
173
|
+
model: string,
|
|
174
|
+
fallback: string,
|
|
175
|
+
): void {
|
|
176
|
+
const key = `${harness}|${cliVersion}|${model}`;
|
|
177
|
+
if (noticed.has(key)) return;
|
|
178
|
+
noticed.add(key);
|
|
179
|
+
console.warn(
|
|
180
|
+
`[model-tiers] ${model} is unsupported on ${harness} CLI ${cliVersion}; tier/alias tasks fall back to ${fallback} until the CLI is bumped`,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { drainHumanFreeReclassifyQueue, setHumanFreeQueueListener } from "./db/tasks/human-free";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Background drain for `human_free_reclassify_queue`: the part of a large
|
|
5
|
+
* subtree's human-free reclassification that did not fit in the request that
|
|
6
|
+
* changed it. It runs one batch per pass and yields between passes, so no
|
|
7
|
+
* pass holds the write lock or the event loop for longer than one batch.
|
|
8
|
+
*
|
|
9
|
+
* It wakes when a mutation queues work, and once at boot and on a slow timer to
|
|
10
|
+
* pick up rows that survived a restart or a failed pass.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const YIELD_MS = 25;
|
|
14
|
+
const IDLE_INTERVAL_MS = 60_000;
|
|
15
|
+
|
|
16
|
+
let running = false;
|
|
17
|
+
let idleTimer: ReturnType<typeof setTimeout> | null = null;
|
|
18
|
+
let passTimer: ReturnType<typeof setTimeout> | null = null;
|
|
19
|
+
|
|
20
|
+
function scheduleIdle(): void {
|
|
21
|
+
if (!running) return;
|
|
22
|
+
if (idleTimer) clearTimeout(idleTimer);
|
|
23
|
+
idleTimer = setTimeout(() => schedulePass(0), IDLE_INTERVAL_MS);
|
|
24
|
+
idleTimer.unref?.();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function schedulePass(delayMs: number): void {
|
|
28
|
+
if (!running || passTimer) return;
|
|
29
|
+
passTimer = setTimeout(() => {
|
|
30
|
+
passTimer = null;
|
|
31
|
+
void runPass();
|
|
32
|
+
}, delayMs);
|
|
33
|
+
passTimer.unref?.();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function runPass(): Promise<void> {
|
|
37
|
+
if (!running) return;
|
|
38
|
+
try {
|
|
39
|
+
const { batches, remaining } = await drainHumanFreeReclassifyQueue({ maxBatches: 1 });
|
|
40
|
+
if (batches > 0 && remaining > 0) {
|
|
41
|
+
schedulePass(YIELD_MS);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
} catch (err) {
|
|
45
|
+
console.error(
|
|
46
|
+
"[human-free-drain] batch failed, retrying later:",
|
|
47
|
+
err instanceof Error ? err.message : String(err),
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
scheduleIdle();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function startHumanFreeDrain(): void {
|
|
54
|
+
if (running) return;
|
|
55
|
+
running = true;
|
|
56
|
+
setHumanFreeQueueListener(() => schedulePass(0));
|
|
57
|
+
schedulePass(0);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function stopHumanFreeDrain(): void {
|
|
61
|
+
running = false;
|
|
62
|
+
setHumanFreeQueueListener(undefined);
|
|
63
|
+
if (idleTimer) clearTimeout(idleTimer);
|
|
64
|
+
if (passTimer) clearTimeout(passTimer);
|
|
65
|
+
idleTimer = null;
|
|
66
|
+
passTimer = null;
|
|
67
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
-- Persist the structurally-human-free classification on agent_tasks.
|
|
2
|
+
--
|
|
3
|
+
-- The usage reports (GET /api/session-costs/summary, /api/attribution/by-person)
|
|
4
|
+
-- rebuilt a recursive CTE over EVERY task on each call to decide which sessions
|
|
5
|
+
-- belong to the swarm maintaining itself. That CTE is seeded from all tasks and
|
|
6
|
+
-- date filters do not prune it, so a 30-day summary paid for the whole history.
|
|
7
|
+
-- The classification is fixed when a task is created, so it is stored once
|
|
8
|
+
-- (src/be/db/tasks/human-free.ts owns the rule) and the reports read a column.
|
|
9
|
+
--
|
|
10
|
+
-- Rule (mirrors HUMAN_FREE_BASE_SQL): heartbeat / boot-triage tasks, scheduled
|
|
11
|
+
-- runs with no human creator (including workflow roots launched by one), and
|
|
12
|
+
-- `source='system'` follow-ups whose parent has no requester. It propagates to
|
|
13
|
+
-- children that stay unattributed (no requester, or one copied from the parent).
|
|
14
|
+
ALTER TABLE agent_tasks
|
|
15
|
+
ADD COLUMN isHumanFree INTEGER NOT NULL DEFAULT 0
|
|
16
|
+
CHECK (isHumanFree IN (0, 1));
|
|
17
|
+
|
|
18
|
+
WITH RECURSIVE human_free_tasks(id) AS (
|
|
19
|
+
SELECT task.id
|
|
20
|
+
FROM agent_tasks task
|
|
21
|
+
LEFT JOIN agent_tasks parent ON parent.id = task.parentTaskId
|
|
22
|
+
WHERE COALESCE(task.taskType, '') IN ('heartbeat', 'heartbeat-checklist', 'boot-triage')
|
|
23
|
+
OR COALESCE(task.tags, '[]') LIKE '%"heartbeat"%'
|
|
24
|
+
OR (COALESCE(task.source, '') = 'schedule' AND task.requestedByUserId IS NULL)
|
|
25
|
+
OR (
|
|
26
|
+
task.parentTaskId IS NULL
|
|
27
|
+
AND COALESCE(task.source, '') = 'workflow'
|
|
28
|
+
AND task.requestedByUserId IS NULL
|
|
29
|
+
AND EXISTS (
|
|
30
|
+
SELECT 1
|
|
31
|
+
FROM workflow_runs run
|
|
32
|
+
WHERE run.id = task.workflowRunId
|
|
33
|
+
AND run.triggerType = 'schedule'
|
|
34
|
+
AND run.created_by IS NULL
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
OR (
|
|
38
|
+
COALESCE(task.source, '') = 'system'
|
|
39
|
+
AND parent.id IS NOT NULL
|
|
40
|
+
AND parent.requestedByUserId IS NULL
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
UNION
|
|
44
|
+
|
|
45
|
+
SELECT child.id
|
|
46
|
+
FROM agent_tasks child
|
|
47
|
+
JOIN human_free_tasks parent ON child.parentTaskId = parent.id
|
|
48
|
+
WHERE child.requestedByUserId IS NULL
|
|
49
|
+
OR child.requestedByUserIdInherited = 1
|
|
50
|
+
)
|
|
51
|
+
UPDATE agent_tasks
|
|
52
|
+
SET isHumanFree = 1
|
|
53
|
+
WHERE id IN (SELECT id FROM human_free_tasks);
|
|
54
|
+
|
|
55
|
+
-- Migration 164's covering index carried the columns the recursive CTE read
|
|
56
|
+
-- (parentTaskId, taskType, source, tags, workflowRunId). Nothing reads them
|
|
57
|
+
-- through it now. The session join needs only the requester, the flag and the
|
|
58
|
+
-- credential, so keep the index to those and let it stay index-only.
|
|
59
|
+
DROP INDEX IF EXISTS idx_agent_tasks_usage_cover;
|
|
60
|
+
CREATE INDEX idx_agent_tasks_usage_cover
|
|
61
|
+
ON agent_tasks(
|
|
62
|
+
id, requestedByUserId, isHumanFree, credentialKeyType, credentialKeySuffix
|
|
63
|
+
);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
-- Continuation queue for the human-free reclassification (migration 182).
|
|
2
|
+
--
|
|
3
|
+
-- Reclassifying a subtree used to walk every descendant inside the request that
|
|
4
|
+
-- changed a classifying input, so a very large task tree could hold the write
|
|
5
|
+
-- lock and the event loop for the whole walk. The walk now handles a bounded
|
|
6
|
+
-- batch inline and parks the rest here; a background drain
|
|
7
|
+
-- (src/be/human-free-drain.ts) works the queue off in bounded batches, each in
|
|
8
|
+
-- its own transaction. Rows are written in the same transaction as the mutation
|
|
9
|
+
-- that needs them, so a crash can neither lose nor half-apply the remainder.
|
|
10
|
+
--
|
|
11
|
+
-- scope = 'self' re-evaluate `taskId` and everything below it
|
|
12
|
+
-- scope = 'children' re-evaluate the children of `taskId` whose rowid is
|
|
13
|
+
-- above `afterRowid`, and everything below them
|
|
14
|
+
--
|
|
15
|
+
-- One row per (taskId, scope): a repeat enqueue restarts that row from the top.
|
|
16
|
+
-- `hops` counts how many batches deep a continuation is, so a parent cycle that
|
|
17
|
+
-- the product never writes cannot re-queue itself forever.
|
|
18
|
+
CREATE TABLE human_free_reclassify_queue (
|
|
19
|
+
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
20
|
+
taskId TEXT NOT NULL,
|
|
21
|
+
scope TEXT NOT NULL CHECK (scope IN ('self', 'children')),
|
|
22
|
+
afterRowid INTEGER NOT NULL DEFAULT 0,
|
|
23
|
+
hops INTEGER NOT NULL DEFAULT 0,
|
|
24
|
+
UNIQUE (taskId, scope)
|
|
25
|
+
);
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
-- Model catalog, claim-time model resolution and harness CLI model support.
|
|
2
|
+
--
|
|
3
|
+
-- One migration for the whole model-catalog stack (persistent catalog, tier
|
|
4
|
+
-- resolution, harness support). Three parts, in dependency order.
|
|
5
|
+
|
|
6
|
+
-- ---------------------------------------------------------------------------
|
|
7
|
+
-- Part 1: persistent model catalog (models.dev projection) + operator overlay.
|
|
8
|
+
--
|
|
9
|
+
-- model_catalog: one row per (provider, modelId) for the picker-reachable
|
|
10
|
+
-- providers (CATALOG_PROVIDER_IDS). Rewritten by the catalog refresh; system
|
|
11
|
+
-- written, so it carries no user-attribution columns (.non-audit-tables).
|
|
12
|
+
-- model_catalog_overlay: hand-verified facts for models models.dev does not
|
|
13
|
+
-- list yet (or lists wrong). Overlay wins per non-null field. Rows with
|
|
14
|
+
-- expiresWhenUpstreamMatches=1 are deleted once upstream agrees.
|
|
15
|
+
-- model_catalog_meta: single-row fetch state (ETag / Last-Modified / times).
|
|
16
|
+
--
|
|
17
|
+
-- JSON columns: reasoningOptions = [{type, values?}], pricing =
|
|
18
|
+
-- {input?, output?, cache_read?, cache_write?} (USD per million tokens).
|
|
19
|
+
-- ---------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
CREATE TABLE IF NOT EXISTS model_catalog (
|
|
22
|
+
provider TEXT NOT NULL,
|
|
23
|
+
modelId TEXT NOT NULL,
|
|
24
|
+
name TEXT,
|
|
25
|
+
family TEXT,
|
|
26
|
+
releaseDate TEXT,
|
|
27
|
+
contextWindow INTEGER,
|
|
28
|
+
maxOutput INTEGER,
|
|
29
|
+
reasoning INTEGER,
|
|
30
|
+
reasoningOptions TEXT,
|
|
31
|
+
pricing TEXT,
|
|
32
|
+
status TEXT,
|
|
33
|
+
providerName TEXT,
|
|
34
|
+
checkedAt INTEGER NOT NULL,
|
|
35
|
+
PRIMARY KEY (provider, modelId)
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
CREATE TABLE IF NOT EXISTS model_catalog_overlay (
|
|
39
|
+
provider TEXT NOT NULL,
|
|
40
|
+
modelId TEXT NOT NULL,
|
|
41
|
+
name TEXT,
|
|
42
|
+
family TEXT,
|
|
43
|
+
releaseDate TEXT,
|
|
44
|
+
contextWindow INTEGER,
|
|
45
|
+
maxOutput INTEGER,
|
|
46
|
+
reasoning INTEGER,
|
|
47
|
+
reasoningOptions TEXT,
|
|
48
|
+
pricing TEXT,
|
|
49
|
+
status TEXT,
|
|
50
|
+
reason TEXT NOT NULL,
|
|
51
|
+
verifiedBy TEXT,
|
|
52
|
+
expiresWhenUpstreamMatches INTEGER NOT NULL DEFAULT 1,
|
|
53
|
+
createdAt INTEGER NOT NULL DEFAULT (CAST(unixepoch('subsec') * 1000 AS INTEGER)),
|
|
54
|
+
updatedAt INTEGER NOT NULL DEFAULT (CAST(unixepoch('subsec') * 1000 AS INTEGER)),
|
|
55
|
+
created_by TEXT REFERENCES users(id),
|
|
56
|
+
updated_by TEXT REFERENCES users(id),
|
|
57
|
+
PRIMARY KEY (provider, modelId)
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
CREATE TABLE IF NOT EXISTS model_catalog_meta (
|
|
61
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
62
|
+
etag TEXT,
|
|
63
|
+
lastModified TEXT,
|
|
64
|
+
lastFetchAt INTEGER,
|
|
65
|
+
lastCheckedAt INTEGER
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
-- ---------------------------------------------------------------------------
|
|
69
|
+
-- Part 2: claim-time model resolution.
|
|
70
|
+
--
|
|
71
|
+
-- agent_tasks.resolvedModel / modelSource / modelAlias: what the server
|
|
72
|
+
-- resolved when the task was claimed. modelSource is one of
|
|
73
|
+
-- model | worker-env | tier-config | tier-default | fallback:cli-unsupported.
|
|
74
|
+
-- modelAlias is the `latest:` alias, when any.
|
|
75
|
+
-- agents.modelTierOverrides: the worker's parsed MODEL_TIER_* env overrides,
|
|
76
|
+
-- JSON {provider: {tier: value}}. Values only, never secrets. Sent on
|
|
77
|
+
-- register and on every poll.
|
|
78
|
+
-- model_alias_resolutions: one row each time a `latest:` alias resolves to a
|
|
79
|
+
-- different concrete model than its previous resolution. System written.
|
|
80
|
+
-- ---------------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
ALTER TABLE agent_tasks ADD COLUMN resolvedModel TEXT;
|
|
83
|
+
ALTER TABLE agent_tasks ADD COLUMN modelSource TEXT;
|
|
84
|
+
ALTER TABLE agent_tasks ADD COLUMN modelAlias TEXT;
|
|
85
|
+
|
|
86
|
+
ALTER TABLE agents ADD COLUMN modelTierOverrides TEXT;
|
|
87
|
+
|
|
88
|
+
CREATE TABLE IF NOT EXISTS model_alias_resolutions (
|
|
89
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
90
|
+
alias TEXT NOT NULL,
|
|
91
|
+
previousModel TEXT,
|
|
92
|
+
newModel TEXT NOT NULL,
|
|
93
|
+
changedAt INTEGER NOT NULL
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
CREATE INDEX IF NOT EXISTS idx_model_alias_resolutions_alias
|
|
97
|
+
ON model_alias_resolutions (alias, id);
|
|
98
|
+
|
|
99
|
+
-- ---------------------------------------------------------------------------
|
|
100
|
+
-- Part 3: harness CLI model support.
|
|
101
|
+
--
|
|
102
|
+
-- harness_model_support: whether a catalog model runs on a given harness CLI
|
|
103
|
+
-- version. Workers write it after a model's first run on their CLI: `ok` on
|
|
104
|
+
-- success, `unsupported` when the CLI rejects the model id. No row means
|
|
105
|
+
-- `unknown`, which is allowed at claim. System written.
|
|
106
|
+
-- agents.harnessCliVersion: the worker's `claude --version` / `codex --version`,
|
|
107
|
+
-- reported on register. Keys claim-time support lookups.
|
|
108
|
+
-- ---------------------------------------------------------------------------
|
|
109
|
+
|
|
110
|
+
CREATE TABLE IF NOT EXISTS harness_model_support (
|
|
111
|
+
harness TEXT NOT NULL,
|
|
112
|
+
cliVersion TEXT NOT NULL,
|
|
113
|
+
modelId TEXT NOT NULL,
|
|
114
|
+
status TEXT NOT NULL CHECK (status IN ('ok', 'unsupported', 'unknown')),
|
|
115
|
+
checkedAt INTEGER NOT NULL,
|
|
116
|
+
error TEXT,
|
|
117
|
+
PRIMARY KEY (harness, cliVersion, modelId)
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
ALTER TABLE agents ADD COLUMN harnessCliVersion TEXT;
|