@desplega.ai/agent-swarm 1.158.0 → 1.160.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{acp-adapter-xaq4n6pc.js → acp-adapter-2mjwcsfh.js} +124 -27
- package/dist/{actions-p9hz5yat.js → actions-e43rxskr.js} +35 -30
- package/dist/{anthropic-messages-51pz26qe.js → anthropic-messages-5qx80j9k.js} +106 -16
- package/dist/{app-g26wdcz6.js → app-mc7xfagn.js} +12 -10
- package/dist/{assistant-wydfqkb3.js → assistant-sedkzv3b.js} +42 -31
- package/dist/{azure-openai-responses-8gksqtmz.js → azure-openai-responses-nfb21z9a.js} +13 -14
- package/dist/{boot-reembed-se8zmdcp.js → boot-reembed-cqz9swn5.js} +13 -12
- package/dist/{boot-reembed-g5ckg9yb.js → boot-reembed-m3656j2n.js} +14 -13
- package/dist/{boot-scrub-logs-wfxqan61.js → boot-scrub-logs-xbxf5d7w.js} +11 -9
- package/dist/boot-seeds-gsetbs5y.js +28 -0
- package/dist/{claude-adapter-tv2x4f36.js → claude-adapter-dc6289mc.js} +28 -23
- package/dist/{claude-managed-adapter-41b2s7fd.js → claude-managed-adapter-naaw51me.js} +4 -3
- package/dist/{claude-managed-setup-60g8aazx.js → claude-managed-setup-eacvchx0.js} +12 -3
- package/dist/claude-sdk-session-bqp2xvym.js +29722 -0
- package/dist/{cli-987hg0aa.js → cli-0jwe9034.js} +2 -2
- package/dist/{cli-vj6h9kc2.js → cli-0m7wct0d.js} +59 -1
- package/dist/{cli-c07r9a2m.js → cli-0q3avq3s.js} +12 -11
- package/dist/{cli-19jenss3.js → cli-0wgxmwf6.js} +31 -10
- package/dist/cli-0x01kwrd.js +491 -0
- package/dist/cli-0xqde53b.js +176 -0
- package/dist/cli-10nhj6xq.js +8355 -0
- package/dist/{cli-tt9k8r35.js → cli-12tszpk9.js} +479 -910
- package/dist/{cli-5b6b62tz.js → cli-13hw4z91.js} +1 -1
- package/dist/{cli-cqt19wg6.js → cli-1a2j50m0.js} +11 -2
- package/dist/{cli-xberxa1r.js → cli-25fcq4b4.js} +2 -2
- package/dist/{cli-npssxgaz.js → cli-2pdaysab.js} +52 -2
- package/dist/cli-3qhyh31j.js +166 -0
- package/dist/cli-447dt1h3.js +3828 -0
- package/dist/cli-4derpgyk.js +115 -0
- package/dist/{cli-00w2c66p.js → cli-523679eh.js} +13 -2
- package/dist/{cli-y95wp3tg.js → cli-58e8yr0d.js} +1 -1
- package/dist/{cli-24k806sc.js → cli-5nbmpy8w.js} +3 -3
- package/dist/{cli-53sz8gkz.js → cli-6cs6wzxt.js} +1 -1
- package/dist/cli-741yb0xx.js +1027 -0
- package/dist/cli-74h9hscv.js +69 -0
- package/dist/cli-7cxw8b60.js +14910 -0
- package/dist/{cli-xhaeg2nc.js → cli-7e398j1h.js} +1 -1
- package/dist/cli-7kyk9jr3.js +22 -0
- package/dist/cli-89s4ngw3.js +183 -0
- package/dist/{cli-9b13ggd1.js → cli-89v6kyq7.js} +1 -0
- package/dist/{cli-ftfvggzx.js → cli-9hsgwz4w.js} +1045 -581
- package/dist/{cli-rqz8qbw7.js → cli-a8dvhzrt.js} +59 -20
- package/dist/{cli-j57qkrpq.js → cli-azhgzetg.js} +395 -56
- package/dist/cli-br0qdrnp.js +2150 -0
- package/dist/{cli-rj19hc34.js → cli-btfv8y88.js} +1 -1
- package/dist/{cli-8qmme6vd.js → cli-c8a36vcc.js} +2 -2
- package/dist/cli-cpvsaqsw.js +333 -0
- package/dist/cli-d5czme6m.js +17805 -0
- package/dist/{cli-h38x0qhg.js → cli-et8m9xsv.js} +4 -3
- package/dist/{cli-x24pshtq.js → cli-evvtk35y.js} +6 -6
- package/dist/{cli-7s3syk22.js → cli-f8gy6r2r.js} +5 -5
- package/dist/{cli-zt1227k1.js → cli-fkqhbg8g.js} +5 -3
- package/dist/{cli-9httdpyq.js → cli-g2whhdrf.js} +1 -1
- package/dist/cli-gt4hxb8p.js +99 -0
- package/dist/{cli-s8eet254.js → cli-jajnsm8q.js} +37184 -53838
- package/dist/{cli-7gtvvpbh.js → cli-jayjpjmb.js} +21 -12
- package/dist/{cli-68w09sjw.js → cli-jyk0ynw2.js} +27 -89
- package/dist/{cli-aehm1rbq.js → cli-jz4w45qt.js} +1152 -61
- package/dist/cli-kmcpfwn2.js +333856 -0
- package/dist/{cli-4198qc9v.js → cli-ktmyrtpn.js} +19 -3
- package/dist/{cli-j55n64b6.js → cli-mfe5btp9.js} +505 -189
- package/dist/{cli-q67y4wzm.js → cli-phsaj4xq.js} +24 -2
- package/dist/{cli-1hkrch3k.js → cli-pm1rst0h.js} +46 -7
- package/dist/{cli-pw9rj5t9.js → cli-pqetb83f.js} +1 -176
- package/dist/{cli-ggm92wcn.js → cli-ptr8h7kp.js} +20 -1
- package/dist/{cli-s8s499xy.js → cli-qbksc292.js} +3 -2
- package/dist/{cli-47m6dkd3.js → cli-qydf22qq.js} +597 -346
- package/dist/{cli-3gmcwfyx.js → cli-r1mxd1xh.js} +13 -6
- package/dist/cli-rb9m6rpe.js +4980 -0
- package/dist/cli-rykfmgwg.js +102 -0
- package/dist/{cli-5d10vd2m.js → cli-shkxm1vn.js} +6 -5
- package/dist/{cli-eetrvv91.js → cli-t3p77qk1.js} +1 -1
- package/dist/cli-tfky2838.js +756 -0
- package/dist/{cli-g8djp6qe.js → cli-tmqt7dpj.js} +40 -0
- package/dist/{cli-fgqxwrpv.js → cli-tpq83z89.js} +1631 -127
- package/dist/cli-trc24wk2.js +143 -0
- package/dist/{cli-aky8tctm.js → cli-tzx2ke1v.js} +3 -3
- package/dist/cli-v7fn5gms.js +1425 -0
- package/dist/{cli-wscdvcqz.js → cli-vctdqs5h.js} +1 -1
- package/dist/{cli-cw9zck8j.js → cli-xjcywfsf.js} +8 -4
- package/dist/{cli-k0g1sjzm.js → cli-ybgv4kw2.js} +18 -11
- package/dist/{cli-rjehvrts.js → cli-ydmcgve1.js} +8 -8
- package/dist/{cli-vhjwh6cz.js → cli-ygwm91nt.js} +1 -1
- package/dist/{cli-mcnhgnn1.js → cli-ykk5jaax.js} +184 -26
- package/dist/{cli-de1396j2.js → cli-ywybs5as.js} +7 -1
- package/dist/cli-zpep9q5e.js +491 -0
- package/dist/cli.js +41 -32
- package/dist/cloudflare-workers-ai-system-one-k6fbxw9d.js +46 -0
- package/dist/{codex-adapter-cnm8wk11.js → codex-adapter-nmxcatww.js} +26 -17
- package/dist/{codex-hook-9hptmdhj.js → codex-hook-20me6byw.js} +2 -2
- package/dist/{codex-login-x3jk4etn.js → codex-login-w6fz4xbz.js} +50 -5
- package/dist/{codex-session-runner-gfg547zj.js → codex-session-runner-90aa80ev.js} +24 -17
- package/dist/{commands-e3bn0p75.js → commands-8wcq1kn7.js} +11 -9
- package/dist/{db-03v9kzr2.js → db-j40kc4se.js} +25 -11
- package/dist/{devin-adapter-8vgkwh29.js → devin-adapter-dpsv6hd9.js} +1 -1
- package/dist/{dispatcher-nrmg8yht.js → dispatcher-4zqy768d.js} +34 -29
- package/dist/{dsh-adapter-k03nfqa6.js → dsh-adapter-mpnzgztj.js} +3 -3
- package/dist/{e2b-93x1k0j5.js → e2b-ez07znam.js} +4 -3
- package/dist/execute-6e7qeyk4.js +1017 -0
- package/dist/{google-generative-ai-bxmv50nc.js → google-generative-ai-k6n5nbpp.js} +6 -5
- package/dist/{google-vertex-809af514.js → google-vertex-44xxtcvp.js} +6 -5
- package/dist/{handlers-aqcedbyb.js → handlers-kjdce1w5.js} +37 -32
- package/dist/{hook-ybvmrmhq.js → hook-czx13n12.js} +19 -15
- package/dist/{hook-7vkskkre.js → hook-r2t4bqfg.js} +18 -14
- package/dist/{http-2eqa8t24.js → http-3bbb19be.js} +900 -145
- package/dist/{index-f6cryxwe.js → index-4a5njnm1.js} +2 -59
- package/dist/{index-x71bvq0p.js → index-668me3f0.js} +35 -30
- package/dist/{index-c6zsv1bd.js → index-7aj368tw.js} +13 -12
- package/dist/{index-1yks3qcr.js → index-fg0dsp6c.js} +34 -29
- package/dist/{index-chgdkq36.js → index-kkv84jq9.js} +34 -29
- package/dist/{index-a3vn96n8.js → index-zg7vp46x.js} +198 -54
- package/dist/{keepalive-vtkf5n99.js → keepalive-yra8dy57.js} +13 -11
- package/dist/{lead-h3sp357x.js → lead-va0pad2c.js} +54 -49
- package/dist/llama-cpp-classify-tgj35k8d.js +335 -0
- package/dist/{maintenance-jzeqtk3q.js → maintenance-hqewtmyg.js} +14 -13
- package/dist/{mistral-conversations-zx0kn554.js → mistral-conversations-pc3ntnxt.js} +14 -17
- package/dist/{oauth-refresh-sweep-qp3dzmdp.js → oauth-refresh-sweep-7rdt1m9n.js} +12 -10
- package/dist/{onboard-tmsp0qbz.js → onboard-j6ms6d0x.js} +6 -5
- package/dist/{openai-codex-responses-4vjqzvjp.js → openai-codex-responses-bbancrr0.js} +21 -8
- package/dist/{openai-completions-9hjrqbw8.js → openai-completions-jpaj5g2b.js} +9 -12
- package/dist/{openai-responses-7b1pn0xt.js → openai-responses-200k1wx4.js} +23 -18
- package/dist/{opencode-adapter-x6x2da1z.js → opencode-adapter-89rabkw2.js} +8 -7
- package/dist/{openrouter-images-9f9ek4rw.js → openrouter-images-8yx3487g.js} +5 -7
- package/dist/{otel-impl-9mz73q65.js → otel-impl-a792rjz0.js} +1 -1
- package/dist/{pi-messages-txpjj39q.js → pi-messages-vxdt8v7j.js} +2 -1
- package/dist/{pi-mono-adapter-m6dvv0dz.js → pi-mono-adapter-g7qzvr3v.js} +295 -70
- package/dist/pricing-refresh-wx0tp7cb.js +39 -0
- package/dist/quickjs-r1pq2107.wasm +0 -0
- package/dist/{rbac-roles-c9wqz83x.js → rbac-roles-8tzawacv.js} +12 -10
- package/dist/{render-v2-gryrh9h8.js → render-v2-mm7zswb8.js} +12 -10
- package/dist/runtime-zyssgse0.js +24 -0
- package/dist/{setup-q9720bt6.js → setup-8y70ys6t.js} +6 -5
- package/dist/typesafe-system-one-2ef30pec.js +27 -0
- package/dist/{virtual-modules-3htpgn8x.js → virtual-modules-6nmk7h3n.js} +22 -13
- package/dist/{work-objects-b76x7deb.js → work-objects-fg6xkb0n.js} +37 -32
- package/dist/{worker-6vrxbv41.js → worker-wmwhxsph.js} +54 -49
- package/openapi.json +1314 -91
- package/package.json +12 -11
- package/src/be/db/agents.ts +20 -0
- package/src/be/db/api-keys.ts +70 -0
- package/src/be/db/tasks/human-free.ts +442 -0
- package/src/be/db/tasks/read.ts +56 -1
- package/src/be/db/tasks/write.ts +107 -11
- package/src/be/db.ts +274 -188
- package/src/be/harness-model-support.ts +182 -0
- package/src/be/human-free-drain.ts +67 -0
- package/src/be/memory/providers/sqlite-store.ts +51 -15
- package/src/be/migrations/182_task_human_free_flag.sql +63 -0
- package/src/be/migrations/183_human_free_reclassify_queue.sql +25 -0
- package/src/be/migrations/184_model_catalog.sql +120 -0
- package/src/be/migrations/185_api_key_auth_failures.sql +6 -0
- package/src/be/migrations/186_api_key_seat_mismatch.sql +8 -0
- package/src/be/model-catalog-store.ts +644 -0
- package/src/be/model-tier-keys.ts +46 -0
- package/src/be/model-tier-resolution.ts +534 -0
- package/src/be/model-validation.ts +188 -0
- package/src/be/models-catalog.ts +61 -7
- package/src/be/modelsdev-cache.json +82 -0
- package/src/be/modelsdev-cache.ts +2 -0
- package/src/be/pricing-refresh.ts +154 -12
- package/src/be/scripts/typecheck.ts +15 -0
- package/src/be/seed/runner.ts +18 -12
- package/src/be/seed/types.ts +1 -0
- package/src/be/seed-scripts/catalog/task-failure-audit.ts +61 -9
- package/src/be/swarm-config-guard.ts +8 -0
- package/src/cli.tsx +10 -0
- package/src/commands/claude-managed-setup.ts +3 -1
- package/src/commands/codex-login.ts +67 -0
- package/src/commands/credential-outcome-report.ts +191 -0
- package/src/commands/rate-limit-outcome.ts +34 -4
- package/src/commands/runner.ts +357 -186
- package/src/commands/seat-mismatch-report.ts +46 -0
- package/src/extensions/apply-task-create.ts +27 -1
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/heartbeat/heartbeat.ts +79 -17
- package/src/heartbeat/templates.ts +2 -2
- package/src/http/agents.ts +62 -1
- package/src/http/api-keys.ts +140 -2
- package/src/http/codex-oauth-keep-warm.ts +3 -5
- package/src/http/index.ts +8 -0
- package/src/http/memory.ts +12 -5
- package/src/http/models-catalog.ts +357 -8
- package/src/http/poll.ts +54 -3
- package/src/http/schedules.ts +51 -6
- package/src/http/script-runs.ts +12 -0
- package/src/http/session-data.ts +2 -5
- package/src/http/stats.ts +6 -8
- package/src/http/status.ts +15 -1
- package/src/http/tasks.ts +90 -7
- package/src/http/usage-cache.ts +56 -15
- package/src/http/workflows.ts +46 -8
- package/src/mcp-client/http-client.ts +5 -0
- package/src/memory/recall-query.ts +74 -0
- package/src/prompts/base-prompt.ts +15 -4
- package/src/prompts/session-templates.ts +23 -1
- package/src/providers/acp-adapter.ts +1 -0
- package/src/providers/claude-adapter.ts +14 -5
- package/src/providers/claude-managed-adapter.ts +1 -0
- package/src/providers/claude-managed-models.ts +47 -161
- package/src/providers/claude-sdk-session.ts +3 -1
- package/src/providers/codex-adapter.ts +14 -14
- package/src/providers/codex-models.ts +65 -198
- package/src/providers/devin-adapter.ts +1 -0
- package/src/providers/dsh-adapter.ts +2 -1
- package/src/providers/harness-version.ts +83 -7
- package/src/providers/opencode-adapter.ts +53 -1
- package/src/providers/pi-binary-root/src/extensions/codemode/worker.ts +11 -0
- package/src/providers/pi-codemode-runtime.ts +17 -0
- package/src/providers/pi-mono-adapter.ts +361 -83
- package/src/providers/pi-mono-extension.ts +16 -1
- package/src/providers/reasoning-effort.ts +44 -140
- package/src/providers/types.ts +8 -0
- package/src/rbac/legacy-policy.ts +43 -0
- package/src/rbac/permissions.ts +15 -0
- package/src/scripts-runtime/sdk-allowlist.ts +4 -0
- package/src/scripts-runtime/types/stdlib.d.ts +15 -0
- package/src/scripts-runtime/types/swarm-extension.d.ts +3 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +15 -0
- package/src/server.ts +6 -0
- package/src/slack/assistant.ts +13 -0
- package/src/slack/message-text.ts +56 -2
- package/src/slack/render-v2.ts +87 -37
- package/src/slack/session-status.ts +541 -0
- package/src/slack/thread-buffer.ts +10 -2
- package/src/tasks/sibling-awareness.ts +3 -2
- package/src/telemetry.ts +5 -4
- package/src/templates.d.ts +6 -0
- package/src/tests/agents-list-model-display.test.ts +4 -6
- package/src/tests/api-key-auth-failure-route.test.ts +142 -0
- package/src/tests/api-key-tracking.test.ts +368 -1
- package/src/tests/base-prompt.test.ts +43 -2
- package/src/tests/claude-adapter.test.ts +68 -0
- package/src/tests/claude-managed-adapter.test.ts +12 -18
- package/src/tests/codex-adapter.test.ts +2 -1
- package/src/tests/codex-auth-failure.test.ts +52 -0
- package/src/tests/codex-gpt6-release.test.ts +66 -5
- package/src/tests/codex-login.test.ts +116 -0
- package/src/tests/context-window.test.ts +7 -5
- package/src/tests/credential-exhaustion-policy.test.ts +118 -3
- package/src/tests/credential-outcome-report.test.ts +171 -0
- package/src/tests/credential-status-api.test.ts +103 -2
- package/src/tests/db-extraction-contract.test.ts +1 -0
- package/src/tests/db-task-read-contract.test.ts +10 -1
- package/src/tests/db-task-write-contract.test.ts +535 -2
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/dokploy-deploy.test.ts +315 -0
- package/src/tests/dsh-adapter.test.ts +1 -1
- package/src/tests/explicit-model-validation.test.ts +583 -0
- package/src/tests/extensions-pre-task.test.ts +73 -0
- package/src/tests/fetch-resolved-env.test.ts +46 -0
- package/src/tests/hang-watchdog-worker.ts +47 -0
- package/src/tests/hang-watchdog.test.ts +45 -0
- package/src/tests/hang-watchdog.ts +30 -0
- package/src/tests/harness-model-guard.test.ts +123 -0
- package/src/tests/harness-model-support.test.ts +374 -0
- package/src/tests/harness-version.test.ts +70 -3
- package/src/tests/heartbeat-checklist-config.test.ts +71 -0
- package/src/tests/heartbeat-checklist.test.ts +4 -2
- package/src/tests/heartbeat-orphan-resume.test.ts +48 -0
- package/src/tests/http-task-write-ownership.test.ts +172 -0
- package/src/tests/human-free-flag.test.ts +279 -0
- package/src/tests/human-free-oracle.ts +57 -0
- package/src/tests/human-free-reclassify.test.ts +669 -0
- package/src/tests/memory-fts-populate.test.ts +139 -0
- package/src/tests/memory-http-recall-gating.test.ts +125 -1
- package/src/tests/migration-046-budgets.test.ts +3 -3
- package/src/tests/model-catalog-migration.test.ts +134 -0
- package/src/tests/model-catalog-table.test.ts +249 -0
- package/src/tests/model-catalog-write-access.test.ts +387 -0
- package/src/tests/model-rate-limit-windows.test.ts +19 -0
- package/src/tests/model-tier-resolution.test.ts +611 -0
- package/src/tests/models-catalog.test.ts +1 -1
- package/src/tests/multi-runtime-enabled-stats.test.ts +65 -1
- package/src/tests/opencode-adapter.test.ts +157 -2
- package/src/tests/package-publish.test.ts +137 -36
- package/src/tests/pi-codemode.test.ts +246 -0
- package/src/tests/pi-mono-adapter.test.ts +3 -0
- package/src/tests/pi-native-mcp.test.ts +193 -0
- package/src/tests/pi-tool-exposure.test.ts +374 -0
- package/src/tests/preload.ts +3 -0
- package/src/tests/pricing-routes.test.ts +2 -2
- package/src/tests/prompt-template-session.test.ts +5 -3
- package/src/tests/rate-limit-event.test.ts +65 -0
- package/src/tests/rate-limit-outcome.test.ts +90 -0
- package/src/tests/rbac-engine.test.ts +77 -3
- package/src/tests/reasoning-effort.test.ts +109 -0
- package/src/tests/refresh-modelsdev-carryforward.test.ts +5 -5
- package/src/tests/runner-key-completion-outcome.test.ts +112 -0
- package/src/tests/runner-key-outcome-late-reset.test.ts +224 -0
- package/src/tests/runner-registration-retry.test.ts +149 -0
- package/src/tests/runtime-model-catalog.test.ts +86 -0
- package/src/tests/script-connections.test.ts +9 -0
- package/src/tests/seat-mismatch-report.test.ts +85 -0
- package/src/tests/seed-scripts.test.ts +138 -0
- package/src/tests/seed.test.ts +25 -0
- package/src/tests/session-costs.test.ts +14 -11
- package/src/tests/slack-message-text.test.ts +101 -0
- package/src/tests/slack-render-v2.test.ts +96 -0
- package/src/tests/slack-session-status.test.ts +626 -0
- package/src/tests/slack-thread-buffer-context.test.ts +93 -0
- package/src/tests/status.test.ts +29 -0
- package/src/tests/steering-promotion.test.ts +51 -0
- package/src/tests/store-progress-ownership.test.ts +136 -0
- package/src/tests/subscription-plans.test.ts +28 -0
- package/src/tests/task-cascade-fail.test.ts +148 -2
- package/src/tests/task-citations.test.ts +3 -2
- package/src/tests/tool-annotations.test.ts +1 -1
- package/src/tests/usage-cache.test.ts +166 -0
- package/src/tests/workflow-agent-task.test.ts +30 -0
- package/src/tests/workflow-executors.test.ts +5 -2
- package/src/tests/workflow-system-one-decision-review.test.ts +1021 -0
- package/src/tests/workflow-system-one-decision-save-warnings.test.ts +365 -0
- package/src/tests/workflow-system-one-decision.test.ts +2063 -0
- package/src/tests/workflow-triggers-v2.test.ts +320 -0
- package/src/tools/model-catalog/index.ts +137 -0
- package/src/tools/schedules/create-schedule.ts +17 -2
- package/src/tools/schedules/patch-schedule.ts +31 -1
- package/src/tools/schedules/update-schedule.ts +31 -1
- package/src/tools/send-task.ts +18 -1
- package/src/tools/store-progress.ts +49 -4
- package/src/tools/task-action.ts +14 -1
- package/src/tools/tool-config.ts +3 -1
- package/src/tools/workflows/create-workflow.ts +17 -4
- package/src/tools/workflows/patch-workflow-node.ts +17 -7
- package/src/tools/workflows/patch-workflow.ts +9 -2
- package/src/tools/workflows/update-workflow.ts +17 -4
- package/src/types.ts +41 -1
- package/src/utils/codex-auth-failure.ts +34 -0
- package/src/utils/context-window.ts +47 -53
- package/src/utils/credentials.ts +72 -30
- package/src/utils/error-tracker.ts +80 -0
- package/src/utils/harness-cli-version.ts +115 -0
- package/src/utils/harness-model-error.ts +33 -0
- package/src/utils/model-rate-limit-windows.ts +12 -0
- package/src/utils/runtime-model-catalog.ts +152 -0
- package/src/utils/spawn-model-guard.ts +43 -0
- package/src/utils/subscription-plans.ts +22 -0
- package/src/workflows/approval-resolution.ts +39 -0
- package/src/workflows/definition.ts +7 -0
- package/src/workflows/engine.ts +69 -1
- package/src/workflows/executors/agent-task.ts +16 -6
- package/src/workflows/executors/base.ts +53 -0
- package/src/workflows/executors/human-in-the-loop.ts +8 -8
- package/src/workflows/executors/index.ts +1 -0
- package/src/workflows/executors/registry.ts +2 -0
- package/src/workflows/executors/system-one-decision.ts +1232 -0
- package/src/workflows/executors/system-one-providers.ts +301 -0
- package/src/workflows/executors/workflow-llm.ts +13 -1
- package/src/workflows/model-validation.ts +45 -0
- package/src/workflows/patch-definition.ts +5 -0
- package/src/workflows/readiness.ts +86 -0
- package/src/workflows/recovery.ts +12 -12
- package/src/workflows/resume.ts +23 -9
- package/src/workflows/triggers.ts +85 -0
- package/templates/schedules/daily-blocker-digest/content.md +4 -5
- package/templates/skills/agent-browser/SKILL.md +6 -5
- package/templates/skills/agent-browser/content.md +6 -5
- package/templates/skills/heartbeat-runbook/config.json +1 -1
- package/templates/skills/heartbeat-runbook/content.md +2 -2
- package/templates/skills/swarm-scripts/SKILL.md +1 -1
- package/templates/skills/swarm-scripts/content.md +1 -1
- package/templates/skills/workflow-iterate/content.md +82 -1
- package/templates/workflows/claude-code-changelog-watch/content.md +2 -0
- package/templates/workflows/pr-review-status-sweep/content.md +2 -0
- package/dist/boot-seeds-s3q01xj3.js +0 -26
- package/dist/claude-sdk-session-t442qz6f.js +0 -39108
- package/dist/cli-0xjkj1j7.js +0 -61
- package/dist/cli-20cj37vt.js +0 -335561
- package/dist/cli-2djtr51b.js +0 -48
- package/dist/cli-65hjmev5.js +0 -1141
- package/dist/cli-aw1jwp1r.js +0 -14075
- package/dist/cli-h967nb6r.js +0 -6603
- package/dist/cli-vapyk8sw.js +0 -20
- package/dist/cli-ws6rqycm.js +0 -247
- package/dist/pricing-refresh-v1cbr9te.js +0 -27
- package/src/providers/modelsdev-reasoning.json +0 -6453
- package/dist/{index-f24115ge.js → index-2wsdsnmm.js} +3 -3
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-wide model catalog for code that runs on BOTH sides of the
|
|
3
|
+
* API/worker boundary (provider adapters, context-window math, reasoning
|
|
4
|
+
* gating). Model-catalog phase 4: this replaces the hand-maintained per-model
|
|
5
|
+
* tables (codex allowlist/windows/pricing, claude-managed allowlist/pricing,
|
|
6
|
+
* per-model context windows, the slim reasoning snapshot).
|
|
7
|
+
*
|
|
8
|
+
* Sources, highest first:
|
|
9
|
+
* 1. the live catalog: the API server's `model_catalog` + overlay rows.
|
|
10
|
+
* Workers pull it over HTTP (`GET /api/models-catalog`, see
|
|
11
|
+
* `refreshRuntimeModelCatalog`); the API process sets it directly after
|
|
12
|
+
* every catalog reload.
|
|
13
|
+
* 2. the vendored models.dev snapshot (`src/be/modelsdev-cache.json`) —
|
|
14
|
+
* offline fallback, and the only source for ids the live catalog has
|
|
15
|
+
* dropped (legacy models still named by old tasks and pricing rows).
|
|
16
|
+
*
|
|
17
|
+
* NO database imports (worker-safe). Every lookup is synchronous; the HTTP
|
|
18
|
+
* refresh is TTL-cached and never throws, so hot paths stay cheap.
|
|
19
|
+
*/
|
|
20
|
+
import modelsDevSnapshot from "../be/modelsdev-cache.json";
|
|
21
|
+
|
|
22
|
+
export interface RuntimeCatalogModel {
|
|
23
|
+
id?: string;
|
|
24
|
+
name?: string;
|
|
25
|
+
cost?: { input?: number; output?: number; cache_read?: number; cache_write?: number };
|
|
26
|
+
limit?: { context?: number };
|
|
27
|
+
release_date?: string;
|
|
28
|
+
status?: string;
|
|
29
|
+
reasoning?: boolean;
|
|
30
|
+
reasoning_options?: { type?: string; values?: string[] }[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface RuntimeCatalogProvider {
|
|
34
|
+
id?: string;
|
|
35
|
+
name?: string;
|
|
36
|
+
models?: Record<string, RuntimeCatalogModel>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type RuntimeCatalog = Record<string, RuntimeCatalogProvider | undefined>;
|
|
40
|
+
|
|
41
|
+
const SNAPSHOT = modelsDevSnapshot as unknown as RuntimeCatalog;
|
|
42
|
+
|
|
43
|
+
/** Default freshness window for the worker-side HTTP pull. */
|
|
44
|
+
export const RUNTIME_CATALOG_TTL_MS = 10 * 60_000;
|
|
45
|
+
const FETCH_TIMEOUT_MS = 5_000;
|
|
46
|
+
|
|
47
|
+
let live: RuntimeCatalog | null = null;
|
|
48
|
+
let liveAt = 0;
|
|
49
|
+
let liveSource: "live" | "snapshot" | null = null;
|
|
50
|
+
const mergedSections = new Map<string, Record<string, RuntimeCatalogModel>>();
|
|
51
|
+
let inFlight: Promise<boolean> | null = null;
|
|
52
|
+
|
|
53
|
+
/** Replace the live layer (API process after a reload; worker after a pull). */
|
|
54
|
+
export function setRuntimeModelCatalog(
|
|
55
|
+
providers: RuntimeCatalog,
|
|
56
|
+
at: number = Date.now(),
|
|
57
|
+
source: "live" | "snapshot" = "live",
|
|
58
|
+
): void {
|
|
59
|
+
live = providers;
|
|
60
|
+
liveAt = at;
|
|
61
|
+
liveSource = source;
|
|
62
|
+
mergedSections.clear();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function resetRuntimeModelCatalogForTests(): void {
|
|
66
|
+
live = null;
|
|
67
|
+
liveAt = 0;
|
|
68
|
+
liveSource = null;
|
|
69
|
+
mergedSections.clear();
|
|
70
|
+
inFlight = null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Where the current live layer came from; null = snapshot only. */
|
|
74
|
+
export function runtimeModelCatalogSource(): "live" | "snapshot" | null {
|
|
75
|
+
return liveSource;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** One provider section: snapshot models overlaid by the live catalog (live wins per id). */
|
|
79
|
+
export function runtimeCatalogSection(provider: string): Record<string, RuntimeCatalogModel> {
|
|
80
|
+
const cached = mergedSections.get(provider);
|
|
81
|
+
if (cached) return cached;
|
|
82
|
+
const merged: Record<string, RuntimeCatalogModel> = {
|
|
83
|
+
...(SNAPSHOT[provider]?.models ?? {}),
|
|
84
|
+
...(live?.[provider]?.models ?? {}),
|
|
85
|
+
};
|
|
86
|
+
mergedSections.set(provider, merged);
|
|
87
|
+
return merged;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The sections the harness-compatibility guard (`harnessModelMismatch`) reads: the two
|
|
92
|
+
* sections the pinned harnesses talk to. Sync, so worker spawn and config validation
|
|
93
|
+
* can judge a model without a round trip.
|
|
94
|
+
*/
|
|
95
|
+
export function runtimeHarnessSections(): Record<
|
|
96
|
+
"anthropic" | "openai",
|
|
97
|
+
{ models: Record<string, RuntimeCatalogModel> }
|
|
98
|
+
> {
|
|
99
|
+
return {
|
|
100
|
+
anthropic: { models: runtimeCatalogSection("anthropic") },
|
|
101
|
+
openai: { models: runtimeCatalogSection("openai") },
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function runtimeCatalogModel(
|
|
106
|
+
provider: string,
|
|
107
|
+
modelId: string,
|
|
108
|
+
): RuntimeCatalogModel | undefined {
|
|
109
|
+
return runtimeCatalogSection(provider)[modelId];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Pull the live catalog from the API server. TTL-cached (`maxAgeMs`),
|
|
114
|
+
* single-flight, never throws: returns false and keeps the previous layer
|
|
115
|
+
* when the server is unreachable or answers non-2xx.
|
|
116
|
+
*/
|
|
117
|
+
export async function refreshRuntimeModelCatalog(opts: {
|
|
118
|
+
apiUrl: string;
|
|
119
|
+
apiKey?: string;
|
|
120
|
+
agentId?: string;
|
|
121
|
+
maxAgeMs?: number;
|
|
122
|
+
fetchImpl?: typeof fetch;
|
|
123
|
+
}): Promise<boolean> {
|
|
124
|
+
const maxAge = opts.maxAgeMs ?? RUNTIME_CATALOG_TTL_MS;
|
|
125
|
+
if (live && Date.now() - liveAt < maxAge) return true;
|
|
126
|
+
if (inFlight) return inFlight;
|
|
127
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
128
|
+
inFlight = (async () => {
|
|
129
|
+
try {
|
|
130
|
+
const headers: Record<string, string> = {};
|
|
131
|
+
if (opts.apiKey) headers.Authorization = `Bearer ${opts.apiKey}`;
|
|
132
|
+
if (opts.agentId) headers["X-Agent-ID"] = opts.agentId;
|
|
133
|
+
const res = await doFetch(`${opts.apiUrl}/api/models-catalog`, {
|
|
134
|
+
headers,
|
|
135
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
136
|
+
});
|
|
137
|
+
if (!res.ok) return false;
|
|
138
|
+
const body = (await res.json()) as {
|
|
139
|
+
source?: "live" | "snapshot";
|
|
140
|
+
providers?: RuntimeCatalog;
|
|
141
|
+
};
|
|
142
|
+
if (!body || typeof body.providers !== "object" || body.providers === null) return false;
|
|
143
|
+
setRuntimeModelCatalog(body.providers, Date.now(), body.source ?? "live");
|
|
144
|
+
return true;
|
|
145
|
+
} catch {
|
|
146
|
+
return false;
|
|
147
|
+
} finally {
|
|
148
|
+
inFlight = null;
|
|
149
|
+
}
|
|
150
|
+
})();
|
|
151
|
+
return inFlight;
|
|
152
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker-side harness guard, run right before a provider CLI starts. The server
|
|
3
|
+
* already refuses cross-harness models at create and at claim; this is the last
|
|
4
|
+
* line for what the server cannot see: the agent-scope `MODEL_OVERRIDE` fallback
|
|
5
|
+
* (a worker whose harness changed while the override still names the old family),
|
|
6
|
+
* and a task model from an API that predates the claim-time backstop.
|
|
7
|
+
*
|
|
8
|
+
* Worker-safe: no `src/be` imports; judges against the runtime catalog.
|
|
9
|
+
*/
|
|
10
|
+
import { harnessModelMismatch } from "@desplega/model-catalog";
|
|
11
|
+
import { runtimeHarnessSections } from "./runtime-model-catalog";
|
|
12
|
+
|
|
13
|
+
export type SpawnModelDecision =
|
|
14
|
+
| { kind: "ok"; model: string; warning?: string }
|
|
15
|
+
| { kind: "mismatch"; reason: string };
|
|
16
|
+
|
|
17
|
+
/** Same literal as the server's claim-time reason (`harnessMismatchReason`). */
|
|
18
|
+
export function spawnHarnessMismatchReason(model: string, harness: string): string {
|
|
19
|
+
return `[model-harness-mismatch] Model "${model}" does not run on the ${harness} harness of this worker. The task pinned a model from another harness family. Re-create the task with modelTier or with a ${harness} model.`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function guardSpawnModel(input: {
|
|
23
|
+
taskModel: string;
|
|
24
|
+
configModel: string;
|
|
25
|
+
harness: string;
|
|
26
|
+
role: string;
|
|
27
|
+
}): SpawnModelDecision {
|
|
28
|
+
const { taskModel, configModel, harness, role } = input;
|
|
29
|
+
const sections = runtimeHarnessSections();
|
|
30
|
+
if (taskModel) {
|
|
31
|
+
return harnessModelMismatch(taskModel, harness, sections)
|
|
32
|
+
? { kind: "mismatch", reason: spawnHarnessMismatchReason(taskModel, harness) }
|
|
33
|
+
: { kind: "ok", model: taskModel };
|
|
34
|
+
}
|
|
35
|
+
if (configModel && harnessModelMismatch(configModel, harness, sections)) {
|
|
36
|
+
return {
|
|
37
|
+
kind: "ok",
|
|
38
|
+
model: "",
|
|
39
|
+
warning: `[${role}] MODEL_OVERRIDE ${configModel} does not run on the ${harness} harness; using the adapter default`,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return { kind: "ok", model: configModel };
|
|
43
|
+
}
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* the operator can always pick it on the dashboard.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import type { ModelFamily } from "./model-rate-limit-windows";
|
|
15
|
+
|
|
14
16
|
export interface SubscriptionPlan {
|
|
15
17
|
id: string;
|
|
16
18
|
label: string;
|
|
@@ -18,6 +20,8 @@ export interface SubscriptionPlan {
|
|
|
18
20
|
keyType: "CLAUDE_CODE_OAUTH_TOKEN" | "CODEX_OAUTH";
|
|
19
21
|
/** Monthly list price in USD, billed monthly. */
|
|
20
22
|
monthlyUsd: number;
|
|
23
|
+
/** Model families the plan cannot run. Absent means the plan runs every family. */
|
|
24
|
+
excludedModelFamilies?: readonly ModelFamily[];
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
/** List prices from claude.com/pricing and chatgpt.com/pricing, checked 2026-09-25. */
|
|
@@ -42,6 +46,9 @@ export const SUBSCRIPTION_PLANS: readonly SubscriptionPlan[] = [
|
|
|
42
46
|
label: "Claude Team, standard seat",
|
|
43
47
|
keyType: "CLAUDE_CODE_OAUTH_TOKEN",
|
|
44
48
|
monthlyUsd: 25,
|
|
49
|
+
// The CLI rejects Fable on this seat with `errorCode: "credits_required"`
|
|
50
|
+
// ("Fable 5.1 requires usage credits"), observed 2026-09-30.
|
|
51
|
+
excludedModelFamilies: ["fable"],
|
|
45
52
|
},
|
|
46
53
|
{
|
|
47
54
|
id: "claude_team_premium",
|
|
@@ -74,6 +81,21 @@ export function isSubscriptionPlanId(id: string): boolean {
|
|
|
74
81
|
return SUBSCRIPTION_PLANS.some((plan) => plan.id === id);
|
|
75
82
|
}
|
|
76
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Whether a key on `plan` can run `family`. Fails open: a null plan, a plan
|
|
86
|
+
* id the map does not know, or an undefined family returns true. Returns
|
|
87
|
+
* false only when the plan record lists the family in `excludedModelFamilies`.
|
|
88
|
+
*/
|
|
89
|
+
export function planAllowsModelFamily(
|
|
90
|
+
plan: string | null | undefined,
|
|
91
|
+
family: ModelFamily | undefined,
|
|
92
|
+
): boolean {
|
|
93
|
+
if (!plan || !family) return true;
|
|
94
|
+
const record = SUBSCRIPTION_PLANS.find((p) => p.id === plan);
|
|
95
|
+
if (!record?.excludedModelFamilies) return true;
|
|
96
|
+
return !record.excludedModelFamilies.includes(family);
|
|
97
|
+
}
|
|
98
|
+
|
|
77
99
|
/**
|
|
78
100
|
* Map the `chatgpt_plan_type` JWT claim to a plan id. The claim says `pro`
|
|
79
101
|
* for both Pro tiers, so `pro` maps to the 20x plan (the operator can change
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { WorkflowDefinition } from "../types";
|
|
2
|
+
import type { ApprovalOutcome, ResolvedApproval } from "./executors/base";
|
|
3
|
+
import type { ExecutorRegistry } from "./executors/registry";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Step output and output port for a step whose approval request resolved.
|
|
7
|
+
*
|
|
8
|
+
* Both resume paths (the live `approval.resolved` event and the recovery sweep)
|
|
9
|
+
* go through here so they cannot disagree. A `human-in-the-loop` step gets its
|
|
10
|
+
* own output on the port named after the status. An executor that parked on the
|
|
11
|
+
* same approval machinery (`system-one-decision` with `humanReview`) shapes its
|
|
12
|
+
* own output from what it stored before it parked.
|
|
13
|
+
*/
|
|
14
|
+
export function shapeApprovalResolution(
|
|
15
|
+
registry: ExecutorRegistry,
|
|
16
|
+
definition: Pick<WorkflowDefinition, "nodes">,
|
|
17
|
+
nodeId: string,
|
|
18
|
+
parkedOutput: unknown,
|
|
19
|
+
approval: ResolvedApproval,
|
|
20
|
+
): ApprovalOutcome {
|
|
21
|
+
const node = definition.nodes.find((candidate) => candidate.id === nodeId);
|
|
22
|
+
if (node && registry.has(node.type)) {
|
|
23
|
+
const shaped = registry.get(node.type).resolveApproval(parkedOutput, approval);
|
|
24
|
+
if (shaped) return shaped;
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
output: {
|
|
28
|
+
requestId: approval.requestId,
|
|
29
|
+
status: approval.status,
|
|
30
|
+
responses: approval.responses,
|
|
31
|
+
},
|
|
32
|
+
nextPort:
|
|
33
|
+
approval.status === "timeout"
|
|
34
|
+
? "timeout"
|
|
35
|
+
: approval.status === "rejected"
|
|
36
|
+
? "rejected"
|
|
37
|
+
: "approved",
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -6,6 +6,10 @@ import type {
|
|
|
6
6
|
WorkflowPatch,
|
|
7
7
|
} from "../types";
|
|
8
8
|
import type { ExecutorRegistry } from "./executors/registry";
|
|
9
|
+
import {
|
|
10
|
+
systemOneRetryViolations,
|
|
11
|
+
systemOneStaticShapeViolations,
|
|
12
|
+
} from "./executors/system-one-decision";
|
|
9
13
|
|
|
10
14
|
/** Extract all target node IDs from a node's `next` field */
|
|
11
15
|
export function getNextTargets(next: string | string[] | Record<string, string>): string[] {
|
|
@@ -206,6 +210,9 @@ export function validateDefinition(
|
|
|
206
210
|
`Node "${node.id}" human-in-the-loop config.questions must be an array or one exact {{interpolation}} token`,
|
|
207
211
|
);
|
|
208
212
|
}
|
|
213
|
+
// A system-one-decision node's question ids and return types are static, and it never carries
|
|
214
|
+
// an engine retry policy (it retries transient transport errors itself).
|
|
215
|
+
errors.push(...systemOneStaticShapeViolations(node), ...systemOneRetryViolations(node));
|
|
209
216
|
if (node.type === "foreach") {
|
|
210
217
|
validateForeachNode(node, errors);
|
|
211
218
|
// A legacy `#` id may stay editable as a NORMAL node, but never as a foreach
|
package/src/workflows/engine.ts
CHANGED
|
@@ -28,10 +28,16 @@ import { shouldSkipCooldown } from "./cooldown";
|
|
|
28
28
|
import { findEntryNodes, getNextTargets, getSuccessors, resolveValidationPort } from "./definition";
|
|
29
29
|
import type { AsyncExecutorResult } from "./executors/base";
|
|
30
30
|
import type { ExecutorRegistry } from "./executors/registry";
|
|
31
|
+
import {
|
|
32
|
+
SYSTEM_ONE_DECISION_NODE_TYPE,
|
|
33
|
+
systemOneRetryViolations,
|
|
34
|
+
systemOneUnresolvedError,
|
|
35
|
+
} from "./executors/system-one-decision";
|
|
31
36
|
import { FOREACH_TERMINAL_STEP_STATUSES, resolveForeachParent } from "./foreach-join";
|
|
32
37
|
import { getSecretInputKeys, redactSecretsForStorage, resolveInputs } from "./input";
|
|
33
38
|
import { validateJsonSchema } from "./json-schema-validator";
|
|
34
39
|
import { getMaxWorkflowStepsPerRun } from "./limits";
|
|
40
|
+
import { findWorkflowReadinessProblems, formatReadinessRunError } from "./readiness";
|
|
35
41
|
import { deepInterpolate } from "./template";
|
|
36
42
|
import { runStepValidation, type ValidationRunResult } from "./validation";
|
|
37
43
|
|
|
@@ -149,6 +155,18 @@ export async function startWorkflowExecution(
|
|
|
149
155
|
triggerType: options.triggerType ?? "manual",
|
|
150
156
|
});
|
|
151
157
|
|
|
158
|
+
// An executor that cannot run (for example a system-one-decision node with no API key) fails
|
|
159
|
+
// the run here, before any node has side effects, instead of partway through.
|
|
160
|
+
const notReady = await findWorkflowReadinessProblems(workflow.definition, registry);
|
|
161
|
+
if (notReady.length > 0) {
|
|
162
|
+
await updateWorkflowRun(runId, {
|
|
163
|
+
status: "failed",
|
|
164
|
+
error: formatReadinessRunError(notReady),
|
|
165
|
+
finishedAt: new Date().toISOString(),
|
|
166
|
+
});
|
|
167
|
+
return runId;
|
|
168
|
+
}
|
|
169
|
+
|
|
152
170
|
// Resolve inputs and merge into initial context
|
|
153
171
|
const ctx: Record<string, unknown> = { trigger: triggerData };
|
|
154
172
|
if (options.requestedByUserId) {
|
|
@@ -744,6 +762,15 @@ async function runClaimedStep(
|
|
|
744
762
|
// 3. Get executor
|
|
745
763
|
const executor = registry.get(node.type);
|
|
746
764
|
|
|
765
|
+
// 3a. A system-one-decision node retries transient transport errors itself (config.maxRetries).
|
|
766
|
+
// Engine retries are not status-aware, so a policy here would re-send rejected
|
|
767
|
+
// requests and multiply paid attempts. Fail before any request is built.
|
|
768
|
+
const systemOneRetryProblems = systemOneRetryViolations(node);
|
|
769
|
+
if (systemOneRetryProblems.length > 0) {
|
|
770
|
+
await checkpointStepFailure(runId, stepId, systemOneRetryProblems.join("; "), 0);
|
|
771
|
+
return { outcome: "failed", successors: [] };
|
|
772
|
+
}
|
|
773
|
+
|
|
747
774
|
// 3b. Build local interpolation context from explicit inputs mapping
|
|
748
775
|
const interpolationCtx = buildNodeInterpolationCtx(node, ctx);
|
|
749
776
|
|
|
@@ -765,6 +792,7 @@ async function runClaimedStep(
|
|
|
765
792
|
value: interpolatedValue,
|
|
766
793
|
unresolved,
|
|
767
794
|
scriptBodyUnresolved,
|
|
795
|
+
strictUnresolved,
|
|
768
796
|
} = interpolateNodeConfig(node, interpolationCtx);
|
|
769
797
|
const interpolatedConfig = interpolatedValue as Record<string, unknown>;
|
|
770
798
|
const executionCtx: Record<string, unknown> = { ...ctx, ...interpolationCtx };
|
|
@@ -775,6 +803,17 @@ async function runClaimedStep(
|
|
|
775
803
|
return { outcome: "failed", successors: [] };
|
|
776
804
|
}
|
|
777
805
|
|
|
806
|
+
// A paid, non-idempotent call must not go out with a blanked field.
|
|
807
|
+
if (strictUnresolved && strictUnresolved.length > 0) {
|
|
808
|
+
await checkpointStepFailure(
|
|
809
|
+
runId,
|
|
810
|
+
stepId,
|
|
811
|
+
systemOneUnresolvedError(node.id, strictUnresolved),
|
|
812
|
+
0,
|
|
813
|
+
);
|
|
814
|
+
return { outcome: "failed", successors: [] };
|
|
815
|
+
}
|
|
816
|
+
|
|
778
817
|
if (unresolved.length > 0) {
|
|
779
818
|
console.warn(
|
|
780
819
|
`[workflow] Step ${node.id}: unresolved interpolation tokens: ${unresolved.join(", ")}`,
|
|
@@ -1063,7 +1102,36 @@ function buildScriptBodyCtx(
|
|
|
1063
1102
|
export function interpolateNodeConfig(
|
|
1064
1103
|
node: Pick<WorkflowNode, "type" | "config" | "inputs">,
|
|
1065
1104
|
interpolationCtx: Record<string, unknown>,
|
|
1066
|
-
): {
|
|
1105
|
+
): {
|
|
1106
|
+
value: unknown;
|
|
1107
|
+
unresolved: string[];
|
|
1108
|
+
scriptBodyUnresolved?: string[];
|
|
1109
|
+
/** Tokens that must resolve before dispatch; the caller fails the step when non-empty. */
|
|
1110
|
+
strictUnresolved?: string[];
|
|
1111
|
+
} {
|
|
1112
|
+
if (node.type === SYSTEM_ONE_DECISION_NODE_TYPE) {
|
|
1113
|
+
// `state` and `questions` keep a whole-token reference's JSON type (an object
|
|
1114
|
+
// stays an object; string interpolation would flatten it to JSON text). The
|
|
1115
|
+
// resolved value is data, never re-interpolated, so a literal `{{` inside a
|
|
1116
|
+
// lead's text is not read as another template. Every other field, and every
|
|
1117
|
+
// unresolved token anywhere in the config, is strict: no blank reaches the API.
|
|
1118
|
+
const { state, questions, ...rest } = node.config;
|
|
1119
|
+
const restResult = deepInterpolate(rest, interpolationCtx);
|
|
1120
|
+
const dynamic: Record<string, unknown> = {};
|
|
1121
|
+
if (Object.hasOwn(node.config, "state")) dynamic.state = state;
|
|
1122
|
+
if (Object.hasOwn(node.config, "questions")) dynamic.questions = questions;
|
|
1123
|
+
const dynamicResult = deepInterpolate(dynamic, interpolationCtx, { preserveRawTokens: true });
|
|
1124
|
+
const unresolved = [...restResult.unresolved, ...dynamicResult.unresolved];
|
|
1125
|
+
return {
|
|
1126
|
+
value: {
|
|
1127
|
+
...(restResult.value as Record<string, unknown>),
|
|
1128
|
+
...(dynamicResult.value as Record<string, unknown>),
|
|
1129
|
+
},
|
|
1130
|
+
unresolved,
|
|
1131
|
+
strictUnresolved: unresolved,
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1067
1135
|
if (node.type === "foreach" && Object.hasOwn(node.config, "over")) {
|
|
1068
1136
|
const { over, body, ...configWithoutOverAndBody } = node.config;
|
|
1069
1137
|
const configResult = deepInterpolate(configWithoutOverAndBody, interpolationCtx);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { explicitModelErrorForAgent } from "../../be/model-validation";
|
|
2
3
|
import { applyPreTaskCreate } from "../../extensions/apply-task-create";
|
|
3
4
|
import { workflowContextKey } from "../../tasks/context-key";
|
|
4
5
|
import { TaskCreationBlockedError } from "../../tasks/errors";
|
|
5
|
-
import { withSiblingAwareness } from "../../tasks/sibling-awareness";
|
|
6
6
|
import type { ExecutorMeta } from "../../types";
|
|
7
7
|
import {
|
|
8
8
|
FollowUpConfigSchema,
|
|
@@ -30,6 +30,8 @@ export const AgentTaskConfigSchema = z.object({
|
|
|
30
30
|
vcsRepo: z.string().min(1).optional(),
|
|
31
31
|
model: z.string().min(1).optional(),
|
|
32
32
|
modelTier: ModelTierSchema.optional(),
|
|
33
|
+
/** Accept a `model` the catalog does not list (a custom id). Without it an unknown id fails the node. */
|
|
34
|
+
allowCustomModel: z.boolean().optional(),
|
|
33
35
|
effort: ReasoningEffortSchema.optional(),
|
|
34
36
|
parentTaskId: z.string().uuid().optional(),
|
|
35
37
|
requestedByUserId: z.string().optional(),
|
|
@@ -98,6 +100,12 @@ export class AgentTaskExecutor extends BaseExecutor<
|
|
|
98
100
|
}
|
|
99
101
|
|
|
100
102
|
// 3. Create the task (config is already deep-interpolated by the engine)
|
|
103
|
+
const modelError = await explicitModelErrorForAgent({
|
|
104
|
+
model: splitLegacyModelAlias({ model: config.model, modelTier: config.modelTier }).model,
|
|
105
|
+
allowCustomModel: config.allowCustomModel,
|
|
106
|
+
agentId: config.agentId,
|
|
107
|
+
});
|
|
108
|
+
if (modelError) return { status: "failed", error: modelError };
|
|
101
109
|
const preCreate = await applyPreTaskCreate({
|
|
102
110
|
description: config.template,
|
|
103
111
|
options: {
|
|
@@ -128,15 +136,17 @@ export class AgentTaskExecutor extends BaseExecutor<
|
|
|
128
136
|
contextKey: workflowContextKey({ workflowRunId: meta.runId }),
|
|
129
137
|
},
|
|
130
138
|
origin: "workflow",
|
|
139
|
+
allowCustomModel: config.allowCustomModel,
|
|
131
140
|
});
|
|
132
141
|
if (preCreate.kind === "blocked") {
|
|
133
142
|
throw new TaskCreationBlockedError(preCreate.reason, preCreate.extension);
|
|
134
143
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
144
|
+
// No sibling awareness: every task of a run shares this contextKey, so its
|
|
145
|
+
// siblings are the engine's own parallel steps, not new user input. The
|
|
146
|
+
// block handed each child its siblings' task ids, and one child completed
|
|
147
|
+
// a sibling's task with its own report. It would also wire parentTaskId to
|
|
148
|
+
// a concurrent same-agent sibling's session.
|
|
149
|
+
const task = await db.createTaskExtended(preCreate.description, preCreate.options);
|
|
140
150
|
|
|
141
151
|
// 4. Return async result — engine will pause the workflow
|
|
142
152
|
return {
|
|
@@ -10,6 +10,34 @@ export interface ExecutorDependencies {
|
|
|
10
10
|
interpolate: (template: string, ctx: Record<string, unknown>) => string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
// ─── Readiness ─────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
export interface ExecutorReadinessNode {
|
|
16
|
+
id: string;
|
|
17
|
+
config: Record<string, unknown>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ExecutorReadinessProblem {
|
|
21
|
+
/** Nodes that hit this problem. */
|
|
22
|
+
nodeIds: string[];
|
|
23
|
+
message: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ─── Approval resolution ───────────────────────────────────
|
|
27
|
+
|
|
28
|
+
/** A resolved approval request, as the resume and recovery paths see it. */
|
|
29
|
+
export interface ResolvedApproval {
|
|
30
|
+
requestId: string;
|
|
31
|
+
status: "approved" | "rejected" | "timeout";
|
|
32
|
+
responses: Record<string, unknown> | null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The step output and output port an executor chooses once its approval resolves. */
|
|
36
|
+
export interface ApprovalOutcome {
|
|
37
|
+
output: unknown;
|
|
38
|
+
nextPort: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
13
41
|
// ─── Input / Result Types ──────────────────────────────────
|
|
14
42
|
|
|
15
43
|
export interface ExecutorInput {
|
|
@@ -47,6 +75,31 @@ export abstract class BaseExecutor<
|
|
|
47
75
|
|
|
48
76
|
constructor(protected readonly deps: ExecutorDependencies) {}
|
|
49
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Can this executor run the given nodes right now? Return one problem per
|
|
80
|
+
* distinct missing prerequisite (for example a credential), or an empty array.
|
|
81
|
+
* The engine asks before a run starts, and authoring surfaces ask after a
|
|
82
|
+
* save, so a missing prerequisite surfaces before any node has side effects.
|
|
83
|
+
* Reads raw (pre-interpolation) node config. Must not call external services
|
|
84
|
+
* and must not put secret values in a message.
|
|
85
|
+
*/
|
|
86
|
+
async checkReadiness(
|
|
87
|
+
_nodes: readonly ExecutorReadinessNode[],
|
|
88
|
+
): Promise<ExecutorReadinessProblem[]> {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* An executor that parks its step on a human-in-the-loop approval decides the
|
|
94
|
+
* step's output and port once a person answers. `parkedOutput` is the step
|
|
95
|
+
* output it stored before it parked. Return null to keep the default: the
|
|
96
|
+
* human-in-the-loop output (`{ requestId, status, responses }`) on the port
|
|
97
|
+
* named after the status.
|
|
98
|
+
*/
|
|
99
|
+
resolveApproval(_parkedOutput: unknown, _approval: ResolvedApproval): ApprovalOutcome | null {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
50
103
|
/**
|
|
51
104
|
* Validate config, execute, validate output.
|
|
52
105
|
* Catches Zod validation errors at both boundaries.
|
|
@@ -59,17 +59,22 @@ const QuestionSchema = z.discriminatedUnion("type", [
|
|
|
59
59
|
}),
|
|
60
60
|
]);
|
|
61
61
|
|
|
62
|
-
const ApproverConfigSchema = z.object({
|
|
62
|
+
export const ApproverConfigSchema = z.object({
|
|
63
63
|
users: z.array(z.string()).optional(),
|
|
64
64
|
roles: z.array(z.string()).optional(),
|
|
65
65
|
policy: z.union([z.literal("any"), z.literal("all"), z.object({ min: z.number().int().min(1) })]),
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
const NotificationConfigSchema = z.object({
|
|
68
|
+
export const NotificationConfigSchema = z.object({
|
|
69
69
|
channel: z.enum(["slack", "email"]),
|
|
70
70
|
target: z.string(),
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
+
export const HITLTimeoutSchema = z.object({
|
|
74
|
+
seconds: z.number().int().min(1),
|
|
75
|
+
action: z.literal("reject"),
|
|
76
|
+
});
|
|
77
|
+
|
|
73
78
|
type HITLQuestion = z.infer<typeof QuestionSchema>;
|
|
74
79
|
|
|
75
80
|
/** Exactly one `{{path}}` token: the dynamic-questions form. */
|
|
@@ -103,12 +108,7 @@ const HITLConfigSchema = z.object({
|
|
|
103
108
|
),
|
|
104
109
|
]),
|
|
105
110
|
approvers: ApproverConfigSchema,
|
|
106
|
-
timeout:
|
|
107
|
-
.object({
|
|
108
|
-
seconds: z.number().int().min(1),
|
|
109
|
-
action: z.literal("reject"),
|
|
110
|
-
})
|
|
111
|
-
.optional(),
|
|
111
|
+
timeout: HITLTimeoutSchema.optional(),
|
|
112
112
|
notifications: z.array(NotificationConfigSchema).optional(),
|
|
113
113
|
});
|
|
114
114
|
|
|
@@ -13,6 +13,7 @@ export { RawLlmExecutor } from "./raw-llm";
|
|
|
13
13
|
export { createExecutorRegistry, ExecutorRegistry } from "./registry";
|
|
14
14
|
export { ScriptExecutor } from "./script";
|
|
15
15
|
export { SwarmScriptExecutor } from "./swarm-script";
|
|
16
|
+
export { SystemOneDecisionExecutor } from "./system-one-decision";
|
|
16
17
|
export { ValidateExecutor } from "./validate";
|
|
17
18
|
export { VcsExecutor } from "./vcs";
|
|
18
19
|
export { WaitExecutor } from "./wait";
|
|
@@ -9,6 +9,7 @@ import { PropertyMatchExecutor } from "./property-match";
|
|
|
9
9
|
import { RawLlmExecutor } from "./raw-llm";
|
|
10
10
|
import { ScriptExecutor } from "./script";
|
|
11
11
|
import { SwarmScriptExecutor } from "./swarm-script";
|
|
12
|
+
import { SystemOneDecisionExecutor } from "./system-one-decision";
|
|
12
13
|
import { ValidateExecutor } from "./validate";
|
|
13
14
|
import { VcsExecutor } from "./vcs";
|
|
14
15
|
import { WaitExecutor } from "./wait";
|
|
@@ -69,6 +70,7 @@ export function createExecutorRegistry(deps: ExecutorDependencies): ExecutorRegi
|
|
|
69
70
|
registry.register(new CodeMatchExecutor(deps));
|
|
70
71
|
registry.register(new NotifyExecutor(deps));
|
|
71
72
|
registry.register(new RawLlmExecutor(deps));
|
|
73
|
+
registry.register(new SystemOneDecisionExecutor(deps));
|
|
72
74
|
registry.register(new ScriptExecutor(deps));
|
|
73
75
|
registry.register(new SwarmScriptExecutor(deps));
|
|
74
76
|
registry.register(new VcsExecutor(deps));
|