@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,137 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
import { getAgentById } from "@/be/db";
|
|
4
|
+
import { upsertModelCatalogOverlay } from "@/be/model-catalog-store";
|
|
5
|
+
import { requestModelCatalogRefresh } from "@/be/pricing-refresh";
|
|
6
|
+
import { can } from "@/rbac";
|
|
7
|
+
import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
|
|
8
|
+
|
|
9
|
+
async function denyUnlessCatalogWriter(agentId: string): Promise<string | null> {
|
|
10
|
+
const agent = await getAgentById(agentId);
|
|
11
|
+
const decision = can({
|
|
12
|
+
principal: { kind: "agent", agentId, isLead: agent?.isLead ?? false },
|
|
13
|
+
verb: "models.catalog.write",
|
|
14
|
+
resource: { kind: "none" },
|
|
15
|
+
source: "mcp",
|
|
16
|
+
});
|
|
17
|
+
return decision.allow ? null : "Writing the model catalog requires the lead agent.";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const registerModelCatalogRefreshTool = (server: McpServer) => {
|
|
21
|
+
createToolRegistrar(server)(
|
|
22
|
+
"model-catalog-refresh",
|
|
23
|
+
{
|
|
24
|
+
title: "Refresh Model Catalog",
|
|
25
|
+
description:
|
|
26
|
+
"Refresh the swarm model catalog (and pricing rows) from models.dev, like `pi update --models`. Without force, skips the network when the last check is under 4h old. A forced refresh is accepted once per minute; a second one inside that window returns `skipped-cooldown` with `retryAfterMs`. Lead agent only. Returns the status, model count, and newly added provider/modelId keys.",
|
|
27
|
+
annotations: { idempotentHint: true, openWorldHint: true },
|
|
28
|
+
inputSchema: z.object({
|
|
29
|
+
force: z
|
|
30
|
+
.boolean()
|
|
31
|
+
.optional()
|
|
32
|
+
.describe(
|
|
33
|
+
"Fetch even if the last check is under 4h old (default false). Rate limited to one per minute.",
|
|
34
|
+
),
|
|
35
|
+
}),
|
|
36
|
+
outputSchema: swarmToolOutputSchema({
|
|
37
|
+
status: z.string().optional(),
|
|
38
|
+
models: z.number().optional(),
|
|
39
|
+
added: z.array(z.string()).optional(),
|
|
40
|
+
checkedAt: z.number().nullable().optional(),
|
|
41
|
+
retryAfterMs: z.number().optional(),
|
|
42
|
+
}),
|
|
43
|
+
},
|
|
44
|
+
async ({ force }, requestInfo) => {
|
|
45
|
+
if (!requestInfo.agentId) {
|
|
46
|
+
return toolErr('Agent ID not found. Set the "X-Agent-ID" header.');
|
|
47
|
+
}
|
|
48
|
+
const denied = await denyUnlessCatalogWriter(requestInfo.agentId);
|
|
49
|
+
if (denied) return toolErr(denied);
|
|
50
|
+
|
|
51
|
+
const result = await requestModelCatalogRefresh({ force });
|
|
52
|
+
const data = {
|
|
53
|
+
status: result.status,
|
|
54
|
+
models: result.models,
|
|
55
|
+
added: result.added,
|
|
56
|
+
checkedAt: result.checkedAt,
|
|
57
|
+
retryAfterMs: result.retryAfterMs,
|
|
58
|
+
};
|
|
59
|
+
if (result.status === "error") {
|
|
60
|
+
return toolErr(`Model catalog refresh failed: ${result.error ?? "unknown error"}`, {
|
|
61
|
+
data,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
const addedText = result.added.length > 0 ? ` Added: ${result.added.join(", ")}.` : "";
|
|
65
|
+
return toolOk(
|
|
66
|
+
`Model catalog ${result.status}: ${result.models} model(s), ${result.added.length} new.`,
|
|
67
|
+
{
|
|
68
|
+
details: `status=${result.status}; models=${result.models}; added=${result.added.length}.${addedText}`,
|
|
69
|
+
data,
|
|
70
|
+
},
|
|
71
|
+
);
|
|
72
|
+
},
|
|
73
|
+
);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const registerModelCatalogOverlayUpsertTool = (server: McpServer) => {
|
|
77
|
+
createToolRegistrar(server)(
|
|
78
|
+
"model-catalog-overlay-upsert",
|
|
79
|
+
{
|
|
80
|
+
title: "Upsert Model Catalog Overlay",
|
|
81
|
+
description:
|
|
82
|
+
"Add or update hand-verified facts for one model (e.g. a launch models.dev has not listed yet). Overlay fields win over models.dev; overlay prices fill pricing-table gaps so cost recompute prices the model. The row auto-expires once models.dev matches every fact you set.",
|
|
83
|
+
annotations: { idempotentHint: true },
|
|
84
|
+
inputSchema: z.object({
|
|
85
|
+
provider: z
|
|
86
|
+
.string()
|
|
87
|
+
.min(1)
|
|
88
|
+
.describe(
|
|
89
|
+
"models.dev provider id: anthropic, openai, openrouter, amazon-bedrock, opencode.",
|
|
90
|
+
),
|
|
91
|
+
modelId: z
|
|
92
|
+
.string()
|
|
93
|
+
.min(1)
|
|
94
|
+
.describe("Model id as models.dev keys it (e.g. claude-opus-5-5)."),
|
|
95
|
+
name: z.string().optional().describe("Display name."),
|
|
96
|
+
releaseDate: z.string().optional().describe("Release date, YYYY-MM-DD."),
|
|
97
|
+
contextWindow: z.number().int().positive().optional().describe("Context window, tokens."),
|
|
98
|
+
maxOutput: z.number().int().positive().optional().describe("Max output tokens."),
|
|
99
|
+
reasoningOptions: z
|
|
100
|
+
.array(z.object({ type: z.string(), values: z.array(z.string()).optional() }))
|
|
101
|
+
.optional()
|
|
102
|
+
.describe("Reasoning options, models.dev shape: [{type, values?}]."),
|
|
103
|
+
pricing: z
|
|
104
|
+
.object({
|
|
105
|
+
input: z.number().nonnegative().optional(),
|
|
106
|
+
output: z.number().nonnegative().optional(),
|
|
107
|
+
cache_read: z.number().nonnegative().optional(),
|
|
108
|
+
cache_write: z.number().nonnegative().optional(),
|
|
109
|
+
})
|
|
110
|
+
.optional()
|
|
111
|
+
.describe("USD per million tokens."),
|
|
112
|
+
reason: z.string().min(1).describe("Why this overlay exists (source of the facts)."),
|
|
113
|
+
verifiedBy: z.string().optional().describe("Who verified the facts (URL, person, agent)."),
|
|
114
|
+
}),
|
|
115
|
+
outputSchema: swarmToolOutputSchema({
|
|
116
|
+
overlay: z.looseObject({}).optional(),
|
|
117
|
+
}),
|
|
118
|
+
},
|
|
119
|
+
async (input, requestInfo) => {
|
|
120
|
+
if (!requestInfo.agentId) {
|
|
121
|
+
return toolErr('Agent ID not found. Set the "X-Agent-ID" header.');
|
|
122
|
+
}
|
|
123
|
+
const denied = await denyUnlessCatalogWriter(requestInfo.agentId);
|
|
124
|
+
if (denied) return toolErr(denied);
|
|
125
|
+
try {
|
|
126
|
+
const overlay = await upsertModelCatalogOverlay(input);
|
|
127
|
+
return toolOk(`Overlay for ${input.provider}/${input.modelId} saved.`, {
|
|
128
|
+
details: `Overlay for ${input.provider}/${input.modelId} saved (reason: ${input.reason}). It expires when models.dev matches every fact set.`,
|
|
129
|
+
data: { overlay },
|
|
130
|
+
});
|
|
131
|
+
} catch (error) {
|
|
132
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
133
|
+
return toolErr(`Failed to save overlay: ${message}`);
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
);
|
|
137
|
+
};
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
getWorkflow,
|
|
12
12
|
isExtensionAgent,
|
|
13
13
|
} from "@/be/db";
|
|
14
|
+
import { explicitModelErrorForAgent } from "@/be/model-validation";
|
|
14
15
|
import { getScript } from "@/be/scripts/db";
|
|
15
16
|
import { calculateNextRun } from "@/scheduler";
|
|
16
17
|
import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
|
|
@@ -109,6 +110,12 @@ export const createScheduleInputSchema = z.object({
|
|
|
109
110
|
modelTier: ModelTierSchema.optional().describe(
|
|
110
111
|
"Portable model tier for tasks created by this schedule: 'smol', 'regular', 'smart', or 'ultra'. Resolved by each assignee's harness/provider at run time.",
|
|
111
112
|
),
|
|
113
|
+
allowCustomModel: z
|
|
114
|
+
.boolean()
|
|
115
|
+
.optional()
|
|
116
|
+
.describe(
|
|
117
|
+
"Accept a `model` the model catalog does not list. Without it an unknown model id is rejected. Only for ids the catalog cannot know yet.",
|
|
118
|
+
),
|
|
112
119
|
});
|
|
113
120
|
|
|
114
121
|
const scheduleDataShape = {
|
|
@@ -177,6 +184,7 @@ export const registerCreateScheduleTool = (server: McpServer) => {
|
|
|
177
184
|
enabled,
|
|
178
185
|
model,
|
|
179
186
|
modelTier,
|
|
187
|
+
allowCustomModel,
|
|
180
188
|
},
|
|
181
189
|
requestInfo,
|
|
182
190
|
_meta,
|
|
@@ -263,8 +271,15 @@ export const registerCreateScheduleTool = (server: McpServer) => {
|
|
|
263
271
|
}
|
|
264
272
|
}
|
|
265
273
|
|
|
274
|
+
const normalizedModel = splitLegacyModelAlias({ model, modelTier });
|
|
275
|
+
const modelError = await explicitModelErrorForAgent({
|
|
276
|
+
model: normalizedModel.model,
|
|
277
|
+
allowCustomModel,
|
|
278
|
+
agentId: targetAgentId,
|
|
279
|
+
});
|
|
280
|
+
if (modelError) return toolErr(modelError);
|
|
281
|
+
|
|
266
282
|
try {
|
|
267
|
-
const normalizedModel = splitLegacyModelAlias({ model, modelTier });
|
|
268
283
|
// Calculate initial nextRunAt
|
|
269
284
|
let nextRunAt: string | undefined;
|
|
270
285
|
if (enabled === false) {
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
isExtensionAgent,
|
|
13
13
|
updateScheduledTask,
|
|
14
14
|
} from "@/be/db";
|
|
15
|
+
import { explicitModelErrorForAgent } from "@/be/model-validation";
|
|
15
16
|
import { mergeScheduleTiming, validateRecurringTiming } from "@/be/schedules/validate";
|
|
16
17
|
import { getScript } from "@/be/scripts/db";
|
|
17
18
|
import { calculateNextRun } from "@/scheduler";
|
|
@@ -73,6 +74,12 @@ export const patchScheduleInputSchema = z.object({
|
|
|
73
74
|
modelTier: ModelTierSchema.nullable()
|
|
74
75
|
.optional()
|
|
75
76
|
.describe("Portable model tier for tasks created by this schedule. Set to null to clear."),
|
|
77
|
+
allowCustomModel: z
|
|
78
|
+
.boolean()
|
|
79
|
+
.optional()
|
|
80
|
+
.describe(
|
|
81
|
+
"Accept a `model` the model catalog does not list. Without it an unknown model id is rejected. Only for ids the catalog cannot know yet.",
|
|
82
|
+
),
|
|
76
83
|
});
|
|
77
84
|
|
|
78
85
|
const scheduleDataShape = {
|
|
@@ -140,6 +147,7 @@ export const registerPatchScheduleTool = (server: McpServer) => {
|
|
|
140
147
|
enabled,
|
|
141
148
|
model,
|
|
142
149
|
modelTier,
|
|
150
|
+
allowCustomModel,
|
|
143
151
|
},
|
|
144
152
|
requestInfo,
|
|
145
153
|
_meta,
|
|
@@ -249,6 +257,15 @@ export const registerPatchScheduleTool = (server: McpServer) => {
|
|
|
249
257
|
if (enabled !== undefined) updateData.enabled = enabled;
|
|
250
258
|
if (model !== undefined || modelTier !== undefined) {
|
|
251
259
|
const normalizedModel = splitLegacyModelAlias({ model, modelTier });
|
|
260
|
+
// A model the schedule already stores is not re-judged, so an unrelated edit still saves.
|
|
261
|
+
if (normalizedModel.model !== schedule.model) {
|
|
262
|
+
const modelError = await explicitModelErrorForAgent({
|
|
263
|
+
model: normalizedModel.model,
|
|
264
|
+
allowCustomModel,
|
|
265
|
+
agentId: targetAgentId ?? schedule.targetAgentId,
|
|
266
|
+
});
|
|
267
|
+
if (modelError) return toolErr(modelError);
|
|
268
|
+
}
|
|
252
269
|
if (model !== undefined) updateData.model = normalizedModel.model ?? null;
|
|
253
270
|
if (modelTier !== undefined || normalizedModel.modelTier) {
|
|
254
271
|
updateData.modelTier = normalizedModel.modelTier ?? null;
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
isExtensionAgent,
|
|
13
13
|
updateScheduledTask,
|
|
14
14
|
} from "@/be/db";
|
|
15
|
+
import { explicitModelErrorForAgent } from "@/be/model-validation";
|
|
15
16
|
import { mergeScheduleTiming, validateRecurringTiming } from "@/be/schedules/validate";
|
|
16
17
|
import { getScript } from "@/be/scripts/db";
|
|
17
18
|
import { calculateNextRun } from "@/scheduler";
|
|
@@ -73,6 +74,12 @@ export const updateScheduleInputSchema = z.object({
|
|
|
73
74
|
modelTier: ModelTierSchema.nullable()
|
|
74
75
|
.optional()
|
|
75
76
|
.describe("Portable model tier for tasks created by this schedule. Set to null to clear."),
|
|
77
|
+
allowCustomModel: z
|
|
78
|
+
.boolean()
|
|
79
|
+
.optional()
|
|
80
|
+
.describe(
|
|
81
|
+
"Accept a `model` the model catalog does not list. Without it an unknown model id is rejected. Only for ids the catalog cannot know yet.",
|
|
82
|
+
),
|
|
76
83
|
});
|
|
77
84
|
|
|
78
85
|
const scheduleDataShape = {
|
|
@@ -139,6 +146,7 @@ export const registerUpdateScheduleTool = (server: McpServer) => {
|
|
|
139
146
|
enabled,
|
|
140
147
|
model,
|
|
141
148
|
modelTier,
|
|
149
|
+
allowCustomModel,
|
|
142
150
|
},
|
|
143
151
|
requestInfo,
|
|
144
152
|
_meta,
|
|
@@ -248,6 +256,15 @@ export const registerUpdateScheduleTool = (server: McpServer) => {
|
|
|
248
256
|
if (enabled !== undefined) updateData.enabled = enabled;
|
|
249
257
|
if (model !== undefined || modelTier !== undefined) {
|
|
250
258
|
const normalizedModel = splitLegacyModelAlias({ model, modelTier });
|
|
259
|
+
// A model the schedule already stores is not re-judged, so an unrelated edit still saves.
|
|
260
|
+
if (normalizedModel.model !== schedule.model) {
|
|
261
|
+
const modelError = await explicitModelErrorForAgent({
|
|
262
|
+
model: normalizedModel.model,
|
|
263
|
+
allowCustomModel,
|
|
264
|
+
agentId: targetAgentId ?? schedule.targetAgentId,
|
|
265
|
+
});
|
|
266
|
+
if (modelError) return toolErr(modelError);
|
|
267
|
+
}
|
|
251
268
|
if (model !== undefined) updateData.model = normalizedModel.model ?? null;
|
|
252
269
|
if (modelTier !== undefined || normalizedModel.modelTier) {
|
|
253
270
|
updateData.modelTier = normalizedModel.modelTier ?? null;
|
package/src/tools/send-task.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
isExtensionAgent,
|
|
18
18
|
} from "@/be/db";
|
|
19
19
|
import { repointTrackerSyncBySwarmId } from "@/be/db-queries/tracker";
|
|
20
|
+
import { explicitModelErrorForAgent } from "@/be/model-validation";
|
|
20
21
|
import { applyPreTaskCreate } from "@/extensions/apply-task-create";
|
|
21
22
|
import { checkSlackRoutingCoherence } from "@/tasks/slack-routing";
|
|
22
23
|
import { findDuplicateTask } from "@/tools/task-dedup";
|
|
@@ -136,6 +137,12 @@ export const sendTaskInputSchema = z
|
|
|
136
137
|
modelTier: ModelTierSchema.optional().describe(
|
|
137
138
|
"Portable model tier for this task: 'smol', 'regular', 'smart', or 'ultra'. Resolved at claim/run time using the assignee's harness/provider. Legacy model shortnames map as haiku→smol, sonnet→regular, opus→smart, fable→ultra.",
|
|
138
139
|
),
|
|
140
|
+
allowCustomModel: z
|
|
141
|
+
.boolean()
|
|
142
|
+
.optional()
|
|
143
|
+
.describe(
|
|
144
|
+
"Accept a `model` the model catalog does not list. Without it an unknown model id is rejected. Only for ids the catalog cannot know yet (a fresh launch, a private deployment).",
|
|
145
|
+
),
|
|
139
146
|
effort: ReasoningEffortSchema.optional().describe(
|
|
140
147
|
"Reasoning effort for this task: 'off', 'low', 'medium', 'high', 'xhigh', or 'max'. If omitted, the assignee's REASONING_EFFORT_OVERRIDE/default applies.",
|
|
141
148
|
),
|
|
@@ -268,6 +275,7 @@ export async function sendTaskHandler(
|
|
|
268
275
|
vcsRepo,
|
|
269
276
|
model,
|
|
270
277
|
modelTier,
|
|
278
|
+
allowCustomModel,
|
|
271
279
|
effort,
|
|
272
280
|
allowDuplicate,
|
|
273
281
|
slackChannelId,
|
|
@@ -313,6 +321,12 @@ export async function sendTaskHandler(
|
|
|
313
321
|
|
|
314
322
|
const effectiveVcsRepo = vcsRepo;
|
|
315
323
|
const normalizedModel = splitLegacyModelAlias({ model, modelTier });
|
|
324
|
+
const modelError = await explicitModelErrorForAgent({
|
|
325
|
+
model: normalizedModel.model,
|
|
326
|
+
allowCustomModel,
|
|
327
|
+
agentId,
|
|
328
|
+
});
|
|
329
|
+
if (modelError) return toolErr(modelError, { data: { yourAgentId: creatorAgentId } });
|
|
316
330
|
|
|
317
331
|
// Auto-default parentTaskId to caller's current task for tree tracking
|
|
318
332
|
const effectiveParentTaskId = parentTaskId ?? sourceTaskId;
|
package/src/tools/task-action.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
releaseTask,
|
|
28
28
|
updateTaskClaudeSessionId,
|
|
29
29
|
} from "@/be/db";
|
|
30
|
+
import { explicitModelErrorForAgent } from "@/be/model-validation";
|
|
30
31
|
import { touchRuntimeInstance } from "@/be/multi-runtime";
|
|
31
32
|
import { applyPreTaskCreate } from "@/extensions/apply-task-create";
|
|
32
33
|
import { assertOwnsTask, ownerCtx, type ToolCtx } from "@/tools/task-tool-ctx";
|
|
@@ -98,6 +99,12 @@ export const taskActionInputSchema = z.object({
|
|
|
98
99
|
modelTier: ModelTierSchema.optional().describe(
|
|
99
100
|
"Portable model tier for the created task: 'smol', 'regular', 'smart', or 'ultra'. Resolved when a worker claims/runs the task. Only used with 'create' action.",
|
|
100
101
|
),
|
|
102
|
+
allowCustomModel: z
|
|
103
|
+
.boolean()
|
|
104
|
+
.optional()
|
|
105
|
+
.describe(
|
|
106
|
+
"Accept a `model` the model catalog does not list. Without it an unknown model id is rejected. Only used with 'create' action.",
|
|
107
|
+
),
|
|
101
108
|
effort: ReasoningEffortSchema.optional().describe(
|
|
102
109
|
"Reasoning effort for the created task: 'off', 'low', 'medium', 'high', 'xhigh', or 'max'. Only used with 'create' action.",
|
|
103
110
|
),
|
|
@@ -275,6 +282,11 @@ export async function taskActionHandler(
|
|
|
275
282
|
agentId,
|
|
276
283
|
);
|
|
277
284
|
}
|
|
285
|
+
const modelError = await explicitModelErrorForAgent({
|
|
286
|
+
model: normalizedModel.model,
|
|
287
|
+
allowCustomModel: input.allowCustomModel,
|
|
288
|
+
});
|
|
289
|
+
if (modelError) return taskActionResult({ success: false, message: modelError }, agentId);
|
|
278
290
|
try {
|
|
279
291
|
assetKey = key
|
|
280
292
|
? await authorizeAssetKeyWrite(key, await resolveTaskAuditUserId(ctx.sourceTaskId, agentId))
|
package/src/tools/tool-config.ts
CHANGED
|
@@ -78,12 +78,14 @@ export const DEFERRED_TOOLS = new Set([
|
|
|
78
78
|
"list-services",
|
|
79
79
|
"update-service-status",
|
|
80
80
|
|
|
81
|
-
// Config (
|
|
81
|
+
// Config (7)
|
|
82
82
|
"set-config",
|
|
83
83
|
"list-config",
|
|
84
84
|
"delete-config",
|
|
85
85
|
"credential-bindings",
|
|
86
86
|
"script-connections",
|
|
87
|
+
"model-catalog-refresh",
|
|
88
|
+
"model-catalog-overlay-upsert",
|
|
87
89
|
|
|
88
90
|
// Repos (1)
|
|
89
91
|
"update-repo",
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from "@/types";
|
|
20
20
|
import { getExecutorRegistry } from "@/workflows";
|
|
21
21
|
import { validateDefinition } from "@/workflows/definition";
|
|
22
|
+
import { withSaveWarnings, workflowSaveWarnings } from "@/workflows/readiness";
|
|
22
23
|
|
|
23
24
|
export const registerCreateWorkflowTool = (server: McpServer) => {
|
|
24
25
|
createToolRegistrar(server)(
|
|
@@ -102,6 +103,7 @@ export const registerCreateWorkflowTool = (server: McpServer) => {
|
|
|
102
103
|
outputSchema: swarmToolOutputSchema({
|
|
103
104
|
yourAgentId: z.string().optional(),
|
|
104
105
|
workflow: z.unknown().optional(),
|
|
106
|
+
warnings: z.array(z.string()).optional(),
|
|
105
107
|
}),
|
|
106
108
|
},
|
|
107
109
|
async (
|
|
@@ -152,11 +154,16 @@ export const registerCreateWorkflowTool = (server: McpServer) => {
|
|
|
152
154
|
"mcp",
|
|
153
155
|
);
|
|
154
156
|
const longScriptTimeoutHint = findLongScriptTimeoutHint(definition.nodes);
|
|
155
|
-
|
|
156
|
-
|
|
157
|
+
const warnings = await workflowSaveWarnings(workflow.definition, getExecutorRegistry());
|
|
158
|
+
return toolOk(withSaveWarnings(`Created workflow "${workflow.name}".`, warnings), {
|
|
159
|
+
details: withSaveWarnings(
|
|
160
|
+
`Created workflow "${workflow.name}" (${workflow.id}).`,
|
|
161
|
+
warnings,
|
|
162
|
+
),
|
|
157
163
|
data: {
|
|
158
164
|
yourAgentId: requestInfo.agentId,
|
|
159
165
|
workflow,
|
|
166
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
160
167
|
...(longScriptTimeoutHint ? { longScriptTimeoutHint } : {}),
|
|
161
168
|
},
|
|
162
169
|
});
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import { WorkflowNodePatchSchema } from "@/types";
|
|
12
12
|
import { getExecutorRegistry } from "@/workflows";
|
|
13
13
|
import { patchWorkflowDefinition } from "@/workflows/patch-definition";
|
|
14
|
+
import { withSaveWarnings, workflowSaveWarnings } from "@/workflows/readiness";
|
|
14
15
|
|
|
15
16
|
export const registerPatchWorkflowNodeTool = (server: McpServer) => {
|
|
16
17
|
createToolRegistrar(server)(
|
|
@@ -30,6 +31,7 @@ export const registerPatchWorkflowNodeTool = (server: McpServer) => {
|
|
|
30
31
|
outputSchema: swarmToolOutputSchema({
|
|
31
32
|
workflow: z.unknown().optional(),
|
|
32
33
|
versionCreated: z.number().optional(),
|
|
34
|
+
warnings: z.array(z.string()).optional(),
|
|
33
35
|
}),
|
|
34
36
|
},
|
|
35
37
|
async ({ id, nodeId, ...nodeFields }, requestInfo) => {
|
|
@@ -59,14 +61,22 @@ export const registerPatchWorkflowNodeTool = (server: McpServer) => {
|
|
|
59
61
|
{ id: nodeId, type: patchedNode?.type, config: nodeFields.config },
|
|
60
62
|
]);
|
|
61
63
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
const warnings = await workflowSaveWarnings(result.definition, getExecutorRegistry());
|
|
65
|
+
return toolOk(
|
|
66
|
+
withSaveWarnings(`Patched node "${nodeId}" in workflow "${workflow.name}".`, warnings),
|
|
67
|
+
{
|
|
68
|
+
details: withSaveWarnings(
|
|
69
|
+
`Patched node "${nodeId}" in workflow "${workflow.name}" (${id}). Version ${version} snapshot created.`,
|
|
70
|
+
warnings,
|
|
71
|
+
),
|
|
72
|
+
data: {
|
|
73
|
+
workflow,
|
|
74
|
+
versionCreated: version ?? undefined,
|
|
75
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
76
|
+
...(longScriptTimeoutHint ? { longScriptTimeoutHint } : {}),
|
|
77
|
+
},
|
|
68
78
|
},
|
|
69
|
-
|
|
79
|
+
);
|
|
70
80
|
} catch (err) {
|
|
71
81
|
return toolErr(String(err));
|
|
72
82
|
}
|
|
@@ -14,6 +14,7 @@ import type { WorkflowPatch } from "@/types";
|
|
|
14
14
|
import { AssetKeySchema, WorkflowNodePatchSchema } from "@/types";
|
|
15
15
|
import { getExecutorRegistry } from "@/workflows";
|
|
16
16
|
import { patchWorkflowDefinition } from "@/workflows/patch-definition";
|
|
17
|
+
import { withSaveWarnings, workflowSaveWarnings } from "@/workflows/readiness";
|
|
17
18
|
|
|
18
19
|
export const registerPatchWorkflowTool = (server: McpServer) => {
|
|
19
20
|
createToolRegistrar(server)(
|
|
@@ -78,6 +79,7 @@ export const registerPatchWorkflowTool = (server: McpServer) => {
|
|
|
78
79
|
nodesCreated: z.number().optional(),
|
|
79
80
|
nodesUpdated: z.number().optional(),
|
|
80
81
|
nodesDeleted: z.number().optional(),
|
|
82
|
+
warnings: z.array(z.string()).optional(),
|
|
81
83
|
}),
|
|
82
84
|
},
|
|
83
85
|
async ({ id, key, update, delete: del, create, onNodeFailure, triggerSchema }, requestInfo) => {
|
|
@@ -133,14 +135,19 @@ export const registerPatchWorkflowTool = (server: McpServer) => {
|
|
|
133
135
|
);
|
|
134
136
|
const longScriptTimeoutHint = findLongScriptTimeoutHint(authoredFinalNodes);
|
|
135
137
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
+
const warnings = await workflowSaveWarnings(result.definition, getExecutorRegistry());
|
|
139
|
+
return toolOk(withSaveWarnings(`Patched workflow "${workflow.name}".`, warnings), {
|
|
140
|
+
details: withSaveWarnings(
|
|
141
|
+
`Patched workflow "${workflow.name}" (${id}). Version ${version} snapshot created.`,
|
|
142
|
+
warnings,
|
|
143
|
+
),
|
|
138
144
|
data: {
|
|
139
145
|
workflow,
|
|
140
146
|
versionCreated: version ?? undefined,
|
|
141
147
|
nodesCreated: create?.length ?? 0,
|
|
142
148
|
nodesUpdated: update?.length ?? 0,
|
|
143
149
|
nodesDeleted: del?.length ?? 0,
|
|
150
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
144
151
|
...(longScriptTimeoutHint ? { longScriptTimeoutHint } : {}),
|
|
145
152
|
},
|
|
146
153
|
});
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from "@/types";
|
|
20
20
|
import { getExecutorRegistry } from "@/workflows";
|
|
21
21
|
import { definitionNodeIds, validateDefinition } from "@/workflows/definition";
|
|
22
|
+
import { withSaveWarnings, workflowSaveWarnings } from "@/workflows/readiness";
|
|
22
23
|
import { snapshotAndUpdateWorkflow } from "@/workflows/version";
|
|
23
24
|
|
|
24
25
|
export const registerUpdateWorkflowTool = (server: McpServer) => {
|
|
@@ -84,6 +85,7 @@ export const registerUpdateWorkflowTool = (server: McpServer) => {
|
|
|
84
85
|
outputSchema: swarmToolOutputSchema({
|
|
85
86
|
workflow: z.unknown().optional(),
|
|
86
87
|
versionCreated: z.number().optional(),
|
|
88
|
+
warnings: z.array(z.string()).optional(),
|
|
87
89
|
}),
|
|
88
90
|
},
|
|
89
91
|
async (
|
|
@@ -152,11 +154,16 @@ export const registerUpdateWorkflowTool = (server: McpServer) => {
|
|
|
152
154
|
const longScriptTimeoutHint = definition
|
|
153
155
|
? findLongScriptTimeoutHint(definition.nodes)
|
|
154
156
|
: undefined;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
+
const warnings = await workflowSaveWarnings(workflow.definition, getExecutorRegistry());
|
|
158
|
+
return toolOk(withSaveWarnings(`Updated workflow "${workflow.name}".`, warnings), {
|
|
159
|
+
details: withSaveWarnings(
|
|
160
|
+
`Updated workflow "${workflow.name}" (${id}). Version ${version.version} snapshot created.`,
|
|
161
|
+
warnings,
|
|
162
|
+
),
|
|
157
163
|
data: {
|
|
158
164
|
workflow,
|
|
159
165
|
versionCreated: version.version,
|
|
166
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
160
167
|
...(longScriptTimeoutHint ? { longScriptTimeoutHint } : {}),
|
|
161
168
|
},
|
|
162
169
|
});
|
package/src/types.ts
CHANGED
|
@@ -231,6 +231,25 @@ export function resolveModelTier(opts: {
|
|
|
231
231
|
return DEFAULT_MODEL_TIER_MAP[opts.harnessProvider]?.[tier];
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
+
export type ModelTierOverrides = Partial<Record<string, Partial<Record<ModelTier, string>>>>;
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* The worker's own MODEL_TIER_<TIER> / MODEL_TIER_MAP overrides, keyed by its
|
|
238
|
+
* harness provider, in the shape the worker sends on register and poll so the
|
|
239
|
+
* server can apply them at claim time. Values only (model ids), never secrets.
|
|
240
|
+
*/
|
|
241
|
+
export function parseWorkerModelTierOverrides(
|
|
242
|
+
env: Record<string, string | undefined>,
|
|
243
|
+
harnessProvider: ProviderName,
|
|
244
|
+
): ModelTierOverrides {
|
|
245
|
+
const tiers: Partial<Record<ModelTier, string>> = { ...parseTierMapJson(env.MODEL_TIER_MAP) };
|
|
246
|
+
for (const tier of MODEL_TIERS) {
|
|
247
|
+
const direct = env[`MODEL_TIER_${tier.toUpperCase()}`]?.trim();
|
|
248
|
+
if (direct) tiers[tier] = direct;
|
|
249
|
+
}
|
|
250
|
+
return Object.keys(tiers).length > 0 ? { [harnessProvider]: tiers } : {};
|
|
251
|
+
}
|
|
252
|
+
|
|
234
253
|
export function resolveTaskModelSelection(opts: {
|
|
235
254
|
model?: string | null;
|
|
236
255
|
modelTier?: string | null;
|
|
@@ -612,6 +631,12 @@ export const AgentTaskSchema = z
|
|
|
612
631
|
model: z.string().optional(),
|
|
613
632
|
modelTier: ModelTierSchema.optional(),
|
|
614
633
|
effort: ReasoningEffortSchema.optional(),
|
|
634
|
+
// Claim-time model resolution (server-side, see runbooks/model-tiers.md).
|
|
635
|
+
// resolvedModel is what the worker runs; modelSource says which layer won;
|
|
636
|
+
// modelAlias is the `latest:` alias the value came from, when any.
|
|
637
|
+
resolvedModel: z.string().optional(),
|
|
638
|
+
modelSource: z.string().optional(),
|
|
639
|
+
modelAlias: z.string().optional(),
|
|
615
640
|
|
|
616
641
|
// Schedule linking (optional — set when task was created by a schedule).
|
|
617
642
|
// z.string(): also reachable verbatim from the create-task query param.
|
|
@@ -1996,7 +2021,7 @@ export const WorkflowNodeSchema = z
|
|
|
1996
2021
|
type: z
|
|
1997
2022
|
.string()
|
|
1998
2023
|
.describe(
|
|
1999
|
-
"Executor type: 'agent-task', 'script', 'swarm-script', 'raw-llm', 'validate', 'property-match'",
|
|
2024
|
+
"Executor type: 'agent-task', 'script', 'swarm-script', 'raw-llm', 'system-one-decision', 'validate', 'property-match'",
|
|
2000
2025
|
),
|
|
2001
2026
|
label: z.string().optional().describe("Human-readable label for UI display"),
|
|
2002
2027
|
config: z
|
|
@@ -2005,6 +2030,7 @@ export const WorkflowNodeSchema = z
|
|
|
2005
2030
|
"Executor-specific config. For agent-task: { template, outputSchema?, agentId?, routingReason?, routingNote?, tags?, priority?, dir?, vcsRepo?, model? }; configured agentId defaults routingReason to human_pinned. " +
|
|
2006
2031
|
"For script: { runtime, script, args?, timeout? }. " +
|
|
2007
2032
|
"For swarm-script: { scriptName, scope?, pinHash?, args?, fsMode?, timeoutMs? (1000-300000) }. " +
|
|
2033
|
+
"For system-one-decision (typed decisions, Jev by default): { provider? ('typesafe' default | 'openrouter' | 'laya'; a literal, never a {{token}}), state, questions: { <id>: { type: 'noul'|'choice'|'score', instructions, criteria? } }, returns: { <id>: { type } }, model? (provider-specific; unset = the provider's default, and laya has none so it sends no model and picks its own checkpoint), timeoutMs?, maxRetries? (0-3), humanReview? { band: { min, max } (0-1, inclusive), approvers: { users?, roles?, policy }, title?, timeout?, notifications? } }; each provider needs its own global secret (TYPESAFE_API_KEY, OPENROUTER_API_KEY, or LAYA_API_KEY; laya also needs the global config LAYA_URL), and a save warns and a run fails before any node executes when one is missing; system-one-decision nodes must not set retry or validation.retry. With humanReview, an answer whose confidence is inside the band waits for a person (human-in-the-loop approval), and next must map ports { approved, rejected?, timeout? }. " +
|
|
2008
2034
|
"Agent-task templates and ordinary config values support {{interpolation}} from the node's inputs context, including trigger and declared upstream aliases. " +
|
|
2009
2035
|
"SECURITY: executable source for script/swarm-script nodes does not interpolate trigger.* or upstream node outputs; only input/workflow/swarm/run values are allowed in inline script source, and named swarm-script source is not workflow-interpolated. " +
|
|
2010
2036
|
"Pass dynamic values through config.args instead (inline script receives them as argv; swarm-script receives its args object). " +
|
|
@@ -2412,6 +2438,9 @@ export type AgentTaskSummary = Pick<
|
|
|
2412
2438
|
| "scheduleId"
|
|
2413
2439
|
| "model"
|
|
2414
2440
|
| "modelTier"
|
|
2441
|
+
| "resolvedModel"
|
|
2442
|
+
| "modelSource"
|
|
2443
|
+
| "modelAlias"
|
|
2415
2444
|
| "effort"
|
|
2416
2445
|
| "provider"
|
|
2417
2446
|
| "requestedByUserId"
|