@desplega.ai/agent-swarm 2.6.0 → 2.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/{acp-adapter-dgfakfpw.js → acp-adapter-8dn5tq56.js} +6 -6
- package/dist/{actions-fbqma8aa.js → actions-3r6gw7dy.js} +18 -18
- package/dist/{amp-adapter-en0jy6hs.js → amp-adapter-wjtq413p.js} +3 -3
- package/dist/{anthropic-messages-txs190dd.js → anthropic-messages-n4cby1rz.js} +4 -4
- package/dist/{app-k8a5cgpp.js → app-d53yhykk.js} +3 -3
- package/dist/{assistant-a16433gr.js → assistant-ksrawj3k.js} +20 -20
- package/dist/{boot-reembed-6c1sjfnr.js → boot-reembed-fxfw17m2.js} +11 -3
- package/dist/{boot-reembed-f4nn561n.js → boot-reembed-k5y5xbj0.js} +15 -8
- package/dist/{boot-scrub-sweep-d4w9gajf.js → boot-scrub-sweep-se41j9fd.js} +3 -3
- package/dist/{boot-seeds-9bmkspyw.js → boot-seeds-r3kwwv8b.js} +4 -4
- package/dist/{claude-adapter-nys1kjxa.js → claude-adapter-ahmyf231.js} +8 -8
- package/dist/{claude-managed-adapter-55207973.js → claude-managed-adapter-5knamegc.js} +3 -3
- package/dist/{claude-managed-setup-3cm35gey.js → claude-managed-setup-mdc7dy6b.js} +3 -3
- package/dist/{claude-sdk-session-nw5yd8cp.js → claude-sdk-session-n5wen9zk.js} +8729 -8600
- package/dist/{cli-683y0b3a.js → cli-0dqtv2jg.js} +2 -2
- package/dist/{cli-a34pr3w1.js → cli-0tww7dnz.js} +2 -2
- package/dist/{cli-w2656ka1.js → cli-0w4e4v58.js} +7 -7
- package/dist/{cli-5sckpqcx.js → cli-2sjm3t4r.js} +1 -1
- package/dist/{cli-gf2850q7.js → cli-3cywb44q.js} +18 -1
- package/dist/{cli-8wkb09q5.js → cli-3k9jej0c.js} +2 -2
- package/dist/{cli-3aqd4jaa.js → cli-3v2cyqb8.js} +3 -3
- package/dist/{cli-emhjcj2y.js → cli-5qt9vzmq.js} +89 -20
- package/dist/{cli-abkm71vr.js → cli-5rp1w4cr.js} +1 -1
- package/dist/{cli-gf95q3bm.js → cli-6vwjsetx.js} +4 -4
- package/dist/{cli-k21gx5ht.js → cli-6w2yrrc0.js} +10 -10
- package/dist/{cli-m70bajzr.js → cli-75tdydgv.js} +1 -1
- package/dist/{cli-5rj83bam.js → cli-8feswf2v.js} +47 -36
- package/dist/{cli-sxgtf5fw.js → cli-8wd3mbyj.js} +1 -1
- package/dist/{cli-ca6banwz.js → cli-9csfthbw.js} +10 -8
- package/dist/{cli-dqygeeh5.js → cli-b41p764a.js} +31 -3
- package/dist/{cli-m5a9m0ak.js → cli-c1t533hh.js} +3 -3
- package/dist/{cli-2sjhpsns.js → cli-c28jxpcv.js} +1 -1
- package/dist/{cli-bqztfyve.js → cli-cb50g9cd.js} +1 -1
- package/dist/{cli-ag2yrnt5.js → cli-d8gaphtr.js} +2 -2
- package/dist/{cli-9abdgn5t.js → cli-dgtfysne.js} +3 -3
- package/dist/{cli-nq677csy.js → cli-dscnhn18.js} +1 -1
- package/dist/{cli-3fwqy71j.js → cli-dv11pgpv.js} +39 -32
- package/dist/{cli-v49fkghp.js → cli-ekptgk2m.js} +6 -6
- package/dist/{cli-c2habvsf.js → cli-f3mzy662.js} +17 -17
- package/dist/{cli-zqne1qcj.js → cli-fk7k8s9x.js} +2 -2
- package/dist/{cli-1tem6ns9.js → cli-gt4h8cxw.js} +3 -2
- package/dist/{cli-vbhgkw9h.js → cli-h3e7bhvn.js} +3 -3
- package/dist/{cli-ffvsj2qw.js → cli-hkxqdmnb.js} +3 -3
- package/dist/{cli-a2rr0fmg.js → cli-hpkvgdad.js} +7 -4
- package/dist/{cli-art7c1zh.js → cli-hq2v4rr5.js} +37 -9
- package/dist/{cli-sqma6vw1.js → cli-k1zb7raw.js} +5 -5
- package/dist/{cli-4rvr7zfx.js → cli-k4awgvzm.js} +5 -0
- package/dist/{cli-3ndmpxxv.js → cli-k7d3hvya.js} +2 -2
- package/dist/{cli-3gfvmzht.js → cli-kbtdftbw.js} +74 -26
- package/dist/{cli-hfeb68z4.js → cli-kj69b9tw.js} +34 -6
- package/dist/{cli-az25fn5f.js → cli-kk9jy4nm.js} +2 -2
- package/dist/{cli-39aet655.js → cli-m52t4cta.js} +1 -1
- package/dist/{cli-cz37jqss.js → cli-mdadewqp.js} +5 -5
- package/dist/{cli-esakngh8.js → cli-nf6k60sn.js} +1 -1
- package/dist/{cli-j6rktc91.js → cli-nzdf09js.js} +1 -1
- package/dist/{cli-rr7zx19j.js → cli-prcyzecg.js} +12 -12
- package/dist/{cli-4hxrf0p4.js → cli-pzafyqtk.js} +393 -62
- package/dist/{cli-g8ka5j4j.js → cli-qvaa1gjp.js} +173 -52
- package/dist/{cli-d2dpf95n.js → cli-rg4b2g6j.js} +1 -1
- package/dist/{cli-kf4jree9.js → cli-rm66vqdz.js} +1 -1
- package/dist/{cli-4rf0f5vg.js → cli-s504c3w7.js} +5 -3
- package/dist/{cli-nn6vx2mq.js → cli-swkx9jm2.js} +7 -5
- package/dist/{cli-g20jjwt4.js → cli-thsaq4pd.js} +16 -16
- package/dist/{cli-aj8djyqt.js → cli-twjnyvs8.js} +2 -2
- package/dist/{cli-03gg9pd1.js → cli-typdpe8g.js} +4 -4
- package/dist/{cli-mtnj4fwa.js → cli-vg8pwmsd.js} +6 -6
- package/dist/{cli-gnsb1zxa.js → cli-vhpckzvp.js} +1 -1
- package/dist/{cli-7enjwya9.js → cli-vz78s5wm.js} +1 -1
- package/dist/{cli-dv97kdzv.js → cli-wdxgsdz5.js} +2 -2
- package/dist/{cli-7a6p4e65.js → cli-wtjg6je3.js} +1 -1
- package/dist/{cli-6y51ryva.js → cli-x3cngb1h.js} +44 -12
- package/dist/{cli-n7asmce6.js → cli-xbz6mcg4.js} +3 -3
- package/dist/{cli-24j1jeeb.js → cli-y4tdxsqb.js} +3 -3
- package/dist/{cli-mvjz5an3.js → cli-yqrsp3ny.js} +7 -7
- package/dist/cli.js +31 -21
- package/dist/{codex-adapter-d95q4qsn.js → codex-adapter-7wepzq15.js} +6 -6
- package/dist/{codex-hook-brpnycn5.js → codex-hook-pbdh4xhx.js} +2 -2
- package/dist/{codex-session-runner-gdwd8h0c.js → codex-session-runner-02sb49e4.js} +6 -6
- package/dist/{commands-jqmgk54b.js → commands-3rcspxct.js} +2 -2
- package/dist/{controller-z3jq589f.js → controller-8ar4avd6.js} +3 -3
- package/dist/{controllers-connect-r2za39ef.js → controllers-connect-ajvh9h0v.js} +19 -19
- package/dist/{cursor-adapter-4mr4jgkw.js → cursor-adapter-gd0sa7bh.js} +1 -1
- package/dist/{db-w36wwb5g.js → db-dv9j7ts5.js} +2 -2
- package/dist/{db-ppv2tze7.js → db-z26vgdvj.js} +3 -3
- package/dist/{dispatcher-pv5h77vf.js → dispatcher-8m0htjq8.js} +17 -17
- package/dist/{dsh-adapter-d4pdpb3r.js → dsh-adapter-y8gyk130.js} +1 -1
- package/dist/{e2b-fn3zacvt.js → e2b-74vg9b7e.js} +1 -1
- package/dist/{e2b-222hsajc.js → e2b-jyay8wzn.js} +1 -1
- package/dist/{easter-egg-3d-4sp4dvmc.js → easter-egg-3d-28f880xx.js} +1 -1
- package/dist/{execute-jev839y1.js → execute-9seb79vh.js} +1 -1
- package/dist/{external-channel-y6nkzhnn.js → external-channel-smz7jyth.js} +2 -2
- package/dist/{getMachineId-bsd-brn0xym3.js → getMachineId-bsd-whfre6gr.js} +3 -3
- package/dist/{getMachineId-darwin-t339sfxm.js → getMachineId-darwin-5zjynb19.js} +3 -3
- package/dist/{getMachineId-linux-5eq95d8b.js → getMachineId-linux-8mksrrsq.js} +3 -3
- package/dist/{getMachineId-unsupported-qavrtc7j.js → getMachineId-unsupported-fxtqsdt2.js} +3 -3
- package/dist/{getMachineId-win-q7fe1wx7.js → getMachineId-win-nzfmz857.js} +3 -3
- package/dist/{grok-adapter-t08b9ntm.js → grok-adapter-z0nnvtbn.js} +7 -7
- package/dist/handlers-pspryh6g.js +41 -0
- package/dist/{heartbeat-vn31xwdk.js → heartbeat-f928aqyp.js} +20 -20
- package/dist/{hook-cpjs8q51.js → hook-d7c605r4.js} +7 -7
- package/dist/{hook-6d0zbvvt.js → hook-q9hv5xf9.js} +5 -5
- package/dist/{http-9c6medda.js → http-q6j4k989.js} +1000 -251
- package/dist/{index-k0yt1tvq.js → index-5nb266jr.js} +17 -17
- package/dist/{index-ysd45kgc.js → index-8fxt5eah.js} +10 -10
- package/dist/{index-zgvqzjsw.js → index-cekzwhez.js} +5 -5
- package/dist/{index-38t35h0n.js → index-ch0c1xff.js} +17 -17
- package/dist/{index-pkma1yf8.js → index-ebnjhxh1.js} +1 -1
- package/dist/{index-yqgbfgaj.js → index-ed8dy8rn.js} +20 -20
- package/dist/{index-qhjf2htm.js → index-ejqdkkqx.js} +262 -96
- package/dist/{index-dr94zbnh.js → index-farm1zsn.js} +32661 -29443
- package/dist/{index-g6k9tctj.js → index-yn1yxg85.js} +1 -1
- package/dist/{keepalive-em0117bp.js → keepalive-zzc3hks1.js} +3 -3
- package/dist/lead-c0pjcaw4.js +79 -0
- package/dist/{maintenance-kbme3qy9.js → maintenance-kqsxzpzx.js} +1 -1
- package/dist/{oauth-refresh-sweep-xysj4yse.js → oauth-refresh-sweep-7qt0g55z.js} +8 -3
- package/dist/{onboard-p6q0vz97.js → onboard-k7x13fqg.js} +121 -8
- package/dist/{opencode-adapter-esv3k9yn.js → opencode-adapter-tqvrtter.js} +5 -5
- package/dist/{otel-impl-54qhqh1z.js → otel-impl-sqkf6k3b.js} +97 -97
- package/dist/{pi-mono-adapter-mymbwhj0.js → pi-mono-adapter-m2qnafxm.js} +89 -14
- package/dist/{pricing-refresh-yb75wv33.js → pricing-refresh-wkpwhgvs.js} +3 -3
- package/dist/{rbac-roles-dexhc9x8.js → rbac-roles-dpn0c0wj.js} +7 -4
- package/dist/{rbac-roles-1vp2mw4p.js → rbac-roles-wk7xd6a6.js} +2 -2
- package/dist/{render-v2-tjay5hhf.js → render-v2-ex2m4g2k.js} +3 -3
- package/dist/{resume-8ayf37g5.js → resume-725g9b0b.js} +7 -7
- package/dist/{runtime-hqe5cqcd.js → runtime-kajas15g.js} +1 -1
- package/dist/{session-access-fdhp987s.js → session-access-0j2ezjje.js} +8 -4
- package/dist/{session-source-backfill-qgtc86z1.js → session-source-backfill-62202awe.js} +2 -2
- package/dist/{setup-4rrmqcq3.js → setup-9sj11ax6.js} +1 -1
- package/dist/{virtual-modules-52km00d6.js → virtual-modules-c6fcq6zr.js} +2 -2
- package/dist/{work-objects-p28fgd70.js → work-objects-0y07495y.js} +20 -20
- package/dist/worker-d4q9sqbc.js +79 -0
- package/openapi.json +1536 -407
- package/package.json +7 -5
- package/src/be/automation-preflight.ts +2 -0
- package/src/be/connector-codes.ts +65 -22
- package/src/be/db/api-keys.ts +9 -0
- package/src/be/db/workflow-runs.ts +36 -5
- package/src/be/db.ts +80 -22
- package/src/be/memory/boot-reembed.ts +4 -1
- package/src/be/memory/constants.ts +6 -0
- package/src/be/memory/embedding-recovery.ts +48 -0
- package/src/be/memory/index-content.ts +5 -2
- package/src/be/memory/providers/openai-embedding.ts +65 -14
- package/src/be/memory/task-learning.ts +257 -0
- package/src/be/memory/types.ts +13 -2
- package/src/be/migrations/245_script_executor.sql +3 -0
- package/src/be/migrations/246_connector_code_kind.sql +11 -0
- package/src/be/oauth-refresh-sweep.ts +2 -1
- package/src/be/onboarding.ts +125 -15
- package/src/be/rbac-roles.ts +4 -1
- package/src/be/scripts/boot-reembed.ts +2 -0
- package/src/be/scripts/db.ts +14 -4
- package/src/be/scripts/embeddings.ts +4 -2
- package/src/be/scripts/run-saved.ts +1 -0
- package/src/be/scripts/typecheck.ts +9 -3
- package/src/be/seed/agent-fs-provision.ts +75 -14
- package/src/be/seed-skills/index.ts +7 -0
- package/src/be/swarm-config-guard.ts +3 -0
- package/src/cli.tsx +9 -0
- package/src/comb/config.ts +6 -1
- package/src/commands/onboard/agent-fs-version.ts +5 -0
- package/src/commands/onboard/compose-generator.ts +117 -4
- package/src/commands/onboard/env-generator.ts +9 -0
- package/src/commands/onboard/tei-image.ts +12 -0
- package/src/commands/onboard/types.ts +7 -0
- package/src/commands/onboard.tsx +11 -1
- package/src/commands/provider-credentials.ts +2 -1
- package/src/commands/runner.ts +14 -1
- package/src/heartbeat/heartbeat.ts +14 -1
- package/src/heartbeat/script-runner-probe.ts +47 -0
- package/src/http/agentfs-proxy.ts +369 -0
- package/src/http/all-routes.ts +1 -0
- package/src/http/api-keys.ts +24 -9
- package/src/http/codex-oauth-device.ts +8 -1
- package/src/http/fs.ts +64 -10
- package/src/http/idempotency-key.ts +1 -1
- package/src/http/inbox-state.ts +27 -1
- package/src/http/index.ts +7 -4
- package/src/http/memory.ts +7 -4
- package/src/http/onboarding.ts +139 -11
- package/src/http/pages-public.ts +38 -5
- package/src/http/scripts.ts +27 -0
- package/src/http/session-access.ts +24 -0
- package/src/http/sessions.ts +10 -6
- package/src/http/tasks.ts +85 -11
- package/src/http/telemetry-ui.ts +1 -1
- package/src/http/user-roles.ts +4 -1
- package/src/http/users.ts +272 -3
- package/src/http/utils.ts +22 -3
- package/src/http/workflows.ts +10 -0
- package/src/http/x.ts +5 -0
- package/src/prompts/base-prompt.ts +45 -10
- package/src/prompts/session-templates.ts +43 -5
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/codex-adapter.ts +6 -1
- package/src/providers/cursor-adapter.ts +1 -1
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/pi-mono-adapter.ts +99 -14
- package/src/rbac/legacy-policy.ts +1 -0
- package/src/rbac/permissions.ts +4 -0
- package/src/scheduler/scheduler.ts +2 -1
- package/src/scripts-runtime/eval-harness.ts +8 -1
- package/src/scripts-runtime/executors/native.ts +22 -16
- package/src/scripts-runtime/executors/quickjs-prelude.ts +103 -1
- package/src/scripts-runtime/executors/quickjs-runner.ts +2 -1
- package/src/scripts-runtime/executors/quickjs.ts +7 -0
- package/src/scripts-runtime/executors/registry.ts +32 -0
- package/src/scripts-runtime/executors/types.ts +7 -0
- package/src/scripts-runtime/host-validated-args.ts +133 -0
- package/src/scripts-runtime/loader.ts +30 -3
- package/src/scripts-runtime/swarm-sdk.ts +9 -2
- package/src/scripts-runtime/types/stdlib.d.ts +9 -1
- package/src/scripts-runtime/types/swarm-sdk.d.ts +9 -1
- package/src/tests/agent-fs-provision-seeder.test.ts +261 -92
- package/src/tests/agentfs-proxy.test.ts +1106 -0
- package/src/tests/api-key-status-by-suffix.test.ts +242 -0
- package/src/tests/base-prompt.test.ts +139 -4
- package/src/tests/codex-adapter.test.ts +46 -8
- package/src/tests/codex-oauth-device.test.ts +12 -1
- package/src/tests/comb-config-guard.test.ts +20 -0
- package/src/tests/comb-links.test.ts +79 -1
- package/src/tests/comb-status.test.ts +44 -0
- package/src/tests/community-linear-delivery-loop.test.ts +2099 -0
- package/src/tests/credential-check.test.ts +4 -2
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/docs-index.test.ts +135 -0
- package/src/tests/dsh-adapter.test.ts +1 -1
- package/src/tests/fetch-resolved-env.test.ts +16 -2
- package/src/tests/fixtures/onboard/baseline-states.ts +68 -0
- package/src/tests/fixtures/onboard/content.compose.yml.txt +190 -0
- package/src/tests/fixtures/onboard/content.env.txt +29 -0
- package/src/tests/fixtures/onboard/dev.compose.yml.txt +155 -0
- package/src/tests/fixtures/onboard/dev.env.txt +28 -0
- package/src/tests/fixtures/onboard/full-all.compose.yml.txt +582 -0
- package/src/tests/fixtures/onboard/full-all.env.txt +59 -0
- package/src/tests/fixtures/onboard/full.compose.yml.txt +435 -0
- package/src/tests/fixtures/onboard/full.env.txt +36 -0
- package/src/tests/fixtures/onboard/research.compose.yml.txt +155 -0
- package/src/tests/fixtures/onboard/research.env.txt +28 -0
- package/src/tests/fixtures/onboard/solo.compose.yml.txt +85 -0
- package/src/tests/fixtures/onboard/solo.env.txt +26 -0
- package/src/tests/heartbeat-checklist.test.ts +89 -0
- package/src/tests/heartbeat-script-runner-probe.test.ts +83 -0
- package/src/tests/http-log-scrubbing.test.ts +34 -1
- package/src/tests/http-task-write-ownership.test.ts +5 -1
- package/src/tests/invites-http.test.ts +464 -0
- package/src/tests/memory-embedding-input-cap.test.ts +122 -1
- package/src/tests/memory-embedding-recovery.test.ts +137 -0
- package/src/tests/oauth-refresh-sweep.test.ts +34 -1
- package/src/tests/onboard-compose.test.ts +162 -0
- package/src/tests/onboard-env.test.ts +24 -0
- package/src/tests/onboard-non-interactive.test.ts +29 -0
- package/src/tests/onboard-tei-image.test.ts +36 -0
- package/src/tests/onboarding.test.ts +451 -4
- package/src/tests/openai-embedding-provider.test.ts +105 -2
- package/src/tests/opencode-adapter.test.ts +2 -1
- package/src/tests/pages-frame-ancestors.test.ts +162 -0
- package/src/tests/pi-codemode.test.ts +2 -1
- package/src/tests/pi-mono-adapter.test.ts +54 -0
- package/src/tests/pi-mono-session-cost-on-abort.test.ts +270 -0
- package/src/tests/pi-native-mcp.test.ts +13 -1
- package/src/tests/pi-tool-exposure.test.ts +8 -8
- package/src/tests/preload-env.test.ts +4 -0
- package/src/tests/preload.ts +4 -0
- package/src/tests/probe-tool-schema-tokens.test.ts +144 -0
- package/src/tests/prompt-template-session.test.ts +20 -3
- package/src/tests/rbac-admission-e2e.test.ts +22 -6
- package/src/tests/rbac-admission.test.ts +342 -4
- package/src/tests/rbac-engine.test.ts +1 -0
- package/src/tests/rbac-roles.test.ts +9 -1
- package/src/tests/request-composition-derive.test.ts +329 -0
- package/src/tests/script-connections.test.ts +2 -0
- package/src/tests/script-executor-conformance.test.ts +36 -1
- package/src/tests/script-executor-quickjs-pool.test.ts +50 -0
- package/src/tests/script-executor-quickjs.test.ts +140 -0
- package/src/tests/script-host-validated-args-native.test.ts +196 -0
- package/src/tests/script-per-script-executor.test.ts +415 -0
- package/src/tests/scripts-external-api.test.ts +45 -0
- package/src/tests/sdk-allowlist.test.ts +3 -2
- package/src/tests/self-improvement.test.ts +4 -4
- package/src/tests/send-task-requester-gate.test.ts +64 -3
- package/src/tests/sessions-move.test.ts +39 -1
- package/src/tests/store-progress-learning.test.ts +381 -0
- package/src/tests/ui-logs-parser.test.ts +180 -0
- package/src/tests/whoami-route.test.ts +41 -0
- package/src/tests/workflow-engine-v2.test.ts +116 -1
- package/src/tests/workflow-mcp-trigger-schema.test.ts +209 -0
- package/src/tests/workflow-run-finished-event.test.ts +263 -0
- package/src/tests/workflow-swarm-script.test.ts +34 -0
- package/src/tools/inject-learning.ts +1 -1
- package/src/tools/memory-edit.ts +1 -1
- package/src/tools/memory-search.ts +1 -1
- package/src/tools/script-common.ts +1 -0
- package/src/tools/script-run.ts +7 -0
- package/src/tools/script-upsert.ts +7 -0
- package/src/tools/store-progress.ts +71 -1
- package/src/tools/utils.ts +12 -1
- package/src/tools/workflows/trigger-workflow.ts +104 -10
- package/src/types.ts +12 -1
- package/src/utils/constants.ts +39 -0
- package/src/utils/credentials.ts +5 -2
- package/src/utils/task-attachment-links.ts +15 -3
- package/src/workflows/engine.ts +18 -16
- package/src/workflows/executors/swarm-script.ts +5 -0
- package/src/workflows/foreach-driver.ts +5 -11
- package/src/workflows/node-timeout.ts +30 -0
- package/templates/community/linear-delivery-loop/PLAYBOOK.md +323 -0
- package/templates/community/linear-delivery-loop/README.md +38 -0
- package/templates/community/linear-delivery-loop/config.example.json +31 -0
- package/templates/community/linear-delivery-loop/lead-prompt.md +34 -0
- package/templates/community/linear-delivery-loop/schedule.json +10 -0
- package/templates/community/linear-delivery-loop/scripts/linear-delivery-setup.ts +148 -0
- package/templates/community/linear-delivery-loop/scripts/linear-delivery-tick.ts +1781 -0
- package/templates/skills/swarm-docs/config.json +14 -0
- package/templates/skills/swarm-docs/content.md +144 -0
- package/dist/handlers-eqsnmv95.js +0 -41
- package/dist/lead-23e7z59t.js +0 -79
- package/dist/worker-e3w7vye1.js +0 -79
- package/dist/{azure-openai-responses-9kfwhmn7.js → azure-openai-responses-43asa23s.js} +0 -0
- package/dist/{node-vxx896fa.js → node-t28cc56b.js} +0 -0
- package/dist/{openai-codex-responses-79s8xnbh.js → openai-codex-responses-jhr3sjtm.js} +3 -3
- package/dist/{openai-completions-tdbyts0x.js → openai-completions-gs6yn37k.js} +3 -3
- /package/dist/{openai-responses-dm3t5vqp.js → openai-responses-jqm9vwwk.js} +0 -0
|
@@ -2,7 +2,8 @@ import OpenAI from "openai";
|
|
|
2
2
|
import { selectCredential } from "../../../utils/credentials";
|
|
3
3
|
import { getOpenRouterAttributionHeaders } from "../../../utils/openrouter-base-url";
|
|
4
4
|
import { DEFAULT_EMBEDDING_MODEL, EMBEDDING_DIMENSIONS } from "../constants";
|
|
5
|
-
import
|
|
5
|
+
import { noteEmbeddingFailure, noteEmbeddingSuccess } from "../embedding-recovery";
|
|
6
|
+
import type { EmbeddingInputKind, EmbeddingProvider, EmbedOptions } from "../types";
|
|
6
7
|
|
|
7
8
|
// text-embedding-3-* reject any input over 8,192 tokens with a 400, and one
|
|
8
9
|
// such input fails every input in its request. There is no tokenizer here, and
|
|
@@ -18,13 +19,19 @@ interface OpenAIEmbeddingConfig {
|
|
|
18
19
|
apiKey?: string;
|
|
19
20
|
model?: string;
|
|
20
21
|
dimensions?: number;
|
|
22
|
+
queryPrefix?: string;
|
|
23
|
+
documentPrefix?: string;
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Flatten and trim the input, then prepend the task prefix. The cleaned text
|
|
28
|
+
* is cut so that prefix plus text stays within MAX_EMBEDDING_INPUT_CHARS. An
|
|
29
|
+
* empty input stays empty, so it is skipped and never embeds a bare prefix.
|
|
30
|
+
*/
|
|
31
|
+
function cleanInput(text: string, prefix = ""): string {
|
|
32
|
+
const cleaned = text.replace(/[\n\r]/g, " ").trim();
|
|
33
|
+
if (!cleaned) return "";
|
|
34
|
+
return prefix + cleaned.slice(0, Math.max(0, MAX_EMBEDDING_INPUT_CHARS - prefix.length));
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
/** A 400 is about the request's inputs, so the other inputs can still embed. */
|
|
@@ -32,12 +39,36 @@ function isInputRejection(err: unknown): boolean {
|
|
|
32
39
|
return err instanceof OpenAI.APIError && err.status === 400;
|
|
33
40
|
}
|
|
34
41
|
|
|
42
|
+
/**
|
|
43
|
+
* The server rejected the request for its size, not for an input: a 413, or a
|
|
44
|
+
* 422 about the batch size (TEI: "batch size 70 > maximum allowed batch size
|
|
45
|
+
* 32"). One request per input then fits. Any other 422 (TEI also answers 422
|
|
46
|
+
* for a bad `dimensions` value) stays a plain failure, so it is not repeated
|
|
47
|
+
* once per input.
|
|
48
|
+
*/
|
|
49
|
+
function isBatchTooLarge(err: unknown): boolean {
|
|
50
|
+
if (!(err instanceof OpenAI.APIError)) return false;
|
|
51
|
+
if (err.status === 413) return true;
|
|
52
|
+
return err.status === 422 && /batch size/i.test(err.message ?? "");
|
|
53
|
+
}
|
|
54
|
+
|
|
35
55
|
function isInputTooLong(err: unknown): boolean {
|
|
36
56
|
return (
|
|
37
57
|
isInputRejection(err) && /maximum (context|input) length/i.test((err as Error).message ?? "")
|
|
38
58
|
);
|
|
39
59
|
}
|
|
40
60
|
|
|
61
|
+
/**
|
|
62
|
+
* The endpoint was down or overloaded (no HTTP response, a 5xx or a 429), so the
|
|
63
|
+
* same input can embed later. Only these start a backfill once calls work again:
|
|
64
|
+
* an input the endpoint rejects would fail again on every backfill.
|
|
65
|
+
*/
|
|
66
|
+
function isOutage(err: unknown): boolean {
|
|
67
|
+
if (!(err instanceof OpenAI.APIError)) return true;
|
|
68
|
+
const status = err.status;
|
|
69
|
+
return status === undefined || status === 429 || status >= 500;
|
|
70
|
+
}
|
|
71
|
+
|
|
41
72
|
export class OpenAIEmbeddingProvider implements EmbeddingProvider {
|
|
42
73
|
readonly name: string;
|
|
43
74
|
readonly dimensions: number;
|
|
@@ -45,6 +76,8 @@ export class OpenAIEmbeddingProvider implements EmbeddingProvider {
|
|
|
45
76
|
private client: OpenAI | null = null;
|
|
46
77
|
private readonly model: string;
|
|
47
78
|
private readonly apiKey: string | undefined;
|
|
79
|
+
private readonly queryPrefix: string;
|
|
80
|
+
private readonly documentPrefix: string;
|
|
48
81
|
|
|
49
82
|
constructor(config?: OpenAIEmbeddingConfig) {
|
|
50
83
|
const apiKey = config?.apiKey ?? process.env.EMBEDDING_API_KEY ?? process.env.OPENAI_API_KEY;
|
|
@@ -59,6 +92,17 @@ export class OpenAIEmbeddingProvider implements EmbeddingProvider {
|
|
|
59
92
|
this.name = config?.model
|
|
60
93
|
? `openai/${config.model}`
|
|
61
94
|
: (process.env.EMBEDDING_MODEL ?? DEFAULT_EMBEDDING_MODEL);
|
|
95
|
+
|
|
96
|
+
// Read once here: a config reload builds a new provider. Used verbatim,
|
|
97
|
+
// so a trailing space (nomic: "search_query: ") is kept.
|
|
98
|
+
this.queryPrefix = config?.queryPrefix ?? process.env.EMBEDDING_QUERY_PREFIX ?? "";
|
|
99
|
+
this.documentPrefix = config?.documentPrefix ?? process.env.EMBEDDING_DOCUMENT_PREFIX ?? "";
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private prefixFor(kind: EmbeddingInputKind | undefined): string {
|
|
103
|
+
if (kind === "query") return this.queryPrefix;
|
|
104
|
+
if (kind === "document") return this.documentPrefix;
|
|
105
|
+
return "";
|
|
62
106
|
}
|
|
63
107
|
|
|
64
108
|
isConfigured(): boolean {
|
|
@@ -107,7 +151,9 @@ export class OpenAIEmbeddingProvider implements EmbeddingProvider {
|
|
|
107
151
|
while (true) {
|
|
108
152
|
try {
|
|
109
153
|
const response = await this.request(client, input);
|
|
110
|
-
|
|
154
|
+
const vector = this.toVector(response.data[0]?.embedding, "Embedding");
|
|
155
|
+
if (vector) noteEmbeddingSuccess();
|
|
156
|
+
return vector;
|
|
111
157
|
} catch (err) {
|
|
112
158
|
const message = (err as Error).message;
|
|
113
159
|
if (isInputTooLong(err) && input.length > MIN_EMBEDDING_INPUT_CHARS) {
|
|
@@ -119,26 +165,28 @@ export class OpenAIEmbeddingProvider implements EmbeddingProvider {
|
|
|
119
165
|
continue;
|
|
120
166
|
}
|
|
121
167
|
console.error("[memory] Embedding failed:", message);
|
|
168
|
+
if (isOutage(err)) noteEmbeddingFailure();
|
|
122
169
|
return null;
|
|
123
170
|
}
|
|
124
171
|
}
|
|
125
172
|
}
|
|
126
173
|
|
|
127
|
-
async embed(text: string): Promise<Float32Array | null> {
|
|
174
|
+
async embed(text: string, opts?: EmbedOptions): Promise<Float32Array | null> {
|
|
128
175
|
const client = this.getClient();
|
|
129
176
|
if (!client) return null;
|
|
130
177
|
|
|
131
|
-
const cleaned = cleanInput(text);
|
|
178
|
+
const cleaned = cleanInput(text, this.prefixFor(opts?.kind));
|
|
132
179
|
if (!cleaned) return null;
|
|
133
180
|
return this.embedOne(client, cleaned);
|
|
134
181
|
}
|
|
135
182
|
|
|
136
|
-
async embedBatch(texts: string[]): Promise<(Float32Array | null)[]> {
|
|
183
|
+
async embedBatch(texts: string[], opts?: EmbedOptions): Promise<(Float32Array | null)[]> {
|
|
137
184
|
const results: (Float32Array | null)[] = texts.map(() => null);
|
|
138
185
|
const client = this.getClient();
|
|
139
186
|
if (!client) return results;
|
|
140
187
|
|
|
141
|
-
const
|
|
188
|
+
const prefix = this.prefixFor(opts?.kind);
|
|
189
|
+
const cleaned = texts.map((text) => cleanInput(text, prefix));
|
|
142
190
|
const groups: number[][] = [];
|
|
143
191
|
let group: number[] = [];
|
|
144
192
|
let groupChars = 0;
|
|
@@ -166,13 +214,16 @@ export class OpenAIEmbeddingProvider implements EmbeddingProvider {
|
|
|
166
214
|
if (originalIndex === undefined) continue;
|
|
167
215
|
results[originalIndex] = this.toVector(item.embedding, "Batch embedding");
|
|
168
216
|
}
|
|
217
|
+
if (indices.some((i) => results[i])) noteEmbeddingSuccess();
|
|
169
218
|
} catch (err) {
|
|
170
|
-
if (!isInputRejection(err)) {
|
|
219
|
+
if (!isInputRejection(err) && !isBatchTooLarge(err)) {
|
|
171
220
|
console.error("[memory] Batch embedding failed:", (err as Error).message);
|
|
221
|
+
if (isOutage(err)) noteEmbeddingFailure();
|
|
172
222
|
continue;
|
|
173
223
|
}
|
|
174
|
-
// One rejected input fails the whole request
|
|
175
|
-
//
|
|
224
|
+
// One rejected input fails the whole request, and a server with a low
|
|
225
|
+
// batch cap rejects the request for its size: embed each input alone so
|
|
226
|
+
// only a bad input is cut down, and the rest embed as they are.
|
|
176
227
|
console.warn(
|
|
177
228
|
`[memory] Batch embedding rejected, embedding ${indices.length} inputs one by one: ${(err as Error).message}`,
|
|
178
229
|
);
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Learnings captured through `store-progress`: one swarm-scope `manual` memory
|
|
3
|
+
* per learning, keyed `/learnings/<workstream>/<slug>` and tied to its task by
|
|
4
|
+
* `sourceTaskId`, so "what did we learn doing X" is one `keyPrefix` search.
|
|
5
|
+
*
|
|
6
|
+
* Source stays `manual` so nightly curation (memtax B1 reads only manual and
|
|
7
|
+
* file_index rows) picks learnings up without a new source value.
|
|
8
|
+
*
|
|
9
|
+
* A learning close to an existing swarm memory (raw cosine at or above
|
|
10
|
+
* LEARNING_DUPLICATE_MIN_SIMILARITY) is not stored: the existing row gets a
|
|
11
|
+
* positive rating instead. Known gap: embeddings are written in the background,
|
|
12
|
+
* so two near-identical learnings seconds apart can both pass the check.
|
|
13
|
+
* Nightly curation catches those.
|
|
14
|
+
*/
|
|
15
|
+
import { getDbClient } from "@/be/db";
|
|
16
|
+
import { getEmbeddingProvider, getMemoryStore } from "@/be/memory";
|
|
17
|
+
import { LEARNING_DUPLICATE_MIN_SIMILARITY } from "@/be/memory/constants";
|
|
18
|
+
import { indexMemoryContent } from "@/be/memory/index-content";
|
|
19
|
+
import { applyRating } from "@/be/memory/raters/store";
|
|
20
|
+
import { contentSha256 } from "@/commands/profile-sync";
|
|
21
|
+
import type { AgentTask } from "@/types";
|
|
22
|
+
import { scrubSecrets } from "@/utils/secret-scrubber";
|
|
23
|
+
|
|
24
|
+
/** ~500 tokens at ceil(chars / 4); also keeps a learning to one memory chunk. */
|
|
25
|
+
export const LEARNING_MAX_CHARS = 2000;
|
|
26
|
+
export const LEARNING_MAX_TAGS = 5;
|
|
27
|
+
const LEARNINGS_ROOT = "/learnings";
|
|
28
|
+
const SEGMENT_MAX_CHARS = 60;
|
|
29
|
+
const KEY_ATTEMPTS = 4;
|
|
30
|
+
/** Where a near duplicate can already live: any workstream's learnings and the workstream digests. */
|
|
31
|
+
const DUPLICATE_SEARCH_PREFIXES = [`${LEARNINGS_ROOT}/`, "/longterm/workstreams/"];
|
|
32
|
+
/** memory_rating.source for the positive rating a duplicate gives the existing row. */
|
|
33
|
+
export const LEARNING_DUPLICATE_RATING_SOURCE = "learning-duplicate";
|
|
34
|
+
|
|
35
|
+
export type TaskLearningInput = string | { text: string; workstream?: string; tags?: string[] };
|
|
36
|
+
|
|
37
|
+
export type TaskLearningResult =
|
|
38
|
+
| { status: "stored"; memoryId: string; key: string; workstream: string }
|
|
39
|
+
| { status: "replayed"; memoryId: string; key: string }
|
|
40
|
+
| { status: "duplicate"; memoryId: string; key: string; similarity: number }
|
|
41
|
+
| { status: "failed"; error: string };
|
|
42
|
+
|
|
43
|
+
/** Lowercase kebab slug: `Agent Swarm!` -> `agent-swarm`. Empty when nothing usable is left. */
|
|
44
|
+
export function toKebab(value: string, maxChars = SEGMENT_MAX_CHARS): string {
|
|
45
|
+
return value
|
|
46
|
+
.normalize("NFKD")
|
|
47
|
+
.toLowerCase()
|
|
48
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
49
|
+
.replace(/^-+/, "")
|
|
50
|
+
.slice(0, maxChars)
|
|
51
|
+
.replace(/-+$/, "");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Explicit workstream, else the repo name from `vcsRepo` (`owner/name` or a URL), else `general`. */
|
|
55
|
+
export function resolveLearningWorkstream(
|
|
56
|
+
explicit: string | undefined,
|
|
57
|
+
vcsRepo: string | undefined,
|
|
58
|
+
): string {
|
|
59
|
+
const fromExplicit = explicit ? toKebab(explicit) : "";
|
|
60
|
+
if (fromExplicit) return fromExplicit;
|
|
61
|
+
const repoName = vcsRepo
|
|
62
|
+
?.trim()
|
|
63
|
+
.replace(/\/+$/, "")
|
|
64
|
+
.split("/")
|
|
65
|
+
.pop()
|
|
66
|
+
?.replace(/\.git$/, "");
|
|
67
|
+
const fromRepo = repoName ? toKebab(repoName) : "";
|
|
68
|
+
return fromRepo || "general";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function storeTaskLearning(params: {
|
|
72
|
+
agentId: string;
|
|
73
|
+
isLead: boolean;
|
|
74
|
+
task: Pick<AgentTask, "id" | "vcsRepo">;
|
|
75
|
+
learning: TaskLearningInput;
|
|
76
|
+
}): Promise<TaskLearningResult> {
|
|
77
|
+
const { agentId, isLead, task } = params;
|
|
78
|
+
const input = typeof params.learning === "string" ? { text: params.learning } : params.learning;
|
|
79
|
+
// Scrub before anything is derived: a token in the title, workstream or a tag
|
|
80
|
+
// would otherwise survive (kebab-cased) in the key and tags, and the replay
|
|
81
|
+
// hash must match the hash of the scrubbed content the store persists.
|
|
82
|
+
const text = scrubSecrets(input.text).trim();
|
|
83
|
+
if (!text) return { status: "failed", error: "learning text is empty" };
|
|
84
|
+
if (text.length > LEARNING_MAX_CHARS) {
|
|
85
|
+
return {
|
|
86
|
+
status: "failed",
|
|
87
|
+
error: `learning text is over ~500 tokens (${LEARNING_MAX_CHARS} characters); shorten it`,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const tags = [
|
|
91
|
+
...new Set((input.tags ?? []).map((tag) => toKebab(scrubSecrets(tag))).filter(Boolean)),
|
|
92
|
+
].filter((tag) => tag !== "learning");
|
|
93
|
+
if (tags.length > LEARNING_MAX_TAGS) {
|
|
94
|
+
return { status: "failed", error: `learning takes at most ${LEARNING_MAX_TAGS} tags` };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const workstream = resolveLearningWorkstream(
|
|
98
|
+
input.workstream === undefined ? undefined : scrubSecrets(input.workstream),
|
|
99
|
+
task.vcsRepo === undefined ? undefined : scrubSecrets(task.vcsRepo),
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
// Replay first: the same text on the same task is a retry, not a duplicate.
|
|
104
|
+
const replay = await findReplay(task.id, contentSha256(text));
|
|
105
|
+
if (replay) return replay;
|
|
106
|
+
// Outside the write transaction: the embedding call goes over the network.
|
|
107
|
+
const duplicate = await findNearDuplicate({ agentId, isLead, text });
|
|
108
|
+
if (duplicate) {
|
|
109
|
+
await rateDuplicate(duplicate, task.id);
|
|
110
|
+
return duplicate;
|
|
111
|
+
}
|
|
112
|
+
// One write transaction for the replay check and the insert, so two
|
|
113
|
+
// overlapping calls with the same learning store it once.
|
|
114
|
+
return await getDbClient().transaction(() =>
|
|
115
|
+
insertLearning({ agentId, isLead, taskId: task.id, text, tags, workstream }),
|
|
116
|
+
);
|
|
117
|
+
} catch (err) {
|
|
118
|
+
return {
|
|
119
|
+
status: "failed",
|
|
120
|
+
error: scrubSecrets(err instanceof Error ? err.message : String(err)),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function findReplay(
|
|
126
|
+
taskId: string,
|
|
127
|
+
contentHash: string,
|
|
128
|
+
): Promise<Extract<TaskLearningResult, { status: "replayed" }> | null> {
|
|
129
|
+
const replay = await getDbClient().get<{ id: string; key: string }>(
|
|
130
|
+
`SELECT id, key FROM agent_memory
|
|
131
|
+
WHERE sourceTaskId = ? AND contentHash = ? AND key LIKE '${LEARNINGS_ROOT}/%'
|
|
132
|
+
LIMIT 1`,
|
|
133
|
+
[taskId, contentHash],
|
|
134
|
+
);
|
|
135
|
+
return replay ? { status: "replayed", memoryId: replay.id, key: replay.key } : null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The closest swarm memory under the duplicate prefixes when its raw cosine to
|
|
140
|
+
* the learning reaches the threshold. No `queryText`, so the store takes the
|
|
141
|
+
* vector or brute-force path and `similarity` is raw cosine, not an FTS rank or
|
|
142
|
+
* a fused score. Any failure (no embedding provider, a search error) returns
|
|
143
|
+
* null so the learning is stored as usual.
|
|
144
|
+
*/
|
|
145
|
+
async function findNearDuplicate(params: {
|
|
146
|
+
agentId: string;
|
|
147
|
+
isLead: boolean;
|
|
148
|
+
text: string;
|
|
149
|
+
}): Promise<Extract<TaskLearningResult, { status: "duplicate" }> | null> {
|
|
150
|
+
try {
|
|
151
|
+
const embedding = await getEmbeddingProvider().embed(params.text);
|
|
152
|
+
if (!embedding) return null;
|
|
153
|
+
const store = getMemoryStore();
|
|
154
|
+
const results = await Promise.all(
|
|
155
|
+
DUPLICATE_SEARCH_PREFIXES.map((keyPrefix) =>
|
|
156
|
+
store.search(embedding, params.agentId, {
|
|
157
|
+
scope: "swarm",
|
|
158
|
+
isLead: params.isLead,
|
|
159
|
+
keyPrefix,
|
|
160
|
+
limit: 1,
|
|
161
|
+
}),
|
|
162
|
+
),
|
|
163
|
+
);
|
|
164
|
+
const best = results
|
|
165
|
+
.flat()
|
|
166
|
+
.filter((candidate) => candidate.key)
|
|
167
|
+
.sort((a, b) => b.similarity - a.similarity)[0];
|
|
168
|
+
if (!best || best.similarity < LEARNING_DUPLICATE_MIN_SIMILARITY) return null;
|
|
169
|
+
return {
|
|
170
|
+
status: "duplicate",
|
|
171
|
+
memoryId: best.id,
|
|
172
|
+
key: best.key!,
|
|
173
|
+
similarity: Math.round(best.similarity * 1000) / 1000,
|
|
174
|
+
};
|
|
175
|
+
} catch (err) {
|
|
176
|
+
console.warn(
|
|
177
|
+
"[task-learning] near-duplicate check failed; storing the learning:",
|
|
178
|
+
scrubSecrets(err instanceof Error ? err.message : String(err)),
|
|
179
|
+
);
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** A repeated lesson is evidence the existing row is useful. A failed rating never fails the call. */
|
|
185
|
+
async function rateDuplicate(
|
|
186
|
+
duplicate: Extract<TaskLearningResult, { status: "duplicate" }>,
|
|
187
|
+
taskId: string,
|
|
188
|
+
): Promise<void> {
|
|
189
|
+
try {
|
|
190
|
+
await applyRating(
|
|
191
|
+
[
|
|
192
|
+
{
|
|
193
|
+
memoryId: duplicate.memoryId,
|
|
194
|
+
signal: 1,
|
|
195
|
+
weight: 1,
|
|
196
|
+
source: LEARNING_DUPLICATE_RATING_SOURCE,
|
|
197
|
+
reasoning: `store-progress learning matched at cosine ${duplicate.similarity}`,
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
{ taskId },
|
|
201
|
+
);
|
|
202
|
+
} catch (err) {
|
|
203
|
+
console.warn(
|
|
204
|
+
"[task-learning] duplicate rating failed:",
|
|
205
|
+
scrubSecrets(err instanceof Error ? err.message : String(err)),
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async function insertLearning(params: {
|
|
211
|
+
agentId: string;
|
|
212
|
+
isLead: boolean;
|
|
213
|
+
taskId: string;
|
|
214
|
+
text: string;
|
|
215
|
+
tags: string[];
|
|
216
|
+
workstream: string;
|
|
217
|
+
}): Promise<TaskLearningResult> {
|
|
218
|
+
const { agentId, isLead, taskId, text, tags, workstream } = params;
|
|
219
|
+
// indexMemoryContent stores text this short as one trimmed chunk, so this
|
|
220
|
+
// hash is the row's contentHash: a replayed call finds its own earlier write.
|
|
221
|
+
const replay = await findReplay(taskId, contentSha256(text));
|
|
222
|
+
if (replay) return replay;
|
|
223
|
+
|
|
224
|
+
const title = text.split(/\r?\n/, 1)[0]!.trim().slice(0, 200);
|
|
225
|
+
const baseKey = `${LEARNINGS_ROOT}/${workstream}/${toKebab(title) || "learning"}`;
|
|
226
|
+
|
|
227
|
+
// Keys are unique per owner and a reused key fails rather than upserts.
|
|
228
|
+
// Keep the path unique across the swarm: on a taken key, add a short suffix.
|
|
229
|
+
for (let attempt = 0; attempt < KEY_ATTEMPTS; attempt++) {
|
|
230
|
+
const key = attempt === 0 ? baseKey : `${baseKey}-${crypto.randomUUID().slice(0, 6)}`;
|
|
231
|
+
const taken = await getDbClient().get<{ id: string }>(
|
|
232
|
+
"SELECT id FROM agent_memory WHERE scope = 'swarm' AND key = ? LIMIT 1",
|
|
233
|
+
[key],
|
|
234
|
+
);
|
|
235
|
+
if (taken) continue;
|
|
236
|
+
try {
|
|
237
|
+
const result = await indexMemoryContent({
|
|
238
|
+
agentId,
|
|
239
|
+
content: text,
|
|
240
|
+
name: title,
|
|
241
|
+
scope: "swarm",
|
|
242
|
+
source: "manual",
|
|
243
|
+
sourceTaskId: taskId,
|
|
244
|
+
tags: ["learning", ...tags],
|
|
245
|
+
intent: "store-progress learning",
|
|
246
|
+
key,
|
|
247
|
+
writer: { principal: { kind: "agent", agentId, isLead }, source: "mcp" },
|
|
248
|
+
});
|
|
249
|
+
return { status: "stored", memoryId: result.memoryIds[0]!, key, workstream };
|
|
250
|
+
} catch (err) {
|
|
251
|
+
// The key is taken after all: try a suffixed one.
|
|
252
|
+
if (/UNIQUE constraint failed/i.test((err as Error).message)) continue;
|
|
253
|
+
throw err;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return { status: "failed", error: `no free key under ${baseKey}` };
|
|
257
|
+
}
|
package/src/be/memory/types.ts
CHANGED
|
@@ -4,11 +4,22 @@ import type { AgentMemory, AgentMemoryScope, AgentMemorySource } from "@/types";
|
|
|
4
4
|
// EmbeddingProvider — text to vector, swappable
|
|
5
5
|
// ============================================================================
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* What an input is for. Some models (nomic-embed) need a task prefix on each
|
|
9
|
+
* input: one for search queries, another for stored documents. No kind means
|
|
10
|
+
* no prefix.
|
|
11
|
+
*/
|
|
12
|
+
export type EmbeddingInputKind = "query" | "document";
|
|
13
|
+
|
|
14
|
+
export interface EmbedOptions {
|
|
15
|
+
kind?: EmbeddingInputKind;
|
|
16
|
+
}
|
|
17
|
+
|
|
7
18
|
export interface EmbeddingProvider {
|
|
8
19
|
readonly name: string;
|
|
9
20
|
readonly dimensions: number;
|
|
10
|
-
embed(text: string): Promise<Float32Array | null>;
|
|
11
|
-
embedBatch(texts: string[]): Promise<(Float32Array | null)[]>;
|
|
21
|
+
embed(text: string, opts?: EmbedOptions): Promise<Float32Array | null>;
|
|
22
|
+
embedBatch(texts: string[], opts?: EmbedOptions): Promise<(Float32Array | null)[]>;
|
|
12
23
|
/** True when the provider has a usable API key — no network call. */
|
|
13
24
|
isConfigured(): boolean;
|
|
14
25
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
-- Kind of single-use code in connector_codes.
|
|
2
|
+
--
|
|
3
|
+
-- `connector`: the agent-swarm.dev ChatGPT connector code (10 minute TTL).
|
|
4
|
+
-- `invite`: a teammate invite from `POST /api/invites` (7 day TTL). Its
|
|
5
|
+
-- exchange at `POST /api/invites/exchange` also flips the user from
|
|
6
|
+
-- `invited` to `active`.
|
|
7
|
+
--
|
|
8
|
+
-- Each exchange route redeems only its own kind. Existing rows are connector
|
|
9
|
+
-- codes, so they keep today's behavior.
|
|
10
|
+
ALTER TABLE connector_codes ADD COLUMN kind TEXT NOT NULL DEFAULT 'connector'
|
|
11
|
+
CHECK (kind IN ('connector', 'invite'));
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* same per-authorization locks in ensure-token, so overlap is safe.
|
|
21
21
|
*/
|
|
22
22
|
import { forceRefreshAuthorizationOrThrow } from "../oauth/ensure-token";
|
|
23
|
+
import { isEnvFlagEnabled } from "../utils/env-flag";
|
|
23
24
|
import { scrubSecrets } from "../utils/secret-scrubber";
|
|
24
25
|
import {
|
|
25
26
|
type AuthorizationSweepRow,
|
|
@@ -138,7 +139,7 @@ async function runSweepTick(trigger: "startup" | "interval"): Promise<void> {
|
|
|
138
139
|
* already running.
|
|
139
140
|
*/
|
|
140
141
|
export function startOAuthRefreshSweep(): void {
|
|
141
|
-
if (
|
|
142
|
+
if (isEnvFlagEnabled("OAUTH_REFRESH_SWEEP_DISABLE", false)) {
|
|
142
143
|
console.log("[oauth-refresh-sweep] Disabled via OAUTH_REFRESH_SWEEP_DISABLE");
|
|
143
144
|
return;
|
|
144
145
|
}
|