@desplega.ai/agent-swarm 2.6.0 → 2.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/{acp-adapter-dgfakfpw.js → acp-adapter-8dn5tq56.js} +6 -6
- package/dist/{actions-fbqma8aa.js → actions-3r6gw7dy.js} +18 -18
- package/dist/{amp-adapter-en0jy6hs.js → amp-adapter-wjtq413p.js} +3 -3
- package/dist/{anthropic-messages-txs190dd.js → anthropic-messages-n4cby1rz.js} +4 -4
- package/dist/{app-k8a5cgpp.js → app-d53yhykk.js} +3 -3
- package/dist/{assistant-a16433gr.js → assistant-ksrawj3k.js} +20 -20
- package/dist/{boot-reembed-6c1sjfnr.js → boot-reembed-fxfw17m2.js} +11 -3
- package/dist/{boot-reembed-f4nn561n.js → boot-reembed-k5y5xbj0.js} +15 -8
- package/dist/{boot-scrub-sweep-d4w9gajf.js → boot-scrub-sweep-se41j9fd.js} +3 -3
- package/dist/{boot-seeds-9bmkspyw.js → boot-seeds-r3kwwv8b.js} +4 -4
- package/dist/{claude-adapter-nys1kjxa.js → claude-adapter-ahmyf231.js} +8 -8
- package/dist/{claude-managed-adapter-55207973.js → claude-managed-adapter-5knamegc.js} +3 -3
- package/dist/{claude-managed-setup-3cm35gey.js → claude-managed-setup-mdc7dy6b.js} +3 -3
- package/dist/{claude-sdk-session-nw5yd8cp.js → claude-sdk-session-n5wen9zk.js} +8729 -8600
- package/dist/{cli-683y0b3a.js → cli-0dqtv2jg.js} +2 -2
- package/dist/{cli-a34pr3w1.js → cli-0tww7dnz.js} +2 -2
- package/dist/{cli-w2656ka1.js → cli-0w4e4v58.js} +7 -7
- package/dist/{cli-5sckpqcx.js → cli-2sjm3t4r.js} +1 -1
- package/dist/{cli-gf2850q7.js → cli-3cywb44q.js} +18 -1
- package/dist/{cli-8wkb09q5.js → cli-3k9jej0c.js} +2 -2
- package/dist/{cli-3aqd4jaa.js → cli-3v2cyqb8.js} +3 -3
- package/dist/{cli-emhjcj2y.js → cli-5qt9vzmq.js} +89 -20
- package/dist/{cli-abkm71vr.js → cli-5rp1w4cr.js} +1 -1
- package/dist/{cli-gf95q3bm.js → cli-6vwjsetx.js} +4 -4
- package/dist/{cli-k21gx5ht.js → cli-6w2yrrc0.js} +10 -10
- package/dist/{cli-m70bajzr.js → cli-75tdydgv.js} +1 -1
- package/dist/{cli-5rj83bam.js → cli-8feswf2v.js} +47 -36
- package/dist/{cli-sxgtf5fw.js → cli-8wd3mbyj.js} +1 -1
- package/dist/{cli-ca6banwz.js → cli-9csfthbw.js} +10 -8
- package/dist/{cli-dqygeeh5.js → cli-b41p764a.js} +31 -3
- package/dist/{cli-m5a9m0ak.js → cli-c1t533hh.js} +3 -3
- package/dist/{cli-2sjhpsns.js → cli-c28jxpcv.js} +1 -1
- package/dist/{cli-bqztfyve.js → cli-cb50g9cd.js} +1 -1
- package/dist/{cli-ag2yrnt5.js → cli-d8gaphtr.js} +2 -2
- package/dist/{cli-9abdgn5t.js → cli-dgtfysne.js} +3 -3
- package/dist/{cli-nq677csy.js → cli-dscnhn18.js} +1 -1
- package/dist/{cli-3fwqy71j.js → cli-dv11pgpv.js} +39 -32
- package/dist/{cli-v49fkghp.js → cli-ekptgk2m.js} +6 -6
- package/dist/{cli-c2habvsf.js → cli-f3mzy662.js} +17 -17
- package/dist/{cli-zqne1qcj.js → cli-fk7k8s9x.js} +2 -2
- package/dist/{cli-1tem6ns9.js → cli-gt4h8cxw.js} +3 -2
- package/dist/{cli-vbhgkw9h.js → cli-h3e7bhvn.js} +3 -3
- package/dist/{cli-ffvsj2qw.js → cli-hkxqdmnb.js} +3 -3
- package/dist/{cli-a2rr0fmg.js → cli-hpkvgdad.js} +7 -4
- package/dist/{cli-art7c1zh.js → cli-hq2v4rr5.js} +37 -9
- package/dist/{cli-sqma6vw1.js → cli-k1zb7raw.js} +5 -5
- package/dist/{cli-4rvr7zfx.js → cli-k4awgvzm.js} +5 -0
- package/dist/{cli-3ndmpxxv.js → cli-k7d3hvya.js} +2 -2
- package/dist/{cli-3gfvmzht.js → cli-kbtdftbw.js} +74 -26
- package/dist/{cli-hfeb68z4.js → cli-kj69b9tw.js} +34 -6
- package/dist/{cli-az25fn5f.js → cli-kk9jy4nm.js} +2 -2
- package/dist/{cli-39aet655.js → cli-m52t4cta.js} +1 -1
- package/dist/{cli-cz37jqss.js → cli-mdadewqp.js} +5 -5
- package/dist/{cli-esakngh8.js → cli-nf6k60sn.js} +1 -1
- package/dist/{cli-j6rktc91.js → cli-nzdf09js.js} +1 -1
- package/dist/{cli-rr7zx19j.js → cli-prcyzecg.js} +12 -12
- package/dist/{cli-4hxrf0p4.js → cli-pzafyqtk.js} +393 -62
- package/dist/{cli-g8ka5j4j.js → cli-qvaa1gjp.js} +173 -52
- package/dist/{cli-d2dpf95n.js → cli-rg4b2g6j.js} +1 -1
- package/dist/{cli-kf4jree9.js → cli-rm66vqdz.js} +1 -1
- package/dist/{cli-4rf0f5vg.js → cli-s504c3w7.js} +5 -3
- package/dist/{cli-nn6vx2mq.js → cli-swkx9jm2.js} +7 -5
- package/dist/{cli-g20jjwt4.js → cli-thsaq4pd.js} +16 -16
- package/dist/{cli-aj8djyqt.js → cli-twjnyvs8.js} +2 -2
- package/dist/{cli-03gg9pd1.js → cli-typdpe8g.js} +4 -4
- package/dist/{cli-mtnj4fwa.js → cli-vg8pwmsd.js} +6 -6
- package/dist/{cli-gnsb1zxa.js → cli-vhpckzvp.js} +1 -1
- package/dist/{cli-7enjwya9.js → cli-vz78s5wm.js} +1 -1
- package/dist/{cli-dv97kdzv.js → cli-wdxgsdz5.js} +2 -2
- package/dist/{cli-7a6p4e65.js → cli-wtjg6je3.js} +1 -1
- package/dist/{cli-6y51ryva.js → cli-x3cngb1h.js} +44 -12
- package/dist/{cli-n7asmce6.js → cli-xbz6mcg4.js} +3 -3
- package/dist/{cli-24j1jeeb.js → cli-y4tdxsqb.js} +3 -3
- package/dist/{cli-mvjz5an3.js → cli-yqrsp3ny.js} +7 -7
- package/dist/cli.js +31 -21
- package/dist/{codex-adapter-d95q4qsn.js → codex-adapter-7wepzq15.js} +6 -6
- package/dist/{codex-hook-brpnycn5.js → codex-hook-pbdh4xhx.js} +2 -2
- package/dist/{codex-session-runner-gdwd8h0c.js → codex-session-runner-02sb49e4.js} +6 -6
- package/dist/{commands-jqmgk54b.js → commands-3rcspxct.js} +2 -2
- package/dist/{controller-z3jq589f.js → controller-8ar4avd6.js} +3 -3
- package/dist/{controllers-connect-r2za39ef.js → controllers-connect-ajvh9h0v.js} +19 -19
- package/dist/{cursor-adapter-4mr4jgkw.js → cursor-adapter-gd0sa7bh.js} +1 -1
- package/dist/{db-w36wwb5g.js → db-dv9j7ts5.js} +2 -2
- package/dist/{db-ppv2tze7.js → db-z26vgdvj.js} +3 -3
- package/dist/{dispatcher-pv5h77vf.js → dispatcher-8m0htjq8.js} +17 -17
- package/dist/{dsh-adapter-d4pdpb3r.js → dsh-adapter-y8gyk130.js} +1 -1
- package/dist/{e2b-fn3zacvt.js → e2b-74vg9b7e.js} +1 -1
- package/dist/{e2b-222hsajc.js → e2b-jyay8wzn.js} +1 -1
- package/dist/{easter-egg-3d-4sp4dvmc.js → easter-egg-3d-28f880xx.js} +1 -1
- package/dist/{execute-jev839y1.js → execute-9seb79vh.js} +1 -1
- package/dist/{external-channel-y6nkzhnn.js → external-channel-smz7jyth.js} +2 -2
- package/dist/{getMachineId-bsd-brn0xym3.js → getMachineId-bsd-whfre6gr.js} +3 -3
- package/dist/{getMachineId-darwin-t339sfxm.js → getMachineId-darwin-5zjynb19.js} +3 -3
- package/dist/{getMachineId-linux-5eq95d8b.js → getMachineId-linux-8mksrrsq.js} +3 -3
- package/dist/{getMachineId-unsupported-qavrtc7j.js → getMachineId-unsupported-fxtqsdt2.js} +3 -3
- package/dist/{getMachineId-win-q7fe1wx7.js → getMachineId-win-nzfmz857.js} +3 -3
- package/dist/{grok-adapter-t08b9ntm.js → grok-adapter-z0nnvtbn.js} +7 -7
- package/dist/handlers-pspryh6g.js +41 -0
- package/dist/{heartbeat-vn31xwdk.js → heartbeat-f928aqyp.js} +20 -20
- package/dist/{hook-cpjs8q51.js → hook-d7c605r4.js} +7 -7
- package/dist/{hook-6d0zbvvt.js → hook-q9hv5xf9.js} +5 -5
- package/dist/{http-9c6medda.js → http-q6j4k989.js} +1000 -251
- package/dist/{index-k0yt1tvq.js → index-5nb266jr.js} +17 -17
- package/dist/{index-ysd45kgc.js → index-8fxt5eah.js} +10 -10
- package/dist/{index-zgvqzjsw.js → index-cekzwhez.js} +5 -5
- package/dist/{index-38t35h0n.js → index-ch0c1xff.js} +17 -17
- package/dist/{index-pkma1yf8.js → index-ebnjhxh1.js} +1 -1
- package/dist/{index-yqgbfgaj.js → index-ed8dy8rn.js} +20 -20
- package/dist/{index-qhjf2htm.js → index-ejqdkkqx.js} +262 -96
- package/dist/{index-dr94zbnh.js → index-farm1zsn.js} +32661 -29443
- package/dist/{index-g6k9tctj.js → index-yn1yxg85.js} +1 -1
- package/dist/{keepalive-em0117bp.js → keepalive-zzc3hks1.js} +3 -3
- package/dist/lead-c0pjcaw4.js +79 -0
- package/dist/{maintenance-kbme3qy9.js → maintenance-kqsxzpzx.js} +1 -1
- package/dist/{oauth-refresh-sweep-xysj4yse.js → oauth-refresh-sweep-7qt0g55z.js} +8 -3
- package/dist/{onboard-p6q0vz97.js → onboard-k7x13fqg.js} +121 -8
- package/dist/{opencode-adapter-esv3k9yn.js → opencode-adapter-tqvrtter.js} +5 -5
- package/dist/{otel-impl-54qhqh1z.js → otel-impl-sqkf6k3b.js} +97 -97
- package/dist/{pi-mono-adapter-mymbwhj0.js → pi-mono-adapter-m2qnafxm.js} +89 -14
- package/dist/{pricing-refresh-yb75wv33.js → pricing-refresh-wkpwhgvs.js} +3 -3
- package/dist/{rbac-roles-dexhc9x8.js → rbac-roles-dpn0c0wj.js} +7 -4
- package/dist/{rbac-roles-1vp2mw4p.js → rbac-roles-wk7xd6a6.js} +2 -2
- package/dist/{render-v2-tjay5hhf.js → render-v2-ex2m4g2k.js} +3 -3
- package/dist/{resume-8ayf37g5.js → resume-725g9b0b.js} +7 -7
- package/dist/{runtime-hqe5cqcd.js → runtime-kajas15g.js} +1 -1
- package/dist/{session-access-fdhp987s.js → session-access-0j2ezjje.js} +8 -4
- package/dist/{session-source-backfill-qgtc86z1.js → session-source-backfill-62202awe.js} +2 -2
- package/dist/{setup-4rrmqcq3.js → setup-9sj11ax6.js} +1 -1
- package/dist/{virtual-modules-52km00d6.js → virtual-modules-c6fcq6zr.js} +2 -2
- package/dist/{work-objects-p28fgd70.js → work-objects-0y07495y.js} +20 -20
- package/dist/worker-d4q9sqbc.js +79 -0
- package/openapi.json +1536 -407
- package/package.json +7 -5
- package/src/be/automation-preflight.ts +2 -0
- package/src/be/connector-codes.ts +65 -22
- package/src/be/db/api-keys.ts +9 -0
- package/src/be/db/workflow-runs.ts +36 -5
- package/src/be/db.ts +80 -22
- package/src/be/memory/boot-reembed.ts +4 -1
- package/src/be/memory/constants.ts +6 -0
- package/src/be/memory/embedding-recovery.ts +48 -0
- package/src/be/memory/index-content.ts +5 -2
- package/src/be/memory/providers/openai-embedding.ts +65 -14
- package/src/be/memory/task-learning.ts +257 -0
- package/src/be/memory/types.ts +13 -2
- package/src/be/migrations/245_script_executor.sql +3 -0
- package/src/be/migrations/246_connector_code_kind.sql +11 -0
- package/src/be/oauth-refresh-sweep.ts +2 -1
- package/src/be/onboarding.ts +125 -15
- package/src/be/rbac-roles.ts +4 -1
- package/src/be/scripts/boot-reembed.ts +2 -0
- package/src/be/scripts/db.ts +14 -4
- package/src/be/scripts/embeddings.ts +4 -2
- package/src/be/scripts/run-saved.ts +1 -0
- package/src/be/scripts/typecheck.ts +9 -3
- package/src/be/seed/agent-fs-provision.ts +75 -14
- package/src/be/seed-skills/index.ts +7 -0
- package/src/be/swarm-config-guard.ts +3 -0
- package/src/cli.tsx +9 -0
- package/src/comb/config.ts +6 -1
- package/src/commands/onboard/agent-fs-version.ts +5 -0
- package/src/commands/onboard/compose-generator.ts +117 -4
- package/src/commands/onboard/env-generator.ts +9 -0
- package/src/commands/onboard/tei-image.ts +12 -0
- package/src/commands/onboard/types.ts +7 -0
- package/src/commands/onboard.tsx +11 -1
- package/src/commands/provider-credentials.ts +2 -1
- package/src/commands/runner.ts +14 -1
- package/src/heartbeat/heartbeat.ts +14 -1
- package/src/heartbeat/script-runner-probe.ts +47 -0
- package/src/http/agentfs-proxy.ts +369 -0
- package/src/http/all-routes.ts +1 -0
- package/src/http/api-keys.ts +24 -9
- package/src/http/codex-oauth-device.ts +8 -1
- package/src/http/fs.ts +64 -10
- package/src/http/idempotency-key.ts +1 -1
- package/src/http/inbox-state.ts +27 -1
- package/src/http/index.ts +7 -4
- package/src/http/memory.ts +7 -4
- package/src/http/onboarding.ts +139 -11
- package/src/http/pages-public.ts +38 -5
- package/src/http/scripts.ts +27 -0
- package/src/http/session-access.ts +24 -0
- package/src/http/sessions.ts +10 -6
- package/src/http/tasks.ts +85 -11
- package/src/http/telemetry-ui.ts +1 -1
- package/src/http/user-roles.ts +4 -1
- package/src/http/users.ts +272 -3
- package/src/http/utils.ts +22 -3
- package/src/http/workflows.ts +10 -0
- package/src/http/x.ts +5 -0
- package/src/prompts/base-prompt.ts +45 -10
- package/src/prompts/session-templates.ts +43 -5
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/codex-adapter.ts +6 -1
- package/src/providers/cursor-adapter.ts +1 -1
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/pi-mono-adapter.ts +99 -14
- package/src/rbac/legacy-policy.ts +1 -0
- package/src/rbac/permissions.ts +4 -0
- package/src/scheduler/scheduler.ts +2 -1
- package/src/scripts-runtime/eval-harness.ts +8 -1
- package/src/scripts-runtime/executors/native.ts +22 -16
- package/src/scripts-runtime/executors/quickjs-prelude.ts +103 -1
- package/src/scripts-runtime/executors/quickjs-runner.ts +2 -1
- package/src/scripts-runtime/executors/quickjs.ts +7 -0
- package/src/scripts-runtime/executors/registry.ts +32 -0
- package/src/scripts-runtime/executors/types.ts +7 -0
- package/src/scripts-runtime/host-validated-args.ts +133 -0
- package/src/scripts-runtime/loader.ts +30 -3
- package/src/scripts-runtime/swarm-sdk.ts +9 -2
- package/src/scripts-runtime/types/stdlib.d.ts +9 -1
- package/src/scripts-runtime/types/swarm-sdk.d.ts +9 -1
- package/src/tests/agent-fs-provision-seeder.test.ts +261 -92
- package/src/tests/agentfs-proxy.test.ts +1106 -0
- package/src/tests/api-key-status-by-suffix.test.ts +242 -0
- package/src/tests/base-prompt.test.ts +139 -4
- package/src/tests/codex-adapter.test.ts +46 -8
- package/src/tests/codex-oauth-device.test.ts +12 -1
- package/src/tests/comb-config-guard.test.ts +20 -0
- package/src/tests/comb-links.test.ts +79 -1
- package/src/tests/comb-status.test.ts +44 -0
- package/src/tests/community-linear-delivery-loop.test.ts +2099 -0
- package/src/tests/credential-check.test.ts +4 -2
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/docs-index.test.ts +135 -0
- package/src/tests/dsh-adapter.test.ts +1 -1
- package/src/tests/fetch-resolved-env.test.ts +16 -2
- package/src/tests/fixtures/onboard/baseline-states.ts +68 -0
- package/src/tests/fixtures/onboard/content.compose.yml.txt +190 -0
- package/src/tests/fixtures/onboard/content.env.txt +29 -0
- package/src/tests/fixtures/onboard/dev.compose.yml.txt +155 -0
- package/src/tests/fixtures/onboard/dev.env.txt +28 -0
- package/src/tests/fixtures/onboard/full-all.compose.yml.txt +582 -0
- package/src/tests/fixtures/onboard/full-all.env.txt +59 -0
- package/src/tests/fixtures/onboard/full.compose.yml.txt +435 -0
- package/src/tests/fixtures/onboard/full.env.txt +36 -0
- package/src/tests/fixtures/onboard/research.compose.yml.txt +155 -0
- package/src/tests/fixtures/onboard/research.env.txt +28 -0
- package/src/tests/fixtures/onboard/solo.compose.yml.txt +85 -0
- package/src/tests/fixtures/onboard/solo.env.txt +26 -0
- package/src/tests/heartbeat-checklist.test.ts +89 -0
- package/src/tests/heartbeat-script-runner-probe.test.ts +83 -0
- package/src/tests/http-log-scrubbing.test.ts +34 -1
- package/src/tests/http-task-write-ownership.test.ts +5 -1
- package/src/tests/invites-http.test.ts +464 -0
- package/src/tests/memory-embedding-input-cap.test.ts +122 -1
- package/src/tests/memory-embedding-recovery.test.ts +137 -0
- package/src/tests/oauth-refresh-sweep.test.ts +34 -1
- package/src/tests/onboard-compose.test.ts +162 -0
- package/src/tests/onboard-env.test.ts +24 -0
- package/src/tests/onboard-non-interactive.test.ts +29 -0
- package/src/tests/onboard-tei-image.test.ts +36 -0
- package/src/tests/onboarding.test.ts +451 -4
- package/src/tests/openai-embedding-provider.test.ts +105 -2
- package/src/tests/opencode-adapter.test.ts +2 -1
- package/src/tests/pages-frame-ancestors.test.ts +162 -0
- package/src/tests/pi-codemode.test.ts +2 -1
- package/src/tests/pi-mono-adapter.test.ts +54 -0
- package/src/tests/pi-mono-session-cost-on-abort.test.ts +270 -0
- package/src/tests/pi-native-mcp.test.ts +13 -1
- package/src/tests/pi-tool-exposure.test.ts +8 -8
- package/src/tests/preload-env.test.ts +4 -0
- package/src/tests/preload.ts +4 -0
- package/src/tests/probe-tool-schema-tokens.test.ts +144 -0
- package/src/tests/prompt-template-session.test.ts +20 -3
- package/src/tests/rbac-admission-e2e.test.ts +22 -6
- package/src/tests/rbac-admission.test.ts +342 -4
- package/src/tests/rbac-engine.test.ts +1 -0
- package/src/tests/rbac-roles.test.ts +9 -1
- package/src/tests/request-composition-derive.test.ts +329 -0
- package/src/tests/script-connections.test.ts +2 -0
- package/src/tests/script-executor-conformance.test.ts +36 -1
- package/src/tests/script-executor-quickjs-pool.test.ts +50 -0
- package/src/tests/script-executor-quickjs.test.ts +140 -0
- package/src/tests/script-host-validated-args-native.test.ts +196 -0
- package/src/tests/script-per-script-executor.test.ts +415 -0
- package/src/tests/scripts-external-api.test.ts +45 -0
- package/src/tests/sdk-allowlist.test.ts +3 -2
- package/src/tests/self-improvement.test.ts +4 -4
- package/src/tests/send-task-requester-gate.test.ts +64 -3
- package/src/tests/sessions-move.test.ts +39 -1
- package/src/tests/store-progress-learning.test.ts +381 -0
- package/src/tests/ui-logs-parser.test.ts +180 -0
- package/src/tests/whoami-route.test.ts +41 -0
- package/src/tests/workflow-engine-v2.test.ts +116 -1
- package/src/tests/workflow-mcp-trigger-schema.test.ts +209 -0
- package/src/tests/workflow-run-finished-event.test.ts +263 -0
- package/src/tests/workflow-swarm-script.test.ts +34 -0
- package/src/tools/inject-learning.ts +1 -1
- package/src/tools/memory-edit.ts +1 -1
- package/src/tools/memory-search.ts +1 -1
- package/src/tools/script-common.ts +1 -0
- package/src/tools/script-run.ts +7 -0
- package/src/tools/script-upsert.ts +7 -0
- package/src/tools/store-progress.ts +71 -1
- package/src/tools/utils.ts +12 -1
- package/src/tools/workflows/trigger-workflow.ts +104 -10
- package/src/types.ts +12 -1
- package/src/utils/constants.ts +39 -0
- package/src/utils/credentials.ts +5 -2
- package/src/utils/task-attachment-links.ts +15 -3
- package/src/workflows/engine.ts +18 -16
- package/src/workflows/executors/swarm-script.ts +5 -0
- package/src/workflows/foreach-driver.ts +5 -11
- package/src/workflows/node-timeout.ts +30 -0
- package/templates/community/linear-delivery-loop/PLAYBOOK.md +323 -0
- package/templates/community/linear-delivery-loop/README.md +38 -0
- package/templates/community/linear-delivery-loop/config.example.json +31 -0
- package/templates/community/linear-delivery-loop/lead-prompt.md +34 -0
- package/templates/community/linear-delivery-loop/schedule.json +10 -0
- package/templates/community/linear-delivery-loop/scripts/linear-delivery-setup.ts +148 -0
- package/templates/community/linear-delivery-loop/scripts/linear-delivery-tick.ts +1781 -0
- package/templates/skills/swarm-docs/config.json +14 -0
- package/templates/skills/swarm-docs/content.md +144 -0
- package/dist/handlers-eqsnmv95.js +0 -41
- package/dist/lead-23e7z59t.js +0 -79
- package/dist/worker-e3w7vye1.js +0 -79
- package/dist/{azure-openai-responses-9kfwhmn7.js → azure-openai-responses-43asa23s.js} +0 -0
- package/dist/{node-vxx896fa.js → node-t28cc56b.js} +0 -0
- package/dist/{openai-codex-responses-79s8xnbh.js → openai-codex-responses-jhr3sjtm.js} +3 -3
- package/dist/{openai-completions-tdbyts0x.js → openai-completions-gs6yn37k.js} +3 -3
- /package/dist/{openai-responses-dm3t5vqp.js → openai-responses-jqm9vwwk.js} +0 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `workflow.run.finished`: every run, top-level or child, emits it once after
|
|
3
|
+
* commit when it turns terminal, so an event-mode `wait` can resume when any
|
|
4
|
+
* run ends.
|
|
5
|
+
*/
|
|
6
|
+
import { afterAll, afterEach, beforeAll, describe, expect, test } from "bun:test";
|
|
7
|
+
import { unlink } from "node:fs/promises";
|
|
8
|
+
import { recordWorkflowPreflightFailure } from "../be/automation-preflight";
|
|
9
|
+
import * as db from "../be/db";
|
|
10
|
+
import {
|
|
11
|
+
closeDb,
|
|
12
|
+
createWorkflow,
|
|
13
|
+
createWorkflowRun,
|
|
14
|
+
getDbClient,
|
|
15
|
+
getWaitStateByStepId,
|
|
16
|
+
getWorkflowRun,
|
|
17
|
+
getWorkflowRunStepsByRunId,
|
|
18
|
+
initDb,
|
|
19
|
+
updateWorkflowRun,
|
|
20
|
+
} from "../be/db";
|
|
21
|
+
import { cancelLiveWorkflowTreeRuns } from "../be/db/workflow-runs";
|
|
22
|
+
import type { Workflow, WorkflowDefinition } from "../types";
|
|
23
|
+
import { startWorkflowExecution } from "../workflows/engine";
|
|
24
|
+
import { workflowEventBus } from "../workflows/event-bus";
|
|
25
|
+
import type { ExecutorDependencies } from "../workflows/executors/base";
|
|
26
|
+
import { createExecutorRegistry } from "../workflows/executors/registry";
|
|
27
|
+
import { _resetWaitBusSubscriptionsForTests, initWaitBusSubscriptions } from "../workflows/resume";
|
|
28
|
+
|
|
29
|
+
const TEST_DB_PATH = "./test-workflow-run-finished-event.sqlite";
|
|
30
|
+
const EVENT = "workflow.run.finished";
|
|
31
|
+
|
|
32
|
+
const deps: ExecutorDependencies = {
|
|
33
|
+
db: db as typeof import("../be/db"),
|
|
34
|
+
eventBus: workflowEventBus,
|
|
35
|
+
interpolate: (t: string) => t,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
type FinishedPayload = { runId: string; workflowId: string; status: string };
|
|
39
|
+
|
|
40
|
+
const received: FinishedPayload[] = [];
|
|
41
|
+
const onFinished = (data: unknown) => received.push(data as FinishedPayload);
|
|
42
|
+
|
|
43
|
+
beforeAll(async () => {
|
|
44
|
+
await unlink(TEST_DB_PATH).catch(() => {});
|
|
45
|
+
initDb(TEST_DB_PATH);
|
|
46
|
+
workflowEventBus.on(EVENT, onFinished);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
afterAll(async () => {
|
|
50
|
+
workflowEventBus.off(EVENT, onFinished);
|
|
51
|
+
closeDb();
|
|
52
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
53
|
+
await unlink(`${TEST_DB_PATH}${suffix}`).catch(() => {});
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
afterEach(() => {
|
|
58
|
+
_resetWaitBusSubscriptionsForTests();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
async function makeWorkflow(name: string, def?: WorkflowDefinition): Promise<Workflow> {
|
|
62
|
+
return createWorkflow({
|
|
63
|
+
name: `${name}-${crypto.randomUUID()}`,
|
|
64
|
+
definition: def ?? {
|
|
65
|
+
nodes: [{ id: "n", type: "notify", config: { channel: "swarm", template: "x" } }],
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function newRun(workflowId: string): Promise<string> {
|
|
71
|
+
const id = crypto.randomUUID();
|
|
72
|
+
await createWorkflowRun({ id, workflowId });
|
|
73
|
+
return id;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const eventsFor = (runId: string) => received.filter((event) => event.runId === runId);
|
|
77
|
+
|
|
78
|
+
// The emit goes through afterCommit and a dynamic import, so let it settle.
|
|
79
|
+
const settle = () => new Promise((resolve) => setTimeout(resolve, 50));
|
|
80
|
+
|
|
81
|
+
async function waitFor(predicate: () => boolean | Promise<boolean>, timeoutMs = 2000) {
|
|
82
|
+
const t0 = Date.now();
|
|
83
|
+
while (!(await predicate())) {
|
|
84
|
+
if (Date.now() - t0 > timeoutMs) throw new Error("waitFor: timeout");
|
|
85
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
describe("workflow.run.finished", () => {
|
|
90
|
+
test("fires once per terminal transition, with the run id as runId", async () => {
|
|
91
|
+
const wf = await makeWorkflow("run-finished-once");
|
|
92
|
+
const runId = await newRun(wf.id);
|
|
93
|
+
|
|
94
|
+
await updateWorkflowRun(runId, { status: "waiting" });
|
|
95
|
+
await settle();
|
|
96
|
+
expect(eventsFor(runId)).toEqual([]);
|
|
97
|
+
|
|
98
|
+
await updateWorkflowRun(runId, { status: "failed", error: "boom" });
|
|
99
|
+
await waitFor(() => eventsFor(runId).length === 1);
|
|
100
|
+
expect(eventsFor(runId)[0]).toEqual({ runId, workflowId: wf.id, status: "failed" });
|
|
101
|
+
expect(eventsFor(runId)[0]).not.toHaveProperty("workflowRunId");
|
|
102
|
+
expect(eventsFor(runId)[0]).not.toHaveProperty("_runId");
|
|
103
|
+
|
|
104
|
+
// Later writes to the terminal run, with or without the same status, stay silent.
|
|
105
|
+
await updateWorkflowRun(runId, { context: { late: true } });
|
|
106
|
+
await updateWorkflowRun(runId, { status: "failed", error: "boom again" });
|
|
107
|
+
await settle();
|
|
108
|
+
expect(eventsFor(runId)).toHaveLength(1);
|
|
109
|
+
|
|
110
|
+
// A retry reopens the run; its next end is a new transition.
|
|
111
|
+
await updateWorkflowRun(runId, { status: "running" });
|
|
112
|
+
await updateWorkflowRun(runId, { status: "completed" });
|
|
113
|
+
await waitFor(() => eventsFor(runId).length === 2);
|
|
114
|
+
expect(eventsFor(runId)[1]?.status).toBe("completed");
|
|
115
|
+
|
|
116
|
+
// A terminal run moved to another terminal status, with no reopen, stays silent.
|
|
117
|
+
const relabelled = await newRun(wf.id);
|
|
118
|
+
await updateWorkflowRun(relabelled, { status: "completed" });
|
|
119
|
+
await waitFor(() => eventsFor(relabelled).length === 1);
|
|
120
|
+
await updateWorkflowRun(relabelled, { status: "failed", error: "late failure" });
|
|
121
|
+
await settle();
|
|
122
|
+
expect(eventsFor(relabelled)).toEqual([
|
|
123
|
+
{ runId: relabelled, workflowId: wf.id, status: "completed" },
|
|
124
|
+
]);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("fires only after commit, and never for a rolled-back end", async () => {
|
|
128
|
+
const wf = await makeWorkflow("run-finished-commit");
|
|
129
|
+
const committed = await newRun(wf.id);
|
|
130
|
+
const rolledBack = await newRun(wf.id);
|
|
131
|
+
|
|
132
|
+
await getDbClient().transaction(async () => {
|
|
133
|
+
await updateWorkflowRun(committed, { status: "completed" });
|
|
134
|
+
await settle();
|
|
135
|
+
expect(eventsFor(committed)).toEqual([]);
|
|
136
|
+
});
|
|
137
|
+
await waitFor(() => eventsFor(committed).length === 1);
|
|
138
|
+
|
|
139
|
+
await expect(
|
|
140
|
+
getDbClient().transaction(async () => {
|
|
141
|
+
await updateWorkflowRun(rolledBack, { status: "skipped" });
|
|
142
|
+
throw new Error("roll back");
|
|
143
|
+
}),
|
|
144
|
+
).rejects.toThrow("roll back");
|
|
145
|
+
await settle();
|
|
146
|
+
expect(eventsFor(rolledBack)).toEqual([]);
|
|
147
|
+
expect((await getWorkflowRun(rolledBack))?.status).toBe("running");
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("cancelLiveWorkflowTreeRuns fires for each run it cancels, not for terminal runs", async () => {
|
|
151
|
+
const wf = await makeWorkflow("run-finished-tree-cancel");
|
|
152
|
+
const live = await newRun(wf.id);
|
|
153
|
+
const waiting = await newRun(wf.id);
|
|
154
|
+
const done = await newRun(wf.id);
|
|
155
|
+
await updateWorkflowRun(waiting, { status: "waiting" });
|
|
156
|
+
await updateWorkflowRun(done, { status: "completed" });
|
|
157
|
+
await waitFor(() => eventsFor(done).length === 1);
|
|
158
|
+
|
|
159
|
+
const now = new Date().toISOString();
|
|
160
|
+
await getDbClient().transaction(() =>
|
|
161
|
+
cancelLiveWorkflowTreeRuns([live, waiting, done], "tree cancelled", now),
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
await waitFor(() => eventsFor(live).length === 1 && eventsFor(waiting).length === 1);
|
|
165
|
+
expect(eventsFor(live)[0]).toEqual({ runId: live, workflowId: wf.id, status: "cancelled" });
|
|
166
|
+
expect(eventsFor(waiting)[0]?.status).toBe("cancelled");
|
|
167
|
+
await settle();
|
|
168
|
+
expect(eventsFor(done)).toHaveLength(1);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test("a preflight failure run fires once, a deduplicated repeat does not", async () => {
|
|
172
|
+
const wf = await makeWorkflow("run-finished-preflight");
|
|
173
|
+
const input = {
|
|
174
|
+
workflowId: wf.id,
|
|
175
|
+
triggerType: "manual" as const,
|
|
176
|
+
triggerData: {},
|
|
177
|
+
failureReason: "needs_setup: slack",
|
|
178
|
+
};
|
|
179
|
+
const first = await recordWorkflowPreflightFailure(input);
|
|
180
|
+
await waitFor(() => eventsFor(first.runId).length === 1);
|
|
181
|
+
expect(eventsFor(first.runId)[0]?.status).toBe("failed");
|
|
182
|
+
|
|
183
|
+
const repeat = await recordWorkflowPreflightFailure(input);
|
|
184
|
+
expect(repeat.runId).toBe(first.runId);
|
|
185
|
+
await settle();
|
|
186
|
+
expect(eventsFor(first.runId)).toHaveLength(1);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("a global wait filtered on a detached run's id resumes when that run ends", async () => {
|
|
190
|
+
const registry = createExecutorRegistry(deps);
|
|
191
|
+
|
|
192
|
+
// The detached run parks on a signal, so it stays live until the test ends it.
|
|
193
|
+
const target = await makeWorkflow("run-finished-target", {
|
|
194
|
+
nodes: [
|
|
195
|
+
{
|
|
196
|
+
id: "hold",
|
|
197
|
+
type: "wait",
|
|
198
|
+
config: { mode: "event", eventName: "test.release-target", scope: "global" },
|
|
199
|
+
next: { event: "end" },
|
|
200
|
+
},
|
|
201
|
+
{ id: "end", type: "notify", config: { channel: "swarm", template: "released" } },
|
|
202
|
+
],
|
|
203
|
+
});
|
|
204
|
+
const targetRunId = await startWorkflowExecution(target, {}, registry);
|
|
205
|
+
expect((await getWorkflowRun(targetRunId))?.status).toBe("waiting");
|
|
206
|
+
|
|
207
|
+
const waiter = await makeWorkflow("run-finished-waiter", {
|
|
208
|
+
nodes: [
|
|
209
|
+
{
|
|
210
|
+
id: "watch",
|
|
211
|
+
type: "wait",
|
|
212
|
+
config: {
|
|
213
|
+
mode: "event",
|
|
214
|
+
eventName: EVENT,
|
|
215
|
+
scope: "global",
|
|
216
|
+
filter: { runId: targetRunId },
|
|
217
|
+
},
|
|
218
|
+
next: { event: "after" },
|
|
219
|
+
},
|
|
220
|
+
{ id: "after", type: "notify", config: { channel: "swarm", template: "target ended" } },
|
|
221
|
+
],
|
|
222
|
+
});
|
|
223
|
+
const waiterRunId = await startWorkflowExecution(waiter, {}, registry);
|
|
224
|
+
// Control: a run-scoped wait on the same event must never match another run's end.
|
|
225
|
+
const scoped = await makeWorkflow("run-finished-run-scope", {
|
|
226
|
+
nodes: [
|
|
227
|
+
{
|
|
228
|
+
id: "watch",
|
|
229
|
+
type: "wait",
|
|
230
|
+
config: { mode: "event", eventName: EVENT, scope: "run" },
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
});
|
|
234
|
+
const scopedRunId = await startWorkflowExecution(scoped, {}, registry);
|
|
235
|
+
await initWaitBusSubscriptions(registry);
|
|
236
|
+
|
|
237
|
+
const watchStep = async (runId: string) =>
|
|
238
|
+
(await getWorkflowRunStepsByRunId(runId)).find((step) => step.nodeId === "watch");
|
|
239
|
+
const watch = await watchStep(waiterRunId);
|
|
240
|
+
const scopedWatch = await watchStep(scopedRunId);
|
|
241
|
+
expect((await getWaitStateByStepId(watch!.id))?.status).toBe("pending");
|
|
242
|
+
|
|
243
|
+
// Another run ending does not match the filter.
|
|
244
|
+
const other = await newRun(target.id);
|
|
245
|
+
await updateWorkflowRun(other, { status: "completed" });
|
|
246
|
+
await waitFor(() => eventsFor(other).length === 1);
|
|
247
|
+
await settle();
|
|
248
|
+
expect((await getWaitStateByStepId(watch!.id))?.status).toBe("pending");
|
|
249
|
+
|
|
250
|
+
workflowEventBus.emit("test.release-target", {});
|
|
251
|
+
await waitFor(async () => (await getWorkflowRun(targetRunId))?.status === "completed");
|
|
252
|
+
await waitFor(async () => (await getWaitStateByStepId(watch!.id))?.status === "fired");
|
|
253
|
+
|
|
254
|
+
const fired = await getWaitStateByStepId(watch!.id);
|
|
255
|
+
expect(fired?.firedPayload).toEqual({
|
|
256
|
+
runId: targetRunId,
|
|
257
|
+
workflowId: target.id,
|
|
258
|
+
status: "completed",
|
|
259
|
+
});
|
|
260
|
+
await waitFor(async () => (await getWorkflowRun(waiterRunId))?.status === "completed");
|
|
261
|
+
expect((await getWaitStateByStepId(scopedWatch!.id))?.status).toBe("pending");
|
|
262
|
+
});
|
|
263
|
+
});
|
|
@@ -760,6 +760,40 @@ describe("SwarmScriptExecutor", () => {
|
|
|
760
760
|
expect(result.output?.exitCode).not.toBe(0);
|
|
761
761
|
});
|
|
762
762
|
|
|
763
|
+
skip(
|
|
764
|
+
"meta.signal abort kills the script before its side effect",
|
|
765
|
+
async () => {
|
|
766
|
+
const marker = `/tmp/swarm-script-abort-${crypto.randomUUID()}`;
|
|
767
|
+
await saveScript(
|
|
768
|
+
"one-shot",
|
|
769
|
+
`export default async () => { await new Promise(r => setTimeout(r, 1500)); await Bun.write(${JSON.stringify(marker)}, "ran"); return { done: true }; };`,
|
|
770
|
+
);
|
|
771
|
+
const executor = new SwarmScriptExecutor(deps);
|
|
772
|
+
const wf = await makeWorkflow({ nodes: [] });
|
|
773
|
+
const controller = new AbortController();
|
|
774
|
+
setTimeout(() => controller.abort(new Error("Step timed out")), 300);
|
|
775
|
+
const result = await executor.run({
|
|
776
|
+
config: { scriptName: "one-shot", timeoutMs: 30_000 },
|
|
777
|
+
context: {},
|
|
778
|
+
meta: {
|
|
779
|
+
runId: crypto.randomUUID(),
|
|
780
|
+
stepId: crypto.randomUUID(),
|
|
781
|
+
nodeId: "script",
|
|
782
|
+
workflowId: wf.id,
|
|
783
|
+
dryRun: false,
|
|
784
|
+
signal: controller.signal,
|
|
785
|
+
},
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
expect(result.status).toBe("failed");
|
|
789
|
+
expect(result.error).toContain("killed");
|
|
790
|
+
// Past the point where the script would have written the marker.
|
|
791
|
+
await Bun.sleep(1_800);
|
|
792
|
+
expect(await Bun.file(marker).exists()).toBe(false);
|
|
793
|
+
},
|
|
794
|
+
CHILD_PROCESS_TEST_BUDGET_MS,
|
|
795
|
+
);
|
|
796
|
+
|
|
763
797
|
skip("Failure in the script surfaces as a workflow-node failure", async () => {
|
|
764
798
|
await saveScript("throws", `export default async () => { throw new Error("boom"); };`);
|
|
765
799
|
const executor = new SwarmScriptExecutor(deps);
|
|
@@ -73,7 +73,7 @@ export const registerInjectLearningTool = (server: McpServer) => {
|
|
|
73
73
|
// Generate and store embedding (async, best-effort)
|
|
74
74
|
try {
|
|
75
75
|
const provider = getEmbeddingProvider();
|
|
76
|
-
const embedding = await provider.embed(memory.content);
|
|
76
|
+
const embedding = await provider.embed(memory.content, { kind: "document" });
|
|
77
77
|
if (embedding) {
|
|
78
78
|
await store.updateEmbedding(memory.id, embedding, provider.name);
|
|
79
79
|
}
|
package/src/tools/memory-edit.ts
CHANGED
|
@@ -183,7 +183,7 @@ export const registerMemoryEditTool = (server: McpServer) => {
|
|
|
183
183
|
content !== undefined || oldString !== undefined || newString !== undefined;
|
|
184
184
|
if (result.changed && (newKey === undefined || contentEdited)) {
|
|
185
185
|
const provider = getEmbeddingProvider();
|
|
186
|
-
const embedding = await provider.embed(result.memory.content);
|
|
186
|
+
const embedding = await provider.embed(result.memory.content, { kind: "document" });
|
|
187
187
|
if (embedding) await store.updateEmbedding(result.memory.id, embedding, provider.name);
|
|
188
188
|
try {
|
|
189
189
|
// Edit path: prune links derived from removed content (sequel links survive).
|
|
@@ -120,7 +120,7 @@ export const registerMemorySearchTool = (server: McpServer) => {
|
|
|
120
120
|
// Try vector search first
|
|
121
121
|
const provider = getEmbeddingProvider();
|
|
122
122
|
const store = getMemoryStore();
|
|
123
|
-
const queryEmbedding = await provider.embed(query);
|
|
123
|
+
const queryEmbedding = await provider.embed(query, { kind: "query" });
|
|
124
124
|
|
|
125
125
|
const candidateLimit = limit * CANDIDATE_SET_MULTIPLIER;
|
|
126
126
|
const candidates = await searchMemoryCandidates(
|
|
@@ -16,6 +16,7 @@ export const SCRIPT_TRANSPORT_ERROR =
|
|
|
16
16
|
export const scriptNameSchema = z.string().min(1).max(200);
|
|
17
17
|
export const scriptScopeSchema = z.enum(["agent", "global"]);
|
|
18
18
|
export const scriptFsModeSchema = z.enum(["none", "workspace-rw"]);
|
|
19
|
+
export const scriptExecutorSchema = z.enum(["native", "quickjs"]);
|
|
19
20
|
|
|
20
21
|
export const scriptToolOutputSchema = swarmToolOutputSchema({
|
|
21
22
|
status: z.number().optional(),
|
package/src/tools/script-run.ts
CHANGED
|
@@ -3,6 +3,7 @@ import * as z from "zod";
|
|
|
3
3
|
import { createToolRegistrar } from "@/tools/utils";
|
|
4
4
|
import {
|
|
5
5
|
proxyScriptsApi,
|
|
6
|
+
scriptExecutorSchema,
|
|
6
7
|
scriptFsModeSchema,
|
|
7
8
|
scriptNameSchema,
|
|
8
9
|
scriptScopeSchema,
|
|
@@ -66,6 +67,12 @@ export const registerScriptRunTool = (server: McpServer) => {
|
|
|
66
67
|
fsMode: scriptFsModeSchema
|
|
67
68
|
.default("none")
|
|
68
69
|
.describe("Filesystem mode. v1 supports none only."),
|
|
70
|
+
executor: scriptExecutorSchema
|
|
71
|
+
.nullable()
|
|
72
|
+
.optional()
|
|
73
|
+
.describe(
|
|
74
|
+
'Optional executor override ("native" or "quickjs"). When omitted, named scripts use their saved executor or SCRIPT_EXECUTOR.',
|
|
75
|
+
),
|
|
69
76
|
idempotencyKey: z
|
|
70
77
|
.string()
|
|
71
78
|
.max(200)
|
|
@@ -3,6 +3,7 @@ import * as z from "zod";
|
|
|
3
3
|
import { createToolRegistrar } from "@/tools/utils";
|
|
4
4
|
import {
|
|
5
5
|
proxyScriptsApi,
|
|
6
|
+
scriptExecutorSchema,
|
|
6
7
|
scriptFsModeSchema,
|
|
7
8
|
scriptNameSchema,
|
|
8
9
|
scriptScopeSchema,
|
|
@@ -33,6 +34,12 @@ export const registerScriptUpsertTool = (server: McpServer) => {
|
|
|
33
34
|
fsMode: scriptFsModeSchema
|
|
34
35
|
.default("none")
|
|
35
36
|
.describe("Filesystem mode. v1 supports none only."),
|
|
37
|
+
executor: scriptExecutorSchema
|
|
38
|
+
.nullable()
|
|
39
|
+
.optional()
|
|
40
|
+
.describe(
|
|
41
|
+
'Optional per-script executor override ("native" or "quickjs"). Null or omitted inherits SCRIPT_EXECUTOR.',
|
|
42
|
+
),
|
|
36
43
|
}),
|
|
37
44
|
outputSchema: scriptToolOutputSchema,
|
|
38
45
|
},
|
|
@@ -13,6 +13,11 @@ import {
|
|
|
13
13
|
updateAgentStatusFromCapacity,
|
|
14
14
|
updateTaskProgress,
|
|
15
15
|
} from "@/be/db";
|
|
16
|
+
import {
|
|
17
|
+
LEARNING_MAX_TAGS,
|
|
18
|
+
storeTaskLearning,
|
|
19
|
+
type TaskLearningResult,
|
|
20
|
+
} from "@/be/memory/task-learning";
|
|
16
21
|
import {
|
|
17
22
|
CitationInputSchema,
|
|
18
23
|
getTaskCitations,
|
|
@@ -114,8 +119,30 @@ export const storeProgressOutputSchema = swarmToolOutputSchema({
|
|
|
114
119
|
.describe(
|
|
115
120
|
"Present only when this progress text reads as blocked-waiting: milliseconds since the task's prior update. Drives the store-progress nudge toward defer-task.",
|
|
116
121
|
),
|
|
122
|
+
learning: z
|
|
123
|
+
.looseObject({
|
|
124
|
+
status: z.string().optional(),
|
|
125
|
+
key: z.string().optional(),
|
|
126
|
+
memoryId: z.string().optional(),
|
|
127
|
+
workstream: z.string().optional(),
|
|
128
|
+
similarity: z.number().optional(),
|
|
129
|
+
error: z.string().optional(),
|
|
130
|
+
})
|
|
131
|
+
.optional()
|
|
132
|
+
.describe(
|
|
133
|
+
"Present when the call carried a learning: stored, replayed (same text already stored for this task), duplicate (an existing swarm memory at key/memoryId says the same; similarity is the raw cosine), or failed with error.",
|
|
134
|
+
),
|
|
117
135
|
});
|
|
118
136
|
|
|
137
|
+
function learningSentence(result: TaskLearningResult): string {
|
|
138
|
+
if (result.status === "stored") return `Learning stored at ${result.key}.`;
|
|
139
|
+
if (result.status === "replayed") return `Learning already stored at ${result.key}; skipped.`;
|
|
140
|
+
if (result.status === "duplicate") {
|
|
141
|
+
return `Learning not stored: it matches ${result.key} (similarity ${result.similarity}).`;
|
|
142
|
+
}
|
|
143
|
+
return `Learning not stored: ${result.error}.`;
|
|
144
|
+
}
|
|
145
|
+
|
|
119
146
|
export const registerStoreProgressTool = (server: McpServer) => {
|
|
120
147
|
createToolRegistrar(server)(
|
|
121
148
|
"store-progress",
|
|
@@ -165,6 +192,19 @@ export const registerStoreProgressTool = (server: McpServer) => {
|
|
|
165
192
|
.describe(
|
|
166
193
|
'Claim sources, upserted by index across calls. Use only for factual claims the reader cannot already see in the thread or task tree, 1-2 sources; none on delegation, routing, acks, or status replies. Reference each one in output with [citation:N], or set general: true for a source that backs the whole answer (it renders under "General sources"). ref per kind: task = task UUID; memory = memory UUID (optional quote must appear verbatim in it); github = owner/repo#N, owner/repo@<sha>, or a github.com pull, issues, or commit URL; slack = permalink or channel/ts; agent-fs = file path; page = page id; script-run = script run id; url = http(s) URL. The first completing call is refused, and the task stays in progress, if a marker has no entry, a citation fails validation, or a non-general citation is unreferenced; the response lists each problem. After one refusal, completion proceeds and those markers and sources are dropped from rendered output. At most 50 citations per call/task, refs up to 2048 characters, labels up to 200. Invalid or oversized batches are ignored; existing indices can still be updated at capacity.',
|
|
167
194
|
),
|
|
195
|
+
learning: z
|
|
196
|
+
.union([
|
|
197
|
+
z.string(),
|
|
198
|
+
z.object({
|
|
199
|
+
text: z.string(),
|
|
200
|
+
workstream: z.string().optional(),
|
|
201
|
+
tags: z.array(z.string()).optional(),
|
|
202
|
+
}),
|
|
203
|
+
])
|
|
204
|
+
.optional()
|
|
205
|
+
.describe(
|
|
206
|
+
`A lesson to keep. Add one only when it would change how the next agent works: a gotcha, a dead end, a pattern that worked. Not a progress line. Stored as a swarm memory at /learnings/<workstream>/<slug>, tagged "learning" and tied to this task. The first line of text is the title. At most ~500 tokens and ${LEARNING_MAX_TAGS} kebab tags. workstream defaults to the task's repo name, else "general". Send it on or before the completing call: a call on a finished task stores nothing. A failed learning write is reported in the result and never blocks the progress update.`,
|
|
207
|
+
),
|
|
168
208
|
persistMemory: z
|
|
169
209
|
.boolean()
|
|
170
210
|
.optional()
|
|
@@ -193,6 +233,7 @@ export const registerStoreProgressTool = (server: McpServer) => {
|
|
|
193
233
|
failureReason,
|
|
194
234
|
attachments,
|
|
195
235
|
citations,
|
|
236
|
+
learning,
|
|
196
237
|
persistMemory,
|
|
197
238
|
force,
|
|
198
239
|
},
|
|
@@ -332,6 +373,7 @@ export const registerStoreProgressTool = (server: McpServer) => {
|
|
|
332
373
|
}
|
|
333
374
|
}
|
|
334
375
|
|
|
376
|
+
let learningWrite: Promise<TaskLearningResult> | undefined;
|
|
335
377
|
const result = await getDbClient().transaction(async () => {
|
|
336
378
|
const agent = await getAgentById(requestInfo.agentId ?? "");
|
|
337
379
|
|
|
@@ -435,6 +477,29 @@ export const registerStoreProgressTool = (server: McpServer) => {
|
|
|
435
477
|
await upsertTaskCitations(taskId, citations);
|
|
436
478
|
}
|
|
437
479
|
|
|
480
|
+
// A finished task takes no new writes, learnings included.
|
|
481
|
+
if (learning && isTerminal) {
|
|
482
|
+
learningWrite = Promise.resolve({
|
|
483
|
+
status: "failed",
|
|
484
|
+
error: "the task is already finished; use memory-store",
|
|
485
|
+
});
|
|
486
|
+
} else if (learning) {
|
|
487
|
+
// Post-commit, so a rolled-back progress write never leaves a learning
|
|
488
|
+
// behind, and a failed learning write never rolls back the progress.
|
|
489
|
+
learningWrite = new Promise((resolve) => {
|
|
490
|
+
getDbClient().afterCommit(() =>
|
|
491
|
+
storeTaskLearning({
|
|
492
|
+
agentId: agent.id,
|
|
493
|
+
isLead: agent.isLead === true,
|
|
494
|
+
task: existingTask,
|
|
495
|
+
learning,
|
|
496
|
+
}).then(resolve, (err) =>
|
|
497
|
+
resolve({ status: "failed", error: scrubSecrets(String(err)) }),
|
|
498
|
+
),
|
|
499
|
+
);
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
|
|
438
503
|
// Idempotency guard: short-circuit terminal-status writes (completed/failed)
|
|
439
504
|
// BEFORE any side-effects fire (event emission, memory write, follow-up task,
|
|
440
505
|
// business-use ensure). Without this, a multi-session race causes duplicate
|
|
@@ -627,7 +692,11 @@ export const registerStoreProgressTool = (server: McpServer) => {
|
|
|
627
692
|
});
|
|
628
693
|
}
|
|
629
694
|
|
|
630
|
-
const
|
|
695
|
+
const learningResult = await learningWrite;
|
|
696
|
+
const { success } = result;
|
|
697
|
+
const message = learningResult
|
|
698
|
+
? `${result.message} ${learningSentence(learningResult)}`
|
|
699
|
+
: result.message;
|
|
631
700
|
const task = result.task
|
|
632
701
|
? {
|
|
633
702
|
id: result.task.id,
|
|
@@ -646,6 +715,7 @@ export const registerStoreProgressTool = (server: McpServer) => {
|
|
|
646
715
|
typeof result.blockedWaitingElapsedMs === "number"
|
|
647
716
|
? { blockedWaitingElapsedMs: result.blockedWaitingElapsedMs }
|
|
648
717
|
: {}),
|
|
718
|
+
...(learningResult ? { learning: learningResult } : {}),
|
|
649
719
|
};
|
|
650
720
|
const warnings =
|
|
651
721
|
success && status === "completed"
|
package/src/tools/utils.ts
CHANGED
|
@@ -297,6 +297,17 @@ const storeProgressBlockedWaitingNudge = (r: SwarmToolResult): string | undefine
|
|
|
297
297
|
return `That reads as blocked-waiting, ${formatIdleDuration(ms)} since your last update — keep working if you can, and if the wait is genuinely long, defer-task is one way to resume later instead of polling.`;
|
|
298
298
|
};
|
|
299
299
|
|
|
300
|
+
const storeProgressNudge = (r: SwarmToolResult): string | undefined => {
|
|
301
|
+
const learning = (r.data as { learning?: { status?: unknown; memoryId?: unknown } } | undefined)
|
|
302
|
+
?.learning;
|
|
303
|
+
// nudgeMiddleware permits exactly one steer: the duplicate-learning steer
|
|
304
|
+
// names a concrete next call, so it wins over the blocked-waiting reminder.
|
|
305
|
+
if (r.ok && learning?.status === "duplicate" && typeof learning.memoryId === "string") {
|
|
306
|
+
return `That learning repeats memory ${learning.memoryId}; if your text adds something, memory-edit that memory instead of storing a new one.`;
|
|
307
|
+
}
|
|
308
|
+
return storeProgressBlockedWaitingNudge(r);
|
|
309
|
+
};
|
|
310
|
+
|
|
300
311
|
const workflowLongScriptTimeoutNudge = (r: SwarmToolResult): string | undefined => {
|
|
301
312
|
if (!r.ok) return undefined;
|
|
302
313
|
const hint = (r.data as { longScriptTimeoutHint?: unknown } | undefined)?.longScriptTimeoutHint;
|
|
@@ -311,7 +322,7 @@ const workflowLongScriptTimeoutNudge = (r: SwarmToolResult): string | undefined
|
|
|
311
322
|
export const NUDGES: Record<string, (result: SwarmToolResult) => string | undefined> = {
|
|
312
323
|
"defer-task": (r) =>
|
|
313
324
|
r.ok ? "Stop working on this task now; the wake-up task will carry your note." : undefined,
|
|
314
|
-
"store-progress":
|
|
325
|
+
"store-progress": storeProgressNudge,
|
|
315
326
|
"script-run": scriptRunNudge,
|
|
316
327
|
"script-upsert": scriptAuthoringNudge,
|
|
317
328
|
"launch-script-run": scriptAuthoringNudge,
|