@desplega.ai/agent-swarm 2.6.0 → 2.7.1
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 +2 -0
- package/dist/{acp-adapter-dgfakfpw.js → acp-adapter-8dn5tq56.js} +6 -6
- package/dist/{actions-fbqma8aa.js → actions-3r6gw7dy.js} +18 -18
- package/dist/{amp-adapter-en0jy6hs.js → amp-adapter-wjtq413p.js} +3 -3
- package/dist/{anthropic-messages-txs190dd.js → anthropic-messages-n4cby1rz.js} +4 -4
- package/dist/{app-k8a5cgpp.js → app-d53yhykk.js} +3 -3
- package/dist/{assistant-a16433gr.js → assistant-ksrawj3k.js} +20 -20
- package/dist/{boot-reembed-6c1sjfnr.js → boot-reembed-fxfw17m2.js} +11 -3
- package/dist/{boot-reembed-f4nn561n.js → boot-reembed-k5y5xbj0.js} +15 -8
- package/dist/{boot-scrub-sweep-d4w9gajf.js → boot-scrub-sweep-se41j9fd.js} +3 -3
- package/dist/{boot-seeds-9bmkspyw.js → boot-seeds-r3kwwv8b.js} +4 -4
- package/dist/{claude-adapter-nys1kjxa.js → claude-adapter-ahmyf231.js} +8 -8
- package/dist/{claude-managed-adapter-55207973.js → claude-managed-adapter-5knamegc.js} +3 -3
- package/dist/{claude-managed-setup-3cm35gey.js → claude-managed-setup-mdc7dy6b.js} +3 -3
- package/dist/{claude-sdk-session-nw5yd8cp.js → claude-sdk-session-n5wen9zk.js} +8729 -8600
- package/dist/{cli-683y0b3a.js → cli-0dqtv2jg.js} +2 -2
- package/dist/{cli-a34pr3w1.js → cli-0tww7dnz.js} +2 -2
- package/dist/{cli-w2656ka1.js → cli-0w4e4v58.js} +7 -7
- package/dist/{cli-5sckpqcx.js → cli-2sjm3t4r.js} +1 -1
- package/dist/{cli-gf2850q7.js → cli-3cywb44q.js} +18 -1
- package/dist/{cli-8wkb09q5.js → cli-3k9jej0c.js} +2 -2
- package/dist/{cli-3aqd4jaa.js → cli-3v2cyqb8.js} +3 -3
- package/dist/{cli-emhjcj2y.js → cli-5qt9vzmq.js} +89 -20
- package/dist/{cli-abkm71vr.js → cli-5rp1w4cr.js} +1 -1
- package/dist/{cli-gf95q3bm.js → cli-6vwjsetx.js} +4 -4
- package/dist/{cli-k21gx5ht.js → cli-6w2yrrc0.js} +10 -10
- package/dist/{cli-m70bajzr.js → cli-75tdydgv.js} +1 -1
- package/dist/{cli-5rj83bam.js → cli-8feswf2v.js} +47 -36
- package/dist/{cli-sxgtf5fw.js → cli-8wd3mbyj.js} +1 -1
- package/dist/{cli-ca6banwz.js → cli-9csfthbw.js} +10 -8
- package/dist/{cli-dqygeeh5.js → cli-b41p764a.js} +31 -3
- package/dist/{cli-m5a9m0ak.js → cli-c1t533hh.js} +3 -3
- package/dist/{cli-2sjhpsns.js → cli-c28jxpcv.js} +1 -1
- package/dist/{cli-bqztfyve.js → cli-cb50g9cd.js} +1 -1
- package/dist/{cli-ag2yrnt5.js → cli-d8gaphtr.js} +2 -2
- package/dist/{cli-9abdgn5t.js → cli-dgtfysne.js} +3 -3
- package/dist/{cli-nq677csy.js → cli-dscnhn18.js} +1 -1
- package/dist/{cli-3fwqy71j.js → cli-dv11pgpv.js} +39 -32
- package/dist/{cli-v49fkghp.js → cli-ekptgk2m.js} +6 -6
- package/dist/{cli-c2habvsf.js → cli-f3mzy662.js} +17 -17
- package/dist/{cli-zqne1qcj.js → cli-fk7k8s9x.js} +2 -2
- package/dist/{cli-1tem6ns9.js → cli-gt4h8cxw.js} +3 -2
- package/dist/{cli-vbhgkw9h.js → cli-h3e7bhvn.js} +3 -3
- package/dist/{cli-ffvsj2qw.js → cli-hkxqdmnb.js} +3 -3
- package/dist/{cli-a2rr0fmg.js → cli-hpkvgdad.js} +7 -4
- package/dist/{cli-art7c1zh.js → cli-hq2v4rr5.js} +37 -9
- package/dist/{cli-sqma6vw1.js → cli-k1zb7raw.js} +5 -5
- package/dist/{cli-4rvr7zfx.js → cli-k4awgvzm.js} +5 -0
- package/dist/{cli-3ndmpxxv.js → cli-k7d3hvya.js} +2 -2
- package/dist/{cli-3gfvmzht.js → cli-kbtdftbw.js} +74 -26
- package/dist/{cli-hfeb68z4.js → cli-kj69b9tw.js} +34 -6
- package/dist/{cli-az25fn5f.js → cli-kk9jy4nm.js} +2 -2
- package/dist/{cli-39aet655.js → cli-m52t4cta.js} +1 -1
- package/dist/{cli-cz37jqss.js → cli-mdadewqp.js} +5 -5
- package/dist/{cli-esakngh8.js → cli-nf6k60sn.js} +1 -1
- package/dist/{cli-j6rktc91.js → cli-nzdf09js.js} +1 -1
- package/dist/{cli-rr7zx19j.js → cli-prcyzecg.js} +12 -12
- package/dist/{cli-4hxrf0p4.js → cli-pzafyqtk.js} +393 -62
- package/dist/{cli-g8ka5j4j.js → cli-qvaa1gjp.js} +173 -52
- package/dist/{cli-d2dpf95n.js → cli-rg4b2g6j.js} +1 -1
- package/dist/{cli-kf4jree9.js → cli-rm66vqdz.js} +1 -1
- package/dist/{cli-4rf0f5vg.js → cli-s504c3w7.js} +5 -3
- package/dist/{cli-nn6vx2mq.js → cli-swkx9jm2.js} +7 -5
- package/dist/{cli-g20jjwt4.js → cli-thsaq4pd.js} +16 -16
- package/dist/{cli-aj8djyqt.js → cli-twjnyvs8.js} +2 -2
- package/dist/{cli-03gg9pd1.js → cli-typdpe8g.js} +4 -4
- package/dist/{cli-mtnj4fwa.js → cli-vg8pwmsd.js} +6 -6
- package/dist/{cli-gnsb1zxa.js → cli-vhpckzvp.js} +1 -1
- package/dist/{cli-7enjwya9.js → cli-vz78s5wm.js} +1 -1
- package/dist/{cli-dv97kdzv.js → cli-wdxgsdz5.js} +2 -2
- package/dist/{cli-7a6p4e65.js → cli-wtjg6je3.js} +1 -1
- package/dist/{cli-6y51ryva.js → cli-x3cngb1h.js} +44 -12
- package/dist/{cli-n7asmce6.js → cli-xbz6mcg4.js} +3 -3
- package/dist/{cli-24j1jeeb.js → cli-y4tdxsqb.js} +3 -3
- package/dist/{cli-mvjz5an3.js → cli-yqrsp3ny.js} +7 -7
- package/dist/cli.js +31 -21
- package/dist/{codex-adapter-d95q4qsn.js → codex-adapter-7wepzq15.js} +6 -6
- package/dist/{codex-hook-brpnycn5.js → codex-hook-pbdh4xhx.js} +2 -2
- package/dist/{codex-session-runner-gdwd8h0c.js → codex-session-runner-02sb49e4.js} +6 -6
- package/dist/{commands-jqmgk54b.js → commands-3rcspxct.js} +2 -2
- package/dist/{controller-z3jq589f.js → controller-8ar4avd6.js} +3 -3
- package/dist/{controllers-connect-r2za39ef.js → controllers-connect-ajvh9h0v.js} +19 -19
- package/dist/{cursor-adapter-4mr4jgkw.js → cursor-adapter-gd0sa7bh.js} +1 -1
- package/dist/{db-w36wwb5g.js → db-dv9j7ts5.js} +2 -2
- package/dist/{db-ppv2tze7.js → db-z26vgdvj.js} +3 -3
- package/dist/{dispatcher-pv5h77vf.js → dispatcher-8m0htjq8.js} +17 -17
- package/dist/{dsh-adapter-d4pdpb3r.js → dsh-adapter-y8gyk130.js} +1 -1
- package/dist/{e2b-fn3zacvt.js → e2b-74vg9b7e.js} +1 -1
- package/dist/{e2b-222hsajc.js → e2b-jyay8wzn.js} +1 -1
- package/dist/{easter-egg-3d-4sp4dvmc.js → easter-egg-3d-28f880xx.js} +1 -1
- package/dist/{execute-jev839y1.js → execute-9seb79vh.js} +1 -1
- package/dist/{external-channel-y6nkzhnn.js → external-channel-smz7jyth.js} +2 -2
- package/dist/{getMachineId-bsd-brn0xym3.js → getMachineId-bsd-whfre6gr.js} +3 -3
- package/dist/{getMachineId-darwin-t339sfxm.js → getMachineId-darwin-5zjynb19.js} +3 -3
- package/dist/{getMachineId-linux-5eq95d8b.js → getMachineId-linux-8mksrrsq.js} +3 -3
- package/dist/{getMachineId-unsupported-qavrtc7j.js → getMachineId-unsupported-fxtqsdt2.js} +3 -3
- package/dist/{getMachineId-win-q7fe1wx7.js → getMachineId-win-nzfmz857.js} +3 -3
- package/dist/{grok-adapter-t08b9ntm.js → grok-adapter-z0nnvtbn.js} +7 -7
- package/dist/handlers-pspryh6g.js +41 -0
- package/dist/{heartbeat-vn31xwdk.js → heartbeat-f928aqyp.js} +20 -20
- package/dist/{hook-cpjs8q51.js → hook-d7c605r4.js} +7 -7
- package/dist/{hook-6d0zbvvt.js → hook-q9hv5xf9.js} +5 -5
- package/dist/{http-9c6medda.js → http-q6j4k989.js} +1000 -251
- package/dist/{index-k0yt1tvq.js → index-5nb266jr.js} +17 -17
- package/dist/{index-ysd45kgc.js → index-8fxt5eah.js} +10 -10
- package/dist/{index-zgvqzjsw.js → index-cekzwhez.js} +5 -5
- package/dist/{index-38t35h0n.js → index-ch0c1xff.js} +17 -17
- package/dist/{index-pkma1yf8.js → index-ebnjhxh1.js} +1 -1
- package/dist/{index-yqgbfgaj.js → index-ed8dy8rn.js} +20 -20
- package/dist/{index-qhjf2htm.js → index-ejqdkkqx.js} +262 -96
- package/dist/{index-dr94zbnh.js → index-farm1zsn.js} +32661 -29443
- package/dist/{index-g6k9tctj.js → index-yn1yxg85.js} +1 -1
- package/dist/{keepalive-em0117bp.js → keepalive-zzc3hks1.js} +3 -3
- package/dist/lead-c0pjcaw4.js +79 -0
- package/dist/{maintenance-kbme3qy9.js → maintenance-kqsxzpzx.js} +1 -1
- package/dist/{oauth-refresh-sweep-xysj4yse.js → oauth-refresh-sweep-7qt0g55z.js} +8 -3
- package/dist/{onboard-p6q0vz97.js → onboard-k7x13fqg.js} +121 -8
- package/dist/{opencode-adapter-esv3k9yn.js → opencode-adapter-tqvrtter.js} +5 -5
- package/dist/{otel-impl-54qhqh1z.js → otel-impl-sqkf6k3b.js} +97 -97
- package/dist/{pi-mono-adapter-mymbwhj0.js → pi-mono-adapter-m2qnafxm.js} +89 -14
- package/dist/{pricing-refresh-yb75wv33.js → pricing-refresh-wkpwhgvs.js} +3 -3
- package/dist/{rbac-roles-dexhc9x8.js → rbac-roles-dpn0c0wj.js} +7 -4
- package/dist/{rbac-roles-1vp2mw4p.js → rbac-roles-wk7xd6a6.js} +2 -2
- package/dist/{render-v2-tjay5hhf.js → render-v2-ex2m4g2k.js} +3 -3
- package/dist/{resume-8ayf37g5.js → resume-725g9b0b.js} +7 -7
- package/dist/{runtime-hqe5cqcd.js → runtime-kajas15g.js} +1 -1
- package/dist/{session-access-fdhp987s.js → session-access-0j2ezjje.js} +8 -4
- package/dist/{session-source-backfill-qgtc86z1.js → session-source-backfill-62202awe.js} +2 -2
- package/dist/{setup-4rrmqcq3.js → setup-9sj11ax6.js} +1 -1
- package/dist/{virtual-modules-52km00d6.js → virtual-modules-c6fcq6zr.js} +2 -2
- package/dist/{work-objects-p28fgd70.js → work-objects-0y07495y.js} +20 -20
- package/dist/worker-d4q9sqbc.js +79 -0
- package/openapi.json +1536 -407
- package/package.json +7 -5
- package/src/be/automation-preflight.ts +2 -0
- package/src/be/connector-codes.ts +65 -22
- package/src/be/db/api-keys.ts +9 -0
- package/src/be/db/workflow-runs.ts +36 -5
- package/src/be/db.ts +80 -22
- package/src/be/memory/boot-reembed.ts +4 -1
- package/src/be/memory/constants.ts +6 -0
- package/src/be/memory/embedding-recovery.ts +48 -0
- package/src/be/memory/index-content.ts +5 -2
- package/src/be/memory/providers/openai-embedding.ts +65 -14
- package/src/be/memory/task-learning.ts +257 -0
- package/src/be/memory/types.ts +13 -2
- package/src/be/migrations/245_script_executor.sql +3 -0
- package/src/be/migrations/246_connector_code_kind.sql +11 -0
- package/src/be/oauth-refresh-sweep.ts +2 -1
- package/src/be/onboarding.ts +125 -15
- package/src/be/rbac-roles.ts +4 -1
- package/src/be/scripts/boot-reembed.ts +2 -0
- package/src/be/scripts/db.ts +14 -4
- package/src/be/scripts/embeddings.ts +4 -2
- package/src/be/scripts/run-saved.ts +1 -0
- package/src/be/scripts/typecheck.ts +9 -3
- package/src/be/seed/agent-fs-provision.ts +75 -14
- package/src/be/seed-skills/index.ts +7 -0
- package/src/be/swarm-config-guard.ts +3 -0
- package/src/cli.tsx +9 -0
- package/src/comb/config.ts +6 -1
- package/src/commands/onboard/agent-fs-version.ts +5 -0
- package/src/commands/onboard/compose-generator.ts +117 -4
- package/src/commands/onboard/env-generator.ts +9 -0
- package/src/commands/onboard/tei-image.ts +12 -0
- package/src/commands/onboard/types.ts +7 -0
- package/src/commands/onboard.tsx +11 -1
- package/src/commands/provider-credentials.ts +2 -1
- package/src/commands/runner.ts +14 -1
- package/src/heartbeat/heartbeat.ts +14 -1
- package/src/heartbeat/script-runner-probe.ts +47 -0
- package/src/http/agentfs-proxy.ts +369 -0
- package/src/http/all-routes.ts +1 -0
- package/src/http/api-keys.ts +24 -9
- package/src/http/codex-oauth-device.ts +8 -1
- package/src/http/fs.ts +64 -10
- package/src/http/idempotency-key.ts +1 -1
- package/src/http/inbox-state.ts +27 -1
- package/src/http/index.ts +7 -4
- package/src/http/memory.ts +7 -4
- package/src/http/onboarding.ts +139 -11
- package/src/http/pages-public.ts +38 -5
- package/src/http/scripts.ts +27 -0
- package/src/http/session-access.ts +24 -0
- package/src/http/sessions.ts +10 -6
- package/src/http/tasks.ts +85 -11
- package/src/http/telemetry-ui.ts +1 -1
- package/src/http/user-roles.ts +4 -1
- package/src/http/users.ts +272 -3
- package/src/http/utils.ts +22 -3
- package/src/http/workflows.ts +10 -0
- package/src/http/x.ts +5 -0
- package/src/prompts/base-prompt.ts +45 -10
- package/src/prompts/session-templates.ts +43 -5
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/codex-adapter.ts +6 -1
- package/src/providers/cursor-adapter.ts +1 -1
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/pi-mono-adapter.ts +99 -14
- package/src/rbac/legacy-policy.ts +1 -0
- package/src/rbac/permissions.ts +4 -0
- package/src/scheduler/scheduler.ts +2 -1
- package/src/scripts-runtime/eval-harness.ts +8 -1
- package/src/scripts-runtime/executors/native.ts +22 -16
- package/src/scripts-runtime/executors/quickjs-prelude.ts +103 -1
- package/src/scripts-runtime/executors/quickjs-runner.ts +2 -1
- package/src/scripts-runtime/executors/quickjs.ts +7 -0
- package/src/scripts-runtime/executors/registry.ts +32 -0
- package/src/scripts-runtime/executors/types.ts +7 -0
- package/src/scripts-runtime/host-validated-args.ts +133 -0
- package/src/scripts-runtime/loader.ts +30 -3
- package/src/scripts-runtime/swarm-sdk.ts +9 -2
- package/src/scripts-runtime/types/stdlib.d.ts +9 -1
- package/src/scripts-runtime/types/swarm-sdk.d.ts +9 -1
- package/src/tests/agent-fs-provision-seeder.test.ts +261 -92
- package/src/tests/agentfs-proxy.test.ts +1106 -0
- package/src/tests/api-key-status-by-suffix.test.ts +242 -0
- package/src/tests/base-prompt.test.ts +139 -4
- package/src/tests/codex-adapter.test.ts +46 -8
- package/src/tests/codex-oauth-device.test.ts +12 -1
- package/src/tests/comb-config-guard.test.ts +20 -0
- package/src/tests/comb-links.test.ts +79 -1
- package/src/tests/comb-status.test.ts +44 -0
- package/src/tests/community-linear-delivery-loop.test.ts +2099 -0
- package/src/tests/credential-check.test.ts +4 -2
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/docs-index.test.ts +135 -0
- package/src/tests/dsh-adapter.test.ts +1 -1
- package/src/tests/fetch-resolved-env.test.ts +16 -2
- package/src/tests/fixtures/onboard/baseline-states.ts +68 -0
- package/src/tests/fixtures/onboard/content.compose.yml.txt +190 -0
- package/src/tests/fixtures/onboard/content.env.txt +29 -0
- package/src/tests/fixtures/onboard/dev.compose.yml.txt +155 -0
- package/src/tests/fixtures/onboard/dev.env.txt +28 -0
- package/src/tests/fixtures/onboard/full-all.compose.yml.txt +582 -0
- package/src/tests/fixtures/onboard/full-all.env.txt +59 -0
- package/src/tests/fixtures/onboard/full.compose.yml.txt +435 -0
- package/src/tests/fixtures/onboard/full.env.txt +36 -0
- package/src/tests/fixtures/onboard/research.compose.yml.txt +155 -0
- package/src/tests/fixtures/onboard/research.env.txt +28 -0
- package/src/tests/fixtures/onboard/solo.compose.yml.txt +85 -0
- package/src/tests/fixtures/onboard/solo.env.txt +26 -0
- package/src/tests/heartbeat-checklist.test.ts +89 -0
- package/src/tests/heartbeat-script-runner-probe.test.ts +83 -0
- package/src/tests/http-log-scrubbing.test.ts +34 -1
- package/src/tests/http-task-write-ownership.test.ts +5 -1
- package/src/tests/invites-http.test.ts +464 -0
- package/src/tests/memory-embedding-input-cap.test.ts +122 -1
- package/src/tests/memory-embedding-recovery.test.ts +137 -0
- package/src/tests/oauth-refresh-sweep.test.ts +34 -1
- package/src/tests/onboard-compose.test.ts +162 -0
- package/src/tests/onboard-env.test.ts +24 -0
- package/src/tests/onboard-non-interactive.test.ts +29 -0
- package/src/tests/onboard-tei-image.test.ts +36 -0
- package/src/tests/onboarding.test.ts +451 -4
- package/src/tests/openai-embedding-provider.test.ts +105 -2
- package/src/tests/opencode-adapter.test.ts +2 -1
- package/src/tests/pages-frame-ancestors.test.ts +162 -0
- package/src/tests/pi-codemode.test.ts +2 -1
- package/src/tests/pi-mono-adapter.test.ts +54 -0
- package/src/tests/pi-mono-session-cost-on-abort.test.ts +270 -0
- package/src/tests/pi-native-mcp.test.ts +13 -1
- package/src/tests/pi-tool-exposure.test.ts +8 -8
- package/src/tests/preload-env.test.ts +4 -0
- package/src/tests/preload.ts +4 -0
- package/src/tests/probe-tool-schema-tokens.test.ts +144 -0
- package/src/tests/prompt-template-session.test.ts +20 -3
- package/src/tests/rbac-admission-e2e.test.ts +22 -6
- package/src/tests/rbac-admission.test.ts +342 -4
- package/src/tests/rbac-engine.test.ts +1 -0
- package/src/tests/rbac-roles.test.ts +9 -1
- package/src/tests/request-composition-derive.test.ts +329 -0
- package/src/tests/script-connections.test.ts +2 -0
- package/src/tests/script-executor-conformance.test.ts +36 -1
- package/src/tests/script-executor-quickjs-pool.test.ts +50 -0
- package/src/tests/script-executor-quickjs.test.ts +140 -0
- package/src/tests/script-host-validated-args-native.test.ts +196 -0
- package/src/tests/script-per-script-executor.test.ts +415 -0
- package/src/tests/scripts-external-api.test.ts +45 -0
- package/src/tests/sdk-allowlist.test.ts +3 -2
- package/src/tests/self-improvement.test.ts +4 -4
- package/src/tests/send-task-requester-gate.test.ts +64 -3
- package/src/tests/sessions-move.test.ts +39 -1
- package/src/tests/store-progress-learning.test.ts +381 -0
- package/src/tests/ui-logs-parser.test.ts +180 -0
- package/src/tests/whoami-route.test.ts +41 -0
- package/src/tests/workflow-engine-v2.test.ts +116 -1
- package/src/tests/workflow-mcp-trigger-schema.test.ts +209 -0
- package/src/tests/workflow-run-finished-event.test.ts +263 -0
- package/src/tests/workflow-swarm-script.test.ts +34 -0
- package/src/tools/inject-learning.ts +1 -1
- package/src/tools/memory-edit.ts +1 -1
- package/src/tools/memory-search.ts +1 -1
- package/src/tools/script-common.ts +1 -0
- package/src/tools/script-run.ts +7 -0
- package/src/tools/script-upsert.ts +7 -0
- package/src/tools/store-progress.ts +71 -1
- package/src/tools/utils.ts +12 -1
- package/src/tools/workflows/trigger-workflow.ts +104 -10
- package/src/types.ts +12 -1
- package/src/utils/constants.ts +39 -0
- package/src/utils/credentials.ts +5 -2
- package/src/utils/task-attachment-links.ts +15 -3
- package/src/workflows/engine.ts +18 -16
- package/src/workflows/executors/swarm-script.ts +5 -0
- package/src/workflows/foreach-driver.ts +5 -11
- package/src/workflows/node-timeout.ts +30 -0
- package/templates/community/linear-delivery-loop/PLAYBOOK.md +323 -0
- package/templates/community/linear-delivery-loop/README.md +38 -0
- package/templates/community/linear-delivery-loop/config.example.json +31 -0
- package/templates/community/linear-delivery-loop/lead-prompt.md +34 -0
- package/templates/community/linear-delivery-loop/schedule.json +10 -0
- package/templates/community/linear-delivery-loop/scripts/linear-delivery-setup.ts +148 -0
- package/templates/community/linear-delivery-loop/scripts/linear-delivery-tick.ts +1781 -0
- package/templates/skills/swarm-docs/config.json +14 -0
- package/templates/skills/swarm-docs/content.md +144 -0
- package/dist/handlers-eqsnmv95.js +0 -41
- package/dist/lead-23e7z59t.js +0 -79
- package/dist/worker-e3w7vye1.js +0 -79
- package/dist/{azure-openai-responses-9kfwhmn7.js → azure-openai-responses-43asa23s.js} +0 -0
- package/dist/{node-vxx896fa.js → node-t28cc56b.js} +0 -0
- package/dist/{openai-codex-responses-79s8xnbh.js → openai-codex-responses-jhr3sjtm.js} +3 -3
- package/dist/{openai-completions-tdbyts0x.js → openai-completions-gs6yn37k.js} +3 -3
- /package/dist/{openai-responses-dm3t5vqp.js → openai-responses-jqm9vwwk.js} +0 -0
|
@@ -2,10 +2,42 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { resolveTaskAuditUserId } from "@/be/audit-user";
|
|
4
4
|
import { getWorkflow, getWorkflowRun } from "@/be/db";
|
|
5
|
+
import {
|
|
6
|
+
findAcceptedWorkflowOperation,
|
|
7
|
+
IdempotencyKeyTakenError,
|
|
8
|
+
recordAcceptedWorkflowOperation,
|
|
9
|
+
type WorkflowIdempotencyClaim,
|
|
10
|
+
workflowRequestFingerprint,
|
|
11
|
+
} from "@/be/db/workflow-idempotency";
|
|
12
|
+
import { IDEMPOTENCY_KEY_PATTERN } from "@/http/idempotency-key";
|
|
5
13
|
import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
|
|
6
14
|
import { getExecutorRegistry, startWorkflowExecution } from "@/workflows";
|
|
7
15
|
import { TriggerSchemaError } from "@/workflows/engine";
|
|
8
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Answer from the run already accepted under this key, or null when there is
|
|
19
|
+
* none. Same record and rules as the HTTP trigger's `Idempotency-Key`.
|
|
20
|
+
*/
|
|
21
|
+
async function replayAcceptedTrigger(claim: WorkflowIdempotencyClaim) {
|
|
22
|
+
const accepted = await findAcceptedWorkflowOperation(claim);
|
|
23
|
+
if (!accepted) return null;
|
|
24
|
+
if (accepted.fingerprint !== claim.fingerprint) {
|
|
25
|
+
return toolErr("idempotencyKey was already used to trigger this workflow with other data.", {
|
|
26
|
+
data: { idempotencyConflict: true },
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
if (accepted.resourceDeletedAt !== null) {
|
|
30
|
+
return toolErr(
|
|
31
|
+
"The workflow this idempotencyKey triggered was deleted; send a new key to start over.",
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
const { runId, skipped } = accepted.body as { runId: string; skipped: boolean };
|
|
35
|
+
return toolOk("Run already accepted under this idempotencyKey; nothing new was started.", {
|
|
36
|
+
details: `Replayed the run accepted under this idempotencyKey — run ID: ${runId}.`,
|
|
37
|
+
data: { runId, skipped, replayed: true },
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
9
41
|
export const registerTriggerWorkflowTool = (server: McpServer) => {
|
|
10
42
|
createToolRegistrar(server)(
|
|
11
43
|
"trigger-workflow",
|
|
@@ -21,16 +53,55 @@ export const registerTriggerWorkflowTool = (server: McpServer) => {
|
|
|
21
53
|
.record(z.string(), z.unknown())
|
|
22
54
|
.optional()
|
|
23
55
|
.describe("Optional data to pass as trigger context to the workflow"),
|
|
56
|
+
wait: z
|
|
57
|
+
.boolean()
|
|
58
|
+
.optional()
|
|
59
|
+
.describe(
|
|
60
|
+
"Default true: return after the run's instant steps ran, when it finishes or parks at its first async step. " +
|
|
61
|
+
"false: return as soon as the run is created and walk it in the background. Use false from a script or step that only needs the runId.",
|
|
62
|
+
),
|
|
63
|
+
idempotencyKey: z
|
|
64
|
+
.string()
|
|
65
|
+
.regex(IDEMPOTENCY_KEY_PATTERN)
|
|
66
|
+
.optional()
|
|
67
|
+
.describe(
|
|
68
|
+
"Optional, 1-255 visible ASCII characters, scoped to the calling agent and this workflow. " +
|
|
69
|
+
"The first trigger accepted under a key starts one run; the key is recorded in the run's own transaction and never expires. " +
|
|
70
|
+
"A retry with the same key and triggerData starts nothing and returns that run (replayed: true), even if the earlier call was killed before it saw the answer. " +
|
|
71
|
+
"The same key with other triggerData is an error with idempotencyConflict: true.",
|
|
72
|
+
),
|
|
24
73
|
}),
|
|
25
74
|
outputSchema: swarmToolOutputSchema({
|
|
26
75
|
runId: z.string().optional(),
|
|
27
76
|
skipped: z.boolean().optional(),
|
|
77
|
+
replayed: z.boolean().optional(),
|
|
78
|
+
idempotencyConflict: z.boolean().optional(),
|
|
28
79
|
validationErrors: z.array(z.string()).optional(),
|
|
29
80
|
triggerSchema: z.record(z.string(), z.unknown()).optional(),
|
|
30
81
|
}),
|
|
31
82
|
},
|
|
32
|
-
async ({ id, triggerData }, requestInfo) => {
|
|
83
|
+
async ({ id, triggerData, wait, idempotencyKey }, requestInfo) => {
|
|
33
84
|
try {
|
|
85
|
+
if (idempotencyKey !== undefined && !requestInfo.agentId) {
|
|
86
|
+
return toolErr("idempotencyKey needs an identified caller (X-Agent-ID).");
|
|
87
|
+
}
|
|
88
|
+
const claim: WorkflowIdempotencyClaim | undefined =
|
|
89
|
+
idempotencyKey === undefined
|
|
90
|
+
? undefined
|
|
91
|
+
: {
|
|
92
|
+
principal: `agent:${requestInfo.agentId}`,
|
|
93
|
+
key: idempotencyKey,
|
|
94
|
+
operation: "workflow.trigger",
|
|
95
|
+
targetId: id,
|
|
96
|
+
fingerprint: workflowRequestFingerprint("workflow.trigger", triggerData ?? {}),
|
|
97
|
+
};
|
|
98
|
+
if (claim) {
|
|
99
|
+
// Before the enabled, cooldown and trigger-schema checks: an
|
|
100
|
+
// accepted trigger replays even after the workflow changed.
|
|
101
|
+
const replayed = await replayAcceptedTrigger(claim);
|
|
102
|
+
if (replayed) return replayed;
|
|
103
|
+
}
|
|
104
|
+
|
|
34
105
|
const workflow = await getWorkflow(id);
|
|
35
106
|
if (!workflow) {
|
|
36
107
|
return toolErr(`Workflow not found: ${id}`);
|
|
@@ -38,21 +109,44 @@ export const registerTriggerWorkflowTool = (server: McpServer) => {
|
|
|
38
109
|
if (!workflow.enabled) {
|
|
39
110
|
return toolErr(`Workflow "${workflow.name}" is disabled.`);
|
|
40
111
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
getExecutorRegistry(),
|
|
45
|
-
{
|
|
112
|
+
let accepted: { runId: string; skipped: boolean } | undefined;
|
|
113
|
+
let runId: string;
|
|
114
|
+
try {
|
|
115
|
+
runId = await startWorkflowExecution(workflow, triggerData ?? {}, getExecutorRegistry(), {
|
|
46
116
|
triggerType: "manual",
|
|
47
117
|
requestedByUserId:
|
|
48
118
|
(await resolveTaskAuditUserId(requestInfo.sourceTaskId, requestInfo.agentId)) ??
|
|
49
119
|
undefined,
|
|
50
|
-
|
|
51
|
-
|
|
120
|
+
detach: wait === false,
|
|
121
|
+
// Recorded in the run's own transaction: the key exists if and
|
|
122
|
+
// only if the run committed.
|
|
123
|
+
accept: claim
|
|
124
|
+
? async (run) => {
|
|
125
|
+
const response = { runId: run.runId, skipped: run.skipped };
|
|
126
|
+
await recordAcceptedWorkflowOperation(claim, {
|
|
127
|
+
workflowId: workflow.id,
|
|
128
|
+
runId: run.runId,
|
|
129
|
+
status: 201,
|
|
130
|
+
body: response,
|
|
131
|
+
});
|
|
132
|
+
accepted = response;
|
|
133
|
+
}
|
|
134
|
+
: undefined,
|
|
135
|
+
});
|
|
136
|
+
} catch (err) {
|
|
137
|
+
// A concurrent call with this key committed its run first; this one
|
|
138
|
+
// created none.
|
|
139
|
+
if (claim && err instanceof IdempotencyKeyTakenError) {
|
|
140
|
+
const replayed = await replayAcceptedTrigger(claim);
|
|
141
|
+
if (replayed) return replayed;
|
|
142
|
+
}
|
|
143
|
+
throw err;
|
|
144
|
+
}
|
|
52
145
|
|
|
53
146
|
// Check if the run was skipped due to cooldown
|
|
54
|
-
const
|
|
55
|
-
|
|
147
|
+
const skipped = accepted
|
|
148
|
+
? accepted.skipped
|
|
149
|
+
: (await getWorkflowRun(runId))?.status === "skipped";
|
|
56
150
|
|
|
57
151
|
if (skipped) {
|
|
58
152
|
return toolOk(`Workflow "${workflow.name}" skipped (cooldown).`, {
|
package/src/types.ts
CHANGED
|
@@ -2282,7 +2282,14 @@ export const ExecutorMetaSchema = z
|
|
|
2282
2282
|
inputCtx: z.record(z.string(), z.unknown()).optional(),
|
|
2283
2283
|
})
|
|
2284
2284
|
.openapi("ExecutorMeta");
|
|
2285
|
-
export type ExecutorMeta = z.infer<typeof ExecutorMetaSchema
|
|
2285
|
+
export type ExecutorMeta = z.infer<typeof ExecutorMetaSchema> & {
|
|
2286
|
+
/**
|
|
2287
|
+
* Aborted when the step's watchdog budget runs out (`startStepDeadline`).
|
|
2288
|
+
* Runtime-only, never serialized. An executor with a long or one-shot side
|
|
2289
|
+
* effect should forward it so a timed-out step stops instead of finishing.
|
|
2290
|
+
*/
|
|
2291
|
+
signal?: AbortSignal;
|
|
2292
|
+
};
|
|
2286
2293
|
|
|
2287
2294
|
// --- Validation ---
|
|
2288
2295
|
|
|
@@ -3203,6 +3210,9 @@ export type ScriptScope = z.infer<typeof ScriptScopeSchema>;
|
|
|
3203
3210
|
export const ScriptFsModeSchema = z.enum(["none", "workspace-rw"]);
|
|
3204
3211
|
export type ScriptFsMode = z.infer<typeof ScriptFsModeSchema>;
|
|
3205
3212
|
|
|
3213
|
+
export const ScriptExecutorNameSchema = z.enum(["native", "quickjs"]);
|
|
3214
|
+
export type ScriptExecutorName = z.infer<typeof ScriptExecutorNameSchema>;
|
|
3215
|
+
|
|
3206
3216
|
export const ScriptRecordSchema = z
|
|
3207
3217
|
.object({
|
|
3208
3218
|
id: z.string(),
|
|
@@ -3219,6 +3229,7 @@ export const ScriptRecordSchema = z
|
|
|
3219
3229
|
isScratch: z.boolean(),
|
|
3220
3230
|
typeChecked: z.boolean(),
|
|
3221
3231
|
fsMode: ScriptFsModeSchema,
|
|
3232
|
+
executor: ScriptExecutorNameSchema.nullable().default(null),
|
|
3222
3233
|
createdByAgentId: z.string().nullable(),
|
|
3223
3234
|
createdAt: z.string(),
|
|
3224
3235
|
updatedAt: z.string(),
|
package/src/utils/constants.ts
CHANGED
|
@@ -89,6 +89,36 @@ export function getAgentFsLiveUrl(): string {
|
|
|
89
89
|
return (raw || DEFAULT_AGENT_FS_LIVE_URL).replace(/\/+$/, "");
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
/** Where people-facing agent-fs file links point: Comb in the dashboard, or the live app. */
|
|
93
|
+
export type AgentFsLinkTarget = "dashboard" | "live";
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The operator's `AGENT_FS_LINK_TARGET` choice (trimmed, lowercased). Null when
|
|
97
|
+
* unset, blank, or invalid: every surface then keeps its automatic behavior.
|
|
98
|
+
*/
|
|
99
|
+
export function readAgentFsLinkTarget(): AgentFsLinkTarget | null {
|
|
100
|
+
const raw = process.env.AGENT_FS_LINK_TARGET?.trim().toLowerCase();
|
|
101
|
+
return raw === "dashboard" || raw === "live" ? raw : null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* The base URL (no trailing slash) that a people-facing file link starts with,
|
|
106
|
+
* per {@link readAgentFsLinkTarget}. `dashboard` uses `APP_URL` when one is
|
|
107
|
+
* configured (and, for attachments, Comb is on), else the live app. Null when
|
|
108
|
+
* no target is set: the caller keeps its current code path.
|
|
109
|
+
*
|
|
110
|
+
* The prompt surface skips the Comb check: workers do not get `COMB_ENABLED`,
|
|
111
|
+
* and an explicit `dashboard` is the operator's choice.
|
|
112
|
+
*/
|
|
113
|
+
export function resolveAgentFsLinkBase(surface: "prompt" | "attachment"): string | null {
|
|
114
|
+
const target = readAgentFsLinkTarget();
|
|
115
|
+
if (target === null) return null;
|
|
116
|
+
if (target === "dashboard" && getConfiguredAppUrls().length > 0) {
|
|
117
|
+
if (surface === "prompt" || isCombEnabled()) return getAppUrl();
|
|
118
|
+
}
|
|
119
|
+
return getAgentFsLiveUrl();
|
|
120
|
+
}
|
|
121
|
+
|
|
92
122
|
/**
|
|
93
123
|
* Optional fallback agent-fs `org_id` for attachments that store only `path`.
|
|
94
124
|
* Strictly opt-in — when neither env var is set, the renderer keeps the
|
|
@@ -166,6 +196,15 @@ export function isCombEnabled(): boolean {
|
|
|
166
196
|
return isEnvFlagEnabled("COMB_ENABLED", false) && Boolean(apiUrl);
|
|
167
197
|
}
|
|
168
198
|
|
|
199
|
+
/**
|
|
200
|
+
* `AGENT_FS_PROXY`: the API serves agent-fs at `/agentfs/*` (plus the public
|
|
201
|
+
* `/share/*` and `/site/*` pages), and Comb uses `<public API URL>/agentfs`.
|
|
202
|
+
* Read per request, so a config upsert applies without a restart. Off by default.
|
|
203
|
+
*/
|
|
204
|
+
export function isAgentFsProxyEnabled(): boolean {
|
|
205
|
+
return isEnvFlagEnabled("AGENT_FS_PROXY", false);
|
|
206
|
+
}
|
|
207
|
+
|
|
169
208
|
/**
|
|
170
209
|
* The dashboard (Comb) URL for an agent-fs file, same id and encoding rules as
|
|
171
210
|
* {@link buildAgentFsLiveUrl}. Null unless Comb is on AND an explicit
|
package/src/utils/credentials.ts
CHANGED
|
@@ -406,10 +406,13 @@ async function fetchAvailableIndices(
|
|
|
406
406
|
for (const envVar of poolVars) {
|
|
407
407
|
const val = env[envVar];
|
|
408
408
|
if (val) {
|
|
409
|
-
|
|
409
|
+
// Status is matched by key suffix, so a key at the same index in another
|
|
410
|
+
// agent's or repo's pool never blocks one of ours (issue #2425).
|
|
411
|
+
const keySuffixes = splitCredentialPool(val).map((key) => key.slice(-5));
|
|
412
|
+
const totalKeys = keySuffixes.length;
|
|
410
413
|
try {
|
|
411
414
|
const resp = await fetch(
|
|
412
|
-
`${apiUrl}/api/keys/available?keyType=${encodeURIComponent(envVar)}&totalKeys=${totalKeys}${modelParam}`,
|
|
415
|
+
`${apiUrl}/api/keys/available?keyType=${encodeURIComponent(envVar)}&totalKeys=${totalKeys}${modelParam}&keySuffixes=${encodeURIComponent(keySuffixes.join(","))}`,
|
|
413
416
|
{ headers: { Authorization: `Bearer ${apiKey}` } },
|
|
414
417
|
);
|
|
415
418
|
if (resp.ok) {
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { TaskAttachment } from "../types";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
agentFsFileRoute,
|
|
4
|
+
buildAgentFsLiveUrl,
|
|
5
|
+
buildCombFileUrl,
|
|
6
|
+
getAppUrl,
|
|
7
|
+
resolveAgentFsLinkBase,
|
|
8
|
+
} from "./constants";
|
|
3
9
|
|
|
4
10
|
function shellQuote(value: string): string {
|
|
5
11
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
@@ -26,8 +32,9 @@ export function taskAttachmentFetchCommand(
|
|
|
26
32
|
/**
|
|
27
33
|
* The link a human (Slack, prompts, citations) sees for an attachment. An
|
|
28
34
|
* agent-fs file links to the dashboard (Comb) when Comb is on and `APP_URL` is
|
|
29
|
-
* set, else to the agent-fs live UI, else to the raw `agent-fs:<path>`.
|
|
30
|
-
*
|
|
35
|
+
* set, else to the agent-fs live UI, else to the raw `agent-fs:<path>`. An
|
|
36
|
+
* explicit `AGENT_FS_LINK_TARGET` picks the base instead (see
|
|
37
|
+
* `resolveAgentFsLinkBase`). `comb: false` always links to the live UI.
|
|
31
38
|
*/
|
|
32
39
|
export function taskAttachmentDisplayUrl(
|
|
33
40
|
attachment: TaskAttachment,
|
|
@@ -40,6 +47,11 @@ export function taskAttachmentDisplayUrl(
|
|
|
40
47
|
|
|
41
48
|
if (attachment.providerId === "agent-fs" || attachment.kind === "agent-fs") {
|
|
42
49
|
const file = { path: attachment.path, orgId: attachment.orgId, driveId: attachment.driveId };
|
|
50
|
+
const linkBase = opts.comb === false ? null : resolveAgentFsLinkBase("attachment");
|
|
51
|
+
if (linkBase !== null) {
|
|
52
|
+
const route = agentFsFileRoute(file);
|
|
53
|
+
return route ? `${linkBase}/file/~/${route}` : `agent-fs:${attachment.path ?? ""}`;
|
|
54
|
+
}
|
|
43
55
|
const combUrl = opts.comb === false ? null : buildCombFileUrl(file);
|
|
44
56
|
return combUrl ?? buildAgentFsLiveUrl(file) ?? `agent-fs:${attachment.path ?? ""}`;
|
|
45
57
|
}
|
package/src/workflows/engine.ts
CHANGED
|
@@ -51,7 +51,7 @@ import { FOREACH_TERMINAL_STEP_STATUSES, resolveForeachParent } from "./foreach-
|
|
|
51
51
|
import { getSecretInputKeys, redactSecretsForStorage, resolveInputs } from "./input";
|
|
52
52
|
import { validateJsonSchema } from "./json-schema-validator";
|
|
53
53
|
import { getMaxWorkflowStepsPerRun } from "./limits";
|
|
54
|
-
import { resolveNodeTimeoutMs } from "./node-timeout";
|
|
54
|
+
import { resolveNodeTimeoutMs, startStepDeadline } from "./node-timeout";
|
|
55
55
|
import { findWorkflowReadinessProblems, formatReadinessRunError } from "./readiness";
|
|
56
56
|
import { deepInterpolate } from "./template";
|
|
57
57
|
import { runStepValidation, type ValidationRunResult } from "./validation";
|
|
@@ -78,6 +78,13 @@ export interface WorkflowExecutionOptions {
|
|
|
78
78
|
* HTTP trigger records its `Idempotency-Key` here.
|
|
79
79
|
*/
|
|
80
80
|
accept?: (run: { runId: string; skipped: boolean }) => Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Return once the run is committed and walk it in the background, as a
|
|
83
|
+
* child run does. By default a top-level start resolves only after the
|
|
84
|
+
* walk reaches its end or its first async step, so a caller that only
|
|
85
|
+
* needs the run id waits for every instant step before it.
|
|
86
|
+
*/
|
|
87
|
+
detach?: boolean;
|
|
81
88
|
}
|
|
82
89
|
|
|
83
90
|
/**
|
|
@@ -209,9 +216,12 @@ export async function startWorkflowExecution(
|
|
|
209
216
|
options,
|
|
210
217
|
);
|
|
211
218
|
// A child run walks on its own, outside the parent executor's timeout. Its
|
|
212
|
-
// terminal status wakes the parent step (`resumeFromChildRun`).
|
|
213
|
-
|
|
214
|
-
|
|
219
|
+
// terminal status wakes the parent step (`resumeFromChildRun`). A detached
|
|
220
|
+
// top-level run walks the same way; the walk holds the run before its
|
|
221
|
+
// first await, so releasing the start hold below leaves no gap.
|
|
222
|
+
if (options.parentStepId || options.detach) {
|
|
223
|
+
const kind = options.parentStepId ? "Child" : "Detached";
|
|
224
|
+
walk.catch((err) => console.error(`[workflows] ${kind} workflow run ${runId} failed:`, err));
|
|
215
225
|
return runId;
|
|
216
226
|
}
|
|
217
227
|
await walk;
|
|
@@ -1045,7 +1055,7 @@ async function runClaimedStep(
|
|
|
1045
1055
|
inputCtx: interpolationCtx,
|
|
1046
1056
|
};
|
|
1047
1057
|
|
|
1048
|
-
const
|
|
1058
|
+
const deadline = startStepDeadline(resolveNodeTimeoutMs(node));
|
|
1049
1059
|
|
|
1050
1060
|
let result: Awaited<ReturnType<typeof executor.run>>;
|
|
1051
1061
|
try {
|
|
@@ -1053,10 +1063,10 @@ async function runClaimedStep(
|
|
|
1053
1063
|
executor.run({
|
|
1054
1064
|
config: interpolatedConfig,
|
|
1055
1065
|
context: executionCtx,
|
|
1056
|
-
meta,
|
|
1066
|
+
meta: { ...meta, signal: deadline.signal },
|
|
1057
1067
|
}),
|
|
1058
|
-
|
|
1059
|
-
]);
|
|
1068
|
+
deadline.expired,
|
|
1069
|
+
]).finally(deadline.clear);
|
|
1060
1070
|
} catch (err) {
|
|
1061
1071
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1062
1072
|
|
|
@@ -1459,11 +1469,3 @@ export function scriptBodyInterpolationError(nodeId: string, tokens: string[]):
|
|
|
1459
1469
|
"Pass dynamic trigger or upstream values through config.args and read them from argv."
|
|
1460
1470
|
);
|
|
1461
1471
|
}
|
|
1462
|
-
|
|
1463
|
-
// ─── Helpers ───────────────────────────────────────────────
|
|
1464
|
-
|
|
1465
|
-
function timeoutPromise(ms: number): Promise<never> {
|
|
1466
|
-
return new Promise((_, reject) => {
|
|
1467
|
-
setTimeout(() => reject(new Error(`Step timed out after ${ms}ms`)), ms);
|
|
1468
|
-
});
|
|
1469
|
-
}
|
|
@@ -123,6 +123,7 @@ export class SwarmScriptExecutor extends BaseExecutor<
|
|
|
123
123
|
source: resolved.source,
|
|
124
124
|
args: config.args,
|
|
125
125
|
fsMode: "none",
|
|
126
|
+
executor: resolved.script.executor,
|
|
126
127
|
agentId: agentId ?? "workflow",
|
|
127
128
|
mcpBaseUrl: getMcpBaseUrl(),
|
|
128
129
|
egressSecrets: credentials.egressSecrets,
|
|
@@ -132,6 +133,10 @@ export class SwarmScriptExecutor extends BaseExecutor<
|
|
|
132
133
|
apiConnections: getScriptApiConnectionDescriptors({ agentId: agentId ?? undefined }),
|
|
133
134
|
mcpConnections: getScriptMcpConnectionDescriptors({ agentId: agentId ?? undefined }),
|
|
134
135
|
timeoutMs: config.timeoutMs,
|
|
136
|
+
// The step watchdog aborts this when the node budget runs out, which
|
|
137
|
+
// kills the script process: a step reported as timed out must not go
|
|
138
|
+
// on to perform the script's side effects.
|
|
139
|
+
signal: meta.signal,
|
|
135
140
|
});
|
|
136
141
|
if (output.exitCode === 0 && !output.error && !output.runtimeError) {
|
|
137
142
|
await touchScratchScriptLastUsed(resolved.script.id);
|
|
@@ -47,7 +47,7 @@ import {
|
|
|
47
47
|
settleForeachChild,
|
|
48
48
|
} from "./foreach-join";
|
|
49
49
|
import { getSecretInputKeys } from "./input";
|
|
50
|
-
import { resolveNodeTimeoutMs } from "./node-timeout";
|
|
50
|
+
import { resolveNodeTimeoutMs, startStepDeadline } from "./node-timeout";
|
|
51
51
|
import { rebuildNodeExecutionInput } from "./step-input";
|
|
52
52
|
|
|
53
53
|
export { FOREACH_ITEMS_WAIT_FOR };
|
|
@@ -480,27 +480,21 @@ async function startChild(input: {
|
|
|
480
480
|
}
|
|
481
481
|
|
|
482
482
|
const executor = registry.get(bodyType);
|
|
483
|
-
const
|
|
484
|
-
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
483
|
+
const deadline = startStepDeadline(resolveNodeTimeoutMs({ type: bodyType, config }));
|
|
485
484
|
let result: Awaited<ReturnType<typeof executor.run>>;
|
|
486
485
|
try {
|
|
487
486
|
result = await Promise.race([
|
|
488
487
|
executor.run({
|
|
489
488
|
config,
|
|
490
489
|
context: { ...ctx, ...bodyCtx },
|
|
491
|
-
meta: { ...meta, stepId: child.id, nodeId: child.nodeId },
|
|
492
|
-
}),
|
|
493
|
-
new Promise<never>((_, reject) => {
|
|
494
|
-
timer = setTimeout(
|
|
495
|
-
() => reject(new Error(`Step timed out after ${timeoutMs}ms`)),
|
|
496
|
-
timeoutMs,
|
|
497
|
-
);
|
|
490
|
+
meta: { ...meta, stepId: child.id, nodeId: child.nodeId, signal: deadline.signal },
|
|
498
491
|
}),
|
|
492
|
+
deadline.expired,
|
|
499
493
|
]);
|
|
500
494
|
} catch (err) {
|
|
501
495
|
return { status: "failed", error: err instanceof Error ? err.message : String(err) };
|
|
502
496
|
} finally {
|
|
503
|
-
|
|
497
|
+
deadline.clear();
|
|
504
498
|
}
|
|
505
499
|
|
|
506
500
|
if (result.status === "failed") {
|
|
@@ -19,3 +19,33 @@ export function resolveNodeTimeoutMs(node: Pick<WorkflowNode, "type" | "config">
|
|
|
19
19
|
if (node.type === "script" && typeof config.timeout === "number") return config.timeout;
|
|
20
20
|
return DEFAULT_NODE_TIMEOUT_MS;
|
|
21
21
|
}
|
|
22
|
+
|
|
23
|
+
export interface StepDeadline {
|
|
24
|
+
/** Aborted when the budget runs out. The executor gets it as `meta.signal`. */
|
|
25
|
+
signal: AbortSignal;
|
|
26
|
+
/** Rejects with "Step timed out after <ms>ms" when the budget runs out. */
|
|
27
|
+
expired: Promise<never>;
|
|
28
|
+
/** Stop the timer once the executor call settled. */
|
|
29
|
+
clear: () => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The watchdog for one executor call. Racing `expired` alone settles the step
|
|
34
|
+
* but leaves the executor running, so a step reported as timed out could still
|
|
35
|
+
* perform its side effects. On expiry the deadline also aborts `signal`, so an
|
|
36
|
+
* executor that forwards it (swarm-script kills its script process) stops.
|
|
37
|
+
*/
|
|
38
|
+
export function startStepDeadline(timeoutMs: number): StepDeadline {
|
|
39
|
+
const controller = new AbortController();
|
|
40
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
41
|
+
const expired = new Promise<never>((_, reject) => {
|
|
42
|
+
timer = setTimeout(() => {
|
|
43
|
+
const error = new Error(`Step timed out after ${timeoutMs}ms`);
|
|
44
|
+
// Reject first so the race settles on the timeout, not on the
|
|
45
|
+
// executor's "killed" result that the abort produces.
|
|
46
|
+
reject(error);
|
|
47
|
+
controller.abort(error);
|
|
48
|
+
}, timeoutMs);
|
|
49
|
+
});
|
|
50
|
+
return { signal: controller.signal, expired, clear: () => clearTimeout(timer) };
|
|
51
|
+
}
|