@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@desplega.ai/agent-swarm",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.1",
|
|
4
4
|
"description": "agent-swarm.dev — an open-source operating system for AI work: a lead agent breaks goals into tasks, routes them to specialized workers such as Claude Code or Codex, runs each worker in an isolated container, and preserves shared memory, tools, schedules, and review gates so delegated work compounds across sessions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-swarm.dev",
|
|
@@ -80,6 +80,8 @@
|
|
|
80
80
|
"sync:ai-toolbox-skills": "bun scripts/sync-ai-toolbox-skills.ts",
|
|
81
81
|
"check:ai-toolbox-skills": "bun scripts/sync-ai-toolbox-skills.ts --check",
|
|
82
82
|
"check:skill-sources": "bun scripts/check-skill-sources.ts",
|
|
83
|
+
"build:docs-index": "bun scripts/build-docs-index.ts",
|
|
84
|
+
"check:docs-index": "bun scripts/build-docs-index.ts --check",
|
|
83
85
|
"check:operator-skill": "bun scripts/check-operator-skill.ts",
|
|
84
86
|
"sync:telemetry-contract": "bun scripts/sync-telemetry-contract.ts",
|
|
85
87
|
"check:telemetry-contract": "bun scripts/sync-telemetry-contract.ts --check",
|
|
@@ -185,7 +187,7 @@
|
|
|
185
187
|
"@oxlint/plugins": "1.78.0",
|
|
186
188
|
"@types/bun": "latest",
|
|
187
189
|
"dependency-cruiser": "^18.0.0",
|
|
188
|
-
"happy-dom": "^
|
|
190
|
+
"happy-dom": "^21.0.4",
|
|
189
191
|
"konsistent": "1.0.0-beta.1",
|
|
190
192
|
"openapi-typescript": "^7",
|
|
191
193
|
"oxlint": "1.78.0",
|
|
@@ -201,7 +203,7 @@
|
|
|
201
203
|
"dependencies": {
|
|
202
204
|
"@agentclientprotocol/sdk": "2.0.2",
|
|
203
205
|
"@ai-sdk/openai": "^3.0.41",
|
|
204
|
-
"@anthropic-ai/claude-agent-sdk": "0.3.
|
|
206
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.311",
|
|
205
207
|
"@anthropic-ai/sdk": "^1.3.0",
|
|
206
208
|
"@asteasolutions/zod-to-openapi": "^8.0.0",
|
|
207
209
|
"@aws-sdk/client-bedrock": "3.1048.0",
|
|
@@ -209,8 +211,8 @@
|
|
|
209
211
|
"@chat-adapter/slack": "4.41.1",
|
|
210
212
|
"@chat-adapter/teams": "4.41.1",
|
|
211
213
|
"@chat-adapter/telegram": "4.41.1",
|
|
212
|
-
"@cursor/sdk": "1.0
|
|
213
|
-
"@desplega.ai/agent-fs-just-bash": "0.18.
|
|
214
|
+
"@cursor/sdk": "1.1.0",
|
|
215
|
+
"@desplega.ai/agent-fs-just-bash": "0.18.9",
|
|
214
216
|
"@desplega.ai/business-use": "^0.4.2",
|
|
215
217
|
"@earendil-works/pi-agent-core": "^1.4.0",
|
|
216
218
|
"@earendil-works/pi-ai": "^1.4.0",
|
|
@@ -3,6 +3,7 @@ import { getSlackConfiguration, isSlackConfigured } from "../slack/config";
|
|
|
3
3
|
import { isEnvFlagEnabled } from "../utils/env-flag";
|
|
4
4
|
import { scrubSecrets } from "../utils/secret-scrubber";
|
|
5
5
|
import { getDbClient } from "./db";
|
|
6
|
+
import { emitRunFinished } from "./db/workflow-runs";
|
|
6
7
|
import { getOAuthApp, getOAuthTokens } from "./db-queries/oauth";
|
|
7
8
|
import { scrubJsonValue } from "./scrub-json";
|
|
8
9
|
|
|
@@ -446,6 +447,7 @@ export async function recordWorkflowPreflightFailure(input: {
|
|
|
446
447
|
timestamp,
|
|
447
448
|
],
|
|
448
449
|
);
|
|
450
|
+
emitRunFinished(runId, input.workflowId, "failed");
|
|
449
451
|
// Claimed in this transaction, which also sees every child failure run
|
|
450
452
|
// committed before it: concurrent failures alert exactly once a day.
|
|
451
453
|
return {
|
|
@@ -1,21 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Single-use
|
|
2
|
+
* Single-use codes that a browser or connector trades once for an `aswt_` token.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Two kinds share the `connector_codes` table:
|
|
5
|
+
*
|
|
6
|
+
* - `connector`: the agent-swarm.dev ChatGPT connector. The dashboard asks for
|
|
7
|
+
* a code (`POST /api/users/:id/connector-codes`) and opens the connector's
|
|
8
|
+
* connect page with it. The connector trades the code once
|
|
9
|
+
* (`POST /api/connector/exchange`).
|
|
10
|
+
* - `invite`: a teammate invite (`POST /api/invites`). The teammate's browser
|
|
11
|
+
* trades it once (`POST /api/invites/exchange`). The exchange also flips the
|
|
12
|
+
* user from `invited` to `active`, because `aswt_` auth admits only active
|
|
13
|
+
* users.
|
|
14
|
+
*
|
|
15
|
+
* The token is minted only at exchange time, through the regular `mintToken`
|
|
16
|
+
* path, so an unused code never leaves a token behind. Each exchange redeems
|
|
17
|
+
* only its own kind.
|
|
9
18
|
*/
|
|
10
19
|
|
|
11
20
|
import { createHash, randomBytes } from "node:crypto";
|
|
12
21
|
import { getDbClient } from "./db";
|
|
13
|
-
import { type IdentityActor, mintToken } from "./users";
|
|
22
|
+
import { type IdentityActor, mintToken, recordIdentityEvent } from "./users";
|
|
23
|
+
|
|
24
|
+
export type ConnectorCodeKind = "connector" | "invite";
|
|
14
25
|
|
|
15
26
|
export const CONNECTOR_CODE_TTL_MS = 10 * 60 * 1000;
|
|
27
|
+
export const INVITE_CODE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
16
28
|
/** Expired rows stay this long before the heartbeat sweep deletes them. */
|
|
17
29
|
const CONNECTOR_CODE_RETENTION_MS = 60 * 60 * 1000;
|
|
18
30
|
export const DEFAULT_CONNECTOR_CODE_LABEL = "ChatGPT connector";
|
|
31
|
+
export const DEFAULT_INVITE_CODE_LABEL = "Dashboard invite";
|
|
19
32
|
|
|
20
33
|
function sha256Hex(input: string): string {
|
|
21
34
|
return createHash("sha256").update(input).digest("hex");
|
|
@@ -27,27 +40,34 @@ export async function createConnectorCode(
|
|
|
27
40
|
label: string,
|
|
28
41
|
actor: IdentityActor,
|
|
29
42
|
now = new Date(),
|
|
43
|
+
opts: { kind?: ConnectorCodeKind; ttlMs?: number } = {},
|
|
30
44
|
): Promise<{ code: string; expiresAt: string }> {
|
|
45
|
+
const kind = opts.kind ?? "connector";
|
|
46
|
+
const ttlMs = opts.ttlMs ?? CONNECTOR_CODE_TTL_MS;
|
|
31
47
|
const code = randomBytes(32).toString("base64url");
|
|
32
|
-
const expiresAt = new Date(now.getTime() +
|
|
48
|
+
const expiresAt = new Date(now.getTime() + ttlMs).toISOString();
|
|
33
49
|
await getDbClient().run(
|
|
34
50
|
`INSERT INTO connector_codes
|
|
35
|
-
(code_hash, user_id, label, actor_kind, actor_id, created_at, expires_at)
|
|
36
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
37
|
-
[sha256Hex(code), userId, label, actor.kind, actor.id, now.toISOString(), expiresAt],
|
|
51
|
+
(code_hash, user_id, label, actor_kind, actor_id, created_at, expires_at, kind)
|
|
52
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
53
|
+
[sha256Hex(code), userId, label, actor.kind, actor.id, now.toISOString(), expiresAt, kind],
|
|
38
54
|
);
|
|
39
55
|
return { code, expiresAt };
|
|
40
56
|
}
|
|
41
57
|
|
|
42
58
|
/**
|
|
43
59
|
* Consume a code and mint its token in one transaction. Returns null when the
|
|
44
|
-
* code is unknown, expired,
|
|
60
|
+
* code is unknown, expired, already used, or of another kind (callers must not
|
|
61
|
+
* tell these apart). For an invite code, also returns null when the user is
|
|
62
|
+
* no longer `invited` (suspended or merged since): the code is spent and no
|
|
63
|
+
* token is minted.
|
|
45
64
|
*/
|
|
46
65
|
export async function exchangeConnectorCode(
|
|
47
66
|
code: string,
|
|
48
|
-
now =
|
|
67
|
+
opts: { kind?: ConnectorCodeKind; now?: Date } = {},
|
|
49
68
|
): Promise<{ token: string; userId: string } | null> {
|
|
50
|
-
const
|
|
69
|
+
const kind = opts.kind ?? "connector";
|
|
70
|
+
const nowIso = (opts.now ?? new Date()).toISOString();
|
|
51
71
|
return getDbClient().transaction(async (tx) => {
|
|
52
72
|
const row = await tx.get<{
|
|
53
73
|
user_id: string;
|
|
@@ -56,21 +76,44 @@ export async function exchangeConnectorCode(
|
|
|
56
76
|
actor_id: string;
|
|
57
77
|
}>(
|
|
58
78
|
`UPDATE connector_codes SET used_at = ?
|
|
59
|
-
WHERE code_hash = ? AND used_at IS NULL AND expires_at > ?
|
|
79
|
+
WHERE code_hash = ? AND used_at IS NULL AND expires_at > ? AND kind = ?
|
|
60
80
|
RETURNING user_id, label, actor_kind, actor_id`,
|
|
61
|
-
[nowIso, sha256Hex(code), nowIso],
|
|
81
|
+
[nowIso, sha256Hex(code), nowIso, kind],
|
|
62
82
|
);
|
|
63
83
|
if (!row) return null;
|
|
64
|
-
const {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
84
|
+
const actor: IdentityActor = { kind: row.actor_kind, id: row.actor_id };
|
|
85
|
+
|
|
86
|
+
if (kind === "invite") {
|
|
87
|
+
const activated = await tx.run(
|
|
88
|
+
"UPDATE users SET status = 'active', lastUpdatedAt = ? WHERE id = ? AND status = 'invited'",
|
|
89
|
+
[nowIso, row.user_id],
|
|
90
|
+
);
|
|
91
|
+
if (activated.changes === 0) return null;
|
|
92
|
+
await recordIdentityEvent(
|
|
93
|
+
row.user_id,
|
|
94
|
+
"status_changed",
|
|
95
|
+
actor,
|
|
96
|
+
{ status: "invited" },
|
|
97
|
+
{ status: "active", source: "invite_exchange" },
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const { plaintext } = await mintToken(row.user_id, row.label, actor, {
|
|
102
|
+
source: kind === "invite" ? "invite_exchange" : "connector_exchange",
|
|
103
|
+
});
|
|
70
104
|
return { token: plaintext, userId: row.user_id };
|
|
71
105
|
});
|
|
72
106
|
}
|
|
73
107
|
|
|
108
|
+
/** Delete a user's unused invite codes, so a re-invite leaves one live link. */
|
|
109
|
+
export async function deleteUnusedInviteCodes(userId: string): Promise<number> {
|
|
110
|
+
const result = await getDbClient().run(
|
|
111
|
+
"DELETE FROM connector_codes WHERE user_id = ? AND kind = 'invite' AND used_at IS NULL",
|
|
112
|
+
[userId],
|
|
113
|
+
);
|
|
114
|
+
return result.changes;
|
|
115
|
+
}
|
|
116
|
+
|
|
74
117
|
/** Delete codes that expired more than 1h ago. Returns the number of deleted rows. */
|
|
75
118
|
export async function deleteExpiredConnectorCodes(now = new Date()): Promise<number> {
|
|
76
119
|
const cutoff = new Date(now.getTime() - CONNECTOR_CODE_RETENTION_MS).toISOString();
|
package/src/be/db/api-keys.ts
CHANGED
|
@@ -110,6 +110,15 @@ export async function recordKeyValidation(
|
|
|
110
110
|
now,
|
|
111
111
|
],
|
|
112
112
|
);
|
|
113
|
+
// A passing test clears the key's rejection in every scope, since the
|
|
114
|
+
// suffix read in `getAvailableKeyIndices` matches them all.
|
|
115
|
+
if (key.ok) {
|
|
116
|
+
await tx.run(
|
|
117
|
+
`UPDATE api_key_status SET lastValidationError = NULL, lastValidationFailedAt = NULL, updatedAt = ?
|
|
118
|
+
WHERE keyType = ? AND keySuffix = ? AND lastValidationFailedAt IS NOT NULL`,
|
|
119
|
+
[now, keyType, key.keySuffix],
|
|
120
|
+
);
|
|
121
|
+
}
|
|
113
122
|
}
|
|
114
123
|
if (options.retireAbsent === false) return;
|
|
115
124
|
const placeholders = keys.map(() => "?").join(", ");
|
|
@@ -465,7 +465,8 @@ export async function disarmWorkflowTreeRetries(
|
|
|
465
465
|
/**
|
|
466
466
|
* Mark the given runs that are still running or waiting `cancelled`, without
|
|
467
467
|
* the per-run `workflow.child.finished` wake-up: it is for runs whose parent
|
|
468
|
-
* step the same cancel closes.
|
|
468
|
+
* step the same cancel closes. Each run it cancels still emits
|
|
469
|
+
* `workflow.run.finished`. A terminal run keeps its status and reason.
|
|
469
470
|
*/
|
|
470
471
|
export async function cancelLiveWorkflowTreeRuns(
|
|
471
472
|
runIds: string[],
|
|
@@ -473,12 +474,14 @@ export async function cancelLiveWorkflowTreeRuns(
|
|
|
473
474
|
finishedAt: string,
|
|
474
475
|
): Promise<void> {
|
|
475
476
|
await forEachChunk(runIds, async (chunk, placeholders) => {
|
|
476
|
-
await getDbClient().
|
|
477
|
+
const cancelled = await getDbClient().query<{ id: string; workflowId: string }>(
|
|
477
478
|
`UPDATE workflow_runs
|
|
478
479
|
SET status = 'cancelled', error = ?, finishedAt = ?, lastUpdatedAt = ?
|
|
479
|
-
WHERE id IN (${placeholders}) AND status IN ('running', 'waiting')
|
|
480
|
+
WHERE id IN (${placeholders}) AND status IN ('running', 'waiting')
|
|
481
|
+
RETURNING id, workflowId`,
|
|
480
482
|
[scrubSecrets(reason), finishedAt, finishedAt, ...chunk],
|
|
481
483
|
);
|
|
484
|
+
for (const run of cancelled) emitRunFinished(run.id, run.workflowId, "cancelled");
|
|
482
485
|
});
|
|
483
486
|
}
|
|
484
487
|
|
|
@@ -643,14 +646,42 @@ export function emitChildRunFinished(
|
|
|
643
646
|
}
|
|
644
647
|
|
|
645
648
|
function emitAfterCommit(childRunId: string, parentStepId: string): void {
|
|
649
|
+
emitBusEventAfterCommit("workflow.child.finished", { childRunId, parentStepId });
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/** Bus event every run emits once when it turns terminal. */
|
|
653
|
+
export const WORKFLOW_RUN_FINISHED_EVENT = "workflow.run.finished";
|
|
654
|
+
|
|
655
|
+
export function isTerminalWorkflowRunStatus(status: WorkflowRunStatus): boolean {
|
|
656
|
+
return TERMINAL_STATUSES.has(status);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Announce after commit that a run turned terminal, top-level and child runs
|
|
661
|
+
* alike, so an event-mode `wait` can resume when any run ends. Callers emit
|
|
662
|
+
* only when a live run (running or waiting) turns terminal, never on a later
|
|
663
|
+
* update of a run that is already terminal. The id is `runId`, never `workflowRunId` or
|
|
664
|
+
* `_runId`: those would put the finished run in the waiting run's own `run`
|
|
665
|
+
* scope, so waiters use scope `global` and filter on `runId`.
|
|
666
|
+
*/
|
|
667
|
+
export function emitRunFinished(
|
|
668
|
+
runId: string,
|
|
669
|
+
workflowId: string,
|
|
670
|
+
status: WorkflowRunStatus,
|
|
671
|
+
): void {
|
|
672
|
+
if (!TERMINAL_STATUSES.has(status)) return;
|
|
673
|
+
emitBusEventAfterCommit(WORKFLOW_RUN_FINISHED_EVENT, { runId, workflowId, status });
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
function emitBusEventAfterCommit(event: string, payload: Record<string, unknown>): void {
|
|
646
677
|
getDbClient().afterCommit(() => {
|
|
647
678
|
import("../../workflows/event-bus")
|
|
648
679
|
.then(({ workflowEventBus }) => {
|
|
649
|
-
workflowEventBus.emit(
|
|
680
|
+
workflowEventBus.emit(event, payload);
|
|
650
681
|
})
|
|
651
682
|
.catch((err) =>
|
|
652
683
|
console.error(
|
|
653
|
-
|
|
684
|
+
`[db] ${event} event not emitted:`,
|
|
654
685
|
scrubSecrets(err instanceof Error ? err.message : String(err)),
|
|
655
686
|
),
|
|
656
687
|
);
|
package/src/be/db.ts
CHANGED
|
@@ -227,8 +227,10 @@ import { configureTaskWriteDependencies, failTask } from "./db/tasks/write";
|
|
|
227
227
|
import { tombstoneWorkflowIdempotencyKeys } from "./db/workflow-idempotency";
|
|
228
228
|
import {
|
|
229
229
|
emitChildRunFinished,
|
|
230
|
+
emitRunFinished,
|
|
230
231
|
getRunLineageBlock,
|
|
231
232
|
getStepExecutionBlock,
|
|
233
|
+
isTerminalWorkflowRunStatus,
|
|
232
234
|
rowToWorkflowRun,
|
|
233
235
|
rowToWorkflowRunStep,
|
|
234
236
|
WORKFLOW_RUN_SUMMARY_COLUMNS,
|
|
@@ -7994,16 +7996,33 @@ export async function updateWorkflowRun(
|
|
|
7994
7996
|
updates.push("lastUpdatedAt = ?");
|
|
7995
7997
|
params.push(new Date().toISOString());
|
|
7996
7998
|
params.push(id);
|
|
7997
|
-
const
|
|
7998
|
-
|
|
7999
|
-
|
|
8000
|
-
|
|
7999
|
+
const update = () =>
|
|
8000
|
+
getDbClient().get<WorkflowRunRow>(
|
|
8001
|
+
`UPDATE workflow_runs SET ${updates.join(", ")} WHERE id = ? RETURNING *`,
|
|
8002
|
+
params,
|
|
8003
|
+
);
|
|
8004
|
+
const endsRun = data.status !== undefined && isTerminalWorkflowRunStatus(data.status);
|
|
8005
|
+
// A run that ends reads the status it replaces in the same transaction, so
|
|
8006
|
+
// only a change out of a live status announces the end: a later terminal
|
|
8007
|
+
// write, same status or not, does not announce it again.
|
|
8008
|
+
const [previousStatus, row] = endsRun
|
|
8009
|
+
? await getDbClient().transaction(async () => {
|
|
8010
|
+
const previous = await getDbClient().get<{ status: WorkflowRunStatus }>(
|
|
8011
|
+
"SELECT status FROM workflow_runs WHERE id = ?",
|
|
8012
|
+
[id],
|
|
8013
|
+
);
|
|
8014
|
+
return [previous?.status, await update()] as const;
|
|
8015
|
+
})
|
|
8016
|
+
: [undefined, await update()];
|
|
8001
8017
|
if (!row) return null;
|
|
8002
8018
|
const run = rowToWorkflowRun(row);
|
|
8003
8019
|
if (data.status === "completed" || data.status === "failed") {
|
|
8004
8020
|
emitWorkflowTerminalTelemetry(run);
|
|
8005
8021
|
}
|
|
8006
8022
|
if (row.parentStepId) emitChildRunFinished(id, row.parentStepId, run.status);
|
|
8023
|
+
if (endsRun && previousStatus && !isTerminalWorkflowRunStatus(previousStatus)) {
|
|
8024
|
+
emitRunFinished(id, run.workflowId, run.status);
|
|
8025
|
+
}
|
|
8007
8026
|
return run;
|
|
8008
8027
|
}
|
|
8009
8028
|
|
|
@@ -11721,6 +11740,14 @@ export interface AvailableKeyIndicesResult {
|
|
|
11721
11740
|
* whose `plan` cannot run the family (`planAllowsModelFamily`) is excluded
|
|
11722
11741
|
* and reported in `seatBlockedIndices`, also when it is key-wide or
|
|
11723
11742
|
* model-window blocked; a seat block has no reset time.
|
|
11743
|
+
*
|
|
11744
|
+
* With `keySuffixes` (the caller's own pool, in order), rows are matched by
|
|
11745
|
+
* key suffix instead of the stored `keyIndex`, across every scope: a key has
|
|
11746
|
+
* one state wherever it is pooled, and a key at the same index in another
|
|
11747
|
+
* agent's pool is never blocked by it. A suffix listed more than once is read
|
|
11748
|
+
* by `keyIndex` in `scope`/`scopeId`, as without the list. Returned indices
|
|
11749
|
+
* are positions in `keySuffixes`. Without it, rows of `scope`/`scopeId` are
|
|
11750
|
+
* read by `keyIndex`.
|
|
11724
11751
|
*/
|
|
11725
11752
|
export async function getAvailableKeyIndices(
|
|
11726
11753
|
keyType: string,
|
|
@@ -11728,41 +11755,58 @@ export async function getAvailableKeyIndices(
|
|
|
11728
11755
|
scope = "global",
|
|
11729
11756
|
scopeId: string | null = null,
|
|
11730
11757
|
modelFamily?: ModelFamily,
|
|
11758
|
+
keySuffixes?: string[],
|
|
11731
11759
|
): Promise<AvailableKeyIndicesResult> {
|
|
11732
11760
|
const now = new Date().toISOString();
|
|
11733
11761
|
const nowMs = Date.now();
|
|
11734
11762
|
const client = getDbClient();
|
|
11735
11763
|
const effectiveScopeId = scopeId ?? "";
|
|
11764
|
+
const suffixes = keySuffixes?.length ? keySuffixes : undefined;
|
|
11765
|
+
const rowFilter = suffixes
|
|
11766
|
+
? `keyType = ? AND keySuffix IN (${suffixes.map(() => "?").join(", ")})`
|
|
11767
|
+
: "keyType = ? AND scope = ? AND scopeId = ?";
|
|
11768
|
+
const rowParams = suffixes ? [keyType, ...suffixes] : [keyType, scope, effectiveScopeId];
|
|
11736
11769
|
|
|
11737
11770
|
// Auto-clear expired rate limits. Never an auth bench: only `clearAuthBench` lifts it.
|
|
11738
11771
|
await client.run(
|
|
11739
11772
|
`UPDATE api_key_status
|
|
11740
11773
|
SET status = 'available', rateLimitedUntil = NULL, updatedAt = ?
|
|
11741
|
-
WHERE
|
|
11774
|
+
WHERE ${rowFilter}
|
|
11742
11775
|
AND status = 'rate_limited' AND rateLimitedUntil <= ? AND consecutiveAuthFailures < ?`,
|
|
11743
|
-
[now,
|
|
11776
|
+
[now, ...rowParams, now, CODEX_AUTH_FAILURE_BENCH_THRESHOLD],
|
|
11744
11777
|
);
|
|
11745
11778
|
|
|
11746
11779
|
const rows = await client.query<{
|
|
11747
11780
|
keyIndex: number;
|
|
11781
|
+
keySuffix: string;
|
|
11782
|
+
scope: string;
|
|
11783
|
+
scopeId: string;
|
|
11748
11784
|
status: string;
|
|
11749
11785
|
rateLimitWindows: string | null;
|
|
11750
11786
|
plan: string | null;
|
|
11751
11787
|
authFailureSeq: number;
|
|
11752
11788
|
lastValidationFailedAt: string | null;
|
|
11753
11789
|
}>(
|
|
11754
|
-
`SELECT keyIndex, status, rateLimitWindows, plan, authFailureSeq, lastValidationFailedAt FROM api_key_status
|
|
11755
|
-
WHERE
|
|
11756
|
-
|
|
11757
|
-
);
|
|
11790
|
+
`SELECT keyIndex, keySuffix, scope, scopeId, status, rateLimitWindows, plan, authFailureSeq, lastValidationFailedAt FROM api_key_status
|
|
11791
|
+
WHERE ${rowFilter}`,
|
|
11792
|
+
rowParams,
|
|
11793
|
+
);
|
|
11794
|
+
// Pool positions a row applies to: the position holding its suffix, or its stored index.
|
|
11795
|
+
// A suffix held by several pool members cannot tell them apart, so it falls back to
|
|
11796
|
+
// the index read: only a row of the requested scope, at its stored index.
|
|
11797
|
+
const positionsOf = (row: (typeof rows)[number]): number[] => {
|
|
11798
|
+
if (!suffixes) return [row.keyIndex];
|
|
11799
|
+
const positions = suffixes.flatMap((suffix, i) => (suffix === row.keySuffix ? [i] : []));
|
|
11800
|
+
if (positions.length < 2) return positions;
|
|
11801
|
+
const sameScope = row.scope === scope && row.scopeId === effectiveScopeId;
|
|
11802
|
+
return sameScope && positions.includes(row.keyIndex) ? [row.keyIndex] : [];
|
|
11803
|
+
};
|
|
11758
11804
|
|
|
11759
11805
|
const blockedIndices = new Set(
|
|
11760
|
-
rows.filter((r) => r.status === "rate_limited").
|
|
11806
|
+
rows.filter((r) => r.status === "rate_limited").flatMap(positionsOf),
|
|
11761
11807
|
);
|
|
11762
11808
|
const rejectedIndices = [
|
|
11763
|
-
...new Set(
|
|
11764
|
-
rows.filter((row) => row.lastValidationFailedAt !== null).map((row) => row.keyIndex),
|
|
11765
|
-
),
|
|
11809
|
+
...new Set(rows.filter((row) => row.lastValidationFailedAt !== null).flatMap(positionsOf)),
|
|
11766
11810
|
];
|
|
11767
11811
|
for (const index of rejectedIndices) blockedIndices.add(index);
|
|
11768
11812
|
|
|
@@ -11773,14 +11817,17 @@ export async function getAvailableKeyIndices(
|
|
|
11773
11817
|
// A key already blocked key-wide doesn't need a separate model-block
|
|
11774
11818
|
// entry — it's excluded from availableIndices either way, and
|
|
11775
11819
|
// modelBlockedIndices means "excluded only by a model-scoped block".
|
|
11776
|
-
|
|
11820
|
+
const positions = positionsOf(row).filter(
|
|
11821
|
+
(i) => !blockedIndices.has(i) && !modelBlockedIndices.includes(i),
|
|
11822
|
+
);
|
|
11823
|
+
if (positions.length === 0) continue;
|
|
11777
11824
|
const block = activeModelBlock(
|
|
11778
11825
|
parseRateLimitWindowsJson(row.rateLimitWindows),
|
|
11779
11826
|
modelFamily,
|
|
11780
11827
|
nowMs,
|
|
11781
11828
|
);
|
|
11782
11829
|
if (!block) continue;
|
|
11783
|
-
modelBlockedIndices.push(
|
|
11830
|
+
modelBlockedIndices.push(...positions);
|
|
11784
11831
|
if (earliestModelResetsAtSec === undefined || block.resetsAt < earliestModelResetsAtSec) {
|
|
11785
11832
|
earliestModelResetsAtSec = block.resetsAt;
|
|
11786
11833
|
}
|
|
@@ -11794,7 +11841,10 @@ export async function getAvailableKeyIndices(
|
|
|
11794
11841
|
const seatBlockedIndices: number[] = [];
|
|
11795
11842
|
if (modelFamily) {
|
|
11796
11843
|
for (const row of rows) {
|
|
11797
|
-
if (
|
|
11844
|
+
if (planAllowsModelFamily(row.plan, modelFamily)) continue;
|
|
11845
|
+
for (const i of positionsOf(row)) {
|
|
11846
|
+
if (!seatBlockedIndices.includes(i)) seatBlockedIndices.push(i);
|
|
11847
|
+
}
|
|
11798
11848
|
}
|
|
11799
11849
|
}
|
|
11800
11850
|
const seatBlockedSet = new Set(seatBlockedIndices);
|
|
@@ -12143,22 +12193,30 @@ export async function clearKeyRateLimit(
|
|
|
12143
12193
|
const effectiveScopeId = scopeId ?? "";
|
|
12144
12194
|
const { keyIndex = -1, authFence = null } = opts;
|
|
12145
12195
|
const clearAuthBench = opts.clearAuthBench === true && authFence !== null;
|
|
12146
|
-
const key = [keyType, keySuffix, scope, effectiveScopeId];
|
|
12147
12196
|
return await getDbClient().transaction(async (tx) => {
|
|
12197
|
+
// The key's rows in every scope: `getAvailableKeyIndices` reads them all by suffix.
|
|
12148
12198
|
const result = await tx.run(
|
|
12149
12199
|
`UPDATE api_key_status
|
|
12150
12200
|
SET status = 'available', rateLimitedUntil = NULL, updatedAt = ?
|
|
12151
|
-
WHERE keyType = ? AND keySuffix = ?
|
|
12201
|
+
WHERE keyType = ? AND keySuffix = ?
|
|
12152
12202
|
AND status = 'rate_limited' AND ((? AND authFailureSeq <= ?) OR consecutiveAuthFailures < ?)`,
|
|
12153
|
-
[
|
|
12203
|
+
[
|
|
12204
|
+
now,
|
|
12205
|
+
keyType,
|
|
12206
|
+
keySuffix,
|
|
12207
|
+
clearAuthBench ? 1 : 0,
|
|
12208
|
+
authFence,
|
|
12209
|
+
CODEX_AUTH_FAILURE_BENCH_THRESHOLD,
|
|
12210
|
+
],
|
|
12154
12211
|
);
|
|
12155
12212
|
const cleared = result.changes > 0;
|
|
12156
12213
|
if (!clearAuthBench) return cleared;
|
|
12157
12214
|
const lifted = await tx.query<{ keySuffix: string }>(
|
|
12158
12215
|
`UPDATE api_key_status SET consecutiveAuthFailures = 0, updatedAt = ?1,
|
|
12159
12216
|
status = CASE WHEN keySuffix = ?2 THEN status ELSE 'available' END, rateLimitedUntil = CASE WHEN keySuffix = ?2 THEN rateLimitedUntil END
|
|
12160
|
-
WHERE keyType = ?3 AND
|
|
12161
|
-
AND ((keySuffix = ?2 AND authFailureSeq <= ?7)
|
|
12217
|
+
WHERE keyType = ?3 AND (consecutiveAuthFailures > 0 OR status = 'rate_limited')
|
|
12218
|
+
AND ((keySuffix = ?2 AND authFailureSeq <= ?7)
|
|
12219
|
+
OR (keySuffix != ?2 AND keyIndex = ?6 AND scope = ?4 AND scopeId = ?5)) RETURNING keySuffix`,
|
|
12162
12220
|
[now, keySuffix, keyType, scope, effectiveScopeId, keyIndex, authFence],
|
|
12163
12221
|
);
|
|
12164
12222
|
const retired = lifted.filter((row) => row.keySuffix !== keySuffix);
|
|
@@ -56,7 +56,10 @@ export async function runBootReembed(): Promise<void> {
|
|
|
56
56
|
for (let i = 0; i < rows.length; i += BATCH_SIZE) {
|
|
57
57
|
const batch = rows.slice(i, i + BATCH_SIZE);
|
|
58
58
|
try {
|
|
59
|
-
const embeddings = await provider.embedBatch(
|
|
59
|
+
const embeddings = await provider.embedBatch(
|
|
60
|
+
batch.map((m) => m.content),
|
|
61
|
+
{ kind: "document" },
|
|
62
|
+
);
|
|
60
63
|
for (let j = 0; j < embeddings.length; j++) {
|
|
61
64
|
if (embeddings[j]) {
|
|
62
65
|
await store.updateEmbedding(batch[j]!.id, embeddings[j]!, provider.name);
|
|
@@ -72,6 +72,12 @@ export function minSimilarity(): number {
|
|
|
72
72
|
return numEnv("MEMORY_MIN_SIMILARITY", 0.1);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
// Raw cosine at or above which a store-progress learning is a near duplicate of
|
|
76
|
+
// an existing swarm memory and is not stored. Measured on production
|
|
77
|
+
// `/learnings` rows: a confirmed twin pair scored 0.877, the closest distinct
|
|
78
|
+
// pair 0.705.
|
|
79
|
+
export const LEARNING_DUPLICATE_MIN_SIMILARITY = 0.8;
|
|
80
|
+
|
|
75
81
|
// Reranking parameters
|
|
76
82
|
export function accessBoostMaxMultiplier(): number {
|
|
77
83
|
return numEnv("MEMORY_ACCESS_BOOST_MAX", 1.5);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A memory or script written while the embeddings endpoint is down keeps no
|
|
3
|
+
* vector, and the backfills (`runBootReembed`, `runBootReembedScripts`) run
|
|
4
|
+
* only at boot or when a config reload turns embeddings on. When an embedding
|
|
5
|
+
* call works again after a failed one (for example, a local TEI restarted),
|
|
6
|
+
* run both backfills once more, so those rows do not wait for the next API
|
|
7
|
+
* restart.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
let failedSinceBackfill = false;
|
|
11
|
+
let backfill: Promise<void> | null = null;
|
|
12
|
+
|
|
13
|
+
/** An embedding call failed (network, server error, rejected request). */
|
|
14
|
+
export function noteEmbeddingFailure(): void {
|
|
15
|
+
failedSinceBackfill = true;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** An embedding call returned a vector. Starts the backfill after a failure. */
|
|
19
|
+
export function noteEmbeddingSuccess(): void {
|
|
20
|
+
if (!failedSinceBackfill || backfill) return;
|
|
21
|
+
failedSinceBackfill = false;
|
|
22
|
+
const report = (what: string) => (err: unknown) => {
|
|
23
|
+
console.error(
|
|
24
|
+
`[memory] ${what} backfill after embeddings recovered failed:`,
|
|
25
|
+
(err as Error).message,
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
backfill = Promise.all([
|
|
29
|
+
import("./boot-reembed").then(({ runBootReembed }) => runBootReembed()).catch(report("Memory")),
|
|
30
|
+
import("../scripts/boot-reembed")
|
|
31
|
+
.then(({ runBootReembedScripts }) => runBootReembedScripts())
|
|
32
|
+
.catch(report("Script")),
|
|
33
|
+
])
|
|
34
|
+
.then(() => undefined)
|
|
35
|
+
.finally(() => {
|
|
36
|
+
backfill = null;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** The backfill started by the last recovery, if it still runs. For tests. */
|
|
41
|
+
export function pendingRecoveryBackfill(): Promise<void> | null {
|
|
42
|
+
return backfill;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Forget a failure seen by an earlier test. */
|
|
46
|
+
export function resetEmbeddingRecoveryForTests(): void {
|
|
47
|
+
failedSinceBackfill = false;
|
|
48
|
+
}
|
|
@@ -104,7 +104,7 @@ export async function indexMemoryContent(
|
|
|
104
104
|
intent: "re-index memory source path",
|
|
105
105
|
changedByAgentId: agentId,
|
|
106
106
|
});
|
|
107
|
-
const embedding = await provider.embed(result.memory.content);
|
|
107
|
+
const embedding = await provider.embed(result.memory.content, { kind: "document" });
|
|
108
108
|
if (embedding) await store.updateEmbedding(result.memory.id, embedding, provider.name);
|
|
109
109
|
try {
|
|
110
110
|
// Re-index of an existing memory: prune stale content-derived links.
|
|
@@ -162,7 +162,10 @@ export async function indexMemoryContent(
|
|
|
162
162
|
// Async batch embed (fire and forget)
|
|
163
163
|
(async () => {
|
|
164
164
|
try {
|
|
165
|
-
const embeddings = await provider.embedBatch(
|
|
165
|
+
const embeddings = await provider.embedBatch(
|
|
166
|
+
memories.map((m) => m.content),
|
|
167
|
+
{ kind: "document" },
|
|
168
|
+
);
|
|
166
169
|
for (let i = 0; i < embeddings.length; i++) {
|
|
167
170
|
if (embeddings[i]) {
|
|
168
171
|
await store.updateEmbedding(memories[i]!.id, embeddings[i]!, provider.name);
|