@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
package/src/http/stats.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
getAgentStatusCounts,
|
|
5
5
|
getAllLogs,
|
|
6
6
|
getAllServices,
|
|
7
7
|
getConcurrentContext,
|
|
@@ -15,6 +15,7 @@ import { getDbRetentionStats } from "../be/db-retention";
|
|
|
15
15
|
import { isSteeringEnabled } from "../be/steering";
|
|
16
16
|
import type { AgentLog } from "../types";
|
|
17
17
|
import { AgentLogSchema, ScheduledTaskSchema, ServiceSchema } from "../types";
|
|
18
|
+
import { isEnvFlagEnabled } from "../utils/env-flag";
|
|
18
19
|
import { isMultiRuntimeEnabled } from "../utils/multi-runtime";
|
|
19
20
|
import { resolveHttpFavoriteOwner } from "./favorite-owner";
|
|
20
21
|
import { route } from "./route-def";
|
|
@@ -51,6 +52,7 @@ const DashboardStatsSchema = z.object({
|
|
|
51
52
|
tasks: TaskCountsSchema,
|
|
52
53
|
steeringEnabled: z.boolean(),
|
|
53
54
|
multiRuntimeEnabled: z.boolean(),
|
|
55
|
+
devMode: z.boolean(),
|
|
54
56
|
});
|
|
55
57
|
|
|
56
58
|
/** Mirrors `DbRetentionTableStats` (src/be/db-retention.ts). */
|
|
@@ -247,16 +249,11 @@ export async function handleStats(
|
|
|
247
249
|
}
|
|
248
250
|
|
|
249
251
|
if (getStats.match(req.method, pathSegments)) {
|
|
250
|
-
const agents = await
|
|
252
|
+
const agents = await getAgentStatusCounts();
|
|
251
253
|
const taskStats = await getTaskStats();
|
|
252
254
|
|
|
253
255
|
const stats = {
|
|
254
|
-
agents
|
|
255
|
-
total: agents.length,
|
|
256
|
-
idle: agents.filter((a) => a.status === "idle").length,
|
|
257
|
-
busy: agents.filter((a) => a.status === "busy").length,
|
|
258
|
-
offline: agents.filter((a) => a.status === "offline").length,
|
|
259
|
-
},
|
|
256
|
+
agents,
|
|
260
257
|
tasks: {
|
|
261
258
|
total: taskStats.total,
|
|
262
259
|
unassigned: taskStats.unassigned,
|
|
@@ -271,6 +268,7 @@ export async function handleStats(
|
|
|
271
268
|
// Authenticated home for the steering feature flag — deliberately NOT on
|
|
272
269
|
// the unauthenticated /health endpoint (config must not leak).
|
|
273
270
|
steeringEnabled: isSteeringEnabled(),
|
|
271
|
+
devMode: isEnvFlagEnabled("SWARM_DEV_MODE", false),
|
|
274
272
|
// Same authenticated home, for the same reason. Read through the helper
|
|
275
273
|
// the server itself branches on, and read PER REQUEST rather than
|
|
276
274
|
// captured at module load, so the reported value tracks a `swarm_config`
|
package/src/http/status.ts
CHANGED
|
@@ -37,6 +37,7 @@ import { getEmbeddingProvider } from "../be/memory";
|
|
|
37
37
|
import { getFileStorageProvider } from "../fs/registry";
|
|
38
38
|
import { getSlackConfiguration } from "../slack/config";
|
|
39
39
|
import { getSlackConnectionState } from "../slack/connection-state";
|
|
40
|
+
import { isTelemetryEnabled } from "../telemetry";
|
|
40
41
|
import { type AgentCredStatus, AutomationIntegrationIdSchema, ProviderNameSchema } from "../types";
|
|
41
42
|
import { route } from "./route-def";
|
|
42
43
|
import { json, jsonError } from "./utils";
|
|
@@ -119,6 +120,16 @@ export const StatusActivitySchema = z.object({
|
|
|
119
120
|
});
|
|
120
121
|
export type StatusActivity = z.infer<typeof StatusActivitySchema>;
|
|
121
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Effective telemetry opt-out state (`ANONYMIZED_TELEMETRY`, on by default).
|
|
125
|
+
* The dashboard reads it to stop its own browser-side notification events when
|
|
126
|
+
* the operator opted out.
|
|
127
|
+
*/
|
|
128
|
+
export const StatusTelemetrySchema = z.object({
|
|
129
|
+
enabled: z.boolean(),
|
|
130
|
+
});
|
|
131
|
+
export type StatusTelemetry = z.infer<typeof StatusTelemetrySchema>;
|
|
132
|
+
|
|
122
133
|
export const StatusAgentFsSchema = z.object({
|
|
123
134
|
configured: z.boolean(),
|
|
124
135
|
base_url: z.string().nullable(),
|
|
@@ -171,6 +182,8 @@ export const StatusResponseSchema = z.object({
|
|
|
171
182
|
automations: z.array(AutomationStatusSchema),
|
|
172
183
|
/** Phase 2: rolled-up health for the always-on header badge. */
|
|
173
184
|
health: StatusHealthSchema,
|
|
185
|
+
/** Additive: absent on servers that predate it, so consumers treat missing as enabled. */
|
|
186
|
+
telemetry: StatusTelemetrySchema.optional(),
|
|
174
187
|
});
|
|
175
188
|
export type StatusResponse = z.infer<typeof StatusResponseSchema>;
|
|
176
189
|
|
|
@@ -692,6 +705,7 @@ export async function buildStatusPayload(): Promise<StatusResponse> {
|
|
|
692
705
|
},
|
|
693
706
|
automations,
|
|
694
707
|
health: computeHealth(setup),
|
|
708
|
+
telemetry: { enabled: isTelemetryEnabled() },
|
|
695
709
|
};
|
|
696
710
|
}
|
|
697
711
|
|
|
@@ -721,7 +735,7 @@ const getStatus = route({
|
|
|
721
735
|
pattern: ["status"],
|
|
722
736
|
summary: "Identity + setup readiness + live activity for the swarm dashboard",
|
|
723
737
|
description:
|
|
724
|
-
"Single source of truth consumed by the UI home page. Identity comes from SWARM_* envs; setup milestones each emit `unverified | configured | verified`; automations report `running | needs_setup` from the same runtime preflight used at dispatch; activity counts agents alive in the last 5 min and tasks created in the last 24h; agent_fs reports whether AGENT_FS_API_URL is set.",
|
|
738
|
+
"Single source of truth consumed by the UI home page. Identity comes from SWARM_* envs; setup milestones each emit `unverified | configured | verified`; automations report `running | needs_setup` from the same runtime preflight used at dispatch; activity counts agents alive in the last 5 min and tasks created in the last 24h; agent_fs reports whether AGENT_FS_API_URL is set; telemetry reports the effective ANONYMIZED_TELEMETRY opt-out state.",
|
|
725
739
|
tags: ["Status"],
|
|
726
740
|
responses: {
|
|
727
741
|
200: { description: "Status payload", schema: StatusResponseSchema },
|
package/src/http/tasks.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
pauseTask,
|
|
28
28
|
promoteDraftTask,
|
|
29
29
|
resumeTask,
|
|
30
|
+
settleSupersededTaskDependents,
|
|
30
31
|
supersedeTask,
|
|
31
32
|
updateAgentStatusFromCapacity,
|
|
32
33
|
updateTaskClaudeSessionId,
|
|
@@ -34,6 +35,7 @@ import {
|
|
|
34
35
|
updateTaskTitle,
|
|
35
36
|
updateTaskVcs,
|
|
36
37
|
} from "../be/db";
|
|
38
|
+
import { explicitModelErrorForAgent } from "../be/model-validation";
|
|
37
39
|
import {
|
|
38
40
|
getTaskSteeringFields,
|
|
39
41
|
markSteeringUndeliverable,
|
|
@@ -102,6 +104,9 @@ const AgentTaskSummarySchema = AgentTaskSchema.pick({
|
|
|
102
104
|
scheduleId: true,
|
|
103
105
|
model: true,
|
|
104
106
|
modelTier: true,
|
|
107
|
+
resolvedModel: true,
|
|
108
|
+
modelSource: true,
|
|
109
|
+
modelAlias: true,
|
|
105
110
|
effort: true,
|
|
106
111
|
provider: true,
|
|
107
112
|
requestedByUserId: true,
|
|
@@ -264,8 +269,18 @@ const createTask = route({
|
|
|
264
269
|
outputSchema: z.record(z.string(), z.unknown()).optional(),
|
|
265
270
|
contextKey: z.string().optional(),
|
|
266
271
|
requestedByUserId: z.string().optional(),
|
|
267
|
-
model: z
|
|
272
|
+
model: z
|
|
273
|
+
.string()
|
|
274
|
+
.optional()
|
|
275
|
+
.describe(
|
|
276
|
+
"Concrete model override for this task, interpreted by the assignee's harness/provider. The model must run on the assignee's harness (an Anthropic model on a Claude agent, an OpenAI model on a Codex agent); a mismatch is rejected.",
|
|
277
|
+
),
|
|
268
278
|
modelTier: ModelTierSchema.optional(),
|
|
279
|
+
/**
|
|
280
|
+
* Accept a `model` the catalog does not list. Without it an unknown id is a 400; with it
|
|
281
|
+
* the id is stored as given (a fresh launch, a private deployment).
|
|
282
|
+
*/
|
|
283
|
+
allowCustomModel: z.boolean().optional(),
|
|
269
284
|
effort: ReasoningEffortSchema.optional(),
|
|
270
285
|
/**
|
|
271
286
|
* Create in `draft` status instead of the normal pending/unassigned/offered
|
|
@@ -287,7 +302,10 @@ const createTask = route({
|
|
|
287
302
|
}),
|
|
288
303
|
responses: {
|
|
289
304
|
201: { description: "Task created", schema: AgentTaskSchema },
|
|
290
|
-
400: {
|
|
305
|
+
400: {
|
|
306
|
+
description:
|
|
307
|
+
"Validation error, an unknown `model` (set `allowCustomModel` to store a custom id), or agentId/offeredTo targets an extension identity",
|
|
308
|
+
},
|
|
291
309
|
422: {
|
|
292
310
|
description: "Task creation blocked by an extension",
|
|
293
311
|
schema: z.object({
|
|
@@ -358,6 +376,7 @@ const cancelTaskRoute = route({
|
|
|
358
376
|
responses: {
|
|
359
377
|
200: { description: "Task cancelled", schema: TaskActionResultSchema },
|
|
360
378
|
400: { description: "Cannot cancel terminal task" },
|
|
379
|
+
403: { description: "Agent caller is neither a lead nor the task creator" },
|
|
361
380
|
404: { description: "Task not found" },
|
|
362
381
|
},
|
|
363
382
|
});
|
|
@@ -524,6 +543,7 @@ const updateTaskProgressRoute = route({
|
|
|
524
543
|
body: z.object({ progress: z.string().min(1) }),
|
|
525
544
|
responses: {
|
|
526
545
|
200: { description: "Progress updated", schema: z.object({ success: z.literal(true) }) },
|
|
546
|
+
403: { description: "Task is assigned to another agent" },
|
|
527
547
|
404: { description: "Task not found" },
|
|
528
548
|
},
|
|
529
549
|
});
|
|
@@ -734,6 +754,24 @@ async function canActOnOwnTask(
|
|
|
734
754
|
return can({ principal, verb: "task.action.own", resource, source: "http" }).allow;
|
|
735
755
|
}
|
|
736
756
|
|
|
757
|
+
/**
|
|
758
|
+
* Principal for task writes (progress, cancel). Workers share the swarm API
|
|
759
|
+
* key, so on an operator bearer the X-Agent-ID header is the caller's
|
|
760
|
+
* identity, as in POST /api/tasks/{id}/finish. A keyed call without it (the
|
|
761
|
+
* runner wrapper, the dashboard) stays the operator.
|
|
762
|
+
*/
|
|
763
|
+
async function resolveTaskWritePrincipal(
|
|
764
|
+
req: IncomingMessage,
|
|
765
|
+
myAgentId: string | undefined,
|
|
766
|
+
): Promise<RbacPrincipal> {
|
|
767
|
+
const auth = getRequestAuth(req);
|
|
768
|
+
if (auth?.kind === "user") return { kind: "user", userId: auth.userId };
|
|
769
|
+
const agentId = auth?.kind === "agent" ? auth.agentId : myAgentId;
|
|
770
|
+
if (!agentId) return { kind: "operator" };
|
|
771
|
+
const agent = await getAgentById(agentId);
|
|
772
|
+
return { kind: "agent", agentId, isLead: agent?.isLead === true };
|
|
773
|
+
}
|
|
774
|
+
|
|
737
775
|
// ─── Handler ─────────────────────────────────────────────────────────────────
|
|
738
776
|
|
|
739
777
|
export async function handleTasks(
|
|
@@ -860,6 +898,17 @@ export async function handleTasks(
|
|
|
860
898
|
if (lead) defaultAgentId = lead.id;
|
|
861
899
|
}
|
|
862
900
|
|
|
901
|
+
const modelError = await explicitModelErrorForAgent({
|
|
902
|
+
model: splitLegacyModelAlias({ model: parsed.body.model, modelTier: parsed.body.modelTier })
|
|
903
|
+
.model,
|
|
904
|
+
allowCustomModel: parsed.body.allowCustomModel,
|
|
905
|
+
agentId: defaultAgentId,
|
|
906
|
+
});
|
|
907
|
+
if (modelError) {
|
|
908
|
+
jsonError(res, modelError, 400);
|
|
909
|
+
return true;
|
|
910
|
+
}
|
|
911
|
+
|
|
863
912
|
const parentTask = parsed.body.parentTaskId
|
|
864
913
|
? await getTaskById(parsed.body.parentTaskId)
|
|
865
914
|
: null;
|
|
@@ -916,7 +965,7 @@ export async function handleTasks(
|
|
|
916
965
|
}),
|
|
917
966
|
effort: parsed.body.effort,
|
|
918
967
|
},
|
|
919
|
-
{ origin: "rest" },
|
|
968
|
+
{ origin: "rest", allowCustomModel: parsed.body.allowCustomModel },
|
|
920
969
|
);
|
|
921
970
|
|
|
922
971
|
ensure({
|
|
@@ -1011,6 +1060,22 @@ export async function handleTasks(
|
|
|
1011
1060
|
return true;
|
|
1012
1061
|
}
|
|
1013
1062
|
|
|
1063
|
+
// Humans may cancel any task. Agents get the MCP cancel-task policy:
|
|
1064
|
+
// lead or task creator.
|
|
1065
|
+
const principal = await resolveTaskWritePrincipal(req, myAgentId);
|
|
1066
|
+
if (
|
|
1067
|
+
principal.kind === "agent" &&
|
|
1068
|
+
!can({
|
|
1069
|
+
principal,
|
|
1070
|
+
verb: "task.cancel.any",
|
|
1071
|
+
resource: { kind: "task", taskId: task.id, creatorAgentId: task.creatorAgentId },
|
|
1072
|
+
source: "http",
|
|
1073
|
+
}).allow
|
|
1074
|
+
) {
|
|
1075
|
+
jsonError(res, "Only the lead or task creator can cancel tasks.", 403);
|
|
1076
|
+
return true;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1014
1079
|
if (isTerminalTaskStatus(task.status)) {
|
|
1015
1080
|
jsonError(res, `Cannot cancel task with status '${task.status}'`, 400);
|
|
1016
1081
|
return true;
|
|
@@ -1345,14 +1410,31 @@ export async function handleTasks(
|
|
|
1345
1410
|
if (updateTaskProgressRoute.match(req.method, pathSegments)) {
|
|
1346
1411
|
const parsed = await updateTaskProgressRoute.parse(req, res, pathSegments, queryParams);
|
|
1347
1412
|
if (!parsed) return true;
|
|
1348
|
-
const
|
|
1413
|
+
const principal = await resolveTaskWritePrincipal(req, myAgentId);
|
|
1414
|
+
|
|
1415
|
+
// Check and write in one transaction so a reassignment cannot slip between.
|
|
1416
|
+
const status = await getDbClient().transaction(async (): Promise<200 | 403 | 404> => {
|
|
1417
|
+
const task = await getTaskById(parsed.params.id);
|
|
1418
|
+
if (!task) return 404;
|
|
1419
|
+
const decision = can({
|
|
1420
|
+
principal,
|
|
1421
|
+
verb: "task.progress.write",
|
|
1422
|
+
resource: { kind: "task", taskId: task.id, agentId: task.agentId },
|
|
1423
|
+
source: "http",
|
|
1424
|
+
});
|
|
1425
|
+
if (!decision.allow) return 403;
|
|
1426
|
+
await updateTaskProgress(parsed.params.id, parsed.body.progress);
|
|
1427
|
+
return 200;
|
|
1428
|
+
});
|
|
1349
1429
|
|
|
1350
|
-
if (
|
|
1430
|
+
if (status === 404) {
|
|
1351
1431
|
jsonError(res, "Task not found", 404);
|
|
1352
1432
|
return true;
|
|
1353
1433
|
}
|
|
1354
|
-
|
|
1355
|
-
|
|
1434
|
+
if (status === 403) {
|
|
1435
|
+
jsonError(res, "Task is assigned to another agent", 403);
|
|
1436
|
+
return true;
|
|
1437
|
+
}
|
|
1356
1438
|
updateTaskProgressRoute.respond(res, 200, { success: true });
|
|
1357
1439
|
return true;
|
|
1358
1440
|
}
|
|
@@ -1750,6 +1832,7 @@ export async function handleTasks(
|
|
|
1750
1832
|
// `skipped` covers parent_not_found / lead_not_found edge cases — the
|
|
1751
1833
|
// supersede already landed, so log + roll forward without a resume task.
|
|
1752
1834
|
if (followUp.kind !== "created") {
|
|
1835
|
+
await settleSupersededTaskDependents(parsed.params.id, null);
|
|
1753
1836
|
console.warn(
|
|
1754
1837
|
`[Supersede] Task ${parsed.params.id.slice(0, 8)} superseded but resume creation skipped (${
|
|
1755
1838
|
followUp.kind === "skipped" ? followUp.reason : followUp.kind
|
package/src/http/usage-cache.ts
CHANGED
|
@@ -3,30 +3,71 @@
|
|
|
3
3
|
* `/api/attribution/by-person`). Each open usage page polls them, and every
|
|
4
4
|
* reply aggregates the whole window.
|
|
5
5
|
*
|
|
6
|
-
* A key holds the request filters
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* A key holds only the request filters. An entry is fresh for
|
|
7
|
+
* `USAGE_CACHE_TTL_MS` (the time bucket). Past that it is served as-is while one
|
|
8
|
+
* background load revalidates it, until its total age reaches
|
|
9
|
+
* `USAGE_CACHE_MAX_STALE_MS`; an older or missing entry blocks on a load.
|
|
10
|
+
* Concurrent misses for one key share that load.
|
|
11
|
+
*
|
|
12
|
+
* The key deliberately carries no data version. The old one changed on every
|
|
13
|
+
* new task or session, and the swarm creates one every few seconds, so it
|
|
14
|
+
* almost never hit. The cost is staleness: the first request past the bucket
|
|
15
|
+
* still gets the old value and only starts the refresh, so a new session shows
|
|
16
|
+
* up on the request after that. A client polling once per bucket sees it about
|
|
17
|
+
* one poll later. A credential plan or name change clears the cache.
|
|
11
18
|
*/
|
|
12
19
|
|
|
13
20
|
export const USAGE_CACHE_TTL_MS = 30_000;
|
|
21
|
+
export const USAGE_CACHE_MAX_STALE_MS = 120_000;
|
|
14
22
|
const MAX_ENTRIES = 100;
|
|
15
23
|
|
|
16
|
-
const entries = new Map<string, {
|
|
24
|
+
const entries = new Map<string, { fetchedAt: number; value: unknown }>();
|
|
25
|
+
const loading = new Map<string, Promise<unknown>>();
|
|
26
|
+
// Bumped by `clearUsageCache`, so a load that started before the clear cannot
|
|
27
|
+
// write its (now outdated) result back.
|
|
28
|
+
let generation = 0;
|
|
29
|
+
|
|
30
|
+
function load<T>(key: string, loader: () => Promise<T>): Promise<T> {
|
|
31
|
+
const running = loading.get(key);
|
|
32
|
+
if (running) return running as Promise<T>;
|
|
33
|
+
|
|
34
|
+
const startedIn = generation;
|
|
35
|
+
const promise: Promise<T> = Promise.resolve()
|
|
36
|
+
.then(loader)
|
|
37
|
+
.then((value) => {
|
|
38
|
+
if (startedIn === generation) {
|
|
39
|
+
entries.delete(key);
|
|
40
|
+
// Map order is insertion order, so the first key is the oldest entry.
|
|
41
|
+
if (entries.size >= MAX_ENTRIES) entries.delete(entries.keys().next().value as string);
|
|
42
|
+
entries.set(key, { fetchedAt: Date.now(), value });
|
|
43
|
+
}
|
|
44
|
+
return value;
|
|
45
|
+
})
|
|
46
|
+
.finally(() => {
|
|
47
|
+
if (loading.get(key) === promise) loading.delete(key);
|
|
48
|
+
});
|
|
49
|
+
loading.set(key, promise);
|
|
50
|
+
return promise;
|
|
51
|
+
}
|
|
17
52
|
|
|
18
|
-
export async function cachedUsageReport<T>(key: string,
|
|
19
|
-
const now = Date.now();
|
|
53
|
+
export async function cachedUsageReport<T>(key: string, loader: () => Promise<T>): Promise<T> {
|
|
20
54
|
const hit = entries.get(key);
|
|
21
|
-
if (hit
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
55
|
+
if (hit) {
|
|
56
|
+
const age = Date.now() - hit.fetchedAt;
|
|
57
|
+
if (age < USAGE_CACHE_TTL_MS) return hit.value as T;
|
|
58
|
+
if (age < USAGE_CACHE_MAX_STALE_MS) {
|
|
59
|
+
// Keep serving the stale value; a failed refresh retries on the next request.
|
|
60
|
+
load(key, loader).catch((error) => {
|
|
61
|
+
console.error(`[usage-cache] background refresh failed for ${key.split(":")[0]}:`, error);
|
|
62
|
+
});
|
|
63
|
+
return hit.value as T;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return load(key, loader);
|
|
28
67
|
}
|
|
29
68
|
|
|
30
69
|
export function clearUsageCache(): void {
|
|
70
|
+
generation++;
|
|
31
71
|
entries.clear();
|
|
72
|
+
loading.clear();
|
|
32
73
|
}
|
package/src/http/workflows.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
CooldownConfigSchema,
|
|
22
22
|
InputValueSchema,
|
|
23
23
|
TriggerConfigSchema,
|
|
24
|
+
type WorkflowDefinition,
|
|
24
25
|
WorkflowDefinitionSchema,
|
|
25
26
|
WorkflowEdgeSchema,
|
|
26
27
|
WorkflowNodePatchSchema,
|
|
@@ -36,7 +37,9 @@ import { getExecutorRegistry, startWorkflowExecution } from "../workflows";
|
|
|
36
37
|
import { definitionNodeIds, generateEdges, validateDefinition } from "../workflows/definition";
|
|
37
38
|
import { TriggerSchemaError } from "../workflows/engine";
|
|
38
39
|
import { validateJsonSchema } from "../workflows/json-schema-validator";
|
|
40
|
+
import { workflowModelErrors } from "../workflows/model-validation";
|
|
39
41
|
import { patchWorkflowDefinition } from "../workflows/patch-definition";
|
|
42
|
+
import { workflowSaveWarnings } from "../workflows/readiness";
|
|
40
43
|
import { cancelWorkflowRun, retryFailedRun } from "../workflows/resume";
|
|
41
44
|
import { handleWebhookTrigger, WebhookError } from "../workflows/triggers";
|
|
42
45
|
import { snapshotAndUpdateWorkflow } from "../workflows/version";
|
|
@@ -49,6 +52,14 @@ import { jsonError, parseBody, triggerSchemaErrorResponse } from "./utils";
|
|
|
49
52
|
/** `Workflow` decorated with the caller-scoped favorite flag (always set once `withFavoriteFlags` runs). */
|
|
50
53
|
const WorkflowWithFavoriteSchema = WorkflowSchema.extend({ favorite: z.boolean() });
|
|
51
54
|
|
|
55
|
+
/**
|
|
56
|
+
* `Workflow` returned by a save (create / update / patch). `warnings` is present only when the
|
|
57
|
+
* save succeeded but a node cannot run yet, e.g. a system-one-decision node whose provider key is not configured.
|
|
58
|
+
*/
|
|
59
|
+
const WorkflowSaveResponseSchema = WorkflowSchema.extend({
|
|
60
|
+
warnings: z.array(z.string()).optional(),
|
|
61
|
+
});
|
|
62
|
+
|
|
52
63
|
/** `/api/workflows` slim list item — mirrors `WorkflowSummary` in src/types.ts (no exported schema there). */
|
|
53
64
|
const WorkflowSummarySchema = WorkflowSchema.omit({
|
|
54
65
|
definition: true,
|
|
@@ -85,6 +96,14 @@ const WorkflowRunPageSchema = z.object({
|
|
|
85
96
|
|
|
86
97
|
const SuccessResponseSchema = z.object({ success: z.literal(true) });
|
|
87
98
|
|
|
99
|
+
/** Adds `warnings` to a just-saved workflow when a node in it cannot run yet. */
|
|
100
|
+
async function withSaveWarnings<T extends { definition: WorkflowDefinition }>(
|
|
101
|
+
workflow: T,
|
|
102
|
+
): Promise<T & { warnings?: string[] }> {
|
|
103
|
+
const warnings = await workflowSaveWarnings(workflow.definition, getExecutorRegistry());
|
|
104
|
+
return warnings.length > 0 ? { ...workflow, warnings } : workflow;
|
|
105
|
+
}
|
|
106
|
+
|
|
88
107
|
// ─── Route Definitions ───────────────────────────────────────────────────────
|
|
89
108
|
|
|
90
109
|
const listWorkflowsRoute = route({
|
|
@@ -137,7 +156,7 @@ const createWorkflowRoute = route({
|
|
|
137
156
|
vcsRepo: z.string().min(1).optional(),
|
|
138
157
|
}),
|
|
139
158
|
responses: {
|
|
140
|
-
201: { description: "Workflow created", schema:
|
|
159
|
+
201: { description: "Workflow created", schema: WorkflowSaveResponseSchema },
|
|
141
160
|
400: { description: "Invalid definition" },
|
|
142
161
|
},
|
|
143
162
|
});
|
|
@@ -188,7 +207,10 @@ const updateWorkflowRoute = route({
|
|
|
188
207
|
enabled: z.boolean().optional(),
|
|
189
208
|
}),
|
|
190
209
|
responses: {
|
|
191
|
-
200: {
|
|
210
|
+
200: {
|
|
211
|
+
description: "Workflow updated (version snapshot created)",
|
|
212
|
+
schema: WorkflowSaveResponseSchema,
|
|
213
|
+
},
|
|
192
214
|
400: { description: "Invalid definition" },
|
|
193
215
|
404: { description: "Workflow not found" },
|
|
194
216
|
},
|
|
@@ -203,7 +225,10 @@ const patchWorkflowRoute = route({
|
|
|
203
225
|
params: z.object({ id: z.string() }),
|
|
204
226
|
body: WorkflowPatchSchema.extend({ key: AssetKeySchema.optional() }),
|
|
205
227
|
responses: {
|
|
206
|
-
200: {
|
|
228
|
+
200: {
|
|
229
|
+
description: "Workflow patched (version snapshot created)",
|
|
230
|
+
schema: WorkflowSaveResponseSchema,
|
|
231
|
+
},
|
|
207
232
|
400: { description: "Invalid patch or resulting definition" },
|
|
208
233
|
404: { description: "Workflow not found" },
|
|
209
234
|
},
|
|
@@ -218,7 +243,10 @@ const patchWorkflowNodeRoute = route({
|
|
|
218
243
|
params: z.object({ id: z.string(), nodeId: z.string() }),
|
|
219
244
|
body: WorkflowNodePatchSchema,
|
|
220
245
|
responses: {
|
|
221
|
-
200: {
|
|
246
|
+
200: {
|
|
247
|
+
description: "Node patched (version snapshot created)",
|
|
248
|
+
schema: WorkflowSaveResponseSchema,
|
|
249
|
+
},
|
|
222
250
|
400: { description: "Invalid patch or resulting definition" },
|
|
223
251
|
404: { description: "Workflow or node not found" },
|
|
224
252
|
},
|
|
@@ -548,6 +576,11 @@ export async function handleWorkflows(
|
|
|
548
576
|
jsonError(res, `Invalid definition: ${validation.errors.join("; ")}`, 400);
|
|
549
577
|
return true;
|
|
550
578
|
}
|
|
579
|
+
const modelErrors = await workflowModelErrors(parsed.body.definition);
|
|
580
|
+
if (modelErrors.length > 0) {
|
|
581
|
+
jsonError(res, `Invalid definition: ${modelErrors.join("; ")}`, 400);
|
|
582
|
+
return true;
|
|
583
|
+
}
|
|
551
584
|
|
|
552
585
|
const trustedUserId = await resolveHttpAuditUserId(req, myAgentId);
|
|
553
586
|
let key: string | undefined;
|
|
@@ -583,7 +616,7 @@ export async function handleWorkflows(
|
|
|
583
616
|
},
|
|
584
617
|
"api",
|
|
585
618
|
);
|
|
586
|
-
createWorkflowRoute.respond(res, 201, workflow);
|
|
619
|
+
createWorkflowRoute.respond(res, 201, await withSaveWarnings(workflow));
|
|
587
620
|
return true;
|
|
588
621
|
}
|
|
589
622
|
|
|
@@ -638,7 +671,7 @@ export async function handleWorkflows(
|
|
|
638
671
|
);
|
|
639
672
|
return true;
|
|
640
673
|
}
|
|
641
|
-
patchWorkflowNodeRoute.respond(res, 200, result.workflow);
|
|
674
|
+
patchWorkflowNodeRoute.respond(res, 200, await withSaveWarnings(result.workflow));
|
|
642
675
|
return true;
|
|
643
676
|
}
|
|
644
677
|
|
|
@@ -695,7 +728,7 @@ export async function handleWorkflows(
|
|
|
695
728
|
);
|
|
696
729
|
return true;
|
|
697
730
|
}
|
|
698
|
-
patchWorkflowRoute.respond(res, 200, result.workflow);
|
|
731
|
+
patchWorkflowRoute.respond(res, 200, await withSaveWarnings(result.workflow));
|
|
699
732
|
return true;
|
|
700
733
|
}
|
|
701
734
|
|
|
@@ -722,6 +755,11 @@ export async function handleWorkflows(
|
|
|
722
755
|
jsonError(res, `Invalid definition: ${validation.errors.join("; ")}`, 400);
|
|
723
756
|
return true;
|
|
724
757
|
}
|
|
758
|
+
const modelErrors = await workflowModelErrors(body.definition);
|
|
759
|
+
if (modelErrors.length > 0) {
|
|
760
|
+
jsonError(res, `Invalid definition: ${modelErrors.join("; ")}`, 400);
|
|
761
|
+
return true;
|
|
762
|
+
}
|
|
725
763
|
}
|
|
726
764
|
|
|
727
765
|
const updatedBy2 = (await resolveHttpAuditUserId(req, myAgentId)) ?? undefined;
|
|
@@ -766,7 +804,7 @@ export async function handleWorkflows(
|
|
|
766
804
|
res.end();
|
|
767
805
|
return true;
|
|
768
806
|
}
|
|
769
|
-
updateWorkflowRoute.respond(res, 200, workflow);
|
|
807
|
+
updateWorkflowRoute.respond(res, 200, await withSaveWarnings(workflow));
|
|
770
808
|
return true;
|
|
771
809
|
}
|
|
772
810
|
|
|
@@ -10,10 +10,15 @@ export interface McpTool {
|
|
|
10
10
|
name: string;
|
|
11
11
|
description?: string;
|
|
12
12
|
inputSchema: Record<string, unknown>;
|
|
13
|
+
/** JSON Schema of `structuredContent`, when the server declares one. */
|
|
14
|
+
outputSchema?: Record<string, unknown>;
|
|
15
|
+
/** Server metadata, e.g. `anthropic/alwaysLoad` from task tool manifests. */
|
|
16
|
+
_meta?: Record<string, unknown>;
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
export interface McpToolCallResult {
|
|
16
20
|
content: Array<{ type: string; text?: string }>;
|
|
21
|
+
structuredContent?: Record<string, unknown>;
|
|
17
22
|
isError?: boolean;
|
|
18
23
|
}
|
|
19
24
|
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { stripSiblingBlock } from "../tasks/sibling-block";
|
|
2
|
+
|
|
3
|
+
const COMPLETED_INSTRUCTIONS =
|
|
4
|
+
"\n\nIMPORTANT: Do NOT re-delegate or re-answer the original request. The worker has already handled it. Your job is ONLY to:\n1. Review the output above\n2. Do not relay the worker's raw output to Slack; the engine owns the thread tree and outcome card\n3. Complete this follow-up task\n\nUse";
|
|
5
|
+
const FAILED_INSTRUCTIONS = "\n\nDecide whether to reassign, retry, or handle the failure. Use";
|
|
6
|
+
const DETAILS_PREFIX = ' `get-task-details` with taskId "';
|
|
7
|
+
const DETAILS_SUFFIX = '" for full details.';
|
|
8
|
+
|
|
9
|
+
/** Shape only pre-task search text; never alter the task or persisted memory. */
|
|
10
|
+
export function buildRecallQuery(description: string): string {
|
|
11
|
+
// Bound every parsing operation, while leaving room for wrapper boilerplate.
|
|
12
|
+
let query = stripSiblingBlock(truncateUtf8(description, 65536));
|
|
13
|
+
// Only the fixed header uses a regex; content is parsed with linear searches.
|
|
14
|
+
const header = query.match(
|
|
15
|
+
/^Worker task (completed|failed) — (?:review|action) needed\.\n\nAgent: [^\n]*\nOriginal task created by agent [^\n]*\nTask: "/,
|
|
16
|
+
);
|
|
17
|
+
if (header) {
|
|
18
|
+
const taskStart = header[0].length;
|
|
19
|
+
const outputMarker = header[1] === "completed" ? '"\n\nOutput:\n' : '"\n\nFailure reason: ';
|
|
20
|
+
const taskEnd = query.indexOf(outputMarker, taskStart);
|
|
21
|
+
const outputStart = taskEnd + outputMarker.length;
|
|
22
|
+
const instructions = header[1] === "completed" ? COMPLETED_INSTRUCTIONS : FAILED_INSTRUCTIONS;
|
|
23
|
+
const outputEnd = query.indexOf(instructions + DETAILS_PREFIX, outputStart);
|
|
24
|
+
const idStart = outputEnd + instructions.length + DETAILS_PREFIX.length;
|
|
25
|
+
const idEnd = query.indexOf('"', idStart);
|
|
26
|
+
if (
|
|
27
|
+
taskEnd !== -1 &&
|
|
28
|
+
outputEnd !== -1 &&
|
|
29
|
+
idEnd !== -1 &&
|
|
30
|
+
!query.slice(idStart, idEnd).includes("\n") &&
|
|
31
|
+
query.startsWith(DETAILS_SUFFIX, idEnd)
|
|
32
|
+
) {
|
|
33
|
+
const creatorInstructions = query.indexOf(
|
|
34
|
+
"\nAdditional instructions from the task creator:\n",
|
|
35
|
+
outputStart,
|
|
36
|
+
);
|
|
37
|
+
const contentEnd =
|
|
38
|
+
creatorInstructions !== -1 && creatorInstructions < outputEnd
|
|
39
|
+
? creatorInstructions
|
|
40
|
+
: outputEnd;
|
|
41
|
+
const threadContext: string[] = [];
|
|
42
|
+
let cursor = idEnd + DETAILS_SUFFIX.length;
|
|
43
|
+
while (cursor < query.length) {
|
|
44
|
+
const start = query.indexOf("<thread_context>", cursor);
|
|
45
|
+
if (start === -1) break;
|
|
46
|
+
const end = query.indexOf("</thread_context>", start);
|
|
47
|
+
if (end === -1) break;
|
|
48
|
+
cursor = end + "</thread_context>".length;
|
|
49
|
+
threadContext.push(query.slice(start, cursor));
|
|
50
|
+
}
|
|
51
|
+
query = [
|
|
52
|
+
query.slice(taskStart, taskEnd),
|
|
53
|
+
query.slice(outputStart, contentEnd),
|
|
54
|
+
...threadContext,
|
|
55
|
+
].join("\n\n");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
query = query.trim();
|
|
59
|
+
return truncateUtf8(query, 8191);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function truncateUtf8(query: string, maxBytes: number): string {
|
|
63
|
+
// OpenAI embeddings accept 8192 tokens. A UTF-8 byte budget of 8191 is
|
|
64
|
+
// conservative for the byte-level tokenizer, even for multilingual text.
|
|
65
|
+
// Iterate code points so truncation cannot split a surrogate pair.
|
|
66
|
+
let bytes = 0;
|
|
67
|
+
let end = 0;
|
|
68
|
+
for (const character of query) {
|
|
69
|
+
bytes += Buffer.byteLength(character, "utf8");
|
|
70
|
+
if (bytes > maxBytes) break;
|
|
71
|
+
end += character.length;
|
|
72
|
+
}
|
|
73
|
+
return query.slice(0, end);
|
|
74
|
+
}
|