@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/vcs/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* VCS Provider types — shared across GitHub/GitLab/Azure DevOps integration.
|
|
2
|
+
* VCS Provider types — shared across GitHub/GitLab/Azure DevOps/Bitbucket integration.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
export type VcsProvider = "github" | "gitlab" | "azure-devops";
|
|
5
|
+
export type VcsProvider = "github" | "gitlab" | "azure-devops" | "bitbucket";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { WorkflowDefinition } from "../types";
|
|
2
2
|
import type { ApprovalOutcome, ResolvedApproval } from "./executors/base";
|
|
3
3
|
import type { ExecutorRegistry } from "./executors/registry";
|
|
4
|
+
import { resolveStepNode } from "./foreach-body";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Step output and output port for a step whose approval request resolved.
|
|
@@ -18,7 +19,9 @@ export function shapeApprovalResolution(
|
|
|
18
19
|
parkedOutput: unknown,
|
|
19
20
|
approval: ResolvedApproval,
|
|
20
21
|
): ApprovalOutcome {
|
|
21
|
-
|
|
22
|
+
// A foreach child resolves to its body node, so a system-one-decision body
|
|
23
|
+
// keeps its own shaping.
|
|
24
|
+
const node = resolveStepNode(definition, nodeId);
|
|
22
25
|
if (node && registry.has(node.type)) {
|
|
23
26
|
const shaped = registry.get(node.type).resolveApproval(parkedOutput, approval);
|
|
24
27
|
if (shaped) return shaped;
|
|
@@ -83,11 +83,16 @@ export async function checkpointStepFailure(
|
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
85
|
* Checkpoint a step entering waiting state (async executor).
|
|
86
|
+
*
|
|
87
|
+
* `keepRunStatus` parks the step and persists the context but leaves the run
|
|
88
|
+
* `running`: a foreach parent parks before its driver starts the children, and
|
|
89
|
+
* the run must not read as `waiting` until they are dispatched.
|
|
86
90
|
*/
|
|
87
91
|
export async function checkpointStepWaiting(
|
|
88
92
|
runId: string,
|
|
89
93
|
stepId: string,
|
|
90
94
|
ctx: Record<string, unknown>,
|
|
95
|
+
options: { keepRunStatus?: boolean } = {},
|
|
91
96
|
): Promise<boolean> {
|
|
92
97
|
return getDbClient().transaction(async () => {
|
|
93
98
|
const step = await getDbClient().get<{ id: string }>(
|
|
@@ -103,7 +108,10 @@ export async function checkpointStepWaiting(
|
|
|
103
108
|
);
|
|
104
109
|
if (!step) return false;
|
|
105
110
|
|
|
106
|
-
await updateWorkflowRun(
|
|
111
|
+
await updateWorkflowRun(
|
|
112
|
+
runId,
|
|
113
|
+
options.keepRunStatus ? { context: ctx } : { status: "waiting", context: ctx },
|
|
114
|
+
);
|
|
107
115
|
return true;
|
|
108
116
|
});
|
|
109
117
|
}
|
|
@@ -11,6 +11,12 @@ import {
|
|
|
11
11
|
systemOneStaticShapeViolations,
|
|
12
12
|
} from "./executors/system-one-decision";
|
|
13
13
|
import { systemOneExtractionStaticShapeViolations } from "./executors/system-one-extraction";
|
|
14
|
+
import {
|
|
15
|
+
FOREACH_FAILURE_POLICIES,
|
|
16
|
+
type ForeachFailurePolicy,
|
|
17
|
+
foreachBodyTypeError,
|
|
18
|
+
isForeachBodyType,
|
|
19
|
+
} from "./foreach-body";
|
|
14
20
|
|
|
15
21
|
/** Extract all target node IDs from a node's `next` field */
|
|
16
22
|
export function getNextTargets(next: string | string[] | Record<string, string>): string[] {
|
|
@@ -220,7 +226,7 @@ export function validateDefinition(
|
|
|
220
226
|
...systemOneRetryViolations(node),
|
|
221
227
|
);
|
|
222
228
|
if (node.type === "foreach") {
|
|
223
|
-
validateForeachNode(node, errors);
|
|
229
|
+
validateForeachNode(node, errors, registry);
|
|
224
230
|
// A legacy `#` id may stay editable as a NORMAL node, but never as a foreach
|
|
225
231
|
// parent: its children would be `a#b#item`, and parseSyntheticNodeId splits on
|
|
226
232
|
// the FIRST `#`, so the join would resolve them to parent "a" and never close.
|
|
@@ -312,20 +318,25 @@ export function validateDefinition(
|
|
|
312
318
|
|
|
313
319
|
reportStaticConfigIssues(node, registry.get(node.type).configSchema, node.config, errors);
|
|
314
320
|
|
|
315
|
-
// A foreach body is executed by
|
|
316
|
-
// static config to the same schema a top-level
|
|
317
|
-
// invalid field (priority: 101, tags: "x") fails at authoring instead of
|
|
321
|
+
// A foreach body is executed by its own executor per item — hold its
|
|
322
|
+
// static config to the same schema a top-level node of that type gets, so
|
|
323
|
+
// an invalid field (priority: 101, tags: "x") fails at authoring instead of
|
|
318
324
|
// after the fan-out materialized. Interpolated fields defer as usual.
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
+
const body = node.type === "foreach" ? node.config.body : undefined;
|
|
326
|
+
const bodyRecord =
|
|
327
|
+
typeof body === "object" && body !== null && !Array.isArray(body)
|
|
328
|
+
? (body as Record<string, unknown>)
|
|
329
|
+
: undefined;
|
|
330
|
+
if (
|
|
331
|
+
bodyRecord &&
|
|
332
|
+
typeof bodyRecord.type === "string" &&
|
|
333
|
+
isForeachBodyType(bodyRecord.type, registry)
|
|
334
|
+
) {
|
|
335
|
+
const bodyConfig = bodyRecord.config;
|
|
325
336
|
if (typeof bodyConfig === "object" && bodyConfig !== null && !Array.isArray(bodyConfig)) {
|
|
326
337
|
reportStaticConfigIssues(
|
|
327
338
|
node,
|
|
328
|
-
registry.get(
|
|
339
|
+
registry.get(bodyRecord.type).configSchema,
|
|
329
340
|
bodyConfig,
|
|
330
341
|
errors,
|
|
331
342
|
"config.body.config",
|
|
@@ -401,10 +412,27 @@ function reportStaticConfigIssues(
|
|
|
401
412
|
}
|
|
402
413
|
}
|
|
403
414
|
|
|
404
|
-
function validateForeachNode(
|
|
415
|
+
function validateForeachNode(
|
|
416
|
+
node: WorkflowNode,
|
|
417
|
+
errors: string[],
|
|
418
|
+
registry?: ExecutorRegistry,
|
|
419
|
+
): void {
|
|
405
420
|
const config = node.config;
|
|
406
|
-
if (
|
|
407
|
-
|
|
421
|
+
if (
|
|
422
|
+
config.concurrency !== undefined &&
|
|
423
|
+
!(Number.isInteger(config.concurrency) && (config.concurrency as number) >= 1)
|
|
424
|
+
) {
|
|
425
|
+
errors.push(
|
|
426
|
+
`Node "${node.id}" foreach config.concurrency must be an integer of 1 or more, got ${JSON.stringify(config.concurrency)}`,
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
if (
|
|
430
|
+
config.failurePolicy !== undefined &&
|
|
431
|
+
!FOREACH_FAILURE_POLICIES.includes(config.failurePolicy as ForeachFailurePolicy)
|
|
432
|
+
) {
|
|
433
|
+
errors.push(
|
|
434
|
+
`Node "${node.id}" foreach config.failurePolicy must be one of ${FOREACH_FAILURE_POLICIES.map((p) => `"${p}"`).join(", ")}, got ${JSON.stringify(config.failurePolicy)}`,
|
|
435
|
+
);
|
|
408
436
|
}
|
|
409
437
|
|
|
410
438
|
// The asynchronous join (joinForeach) checkpoints the aggregate and routes
|
|
@@ -436,8 +464,11 @@ function validateForeachNode(node: WorkflowNode, errors: string[]): void {
|
|
|
436
464
|
return;
|
|
437
465
|
}
|
|
438
466
|
const bodyRecord = body as Record<string, unknown>;
|
|
439
|
-
if (bodyRecord.type !== "
|
|
440
|
-
errors.push(`Node "${node.id}" foreach config.body.type must be
|
|
467
|
+
if (typeof bodyRecord.type !== "string" || bodyRecord.type.length === 0) {
|
|
468
|
+
errors.push(`Node "${node.id}" foreach config.body.type must be a non-empty string`);
|
|
469
|
+
} else {
|
|
470
|
+
const typeError = foreachBodyTypeError(node.id, bodyRecord.type, registry);
|
|
471
|
+
if (typeError) errors.push(typeError);
|
|
441
472
|
}
|
|
442
473
|
if (
|
|
443
474
|
typeof bodyRecord.config !== "object" ||
|
|
@@ -448,7 +479,7 @@ function validateForeachNode(node: WorkflowNode, errors: string[]): void {
|
|
|
448
479
|
return;
|
|
449
480
|
}
|
|
450
481
|
const bodyConfig = bodyRecord.config as Record<string, unknown>;
|
|
451
|
-
if (typeof bodyConfig.template !== "string") {
|
|
482
|
+
if (bodyRecord.type === "agent-task" && typeof bodyConfig.template !== "string") {
|
|
452
483
|
errors.push(`Node "${node.id}" foreach agent-task body.config.template must be a string`);
|
|
453
484
|
}
|
|
454
485
|
}
|
package/src/workflows/engine.ts
CHANGED
|
@@ -25,6 +25,7 @@ import { telemetry } from "../telemetry";
|
|
|
25
25
|
import type { Workflow, WorkflowDefinition, WorkflowNode, WorkflowRunStep } from "../types";
|
|
26
26
|
import { checkpointStep, checkpointStepFailure, checkpointStepWaiting } from "./checkpoint";
|
|
27
27
|
import { loadCompletedStepRouting } from "./completed-step-routing";
|
|
28
|
+
import { FOREACH_ITEMS_WAIT_FOR } from "./constants";
|
|
28
29
|
import { shouldSkipCooldown } from "./cooldown";
|
|
29
30
|
import { findEntryNodes, getNextTargets, getSuccessors, resolveValidationPort } from "./definition";
|
|
30
31
|
import type { AsyncExecutorResult } from "./executors/base";
|
|
@@ -34,15 +35,16 @@ import {
|
|
|
34
35
|
systemOneRetryViolations,
|
|
35
36
|
systemOneUnresolvedError,
|
|
36
37
|
} from "./executors/system-one-decision";
|
|
38
|
+
import { parkAndDriveForeach } from "./foreach-driver";
|
|
37
39
|
import { FOREACH_TERMINAL_STEP_STATUSES, resolveForeachParent } from "./foreach-join";
|
|
38
40
|
import { getSecretInputKeys, redactSecretsForStorage, resolveInputs } from "./input";
|
|
39
41
|
import { validateJsonSchema } from "./json-schema-validator";
|
|
40
42
|
import { getMaxWorkflowStepsPerRun } from "./limits";
|
|
43
|
+
import { resolveNodeTimeoutMs } from "./node-timeout";
|
|
41
44
|
import { findWorkflowReadinessProblems, formatReadinessRunError } from "./readiness";
|
|
42
45
|
import { deepInterpolate } from "./template";
|
|
43
46
|
import { runStepValidation, type ValidationRunResult } from "./validation";
|
|
44
47
|
|
|
45
|
-
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
46
48
|
// Read dynamically, NOT captured at module load: `src/http/index.ts` imports
|
|
47
49
|
// this module before `loadGlobalConfigsIntoEnv()` hydrates `swarm_config` into
|
|
48
50
|
// `process.env`, so a module-level const would permanently ignore DB-saved
|
|
@@ -510,14 +512,19 @@ async function walkGraphOwned(
|
|
|
510
512
|
const hasPendingRetries = finalSteps.some(
|
|
511
513
|
(s) => s.status === "failed" && s.nextRetryAt != null,
|
|
512
514
|
);
|
|
513
|
-
|
|
515
|
+
// A foreach child's outcome is in its parent's aggregate; the parent step
|
|
516
|
+
// is authoritative for the run outcome.
|
|
517
|
+
const isForeachChild = (s: WorkflowRunStep) => resolveForeachParent(def, s.nodeId) !== null;
|
|
518
|
+
const failedSteps = finalSteps.filter(
|
|
519
|
+
(s) => s.status === "failed" && s.nextRetryAt == null && !isForeachChild(s),
|
|
520
|
+
);
|
|
514
521
|
// Exclude entry/trigger nodes when checking for completed steps — a trigger
|
|
515
522
|
// completing doesn't mean a meaningful branch succeeded. Without this filter,
|
|
516
523
|
// a linear workflow (trigger → mustPass validator → action) would be marked
|
|
517
524
|
// as partial-failure instead of failed when the validator fails.
|
|
518
525
|
const entryNodeIds = new Set(findEntryNodes(def).map((n) => n.id));
|
|
519
526
|
const hasCompletedSteps = finalSteps.some(
|
|
520
|
-
(s) => s.status === "completed" && !entryNodeIds.has(s.nodeId),
|
|
527
|
+
(s) => s.status === "completed" && !entryNodeIds.has(s.nodeId) && !isForeachChild(s),
|
|
521
528
|
);
|
|
522
529
|
|
|
523
530
|
if (hasWaitingSteps) {
|
|
@@ -880,18 +887,7 @@ async function runClaimedStep(
|
|
|
880
887
|
inputCtx: interpolationCtx,
|
|
881
888
|
};
|
|
882
889
|
|
|
883
|
-
|
|
884
|
-
// `config.timeout`, while the workflow watchdog reads `config.timeoutMs` for
|
|
885
|
-
// every other executor. Keep the outer watchdog aligned with the script
|
|
886
|
-
// executor so a script configured for (for example) 90s is not killed by the
|
|
887
|
-
// engine's 30s default first.
|
|
888
|
-
const configuredTimeoutMs =
|
|
889
|
-
typeof node.config?.timeoutMs === "number"
|
|
890
|
-
? node.config.timeoutMs
|
|
891
|
-
: node.type === "script" && typeof node.config?.timeout === "number"
|
|
892
|
-
? node.config.timeout
|
|
893
|
-
: undefined;
|
|
894
|
-
const timeoutMs = configuredTimeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
890
|
+
const timeoutMs = resolveNodeTimeoutMs(node);
|
|
895
891
|
|
|
896
892
|
let result: Awaited<ReturnType<typeof executor.run>>;
|
|
897
893
|
try {
|
|
@@ -949,6 +945,20 @@ async function runClaimedStep(
|
|
|
949
945
|
|
|
950
946
|
// Check for async result
|
|
951
947
|
if ("async" in result && (result as AsyncExecutorResult).async) {
|
|
948
|
+
if ((result as AsyncExecutorResult).waitFor === FOREACH_ITEMS_WAIT_FOR) {
|
|
949
|
+
// The foreach executor materialized its children; park the parent and
|
|
950
|
+
// start them. Instant bodies run here, each against its own budget, and
|
|
951
|
+
// can close the join before the drive returns.
|
|
952
|
+
const drive = await parkAndDriveForeach({
|
|
953
|
+
runId,
|
|
954
|
+
stepId,
|
|
955
|
+
parentNodeId: node.id,
|
|
956
|
+
ctx,
|
|
957
|
+
registry,
|
|
958
|
+
});
|
|
959
|
+
if (drive.joined) return { outcome: "completed", successors: drive.successors };
|
|
960
|
+
return { outcome: drive.parked ? "waiting" : "completed", successors: [] };
|
|
961
|
+
}
|
|
952
962
|
const waiting = await checkpointStepWaiting(runId, stepId, ctx);
|
|
953
963
|
// A child run that finished before the step parked had its event dropped.
|
|
954
964
|
if (waiting && (result as AsyncExecutorResult).waitFor === "workflow.child.finished") {
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { createForeachChildSteps } from "../../be/db/workflow-runs";
|
|
2
3
|
import type { ExecutorMeta } from "../../types";
|
|
3
|
-
import {
|
|
4
|
+
import { FOREACH_ITEMS_WAIT_FOR } from "../constants";
|
|
4
5
|
import { getSuccessors } from "../definition";
|
|
6
|
+
import { foreachBodyTypeError } from "../foreach-body";
|
|
5
7
|
import {
|
|
6
8
|
buildForeachAggregate,
|
|
7
9
|
FOREACH_TERMINAL_STEP_STATUSES,
|
|
8
10
|
parseSyntheticNodeId,
|
|
9
11
|
} from "../foreach-join";
|
|
10
12
|
import { getMaxWorkflowStepsPerRun } from "../limits";
|
|
11
|
-
import { AgentTaskExecutor } from "./agent-task";
|
|
12
13
|
import type { AsyncExecutorResult, ExecutorResult } from "./base";
|
|
13
14
|
import { BaseExecutor } from "./base";
|
|
14
15
|
|
|
16
|
+
// Any registered executor except `foreach` (see foreach-body.ts); the guard in
|
|
17
|
+
// execute() and definition validation enforce the policy.
|
|
15
18
|
const ForeachBodySchema = z.object({
|
|
16
|
-
type: z.
|
|
19
|
+
type: z.string().min(1),
|
|
17
20
|
config: z.record(z.string(), z.unknown()),
|
|
18
21
|
});
|
|
19
22
|
|
|
@@ -21,7 +24,12 @@ export const ForeachConfigSchema = z.object({
|
|
|
21
24
|
over: z.array(z.record(z.string(), z.unknown())),
|
|
22
25
|
itemKey: z.string().min(1),
|
|
23
26
|
body: ForeachBodySchema,
|
|
24
|
-
|
|
27
|
+
/** Items in flight at once (`running` or `waiting` children). Unbounded when absent. */
|
|
28
|
+
concurrency: z.number().int().min(1).optional(),
|
|
29
|
+
/** Defaults from the workflow `onNodeFailure` (see effectiveFailurePolicy). */
|
|
30
|
+
// Literal, not FOREACH_FAILURE_POLICIES: foreach-body.ts sits in an import
|
|
31
|
+
// cycle with this module and would be read before it initializes.
|
|
32
|
+
failurePolicy: z.enum(["fail-fast", "collect-outcomes"]).optional(),
|
|
25
33
|
});
|
|
26
34
|
|
|
27
35
|
const ForeachResultSchema = z.object({
|
|
@@ -34,6 +42,8 @@ export const ForeachOutputSchema = z.object({
|
|
|
34
42
|
results: z.array(ForeachResultSchema),
|
|
35
43
|
okCount: z.number().int().nonnegative(),
|
|
36
44
|
failedCount: z.number().int().nonnegative(),
|
|
45
|
+
/** Items cancelled before they finished (fail-fast queued items, cancelled bodies). Counted in failedCount too. */
|
|
46
|
+
cancelledCount: z.number().int().nonnegative().optional(),
|
|
37
47
|
});
|
|
38
48
|
|
|
39
49
|
export type ForeachOutput = z.infer<typeof ForeachOutputSchema>;
|
|
@@ -49,7 +59,7 @@ export class ForeachExecutor extends BaseExecutor<
|
|
|
49
59
|
|
|
50
60
|
protected async execute(
|
|
51
61
|
config: z.infer<typeof ForeachConfigSchema>,
|
|
52
|
-
|
|
62
|
+
_context: Readonly<Record<string, unknown>>,
|
|
53
63
|
meta: ExecutorMeta,
|
|
54
64
|
): Promise<ExecutorResult<ForeachOutput>> {
|
|
55
65
|
if (config.over.length === 0) {
|
|
@@ -77,16 +87,19 @@ export class ForeachExecutor extends BaseExecutor<
|
|
|
77
87
|
seenItemKeys.add(itemKey);
|
|
78
88
|
}
|
|
79
89
|
|
|
80
|
-
const
|
|
90
|
+
const bodyTypeError = foreachBodyTypeError(meta.nodeId, config.body.type);
|
|
91
|
+
if (bodyTypeError) return { status: "failed", error: bodyTypeError };
|
|
92
|
+
|
|
81
93
|
const runSteps = await this.deps.db.getWorkflowRunStepsByRunId(meta.runId);
|
|
82
|
-
const
|
|
94
|
+
const existingChildNodeIds = new Set(
|
|
83
95
|
runSteps
|
|
84
96
|
.filter((step) => parseSyntheticNodeId(step.nodeId)?.parentNodeId === meta.nodeId)
|
|
85
|
-
.map((step) =>
|
|
97
|
+
.map((step) => step.nodeId),
|
|
98
|
+
);
|
|
99
|
+
const missingItems = items.filter(
|
|
100
|
+
({ itemKey }) => !existingChildNodeIds.has(`${meta.nodeId}#${itemKey}`),
|
|
86
101
|
);
|
|
87
|
-
const childCountToCreate =
|
|
88
|
-
({ itemKey }) => !childStepsByNodeId.has(`${meta.nodeId}#${itemKey}`),
|
|
89
|
-
).length;
|
|
102
|
+
const childCountToCreate = missingItems.length;
|
|
90
103
|
const maxSteps = getMaxWorkflowStepsPerRun();
|
|
91
104
|
// walkGraph's circuit breaker rejects any post-join walk once allSteps.length
|
|
92
105
|
// >= maxSteps, so a fan-out with a successor that lands exactly on the cap would
|
|
@@ -107,79 +120,26 @@ export class ForeachExecutor extends BaseExecutor<
|
|
|
107
120
|
};
|
|
108
121
|
}
|
|
109
122
|
|
|
110
|
-
// Materialize EVERY child
|
|
111
|
-
//
|
|
112
|
-
// later children are still being set up
|
|
113
|
-
// child set
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
input: { itemKey, index },
|
|
124
|
-
});
|
|
125
|
-
await this.deps.db.updateWorkflowRunStep(childStepId, {
|
|
126
|
-
idempotencyKey: `${meta.runId}:${childNodeId}:0`,
|
|
127
|
-
});
|
|
128
|
-
childStepsByNodeId.set(childNodeId, childStep);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
for (const { item, index, itemKey } of items) {
|
|
132
|
-
const childNodeId = `${meta.nodeId}#${itemKey}`;
|
|
133
|
-
const childStep = childStepsByNodeId.get(childNodeId)!;
|
|
134
|
-
|
|
135
|
-
// Re-walks must not regress terminal children or disturb their aggregate
|
|
136
|
-
// inputs. Retry resets only the failed child to pending before arriving here.
|
|
137
|
-
if (FOREACH_TERMINAL_STEP_STATUSES.has(childStep.status)) continue;
|
|
138
|
-
|
|
139
|
-
// The deferred per-item pass interpolates against the node's declared-inputs
|
|
140
|
-
// context (meta.inputCtx) plus item/index — NOT the full run context — so
|
|
141
|
-
// body.config obeys the same explicit-dataflow boundary as normal node
|
|
142
|
-
// config instead of silently reading undeclared upstream outputs.
|
|
143
|
-
const bodyCtx = { ...(meta.inputCtx ?? context), item, index };
|
|
144
|
-
const childInterpolation = deepInterpolate(config.body.config, bodyCtx);
|
|
145
|
-
if (childInterpolation.unresolved.length > 0) {
|
|
146
|
-
console.warn(
|
|
147
|
-
`[workflow] Step ${childNodeId}: unresolved interpolation tokens: ${childInterpolation.unresolved.join(", ")}`,
|
|
148
|
-
);
|
|
149
|
-
await this.deps.db.updateWorkflowRunStep(childStep.id, {
|
|
150
|
-
diagnostics: JSON.stringify({ unresolvedTokens: childInterpolation.unresolved }),
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const linkedTask = await this.deps.db.getTaskByWorkflowRunStepId(childStep.id);
|
|
155
|
-
if (linkedTask?.status === "failed" || linkedTask?.status === "cancelled") {
|
|
156
|
-
// A retry needs a fresh task. Detaching the stale terminal task keeps the
|
|
157
|
-
// executor's one-task-per-step de-duplication deterministic and scoped.
|
|
158
|
-
await this.deps.db.detachTaskFromWorkflowRunStep(linkedTask.id);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const childResult = await agentTaskExecutor.run({
|
|
162
|
-
config: childInterpolation.value as Record<string, unknown>,
|
|
163
|
-
context,
|
|
164
|
-
meta: {
|
|
165
|
-
...meta,
|
|
166
|
-
stepId: childStep.id,
|
|
123
|
+
// Materialize EVERY child row, queued (`pending`), in one transaction and
|
|
124
|
+
// before any child starts. An early child can complete (and trigger the
|
|
125
|
+
// join) while later children are still being set up, so the join must
|
|
126
|
+
// always see the full child set. The item value is not persisted: the
|
|
127
|
+
// driver looks it up by key in the re-interpolated `over`, which keeps
|
|
128
|
+
// resolved secrets out of step rows.
|
|
129
|
+
await createForeachChildSteps(
|
|
130
|
+
meta.runId,
|
|
131
|
+
meta.nodeId,
|
|
132
|
+
missingItems.map(({ index, itemKey }) => {
|
|
133
|
+
const childNodeId = `${meta.nodeId}#${itemKey}`;
|
|
134
|
+
return {
|
|
135
|
+
id: crypto.randomUUID(),
|
|
167
136
|
nodeId: childNodeId,
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
await this.deps.db.updateWorkflowRunStep(childStep.id, { status: "waiting" });
|
|
175
|
-
} else {
|
|
176
|
-
await this.deps.db.updateWorkflowRunStep(childStep.id, {
|
|
177
|
-
status: "completed",
|
|
178
|
-
output: normalizeExistingTaskOutput(childResult.output),
|
|
179
|
-
finishedAt: new Date().toISOString(),
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
}
|
|
137
|
+
nodeType: config.body.type,
|
|
138
|
+
input: { itemKey, index, attempt: 0 },
|
|
139
|
+
idempotencyKey: `${meta.runId}:${childNodeId}:0`,
|
|
140
|
+
};
|
|
141
|
+
}),
|
|
142
|
+
);
|
|
183
143
|
|
|
184
144
|
const childSteps = (await this.deps.db.getWorkflowRunStepsByRunId(meta.runId)).filter(
|
|
185
145
|
(step) => parseSyntheticNodeId(step.nodeId)?.parentNodeId === meta.nodeId,
|
|
@@ -188,26 +148,12 @@ export class ForeachExecutor extends BaseExecutor<
|
|
|
188
148
|
return { status: "success", output: buildForeachAggregate(childSteps) };
|
|
189
149
|
}
|
|
190
150
|
|
|
151
|
+
// The engine parks the parent and the foreach driver starts the children.
|
|
191
152
|
return {
|
|
192
153
|
status: "success",
|
|
193
154
|
async: true,
|
|
194
|
-
waitFor:
|
|
155
|
+
waitFor: FOREACH_ITEMS_WAIT_FOR,
|
|
195
156
|
correlationId: meta.stepId,
|
|
196
157
|
} as AsyncExecutorResult<ForeachOutput>;
|
|
197
158
|
}
|
|
198
159
|
}
|
|
199
|
-
|
|
200
|
-
function normalizeExistingTaskOutput(output: unknown): unknown {
|
|
201
|
-
if (typeof output !== "object" || output === null) return output;
|
|
202
|
-
const record = output as Record<string, unknown>;
|
|
203
|
-
if (typeof record.taskOutput !== "string") return output;
|
|
204
|
-
try {
|
|
205
|
-
const parsed = JSON.parse(record.taskOutput);
|
|
206
|
-
if (typeof parsed === "object" && parsed !== null) {
|
|
207
|
-
return { ...record, taskOutput: parsed };
|
|
208
|
-
}
|
|
209
|
-
} catch {
|
|
210
|
-
// Non-structured task output remains a string.
|
|
211
|
-
}
|
|
212
|
-
return output;
|
|
213
|
-
}
|
|
@@ -18,8 +18,8 @@ import {
|
|
|
18
18
|
} from "./human-in-the-loop";
|
|
19
19
|
import { SYSTEM_ONE_EXTRACTION_NODE_TYPE } from "./system-one-extraction";
|
|
20
20
|
import {
|
|
21
|
+
resolveSystemOneDefaultProvider,
|
|
21
22
|
resolveSystemOneTarget,
|
|
22
|
-
SYSTEM_ONE_DEFAULT_PROVIDER,
|
|
23
23
|
SYSTEM_ONE_PROVIDER_IDS,
|
|
24
24
|
SYSTEM_ONE_PROVIDERS,
|
|
25
25
|
type SystemOneProvider,
|
|
@@ -164,7 +164,11 @@ export type SystemOneHumanReview = z.infer<typeof HumanReviewSchema>;
|
|
|
164
164
|
|
|
165
165
|
export const SystemOneDecisionConfigSchema = z
|
|
166
166
|
.strictObject({
|
|
167
|
-
|
|
167
|
+
/**
|
|
168
|
+
* Decision host. Unset means the `SYSTEM_ONE_DEFAULT_PROVIDER` global config
|
|
169
|
+
* value, read at run time (`typesafe` when that is unset too).
|
|
170
|
+
*/
|
|
171
|
+
provider: z.enum(SYSTEM_ONE_PROVIDER_IDS).optional(),
|
|
168
172
|
/** Provider-specific model id. Unset means the provider's default. */
|
|
169
173
|
model: z.string().min(1).optional(),
|
|
170
174
|
state: SystemOneStateSchema,
|
|
@@ -235,7 +239,9 @@ export const SystemOneDecisionConfigSchema = z
|
|
|
235
239
|
}
|
|
236
240
|
}
|
|
237
241
|
// Provider limits. A {{token}} model is skipped at save and checked again at run,
|
|
238
|
-
// where the interpolated config is parsed by this same schema.
|
|
242
|
+
// where the interpolated config is parsed by this same schema. With no provider
|
|
243
|
+
// the default is read at run time, so `decide` checks the limits there.
|
|
244
|
+
if (config.provider === undefined) return;
|
|
239
245
|
const modelProblem = systemOneModelProblem(
|
|
240
246
|
config.provider,
|
|
241
247
|
config.model ?? (SYSTEM_ONE_PROVIDERS[config.provider] as SystemOneProvider).defaultModel,
|
|
@@ -847,6 +853,11 @@ export class SystemOneDecisionExecutor extends BaseExecutor<
|
|
|
847
853
|
return { db: this.deps.db, env: this.options.env ?? process.env };
|
|
848
854
|
}
|
|
849
855
|
|
|
856
|
+
/** The provider a config without `provider` uses, read now so a settings change applies at once. */
|
|
857
|
+
private defaultProvider(): Promise<SystemOneProviderId> {
|
|
858
|
+
return resolveSystemOneDefaultProvider(this.providerContext());
|
|
859
|
+
}
|
|
860
|
+
|
|
850
861
|
private resolveTarget(id: SystemOneProviderId, model?: string) {
|
|
851
862
|
return resolveSystemOneTarget(id, this.providerContext(), {
|
|
852
863
|
apiKey: this.options.getApiKey,
|
|
@@ -863,9 +874,10 @@ export class SystemOneDecisionExecutor extends BaseExecutor<
|
|
|
863
874
|
nodes: readonly ExecutorReadinessNode[],
|
|
864
875
|
): Promise<ExecutorReadinessProblem[]> {
|
|
865
876
|
const nodeIdsByProvider = new Map<SystemOneProviderId, string[]>();
|
|
877
|
+
const defaultId = await this.defaultProvider();
|
|
866
878
|
for (const node of nodes) {
|
|
867
879
|
// An unknown provider is a config error the schema reports; it names no credential.
|
|
868
|
-
const id = systemOneProviderOf(node.config);
|
|
880
|
+
const id = systemOneProviderOf(node.config, defaultId);
|
|
869
881
|
if (id) nodeIdsByProvider.set(id, [...(nodeIdsByProvider.get(id) ?? []), node.id]);
|
|
870
882
|
}
|
|
871
883
|
const problems: ExecutorReadinessProblem[] = [];
|
|
@@ -903,16 +915,25 @@ export class SystemOneDecisionExecutor extends BaseExecutor<
|
|
|
903
915
|
*/
|
|
904
916
|
async decide(
|
|
905
917
|
config: SystemOneDecisionConfig,
|
|
906
|
-
): Promise<
|
|
907
|
-
|
|
918
|
+
): Promise<
|
|
919
|
+
| { ok: true; output: SystemOneDecisionOutput }
|
|
920
|
+
| { ok: false; error: string; deploymentRefused?: true }
|
|
921
|
+
> {
|
|
922
|
+
const providerId = config.provider ?? (await this.defaultProvider());
|
|
908
923
|
const provider: SystemOneProvider = SYSTEM_ONE_PROVIDERS[providerId];
|
|
909
924
|
|
|
910
925
|
// A provider with no default sends no `model` when the node sets none.
|
|
911
926
|
const model = config.model ?? provider.defaultModel;
|
|
912
927
|
const modelProblem = systemOneModelProblem(providerId, model);
|
|
913
928
|
if (modelProblem) return { ok: false, error: modelProblem };
|
|
929
|
+
// Checked at save only for a named provider; a defaulted one is checked here.
|
|
930
|
+
const limitProblem = systemOneQuestionLimitProblem(
|
|
931
|
+
providerId,
|
|
932
|
+
Object.keys(config.questions).length,
|
|
933
|
+
);
|
|
934
|
+
if (limitProblem) return { ok: false, error: limitProblem };
|
|
914
935
|
const target = await this.resolveTarget(providerId, model);
|
|
915
|
-
if (!target.ok) return
|
|
936
|
+
if (!target.ok) return target;
|
|
916
937
|
const { apiKey, endpoint, keyName } = target;
|
|
917
938
|
|
|
918
939
|
const wire = provider.wire ?? SYSTEM_ONE_WIRE;
|
|
@@ -288,15 +288,41 @@ export const SYSTEM_ONE_PROVIDERS = Object.fromEntries(
|
|
|
288
288
|
export const SYSTEM_ONE_PROVIDER_IDS = hostsServing("decide");
|
|
289
289
|
export const SYSTEM_ONE_EXTRACT_PROVIDER_IDS = hostsServing("extract");
|
|
290
290
|
|
|
291
|
+
/** The decision provider when neither the call nor `SYSTEM_ONE_DEFAULT_PROVIDER` names one. */
|
|
291
292
|
export const SYSTEM_ONE_DEFAULT_PROVIDER: SystemOneProviderId = "typesafe";
|
|
292
293
|
export const SYSTEM_ONE_EXTRACT_DEFAULT_PROVIDER: SystemOneExtractProviderId = "cloudflare";
|
|
293
294
|
|
|
295
|
+
/** Global config key naming the decision provider a node or call without `provider` uses. */
|
|
296
|
+
export const SYSTEM_ONE_DEFAULT_PROVIDER_CONFIG_KEY = "SYSTEM_ONE_DEFAULT_PROVIDER";
|
|
297
|
+
|
|
294
298
|
export function isSystemOneProviderId(value: unknown): value is SystemOneProviderId {
|
|
295
299
|
return (
|
|
296
300
|
typeof value === "string" && (SYSTEM_ONE_PROVIDER_IDS as readonly string[]).includes(value)
|
|
297
301
|
);
|
|
298
302
|
}
|
|
299
303
|
|
|
304
|
+
/**
|
|
305
|
+
* The decision provider for a node or call that names none: the global
|
|
306
|
+
* `SYSTEM_ONE_DEFAULT_PROVIDER` row, then the same key in the environment, then
|
|
307
|
+
* `typesafe`. Read at run time, so changing the setting needs no workflow edit.
|
|
308
|
+
* A value that is not a decide host is skipped; the config guard refuses it on save.
|
|
309
|
+
*/
|
|
310
|
+
export async function resolveSystemOneDefaultProvider(
|
|
311
|
+
ctx: SystemOneProviderContext,
|
|
312
|
+
): Promise<SystemOneProviderId> {
|
|
313
|
+
let stored: string | undefined;
|
|
314
|
+
try {
|
|
315
|
+
stored = await readGlobalConfig(ctx.db, SYSTEM_ONE_DEFAULT_PROVIDER_CONFIG_KEY);
|
|
316
|
+
} catch {
|
|
317
|
+
// An unreadable row falls back like an unset one: the default stays usable.
|
|
318
|
+
}
|
|
319
|
+
for (const raw of [stored, ctx.env[SYSTEM_ONE_DEFAULT_PROVIDER_CONFIG_KEY]]) {
|
|
320
|
+
const id = raw?.trim().toLowerCase();
|
|
321
|
+
if (isSystemOneProviderId(id)) return id;
|
|
322
|
+
}
|
|
323
|
+
return SYSTEM_ONE_DEFAULT_PROVIDER;
|
|
324
|
+
}
|
|
325
|
+
|
|
300
326
|
export function isSystemOneExtractProviderId(value: unknown): value is SystemOneExtractProviderId {
|
|
301
327
|
return (
|
|
302
328
|
typeof value === "string" &&
|
|
@@ -304,11 +330,15 @@ export function isSystemOneExtractProviderId(value: unknown): value is SystemOne
|
|
|
304
330
|
);
|
|
305
331
|
}
|
|
306
332
|
|
|
307
|
-
/**
|
|
333
|
+
/**
|
|
334
|
+
* Provider a raw node config selects, `defaultId` when it names none. Undefined
|
|
335
|
+
* when the field is present but not a known id.
|
|
336
|
+
*/
|
|
308
337
|
export function systemOneProviderOf(
|
|
309
338
|
config: Record<string, unknown>,
|
|
339
|
+
defaultId: SystemOneProviderId = SYSTEM_ONE_DEFAULT_PROVIDER,
|
|
310
340
|
): SystemOneProviderId | undefined {
|
|
311
|
-
if (config.provider === undefined) return
|
|
341
|
+
if (config.provider === undefined) return defaultId;
|
|
312
342
|
return isSystemOneProviderId(config.provider) ? config.provider : undefined;
|
|
313
343
|
}
|
|
314
344
|
|
|
@@ -489,7 +519,12 @@ export type SystemOneTarget =
|
|
|
489
519
|
/** The secret name the key was read under, for a rejection message. */
|
|
490
520
|
keyName: string;
|
|
491
521
|
}
|
|
492
|
-
| {
|
|
522
|
+
| {
|
|
523
|
+
ok: false;
|
|
524
|
+
error: string;
|
|
525
|
+
/** This deployment refuses the host whatever its credential (`deploymentProblem`). */
|
|
526
|
+
deploymentRefused?: true;
|
|
527
|
+
};
|
|
493
528
|
|
|
494
529
|
export interface SystemOneTargetOverrides {
|
|
495
530
|
/** Replaces the credential lookup. */
|
|
@@ -517,7 +552,7 @@ export async function resolveSystemOneTarget(
|
|
|
517
552
|
const capability = overrides.capability ?? "decide";
|
|
518
553
|
const service = serviceOf(id, capability);
|
|
519
554
|
const deploymentProblem = host.deploymentProblem?.(ctx.env);
|
|
520
|
-
if (deploymentProblem) return { ok: false, error: deploymentProblem };
|
|
555
|
+
if (deploymentProblem) return { ok: false, error: deploymentProblem, deploymentRefused: true };
|
|
521
556
|
|
|
522
557
|
const errors: string[] = [];
|
|
523
558
|
|