@desplega.ai/agent-swarm 2.4.4 → 2.5.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 +4 -2
- package/dist/{acp-adapter-thq04adh.js → acp-adapter-11gp67nr.js} +8 -6
- package/dist/{actions-j6ywh6x5.js → actions-8p2kze8j.js} +24 -22
- package/dist/{amp-adapter-k8b5xmj8.js → amp-adapter-z4qvhacj.js} +2 -2
- package/dist/{anthropic-messages-g6pqmqk4.js → anthropic-messages-txs190dd.js} +17 -20
- package/dist/{app-rb7vc02s.js → app-0qp0a2gk.js} +3 -3
- package/dist/{assistant-ftta1zjf.js → assistant-jedd3ktm.js} +27 -25
- package/dist/{azure-openai-responses-9dbmj346.js → azure-openai-responses-9kfwhmn7.js} +18 -13
- package/dist/{boot-reembed-xns7h5h9.js → boot-reembed-4xwq5tg0.js} +1 -1
- package/dist/{boot-reembed-srp6v6af.js → boot-reembed-n4wq5dd2.js} +1 -1
- package/dist/{boot-scrub-sweep-3qqrh2mz.js → boot-scrub-sweep-69h90xcq.js} +4 -4
- package/dist/{boot-seeds-9ks8hce7.js → boot-seeds-3vn0m9sg.js} +4 -4
- package/dist/{claude-adapter-k5ksyres.js → claude-adapter-221mvad5.js} +8 -6
- package/dist/{claude-managed-adapter-h2y889ge.js → claude-managed-adapter-gt6pa35d.js} +2 -2
- package/dist/{claude-managed-setup-n5a5eqks.js → claude-managed-setup-c3hp36ph.js} +4 -4
- package/dist/{claude-sdk-session-2j3j4kwn.js → claude-sdk-session-8thjsk5e.js} +9729 -9628
- package/dist/{cli-b9fcxxev.js → cli-007cnhm1.js} +50 -4
- package/dist/{cli-j4rqdf2n.js → cli-03vpq45j.js} +2 -2
- package/dist/{cli-amyhm3gj.js → cli-0y6f0ckq.js} +1 -1
- package/dist/{cli-4dg55wsk.js → cli-0z3vjrpm.js} +1 -1
- package/dist/{cli-pccfmtb5.js → cli-179xcyky.js} +11 -11
- package/dist/{cli-3r8samv3.js → cli-1f0gp9vz.js} +3 -2
- package/dist/{cli-hj3debgw.js → cli-1pr672hz.js} +3 -3
- package/dist/{cli-0bqteszy.js → cli-2nf3b08t.js} +328 -25
- package/dist/{cli-t7cyef5g.js → cli-36j4jnk3.js} +4 -4
- package/dist/{cli-er83cjbn.js → cli-3qv8naq8.js} +527 -231
- package/dist/cli-3zwye9sn.js +1193 -0
- package/dist/{cli-60cgh1rv.js → cli-459fk97x.js} +7 -7
- package/dist/{cli-xj0re5tm.js → cli-4w24f7z3.js} +1 -1
- package/dist/{cli-v3yr2gbg.js → cli-5kzk3472.js} +2198 -968
- package/dist/{cli-h7gc0b4r.js → cli-5m84zd2k.js} +55 -18
- package/dist/{cli-w6nadkh4.js → cli-5rvg1ddk.js} +5 -5
- package/dist/{cli-dvkmp9bt.js → cli-6ctwrf1z.js} +20 -10
- package/dist/{cli-9qtjfd60.js → cli-6rg2jn2y.js} +3 -2
- package/dist/{cli-2v045xyf.js → cli-7bwtc6yx.js} +3 -3
- package/dist/{cli-3cgngyd8.js → cli-7g40g8hq.js} +35 -17
- package/dist/{cli-q6r7t299.js → cli-92mqenkc.js} +2379 -154
- package/dist/{cli-cynvc9fj.js → cli-9ce2xw98.js} +8 -8
- package/dist/{cli-gqvh8d8a.js → cli-bgdtv62r.js} +36 -22
- package/dist/{cli-wxbn3yfx.js → cli-cz5nmdxt.js} +2 -2
- package/dist/{cli-wt645v7q.js → cli-dabehxhp.js} +1 -1
- package/dist/{cli-vfha21c9.js → cli-e5bxnjps.js} +2 -2
- package/dist/{cli-p20r8w42.js → cli-fcc5qe3f.js} +763 -268
- package/dist/{cli-1ephgwnq.js → cli-fes51ya2.js} +3 -3
- package/dist/{cli-1d70crsy.js → cli-ftmq4nvk.js} +2 -2
- package/dist/{cli-qkhc5aqy.js → cli-fy1jyyym.js} +13 -13
- package/dist/{cli-s95dknxh.js → cli-g1cw7xtt.js} +1 -1
- package/dist/{cli-qje9k2nh.js → cli-g9ypd5en.js} +12 -2
- package/dist/{cli-74c1wpm0.js → cli-gpv60278.js} +1 -1
- package/dist/{cli-pemywhfq.js → cli-ha9rmwgq.js} +2 -1
- package/dist/{cli-bnz0r3kc.js → cli-j1eghqfm.js} +5 -5
- package/dist/{cli-2zqpkvtt.js → cli-jpxsrxcb.js} +1 -1
- package/dist/{cli-ejs3m1sx.js → cli-kxpfx2xr.js} +1 -1
- package/dist/{cli-37yb7307.js → cli-mbfj11fq.js} +1 -1
- package/dist/{cli-pnp2j5zg.js → cli-mmhj8vd7.js} +2 -2
- package/dist/{cli-xf234ws8.js → cli-n4c8e3s5.js} +2 -0
- package/dist/{cli-a0kcty51.js → cli-n6kmqngw.js} +2 -2
- package/dist/{cli-te6b5wvg.js → cli-nnvjb17s.js} +3 -3
- package/dist/{cli-1dmcq2eh.js → cli-nzfnt2gs.js} +1 -1
- package/dist/{cli-625tb7aq.js → cli-p859hcc8.js} +2 -2
- package/dist/{cli-smvcw9z5.js → cli-pasyvjmm.js} +1 -1
- package/dist/cli-pkaqk57w.js +128 -0
- package/dist/{cli-8nyhf7za.js → cli-qqzmjxkf.js} +129 -105
- package/dist/{cli-zrwpctp4.js → cli-r25qpaxt.js} +161 -63
- package/dist/{cli-nc35pxb7.js → cli-rh5smt8t.js} +19 -4
- package/dist/{cli-qwkcwjdn.js → cli-rycdaj0a.js} +1 -1
- package/dist/{cli-esxerzg7.js → cli-s4sntwt4.js} +2 -2
- package/dist/{cli-b8k7epj1.js → cli-segs7hx9.js} +8 -8
- package/dist/{cli-36ckw0wx.js → cli-stxdrqs2.js} +2 -2
- package/dist/{cli-vdm20wej.js → cli-sy8f9705.js} +4 -4
- package/dist/{cli-1m17dc9d.js → cli-ta8bd7th.js} +2 -2
- package/dist/{cli-2y91wnbh.js → cli-we0ra057.js} +222 -10
- package/dist/{cli-55qmbp6z.js → cli-x21dqn18.js} +5 -5
- package/dist/{cli-dqb4n629.js → cli-x6vf4sad.js} +11 -11
- package/dist/{cli-d2mb5s8a.js → cli-x77gen2p.js} +680 -440
- package/dist/{cli-vq97g1jq.js → cli-xawhpgc9.js} +95 -195
- package/dist/{cli-82d3nkc3.js → cli-xd4eqtr2.js} +2 -2
- package/dist/{cli-9fhkkpj5.js → cli-xt768r5m.js} +2 -2
- package/dist/{cli-zrk6jxv8.js → cli-y4x8ww6s.js} +2 -2
- package/dist/{cli-xc27fwzg.js → cli-y9jvxfhk.js} +3 -3
- package/dist/{cli-44159k28.js → cli-z63a269p.js} +2 -2
- package/dist/{cli-sm8rses9.js → cli-zfdp01hn.js} +8 -5
- package/dist/cli.js +31 -29
- package/dist/{cloudflare-workers-ai-system-one-ch2wxfcp.js → cloudflare-workers-ai-system-one-43v9qkjk.js} +7 -2
- package/dist/{codex-adapter-968xwfy7.js → codex-adapter-tcg1q3jk.js} +6 -4
- package/dist/{codex-login-m819c41r.js → codex-login-rn7c5gxt.js} +1 -1
- package/dist/{codex-session-runner-ht4z99fa.js → codex-session-runner-xfx4wd9x.js} +7 -5
- package/dist/{commands-pcymaeg9.js → commands-ew83nj70.js} +2 -2
- package/dist/{controller-fpjyk46r.js → controller-gnq1k1mf.js} +4 -4
- package/dist/{controllers-connect-mwzx1fh6.js → controllers-connect-tw1dan9r.js} +26 -24
- package/dist/{cursor-adapter-cp4s2e2f.js → cursor-adapter-yq5xgfcg.js} +1 -1
- package/dist/{db-xsjg9jqq.js → db-7gbc53fd.js} +14 -2
- package/dist/{db-sgd12bt9.js → db-t86xrnes.js} +3 -3
- package/dist/{devin-adapter-zgqadvnm.js → devin-adapter-4ycy4vrf.js} +1 -1
- package/dist/{dispatcher-x0jfw2c9.js → dispatcher-64bdyhps.js} +23 -21
- package/dist/{docker-fkgtr75t.js → docker-nfy2gc8t.js} +2 -2
- package/dist/{dsh-adapter-jrq7ma7h.js → dsh-adapter-tvhc4k2d.js} +1 -1
- package/dist/{e2b-16d6ch39.js → e2b-9gkdhm3c.js} +2 -2
- package/dist/{e2b-zn0b4w5w.js → e2b-cjtf8k1z.js} +2 -2
- package/dist/{easter-egg-3d-5cgynaja.js → easter-egg-3d-e4qv8dxj.js} +1 -1
- package/dist/{event-bus-9vpk8gmq.js → event-bus-3z78ep5n.js} +1 -1
- package/dist/{execute-t4e6m3rw.js → execute-0xpz8950.js} +85 -23
- package/dist/{external-channel-8njj4h3b.js → external-channel-e9nw24zv.js} +2 -2
- package/dist/{google-generative-ai-bmq8gkf3.js → google-generative-ai-mk9vcpe1.js} +15 -15
- package/dist/{google-vertex-b1rvp52s.js → google-vertex-q580kz8k.js} +20 -20
- package/dist/{grok-adapter-kkr97v2f.js → grok-adapter-v82srhrx.js} +9 -7
- package/dist/handlers-c9ajzhbn.js +41 -0
- package/dist/{heartbeat-wd03zhn0.js → heartbeat-2vqs1319.js} +26 -24
- package/dist/{hook-xzj2ts2p.js → hook-01nk13wx.js} +7 -5
- package/dist/{hook-f0vghj42.js → hook-jzpheegg.js} +9 -7
- package/dist/{http-4hwxv62p.js → http-fc1k6ec8.js} +859 -252
- package/dist/index-07ef7nkm.js +42 -0
- package/dist/{index-g6tbb6ve.js → index-31ceyf42.js} +1 -1
- package/dist/index-7yts8n9s.js +43 -0
- package/dist/{index-c4ykd0f1.js → index-dr94zbnh.js} +13348 -11611
- package/dist/index-q3t40ryv.js +46 -0
- package/dist/{index-ww2vznkp.js → index-sepja052.js} +1 -1
- package/dist/{index-97q4xn03.js → index-zdycv3ja.js} +135 -79
- package/dist/{index-g216q6jr.js → index-zsyx51ap.js} +1 -4
- package/dist/{keepalive-hrh7xnr9.js → keepalive-5nh2rhmr.js} +4 -4
- package/dist/lead-aayq64jn.js +79 -0
- package/dist/{llama-cpp-classify-vv43gswy.js → llama-cpp-classify-ee7vmd7q.js} +4 -1
- package/dist/{maintenance-e8wnwnqx.js → maintenance-hjhzdt3k.js} +1 -1
- package/dist/{mistral-conversations-1medfwv7.js → mistral-conversations-sd0xf1tn.js} +13 -13
- package/dist/{node-3v3f08f7.js → node-jzjj7r9x.js} +10 -9
- package/dist/{oauth-refresh-sweep-zm0yj4sj.js → oauth-refresh-sweep-y3n1knmd.js} +3 -3
- package/dist/{onboard-dtwcq603.js → onboard-1zq3m7db.js} +2 -2
- package/dist/{openai-codex-responses-de9pvs4d.js → openai-codex-responses-79s8xnbh.js} +29 -23
- package/dist/{openai-completions-mtqge3nf.js → openai-completions-tdbyts0x.js} +21 -20
- package/dist/{openai-decisions-ygk4kkxd.js → openai-decisions-bqhnjyfz.js} +2 -1
- package/dist/{openai-responses-dkyng71g.js → openai-responses-dm3t5vqp.js} +26 -17
- package/dist/{opencode-adapter-x4tqzhkh.js → opencode-adapter-e56086m1.js} +7 -5
- package/dist/{openrouter-images-wvv7camj.js → openrouter-images-ssttr1y4.js} +6 -3
- package/dist/{otel-impl-w3fbhpn0.js → otel-impl-h9yyza47.js} +2 -2
- package/dist/{pi-messages-nhh1pkz2.js → pi-messages-3nf8bq1a.js} +6 -4
- package/dist/{pi-mono-adapter-mcfkn9vf.js → pi-mono-adapter-tzps8f38.js} +9 -37
- package/dist/{pricing-refresh-j38dehfs.js → pricing-refresh-nevhat8h.js} +3 -3
- package/dist/{rbac-roles-ae8yttq4.js → rbac-roles-4y6ngvzq.js} +1 -1
- package/dist/{rbac-roles-ejxx9b35.js → rbac-roles-exbg8xb6.js} +2 -2
- package/dist/{registry-rxw10xzg.js → registry-ajhexe44.js} +1 -1
- package/dist/{render-v2-n16n0phe.js → render-v2-cfsh13z3.js} +3 -3
- package/dist/{resume-z0fgjc38.js → resume-5s2t0dbs.js} +10 -8
- package/dist/{runtime-xe3aqy4x.js → runtime-bt5pkpm1.js} +1 -1
- package/dist/{session-access-cv3mq6mc.js → session-access-dnbyp3cn.js} +6 -4
- package/dist/{setup-mafhj3yt.js → setup-1wj1y77j.js} +1 -1
- package/dist/{typesafe-system-one-2hgpayzy.js → typesafe-system-one-bre38hvs.js} +4 -2
- package/dist/{virtual-modules-4r3zkc20.js → virtual-modules-mnfbh5mr.js} +8 -2
- package/dist/{work-objects-8b86mgjp.js → work-objects-7mg6216g.js} +26 -24
- package/dist/worker-nwb789mg.js +79 -0
- package/dist/{x-y45mthwn.js → x-pbpezr0s.js} +2 -2
- package/openapi.json +793 -6
- package/package.json +11 -11
- package/src/apps/catalog.generated.json +206 -4
- package/src/apps/page-validator.ts +13 -1
- package/src/assets/key.ts +17 -0
- package/src/azure-devops/handlers.ts +1 -1
- package/src/be/chunking.ts +42 -9
- package/src/be/db/approvals.ts +18 -0
- package/src/be/db/session-moves.ts +164 -0
- package/src/be/db/session-tree.ts +384 -0
- package/src/be/db/tasks/draft-text.ts +15 -0
- package/src/be/db/tasks/read.ts +54 -5
- package/src/be/db/workflow-runs.ts +96 -0
- package/src/be/db.ts +85 -237
- package/src/be/memory/index-content.ts +36 -17
- package/src/be/memory/providers/openai-embedding.ts +115 -61
- package/src/be/memory/providers/sqlite-store.ts +69 -23
- package/src/be/memory/reranker.ts +31 -1
- package/src/be/memory/search-candidates.ts +47 -0
- package/src/be/memory/types.ts +14 -0
- package/src/be/onboarding.ts +3 -0
- package/src/be/scripts/typecheck.ts +2 -0
- package/src/be/seed-scripts/catalog/heartbeat-jev-gate.ts +106 -54
- package/src/be/seed-scripts/index.ts +2 -2
- package/src/be/skill-sync.ts +43 -2
- package/src/be/swarm-config-guard.ts +25 -1
- package/src/be/task-attachment-store.ts +30 -0
- package/src/bitbucket/api.ts +81 -0
- package/src/bitbucket/auth.ts +64 -0
- package/src/bitbucket/handlers.ts +434 -0
- package/src/bitbucket/index.ts +28 -0
- package/src/bitbucket/templates.ts +134 -0
- package/src/bitbucket/types.ts +65 -0
- package/src/commands/runner.ts +55 -3
- package/src/e2b/env.ts +1 -0
- package/src/extensions/catalog.generated.json +4 -4
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/extensions/contract.ts +9 -2
- package/src/extensions/post-bridge.ts +1 -0
- package/src/fs/provider.ts +13 -1
- package/src/github/handlers.ts +6 -6
- package/src/gitlab/handlers.ts +5 -5
- package/src/heartbeat/heartbeat.ts +31 -11
- package/src/http/assets.ts +14 -2
- package/src/http/core.ts +12 -0
- package/src/http/fs.ts +54 -34
- package/src/http/index.ts +4 -0
- package/src/http/mcp-body.ts +37 -0
- package/src/http/mcp-user.ts +3 -5
- package/src/http/mcp.ts +6 -7
- package/src/http/memory.ts +26 -15
- package/src/http/onboarding.ts +1 -0
- package/src/http/page-proxy.ts +61 -5
- package/src/http/pages-public.ts +27 -44
- package/src/http/pages.ts +4 -4
- package/src/http/script-runs.ts +2 -0
- package/src/http/sessions.ts +366 -12
- package/src/http/skills.ts +48 -1
- package/src/http/tasks.ts +1 -1
- package/src/http/users.ts +13 -1
- package/src/http/webhooks.ts +141 -0
- package/src/integrations/chat-sdk/adapters.ts +24 -4
- package/src/integrations/chat-sdk/attachments.ts +90 -12
- package/src/integrations/chat-sdk/facts.ts +138 -0
- package/src/integrations/chat-sdk/inbound.ts +156 -8
- package/src/integrations/chat-sdk/limits.ts +24 -0
- package/src/integrations/chat-sdk/outbound-files.ts +186 -0
- package/src/integrations/chat-sdk/outbound.ts +272 -5
- package/src/integrations/chat-sdk/relay.ts +4 -1
- package/src/integrations/chat-sdk/router.ts +28 -2
- package/src/integrations/chat-sdk/steering.ts +9 -0
- package/src/integrations/chat-sdk/templates.ts +14 -0
- package/src/integrations/chat-sdk/thread-buffer.ts +4 -0
- package/src/mcp-limits.ts +20 -0
- package/src/pages/browser-sdk.ts +8 -3
- package/src/pages/feedback-overlay.ts +1 -1
- package/src/providers/acp-targets.ts +1 -1
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/codex-adapter.ts +106 -119
- package/src/providers/codex-agents-md.ts +35 -229
- package/src/providers/cursor-adapter.ts +1 -1
- package/src/providers/pi-mono-adapter.ts +5 -53
- package/src/realtime/transport.ts +1 -2
- package/src/script-workflows/workflow-ctx.ts +1 -0
- package/src/scripts-runtime/types/stdlib.d.ts +2 -0
- package/src/scripts-runtime/types/swarm-extension.d.ts +14 -4
- package/src/scripts-runtime/types/swarm-sdk.d.ts +2 -0
- package/src/server-user.ts +2 -0
- package/src/server.ts +2 -0
- package/src/slack/assistant.ts +2 -2
- package/src/slack/files.ts +2 -0
- package/src/slack/handlers.test.ts +158 -0
- package/src/slack/handlers.ts +23 -32
- package/src/slack/inbound-files.ts +39 -6
- package/src/slack/thread-buffer.ts +2 -1
- package/src/tasks/context-key.ts +61 -1
- package/src/tasks/task-terminal-effects.ts +21 -17
- package/src/tests/apps-card-grid.test.ts +197 -0
- package/src/tests/apps-catalog-additions.test.ts +120 -0
- package/src/tests/apps-tree.test.ts +129 -0
- package/src/tests/bitbucket-auth.test.ts +156 -0
- package/src/tests/bitbucket-handlers.test.ts +425 -0
- package/src/tests/bitbucket-webhook.test.ts +220 -0
- package/src/tests/chat-sdk-attachments-reply.test.ts +52 -0
- package/src/tests/chat-sdk-config.test.ts +11 -0
- package/src/tests/chat-sdk-facts.test.ts +23 -0
- package/src/tests/chat-sdk-inbound.test.ts +597 -4
- package/src/tests/chat-sdk-outbound-files.test.ts +652 -0
- package/src/tests/chat-sdk-telegram-markdown.test.ts +92 -0
- package/src/tests/codex-adapter.test.ts +167 -242
- package/src/tests/config-api-only-keys.test.ts +9 -0
- package/src/tests/context-key.test.ts +50 -0
- package/src/tests/core-auth.test.ts +5 -0
- package/src/tests/cors-allowlist.test.ts +1 -1
- package/src/tests/db-task-read-contract.test.ts +7 -0
- package/src/tests/db-task-write-contract.test.ts +6 -0
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/extensions-example-slack-reply-gate.test.ts +262 -91
- package/src/tests/fixtures/bitbucket/pullrequest-comment-created-inline.json +174 -0
- package/src/tests/fixtures/bitbucket/pullrequest-comment-created.json +170 -0
- package/src/tests/fixtures/bitbucket/pullrequest-created-mention.json +132 -0
- package/src/tests/fixtures/bitbucket/pullrequest-created-reviewer.json +140 -0
- package/src/tests/fixtures/bitbucket/pullrequest-fulfilled.json +134 -0
- package/src/tests/fixtures/bitbucket/pullrequest-rejected.json +133 -0
- package/src/tests/generate-mcp-docs.test.ts +101 -0
- package/src/tests/gitlab-handlers.test.ts +3 -3
- package/src/tests/gitlab-vcs-db.test.ts +4 -4
- package/src/tests/heartbeat-checklist.test.ts +33 -0
- package/src/tests/heartbeat-jev-gate-credentials.test.ts +44 -0
- package/src/tests/heartbeat-jev-gate.test.ts +242 -1
- package/src/tests/integration-disable-flags.test.ts +36 -0
- package/src/tests/kv-page-proxy.test.ts +91 -5
- package/src/tests/launch-password-rejection.test.ts +2 -2
- package/src/tests/mcp-input-caps.test.ts +294 -0
- package/src/tests/mcp-tools.test.ts +1 -1
- package/src/tests/memory-chunked-search-starvation.test.ts +200 -0
- package/src/tests/memory-embedding-input-cap.test.ts +114 -0
- package/src/tests/memory.test.ts +14 -0
- package/src/tests/onboarding.test.ts +12 -0
- package/src/tests/page-guest-session.test.ts +19 -8
- package/src/tests/page-proxy-authed.test.ts +1 -1
- package/src/tests/page-proxy.test.ts +17 -10
- package/src/tests/page-session.test.ts +102 -1
- package/src/tests/pages-authed-mode.test.ts +82 -12
- package/src/tests/pages-password-mode.test.ts +5 -5
- package/src/tests/pi-codemode.test.ts +8 -2
- package/src/tests/preload-env.test.ts +57 -0
- package/src/tests/preload.ts +21 -0
- package/src/tests/prompt-template-remaining.test.ts +38 -0
- package/src/tests/realtime-transport.test.ts +13 -7
- package/src/tests/run-root-coverage.test.ts +39 -0
- package/src/tests/runner-bitbucket-vcs.test.ts +191 -0
- package/src/tests/runner-skills-refresh.test.ts +58 -0
- package/src/tests/script-runs-http.test.ts +84 -0
- package/src/tests/scripts-http.test.ts +10 -1
- package/src/tests/secret-scrubber.test.ts +15 -0
- package/src/tests/sessions-move.test.ts +576 -0
- package/src/tests/sessions-tree.test.ts +493 -0
- package/src/tests/skill-files-http.test.ts +50 -0
- package/src/tests/skills-bundle.test.ts +99 -0
- package/src/tests/swarm-config-export-to-env.test.ts +23 -3
- package/src/tests/task-audio-transcription.test.ts +299 -0
- package/src/tests/task-completion-memory-chunking.test.ts +434 -0
- package/src/tests/task-terminal-secret-scrub.test.ts +6 -1
- package/src/tests/transcription.test.ts +299 -0
- package/src/tests/vcs-bitbucket.test.ts +91 -0
- package/src/tests/vcs-provider-isolation.test.ts +123 -0
- package/src/tests/vcs-provider.test.ts +16 -1
- package/src/tests/vcs-tracking.test.ts +3 -3
- package/src/tests/workflow-foreach-bodies.test.ts +637 -0
- package/src/tests/workflow-foreach-concurrency.test.ts +342 -0
- package/src/tests/workflow-foreach-retry.test.ts +537 -0
- package/src/tests/workflow-foreach.test.ts +44 -23
- package/src/tests/workflow-step-claims.test.ts +6 -6
- package/src/tests/workflow-system-one-decision-providers.test.ts +98 -1
- package/src/tests/workflow-system-one-decision.test.ts +2 -1
- package/src/tests/workflow-tla-races.test.ts +2 -2
- package/src/tools/chat-access.ts +10 -0
- package/src/tools/chat-post.ts +31 -11
- package/src/tools/chat-reply.ts +32 -10
- package/src/tools/memory-search.ts +3 -1
- package/src/tools/script-delete.ts +1 -1
- package/src/tools/system-one-decide.ts +17 -4
- package/src/tools/workflows/create-workflow.ts +5 -3
- package/src/types.ts +9 -2
- package/src/utils/config-env-export.ts +4 -1
- package/src/utils/page-session.ts +133 -20
- package/src/utils/secret-scrubber.ts +4 -0
- package/src/utils/skills-refresh.ts +83 -43
- package/src/utils/transcription.ts +246 -0
- package/src/vcs/bitbucket.ts +58 -0
- package/src/vcs/index.ts +2 -0
- package/src/vcs/types.ts +2 -2
- package/src/workflows/approval-resolution.ts +4 -1
- package/src/workflows/checkpoint.ts +9 -1
- package/src/workflows/constants.ts +3 -0
- package/src/workflows/definition.ts +48 -17
- package/src/workflows/engine.ts +25 -15
- package/src/workflows/executors/foreach.ts +45 -99
- package/src/workflows/executors/system-one-decision.ts +28 -7
- package/src/workflows/executors/system-one-providers.ts +39 -4
- package/src/workflows/foreach-body.ts +146 -0
- package/src/workflows/foreach-driver.ts +552 -0
- package/src/workflows/foreach-join.ts +206 -7
- package/src/workflows/node-timeout.ts +21 -0
- package/src/workflows/recovery.ts +68 -3
- package/src/workflows/resume.ts +221 -43
- package/src/workflows/retry-poller.ts +39 -24
- package/src/workflows/step-input.ts +59 -0
- package/src/workflows/task-step-routing.ts +114 -56
- package/templates/extensions/slack-reply-gate/README.md +24 -22
- package/templates/extensions/slack-reply-gate/hooks.ts +146 -95
- package/templates/extensions/slack-reply-gate/manifest.json +2 -2
- package/templates/skills/apps/SKILL.md +28 -6
- package/templates/skills/apps/content.md +28 -6
- package/templates/skills/chat-sdk/content.md +6 -2
- package/templates/skills/code-quality/content.md +2 -1
- package/templates/skills/heartbeat-runbook/content.md +5 -5
- package/templates/skills/pages/content.md +6 -4
- package/templates/skills/slack-interaction/content.md +5 -0
- package/templates/skills/swarm-scripts/SKILL.md +2 -2
- package/templates/skills/swarm-scripts/content.md +2 -2
- package/templates/skills/work-on-task/SKILL.md +1 -1
- package/templates/skills/work-on-task/content.md +1 -1
- package/dist/cli-51y09c6p.js +0 -2657
- package/dist/cli-yw91rnmw.js +0 -13
- package/dist/handlers-0pvee9nw.js +0 -39
- package/dist/index-gdh8jrdm.js +0 -43
- package/dist/index-hg0x2cdv.js +0 -40
- package/dist/index-thm0y6a8.js +0 -41
- package/dist/lead-ee8vazz2.js +0 -77
- package/dist/worker-16s75hm5.js +0 -77
package/src/workflows/resume.ts
CHANGED
|
@@ -17,8 +17,14 @@ import {
|
|
|
17
17
|
updateWorkflowRun,
|
|
18
18
|
updateWorkflowRunStep,
|
|
19
19
|
} from "../be/db";
|
|
20
|
-
import { getChildWorkflowRunId } from "../be/db/workflow-runs";
|
|
21
|
-
import type {
|
|
20
|
+
import { createForeachAttemptSteps, getChildWorkflowRunId } from "../be/db/workflow-runs";
|
|
21
|
+
import type {
|
|
22
|
+
WaitStateRow,
|
|
23
|
+
WorkflowDefinition,
|
|
24
|
+
WorkflowNode,
|
|
25
|
+
WorkflowRun,
|
|
26
|
+
WorkflowRunStep,
|
|
27
|
+
} from "../types";
|
|
22
28
|
import { scrubSecrets } from "../utils/secret-scrubber";
|
|
23
29
|
import {
|
|
24
30
|
type ApprovalSlackClient,
|
|
@@ -34,8 +40,17 @@ import { workflowEventBus } from "./event-bus";
|
|
|
34
40
|
import type { ExecutorRegistry } from "./executors/registry";
|
|
35
41
|
import { childRunOutcome } from "./executors/sub-workflow";
|
|
36
42
|
import { computeNextPort } from "./executors/wait";
|
|
37
|
-
import {
|
|
43
|
+
import { stepFailureMode } from "./foreach-body";
|
|
44
|
+
import { startForeachDrive } from "./foreach-driver";
|
|
45
|
+
import {
|
|
46
|
+
FOREACH_TERMINAL_STEP_STATUSES,
|
|
47
|
+
type ForeachChildOutcome,
|
|
48
|
+
parseSyntheticNodeId,
|
|
49
|
+
resolveForeachParent,
|
|
50
|
+
settleForeachChild,
|
|
51
|
+
} from "./foreach-join";
|
|
38
52
|
import { getSecretInputKeys } from "./input";
|
|
53
|
+
import { getMaxWorkflowStepsPerRun } from "./limits";
|
|
39
54
|
import { findWorkflowReadinessProblems, formatReadinessProblems } from "./readiness";
|
|
40
55
|
import {
|
|
41
56
|
checkpointPortStepAndResolveSuccessors,
|
|
@@ -181,17 +196,7 @@ async function resumeFromTaskCompletionUnguarded(
|
|
|
181
196
|
registry: ExecutorRegistry,
|
|
182
197
|
): Promise<void> {
|
|
183
198
|
const run = await getWorkflowRun(event.workflowRunId!);
|
|
184
|
-
if (!run
|
|
185
|
-
|
|
186
|
-
const step = await getWorkflowRunStep(event.workflowRunStepId!);
|
|
187
|
-
if (!step || step.status !== "waiting") return;
|
|
188
|
-
if (await isStaleTaskEvent(step.id, event)) return;
|
|
189
|
-
|
|
190
|
-
const workflow = await getWorkflow(run.workflowId);
|
|
191
|
-
if (!workflow) return;
|
|
192
|
-
|
|
193
|
-
// Checkpoint: atomic step completion + context update
|
|
194
|
-
const ctx = (run.context ?? {}) as Record<string, unknown>;
|
|
199
|
+
if (!run) return;
|
|
195
200
|
|
|
196
201
|
// JSON-parse structured output so downstream nodes can access nested fields
|
|
197
202
|
let taskOutput: unknown = event.output;
|
|
@@ -207,12 +212,33 @@ async function resumeFromTaskCompletionUnguarded(
|
|
|
207
212
|
}
|
|
208
213
|
const stepOutput = { taskId: event.taskId, taskOutput };
|
|
209
214
|
|
|
215
|
+
if (run.status !== "waiting" && run.status !== "running") {
|
|
216
|
+
await recordLateForeachOutcome(run, event.workflowRunStepId!, event, {
|
|
217
|
+
status: "completed",
|
|
218
|
+
output: stepOutput,
|
|
219
|
+
});
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const step = await getWorkflowRunStep(event.workflowRunStepId!);
|
|
224
|
+
if (!step || step.status !== "waiting") return;
|
|
225
|
+
if (await isStaleTaskEvent(step.id, event)) return;
|
|
226
|
+
|
|
227
|
+
const workflow = await getWorkflow(run.workflowId);
|
|
228
|
+
if (!workflow) return;
|
|
229
|
+
|
|
230
|
+
// Checkpoint: atomic step completion + context update
|
|
231
|
+
const ctx = (run.context ?? {}) as Record<string, unknown>;
|
|
232
|
+
|
|
210
233
|
const routing = await completeTaskStepAndResolveSuccessors(
|
|
211
234
|
workflow.definition,
|
|
212
235
|
run.id,
|
|
213
236
|
step,
|
|
214
237
|
stepOutput,
|
|
215
238
|
ctx,
|
|
239
|
+
undefined,
|
|
240
|
+
undefined,
|
|
241
|
+
registry,
|
|
216
242
|
);
|
|
217
243
|
// Another handler already routed this step — routing it twice would create
|
|
218
244
|
// duplicate successor steps.
|
|
@@ -299,7 +325,14 @@ async function handleTaskFailureUnguarded(
|
|
|
299
325
|
options: { retryable: boolean },
|
|
300
326
|
): Promise<void> {
|
|
301
327
|
const run = await getWorkflowRun(event.workflowRunId!);
|
|
302
|
-
if (!run
|
|
328
|
+
if (!run) return;
|
|
329
|
+
if (run.status !== "waiting" && run.status !== "running") {
|
|
330
|
+
await recordLateForeachOutcome(run, event.workflowRunStepId!, event, {
|
|
331
|
+
status: "failed",
|
|
332
|
+
error: reason,
|
|
333
|
+
});
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
303
336
|
|
|
304
337
|
const step = await getWorkflowRunStep(event.workflowRunStepId!);
|
|
305
338
|
if (!step || step.status !== "waiting") return;
|
|
@@ -325,10 +358,12 @@ async function handleTaskFailureUnguarded(
|
|
|
325
358
|
if (retry === "not-claimed") return;
|
|
326
359
|
}
|
|
327
360
|
|
|
328
|
-
|
|
361
|
+
// A foreach item follows its fan-out's failure policy (fail-fast or
|
|
362
|
+
// collect-outcomes); any other step follows the workflow's onNodeFailure.
|
|
363
|
+
const onFailure = stepFailureMode(workflow.definition, step.nodeId);
|
|
329
364
|
|
|
330
365
|
if (onFailure === "fail") {
|
|
331
|
-
await markRunFailed(event, reason);
|
|
366
|
+
await markRunFailed(workflow.definition, event, reason, registry);
|
|
332
367
|
return;
|
|
333
368
|
}
|
|
334
369
|
|
|
@@ -345,6 +380,8 @@ async function handleTaskFailureUnguarded(
|
|
|
345
380
|
stepOutput,
|
|
346
381
|
ctx,
|
|
347
382
|
reason,
|
|
383
|
+
undefined,
|
|
384
|
+
registry,
|
|
348
385
|
);
|
|
349
386
|
if (!routing.claimed) return;
|
|
350
387
|
|
|
@@ -367,6 +404,32 @@ async function handleTaskFailureUnguarded(
|
|
|
367
404
|
}
|
|
368
405
|
}
|
|
369
406
|
|
|
407
|
+
/**
|
|
408
|
+
* A fail-fast fan-out lets its in-flight items finish after the run failed.
|
|
409
|
+
* Record such a late outcome on the item row so the run's history (and a
|
|
410
|
+
* later retry) sees it; the run itself stays failed and nothing routes.
|
|
411
|
+
*/
|
|
412
|
+
async function recordLateForeachOutcome(
|
|
413
|
+
run: WorkflowRun,
|
|
414
|
+
stepId: string,
|
|
415
|
+
event: TaskEvent,
|
|
416
|
+
outcome: ForeachChildOutcome,
|
|
417
|
+
): Promise<void> {
|
|
418
|
+
if (run.status !== "failed") return;
|
|
419
|
+
const step = await getWorkflowRunStep(stepId);
|
|
420
|
+
if (!step || step.status !== "waiting") return;
|
|
421
|
+
if (await isStaleTaskEvent(step.id, event)) return;
|
|
422
|
+
const workflow = await getWorkflow(run.workflowId);
|
|
423
|
+
if (!workflow || !resolveForeachParent(workflow.definition, step.nodeId)) return;
|
|
424
|
+
await settleForeachChild(
|
|
425
|
+
workflow.definition,
|
|
426
|
+
run.id,
|
|
427
|
+
step.id,
|
|
428
|
+
outcome,
|
|
429
|
+
(run.context ?? {}) as Record<string, unknown>,
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
|
|
370
433
|
/**
|
|
371
434
|
* A retried step has a NEW task bound to it. Task lifecycle events are emitted
|
|
372
435
|
* from several places (the db mutators' after-commit emits, test/manual emits,
|
|
@@ -386,8 +449,20 @@ async function isStaleTaskEvent(stepId: string, event: TaskEvent): Promise<boole
|
|
|
386
449
|
* both route here and can also race a recovery sweep or a completion that
|
|
387
450
|
* already claimed the step — only the first writer may fail the run.
|
|
388
451
|
*/
|
|
389
|
-
async function markRunFailed(
|
|
390
|
-
|
|
452
|
+
async function markRunFailed(
|
|
453
|
+
def: WorkflowDefinition,
|
|
454
|
+
event: TaskEvent,
|
|
455
|
+
reason: string,
|
|
456
|
+
registry: ExecutorRegistry,
|
|
457
|
+
): Promise<void> {
|
|
458
|
+
await failStepAndRunIfWaiting(
|
|
459
|
+
def,
|
|
460
|
+
event.workflowRunStepId!,
|
|
461
|
+
event.workflowRunId!,
|
|
462
|
+
reason,
|
|
463
|
+
undefined,
|
|
464
|
+
{ registry },
|
|
465
|
+
);
|
|
391
466
|
}
|
|
392
467
|
|
|
393
468
|
/**
|
|
@@ -400,9 +475,11 @@ export async function retryFailedRun(runId: string, registry: ExecutorRegistry):
|
|
|
400
475
|
const workflow = await getWorkflow(run.workflowId);
|
|
401
476
|
if (!workflow) throw new Error("Workflow not found");
|
|
402
477
|
|
|
403
|
-
// Find the failed step
|
|
478
|
+
// Find the failed step. Only the latest row per node id counts: a foreach
|
|
479
|
+
// item keeps its earlier attempt rows as history.
|
|
404
480
|
const steps = await getWorkflowRunStepsByRunId(runId);
|
|
405
|
-
const
|
|
481
|
+
const latestSteps = [...latestStepPerNode(steps).values()];
|
|
482
|
+
const failedStep = latestSteps.find((s) => s.status === "failed");
|
|
406
483
|
if (!failedStep) throw new Error("No failed step found");
|
|
407
484
|
|
|
408
485
|
const completedNodeIds = new Set(await getCompletedStepNodeIds(runId));
|
|
@@ -438,17 +515,36 @@ export async function retryFailedRun(runId: string, registry: ExecutorRegistry):
|
|
|
438
515
|
// (HTTP route + MCP tool) both pass the guard above, and a double reset
|
|
439
516
|
// walks the same failed node twice.
|
|
440
517
|
const ctx = (run.context ?? {}) as Record<string, unknown>;
|
|
441
|
-
const
|
|
518
|
+
const attempts = await getDbClient().transaction(async () => {
|
|
442
519
|
const current = await getWorkflowRun(runId);
|
|
443
|
-
if (!current || current.status !== "failed") return
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
520
|
+
if (!current || current.status !== "failed") return null;
|
|
521
|
+
// Fan-outs with failed or cancelled items get one new attempt row per such
|
|
522
|
+
// item; completed items are never rerun. Selected from the rows as of this
|
|
523
|
+
// transaction: an in-flight sibling of a fail-fast fan-out can record its
|
|
524
|
+
// late failure while the readiness check above is awaited.
|
|
525
|
+
const currentSteps = await getWorkflowRunStepsByRunId(runId);
|
|
526
|
+
const attempts = foreachRetryAttempts(workflow.definition, [
|
|
527
|
+
...latestStepPerNode(currentSteps).values(),
|
|
528
|
+
]);
|
|
529
|
+
const capError = foreachRetryCapError(currentSteps.length, attempts);
|
|
530
|
+
if (capError) throw new Error(`Retry not started, run left failed: ${capError}`);
|
|
531
|
+
// A foreach item retries as a new attempt row instead of a reset: a fresh
|
|
532
|
+
// step id is a fresh invocation identity for every body type, and the
|
|
533
|
+
// failed row stays as history.
|
|
534
|
+
if (!foreachParent) {
|
|
535
|
+
await updateWorkflowRunStep(failedStep.id, {
|
|
536
|
+
status: shouldRetryFailedNode ? "pending" : "cancelled",
|
|
537
|
+
error: shouldRetryFailedNode ? null : "Skipped on retry because its branch was not active",
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
await createForeachAttemptSteps(
|
|
541
|
+
runId,
|
|
542
|
+
attempts.flatMap((attempt) => attempt.rows),
|
|
543
|
+
);
|
|
448
544
|
await updateWorkflowRun(runId, { status: "running", error: null, context: ctx });
|
|
449
|
-
return
|
|
545
|
+
return attempts;
|
|
450
546
|
});
|
|
451
|
-
if (!
|
|
547
|
+
if (!attempts) throw new Error("Run is not in failed state");
|
|
452
548
|
|
|
453
549
|
// Resume from the failed node — use findReadyNodes for convergence safety.
|
|
454
550
|
// findReadyNodes returns every node without a completed step, including a
|
|
@@ -469,6 +565,75 @@ export async function retryFailedRun(runId: string, registry: ExecutorRegistry):
|
|
|
469
565
|
: [failedNode, ...readyNodes];
|
|
470
566
|
const secretKeys = getSecretInputKeys(workflow.input);
|
|
471
567
|
await walkGraph(workflow.definition, runId, ctx, nodesToRun, registry, workflow.id, secretKeys);
|
|
568
|
+
// Another fan-out with new attempts is not on the walk (its parent step is
|
|
569
|
+
// still waiting): drive it directly.
|
|
570
|
+
for (const attempt of attempts) {
|
|
571
|
+
if (attempt.parent.id !== failedNode.id) {
|
|
572
|
+
startForeachDrive(runId, attempt.parent.id, registry);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/** The latest row per node id, in insert order of each node's first row. */
|
|
578
|
+
function latestStepPerNode(steps: WorkflowRunStep[]): Map<string, WorkflowRunStep> {
|
|
579
|
+
const latest = new Map<string, WorkflowRunStep>();
|
|
580
|
+
for (const step of steps) latest.set(step.nodeId, step);
|
|
581
|
+
return latest;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
interface ForeachRetryAttempts {
|
|
585
|
+
parent: WorkflowNode;
|
|
586
|
+
rows: Array<{ nodeId: string; nodeType: string; itemKey: string; index: number }>;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* The new attempt rows a run retry adds: one per foreach item whose latest
|
|
591
|
+
* row is `failed` or `cancelled`, for every fan-out whose parent step is still
|
|
592
|
+
* open (a closed join already routed its successors).
|
|
593
|
+
*/
|
|
594
|
+
function foreachRetryAttempts(
|
|
595
|
+
def: WorkflowDefinition,
|
|
596
|
+
latestSteps: WorkflowRunStep[],
|
|
597
|
+
): ForeachRetryAttempts[] {
|
|
598
|
+
const byParent = new Map<string, ForeachRetryAttempts>();
|
|
599
|
+
const latestByNode = new Map(latestSteps.map((step) => [step.nodeId, step]));
|
|
600
|
+
for (const step of latestSteps) {
|
|
601
|
+
if (step.status !== "failed" && step.status !== "cancelled") continue;
|
|
602
|
+
const parent = resolveForeachParent(def, step.nodeId);
|
|
603
|
+
if (!parent) continue;
|
|
604
|
+
const parentStep = latestByNode.get(parent.id);
|
|
605
|
+
if (!parentStep || FOREACH_TERMINAL_STEP_STATUSES.has(parentStep.status)) continue;
|
|
606
|
+
const input = (step.input ?? {}) as Record<string, unknown>;
|
|
607
|
+
const entry = byParent.get(parent.id) ?? { parent, rows: [] };
|
|
608
|
+
entry.rows.push({
|
|
609
|
+
nodeId: step.nodeId,
|
|
610
|
+
nodeType: step.nodeType,
|
|
611
|
+
itemKey:
|
|
612
|
+
typeof input.itemKey === "string"
|
|
613
|
+
? input.itemKey
|
|
614
|
+
: (parseSyntheticNodeId(step.nodeId)?.itemKey ?? ""),
|
|
615
|
+
index: typeof input.index === "number" ? input.index : 0,
|
|
616
|
+
});
|
|
617
|
+
byParent.set(parent.id, entry);
|
|
618
|
+
}
|
|
619
|
+
return [...byParent.values()];
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Refuse a retry whose new attempt rows would breach WORKFLOW_MAX_STEPS_PER_RUN.
|
|
624
|
+
* The retry's own walk runs walkGraph's circuit breaker after the rows are
|
|
625
|
+
* inserted, and it trips at `>= max`, so the rows must leave one step of
|
|
626
|
+
* headroom even for a terminal fan-out.
|
|
627
|
+
*/
|
|
628
|
+
function foreachRetryCapError(
|
|
629
|
+
existingSteps: number,
|
|
630
|
+
attempts: ForeachRetryAttempts[],
|
|
631
|
+
): string | null {
|
|
632
|
+
const added = attempts.reduce((sum, attempt) => sum + attempt.rows.length, 0);
|
|
633
|
+
if (added === 0) return null;
|
|
634
|
+
const maxSteps = getMaxWorkflowStepsPerRun();
|
|
635
|
+
if (existingSteps + added < maxSteps) return null;
|
|
636
|
+
return `foreach retry would reach ${maxSteps} total steps (WORKFLOW_MAX_STEPS_PER_RUN): ${existingSteps} existing + ${added} new attempts leaves no room for the retry walk`;
|
|
472
637
|
}
|
|
473
638
|
|
|
474
639
|
/**
|
|
@@ -646,6 +811,7 @@ async function resumeFromApprovalResolutionUnguarded(
|
|
|
646
811
|
stepOutput,
|
|
647
812
|
nextPort,
|
|
648
813
|
ctx,
|
|
814
|
+
registry,
|
|
649
815
|
);
|
|
650
816
|
// Another handler (the recovery sweep) already routed this step — routing
|
|
651
817
|
// it twice would create duplicate successor steps.
|
|
@@ -768,6 +934,7 @@ async function resumeClaimedWait(
|
|
|
768
934
|
stepOutput,
|
|
769
935
|
nextPort,
|
|
770
936
|
ctx,
|
|
937
|
+
registry,
|
|
771
938
|
);
|
|
772
939
|
if (!routing.claimed) return;
|
|
773
940
|
|
|
@@ -822,22 +989,33 @@ export async function resumeFromChildRun(
|
|
|
822
989
|
const workflow = await getWorkflow(run.workflowId);
|
|
823
990
|
if (!workflow) return;
|
|
824
991
|
|
|
825
|
-
|
|
826
|
-
|
|
992
|
+
const ctx = (run.context ?? {}) as Record<string, unknown>;
|
|
993
|
+
// A foreach item's failure settles the item: under `onNodeFailure:
|
|
994
|
+
// "continue"` it can close the join, so it routes like a success.
|
|
995
|
+
const routing =
|
|
996
|
+
"error" in outcome
|
|
997
|
+
? await failStepAndRunIfWaiting(
|
|
998
|
+
workflow.definition,
|
|
999
|
+
step.id,
|
|
1000
|
+
run.id,
|
|
1001
|
+
outcome.error,
|
|
1002
|
+
undefined,
|
|
1003
|
+
{ ctx, registry },
|
|
1004
|
+
)
|
|
1005
|
+
: await checkpointPortStepAndResolveSuccessors(
|
|
1006
|
+
workflow.definition,
|
|
1007
|
+
run.id,
|
|
1008
|
+
step.id,
|
|
1009
|
+
step.nodeId,
|
|
1010
|
+
outcome.output,
|
|
1011
|
+
"success",
|
|
1012
|
+
ctx,
|
|
1013
|
+
registry,
|
|
1014
|
+
);
|
|
1015
|
+
if (!routing.claimed || routing.runFailed) {
|
|
1016
|
+
claimed = routing.claimed;
|
|
827
1017
|
return;
|
|
828
1018
|
}
|
|
829
|
-
|
|
830
|
-
const ctx = (run.context ?? {}) as Record<string, unknown>;
|
|
831
|
-
const routing = await checkpointPortStepAndResolveSuccessors(
|
|
832
|
-
workflow.definition,
|
|
833
|
-
run.id,
|
|
834
|
-
step.id,
|
|
835
|
-
step.nodeId,
|
|
836
|
-
outcome.output,
|
|
837
|
-
"success",
|
|
838
|
-
ctx,
|
|
839
|
-
);
|
|
840
|
-
if (!routing.claimed) return;
|
|
841
1019
|
claimed = true;
|
|
842
1020
|
|
|
843
1021
|
if (routing.successors.length > 0) {
|
|
@@ -10,16 +10,15 @@ import type { RetryPolicy } from "../types";
|
|
|
10
10
|
import { checkpointStep, checkpointStepFailure, checkpointStepWaiting } from "./checkpoint";
|
|
11
11
|
import { getSuccessors, resolveValidationPort } from "./definition";
|
|
12
12
|
import {
|
|
13
|
-
buildNodeInterpolationCtx,
|
|
14
13
|
failRunOnUnreadableReplay,
|
|
15
14
|
holdWorkflowRun,
|
|
16
|
-
interpolateNodeConfig,
|
|
17
|
-
rehydrateCompletedStepOutputs,
|
|
18
15
|
scriptBodyInterpolationError,
|
|
19
16
|
walkGraph,
|
|
20
17
|
} from "./engine";
|
|
21
18
|
import type { AsyncExecutorResult } from "./executors/base";
|
|
22
19
|
import type { ExecutorRegistry } from "./executors/registry";
|
|
20
|
+
import { FOREACH_ITEMS_WAIT_FOR, parkAndDriveForeach } from "./foreach-driver";
|
|
21
|
+
import { rebuildNodeExecutionInput } from "./step-input";
|
|
23
22
|
import { runStepValidation } from "./validation";
|
|
24
23
|
|
|
25
24
|
let pollerTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
@@ -63,27 +62,16 @@ export function startRetryPoller(registry: ExecutorRegistry, intervalMs = 5000):
|
|
|
63
62
|
// win, or the step re-executes inside a cancelled run.
|
|
64
63
|
if (!(await claimRetry(run.id, step.id))) continue;
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
//
|
|
68
|
-
// context
|
|
69
|
-
//
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
// The persisted run context can lag behind completed step checkpoints.
|
|
73
|
-
// Reuse walkGraph's recovery path before resolving declared inputs so
|
|
74
|
-
// retries see the same upstream outputs as the original execution.
|
|
75
|
-
await rehydrateCompletedStepOutputs(workflow.definition, run.id, ctx, registry);
|
|
76
|
-
|
|
77
|
-
// Deep-interpolate config against the node's declared-inputs context —
|
|
78
|
-
// the raw run context has no `inputs` aliases, so interpolating against
|
|
79
|
-
// it directly resolves every {{alias}} to "" and (e.g.) a retried
|
|
80
|
-
// foreach with `over: "{{items}}"` burns all its retries on schema
|
|
81
|
-
// rejections without ever re-dispatching.
|
|
82
|
-
const inputCtx = buildNodeInterpolationCtx(node, ctx);
|
|
83
|
-
const { value: interpolatedValue, scriptBodyUnresolved } = interpolateNodeConfig(
|
|
84
|
-
node,
|
|
65
|
+
// Rebuild `run.id`, upstream outputs and the node's declared-inputs
|
|
66
|
+
// context the way the original execution saw them. Without the alias
|
|
67
|
+
// context a retried foreach with `over: "{{items}}"` burns all its
|
|
68
|
+
// retries on schema rejections without ever re-dispatching.
|
|
69
|
+
const {
|
|
70
|
+
ctx,
|
|
85
71
|
inputCtx,
|
|
86
|
-
|
|
72
|
+
config: interpolatedConfig,
|
|
73
|
+
scriptBodyUnresolved,
|
|
74
|
+
} = await rebuildNodeExecutionInput(workflow.definition, run, node, registry);
|
|
87
75
|
if (scriptBodyUnresolved && scriptBodyUnresolved.length > 0) {
|
|
88
76
|
await checkpointStepFailure(
|
|
89
77
|
run.id,
|
|
@@ -93,7 +81,6 @@ export function startRetryPoller(registry: ExecutorRegistry, intervalMs = 5000):
|
|
|
93
81
|
);
|
|
94
82
|
continue;
|
|
95
83
|
}
|
|
96
|
-
const interpolatedConfig = interpolatedValue as Record<string, unknown>;
|
|
97
84
|
|
|
98
85
|
// Get executor and re-run
|
|
99
86
|
const executor = registry.get(node.type);
|
|
@@ -126,6 +113,34 @@ export function startRetryPoller(registry: ExecutorRegistry, intervalMs = 5000):
|
|
|
126
113
|
step.retryCount,
|
|
127
114
|
retryPolicy,
|
|
128
115
|
);
|
|
116
|
+
} else if (
|
|
117
|
+
"async" in result &&
|
|
118
|
+
(result as AsyncExecutorResult).async &&
|
|
119
|
+
(result as AsyncExecutorResult).waitFor === FOREACH_ITEMS_WAIT_FOR
|
|
120
|
+
) {
|
|
121
|
+
// A retried foreach materialized its children: park it and start
|
|
122
|
+
// them, as executeStep does. An all-inline fan-out can close its
|
|
123
|
+
// join inside the drive; route the successors here then.
|
|
124
|
+
const drive = await parkAndDriveForeach({
|
|
125
|
+
runId: run.id,
|
|
126
|
+
stepId: step.id,
|
|
127
|
+
parentNodeId: node.id,
|
|
128
|
+
ctx,
|
|
129
|
+
registry,
|
|
130
|
+
});
|
|
131
|
+
if (!drive.joined) continue;
|
|
132
|
+
if (drive.successors.length > 0) {
|
|
133
|
+
await walkGraph(
|
|
134
|
+
workflow.definition,
|
|
135
|
+
run.id,
|
|
136
|
+
ctx,
|
|
137
|
+
drive.successors,
|
|
138
|
+
registry,
|
|
139
|
+
workflow.id,
|
|
140
|
+
);
|
|
141
|
+
} else {
|
|
142
|
+
await completeRunIfSettled(run.id, ctx);
|
|
143
|
+
}
|
|
129
144
|
} else if ("async" in result && (result as AsyncExecutorResult).async) {
|
|
130
145
|
// An async executor (agent-task, foreach) re-dispatched its work —
|
|
131
146
|
// the step is waiting on task events again, exactly like the
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { WorkflowDefinition, WorkflowNode, WorkflowRun } from "../types";
|
|
2
|
+
import {
|
|
3
|
+
buildNodeInterpolationCtx,
|
|
4
|
+
interpolateNodeConfig,
|
|
5
|
+
rehydrateCompletedStepOutputs,
|
|
6
|
+
} from "./engine";
|
|
7
|
+
import type { ExecutorRegistry } from "./executors/registry";
|
|
8
|
+
|
|
9
|
+
/** A node's execution input rebuilt outside the live walk that first ran it. */
|
|
10
|
+
export interface NodeExecutionInput {
|
|
11
|
+
/** The run context, hydrated with `run.id` and every completed step output. */
|
|
12
|
+
ctx: Record<string, unknown>;
|
|
13
|
+
/** The node's declared-inputs interpolation context (`meta.inputCtx`). */
|
|
14
|
+
inputCtx: Record<string, unknown>;
|
|
15
|
+
/** The node config interpolated against `inputCtx`. */
|
|
16
|
+
config: Record<string, unknown>;
|
|
17
|
+
unresolved: string[];
|
|
18
|
+
scriptBodyUnresolved?: string[];
|
|
19
|
+
strictUnresolved?: string[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Rebuild a node's interpolated config and input context from persisted run
|
|
24
|
+
* state, the way the live walk built them. The retry poller and the foreach
|
|
25
|
+
* driver run executors outside walkGraph and go through here.
|
|
26
|
+
*
|
|
27
|
+
* The persisted run context can lag behind completed step checkpoints, and a
|
|
28
|
+
* step can fail before ANY checkpoint persisted the walkGraph-hydrated
|
|
29
|
+
* context, so `run.id` may be absent: both are restored before resolving the
|
|
30
|
+
* node's declared inputs. Interpolating against the raw run context instead
|
|
31
|
+
* resolves every `{{alias}}` to "" (it has no `inputs` aliases).
|
|
32
|
+
*
|
|
33
|
+
* Pass `ctx` to reuse a live walk's already hydrated context as-is.
|
|
34
|
+
*/
|
|
35
|
+
export async function rebuildNodeExecutionInput(
|
|
36
|
+
def: WorkflowDefinition,
|
|
37
|
+
run: Pick<WorkflowRun, "id" | "context">,
|
|
38
|
+
node: WorkflowNode,
|
|
39
|
+
registry: ExecutorRegistry,
|
|
40
|
+
options: { ctx?: Record<string, unknown> } = {},
|
|
41
|
+
): Promise<NodeExecutionInput> {
|
|
42
|
+
const ctx = options.ctx ?? ((run.context ?? {}) as Record<string, unknown>);
|
|
43
|
+
if (!("run" in ctx)) ctx.run = { id: run.id };
|
|
44
|
+
if (!options.ctx) await rehydrateCompletedStepOutputs(def, run.id, ctx, registry);
|
|
45
|
+
|
|
46
|
+
const inputCtx = buildNodeInterpolationCtx(node, ctx);
|
|
47
|
+
const { value, unresolved, scriptBodyUnresolved, strictUnresolved } = interpolateNodeConfig(
|
|
48
|
+
node,
|
|
49
|
+
inputCtx,
|
|
50
|
+
);
|
|
51
|
+
return {
|
|
52
|
+
ctx,
|
|
53
|
+
inputCtx,
|
|
54
|
+
config: value as Record<string, unknown>,
|
|
55
|
+
unresolved,
|
|
56
|
+
scriptBodyUnresolved,
|
|
57
|
+
strictUnresolved,
|
|
58
|
+
};
|
|
59
|
+
}
|