@desplega.ai/agent-swarm 2.5.4 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/{acp-adapter-ek38cwsv.js → acp-adapter-ma1js5n7.js} +9 -9
- package/dist/{actions-92mepq03.js → actions-1vhmtvg9.js} +21 -21
- package/dist/{amp-adapter-9gaxg015.js → amp-adapter-sxqqvmdk.js} +4 -4
- package/dist/{app-ctn8gsgx.js → app-mwx1d5a9.js} +3 -3
- package/dist/{assistant-zyr5kz47.js → assistant-yx1p6g82.js} +30 -26
- package/dist/{boot-reembed-w75maga2.js → boot-reembed-5h37a2c2.js} +1 -1
- package/dist/{boot-reembed-dwzfttdr.js → boot-reembed-7h78vmvv.js} +2 -1
- package/dist/{boot-scrub-sweep-e9kq43kf.js → boot-scrub-sweep-8mawd4gw.js} +4 -4
- package/dist/{boot-seeds-x28v3fhb.js → boot-seeds-ca84dhnn.js} +4 -4
- package/dist/{claude-adapter-f0afqnne.js → claude-adapter-wqt4hfkq.js} +9 -9
- package/dist/{claude-managed-adapter-x81a7r65.js → claude-managed-adapter-6fjqtr2z.js} +2 -2
- package/dist/{claude-managed-setup-bqzwjd9x.js → claude-managed-setup-rqv98sea.js} +10 -3
- package/dist/{claude-sdk-session-7b7whkaq.js → claude-sdk-session-f29f3b2x.js} +11 -11
- package/dist/{cli-sxgtf5fw.js → cli-07q03xm2.js} +1 -1
- package/dist/{cli-kxpfx2xr.js → cli-0z1xjzwa.js} +2 -2
- package/dist/{cli-g9vd9ece.js → cli-1hhbzkbb.js} +14 -16
- package/dist/{cli-qqzmjxkf.js → cli-1m9x8dr5.js} +11 -6
- package/dist/{cli-jbnn4vxb.js → cli-2pvzmas1.js} +3 -3
- package/dist/{cli-x77gen2p.js → cli-2y7v133v.js} +7 -7
- package/dist/{cli-dew6x255.js → cli-39aet655.js} +2 -2
- package/dist/{cli-g1cw7xtt.js → cli-39rrw8yp.js} +1 -1
- package/dist/{cli-jb30dzvk.js → cli-48gxjmcz.js} +530 -190
- package/dist/{cli-pzatvw94.js → cli-54sj64fc.js} +2 -2
- package/dist/{cli-bvpgr9ny.js → cli-60sty27p.js} +5 -5
- package/dist/{cli-asq5ydm4.js → cli-6a9qfa8b.js} +411 -69
- package/dist/{cli-277c31dw.js → cli-6mgdba42.js} +225 -3
- package/dist/{cli-19202kew.js → cli-6srv653c.js} +5 -5
- package/dist/{cli-n4c8e3s5.js → cli-708wdy32.js} +1 -1
- package/dist/{cli-pasyvjmm.js → cli-737pyqxr.js} +1 -1
- package/dist/{cli-6x9ma5w5.js → cli-803188pb.js} +1 -1
- package/dist/{cli-hh5we352.js → cli-86ehn4s5.js} +7 -7
- package/dist/{cli-9s05422w.js → cli-8mbc9za9.js} +1 -1
- package/dist/{cli-ff1tm9n9.js → cli-8wmj18cz.js} +1 -1
- package/dist/{cli-1r5ad7j5.js → cli-9gsm2rzp.js} +12 -12
- package/dist/{cli-mz9afxxf.js → cli-9pshx6rd.js} +17 -3
- package/dist/{cli-s682zgqz.js → cli-a6ds2epe.js} +1 -1
- package/dist/{cli-6a1xg1p7.js → cli-a89zdqz9.js} +2 -2
- package/dist/{cli-03vpq45j.js → cli-ay40dnj3.js} +2 -2
- package/dist/{cli-tsma7p8v.js → cli-aypd09nf.js} +2 -2
- package/dist/{cli-ethgy7ha.js → cli-b3yzyc57.js} +2 -2
- package/dist/{cli-bn0rewsy.js → cli-c081bj3b.js} +2 -2
- package/dist/{cli-mmhj8vd7.js → cli-c668vvcf.js} +11 -2
- package/dist/{cli-sbwsxvtd.js → cli-d20qvpr2.js} +2 -2
- package/dist/{cli-c952c7ac.js → cli-e1595bgx.js} +3 -3
- package/dist/{cli-621ce7kw.js → cli-f12mr20z.js} +3 -3
- package/dist/{cli-dqygeeh5.js → cli-f1e0ptjj.js} +10 -1
- package/dist/{cli-xzfq4at3.js → cli-f4dbh7a5.js} +16 -12
- package/dist/{cli-ha9rmwgq.js → cli-fb8m699c.js} +2 -2
- package/dist/{cli-5bnsc0e1.js → cli-gsfk58nw.js} +5 -5
- package/dist/cli-gt4h8cxw.js +895 -0
- package/dist/{cli-nwyw3zhg.js → cli-gxee0bqa.js} +1 -1
- package/dist/{cli-25n3mv5d.js → cli-h5jeg46p.js} +6 -6
- package/dist/{cli-gn5af0x5.js → cli-he9gbdx0.js} +62 -12
- package/dist/{cli-gbfxp48k.js → cli-j0ppywdr.js} +42 -35
- package/dist/{cli-k4tjsdx9.js → cli-j3y0x2n4.js} +277 -199
- package/dist/{cli-7p4jxgtt.js → cli-j9bpmykh.js} +75 -27
- package/dist/{cli-dabehxhp.js → cli-kt907da6.js} +1 -1
- package/dist/{cli-wx2nj19m.js → cli-kv7sbc2c.js} +6 -6
- package/dist/{cli-abs8ndtr.js → cli-m30k9777.js} +26 -26
- package/dist/{cli-cz5nmdxt.js → cli-m5e802r7.js} +2 -2
- package/dist/{cli-0shx9j93.js → cli-pfk92eqz.js} +2 -2
- package/dist/{cli-xtrz6bzs.js → cli-ptfq202x.js} +5 -3
- package/dist/{cli-6n8axjv1.js → cli-qj442fws.js} +2 -2
- package/dist/{cli-c61mbdds.js → cli-rd5m70gj.js} +4 -4
- package/dist/{cli-eftzyh9y.js → cli-s09j9vp5.js} +2 -2
- package/dist/{cli-m4ecdxvz.js → cli-s0mgx214.js} +7 -7
- package/dist/{cli-wghjkr0e.js → cli-sgqm2582.js} +10 -7
- package/dist/{cli-2655k16q.js → cli-sjctjr48.js} +2 -2
- package/dist/{cli-nzfnt2gs.js → cli-tf1n0r0b.js} +1 -1
- package/dist/{cli-x0f2v69c.js → cli-twctnkng.js} +12 -7
- package/dist/{cli-j6rktc91.js → cli-v6shg7na.js} +1 -1
- package/dist/{cli-gx4rm0qx.js → cli-vz78s5wm.js} +1 -1
- package/dist/{cli-gwhjmh8g.js → cli-wqr7cea4.js} +5 -5
- package/dist/{cli-e42vss7b.js → cli-x37r7qwf.js} +16 -8
- package/dist/{cli-4b2z6n0m.js → cli-yd2nreqw.js} +17 -8
- package/dist/{cli-64mh9mf3.js → cli-ymjpkxgn.js} +69 -17
- package/dist/{cli-5kqnemen.js → cli-yvy48785.js} +1 -1
- package/dist/{cli-9j129brq.js → cli-yxbsmx56.js} +6 -6
- package/dist/cli.js +22 -20
- package/dist/{codex-adapter-wch12q99.js → codex-adapter-zk59x2zh.js} +7 -7
- package/dist/{codex-hook-brpnycn5.js → codex-hook-xyjhvxwd.js} +1 -1
- package/dist/{codex-login-rn7c5gxt.js → codex-login-8cg0vhf2.js} +1 -1
- package/dist/{codex-session-runner-41pfcr95.js → codex-session-runner-kh6s4j3n.js} +8 -8
- package/dist/{commands-9849dkj6.js → commands-68et1qv2.js} +2 -2
- package/dist/{controller-ja0sd95p.js → controller-fyrbnzg4.js} +4 -4
- package/dist/{controllers-connect-xp05dj6x.js → controllers-connect-nep9h2xv.js} +23 -23
- package/dist/{cursor-adapter-623qxqew.js → cursor-adapter-yss3pg7t.js} +2 -2
- package/dist/{db-w759qmx0.js → db-28gjyvxj.js} +4 -2
- package/dist/{db-47vj40dy.js → db-8ftwvzba.js} +3 -3
- package/dist/{devin-adapter-4ycy4vrf.js → devin-adapter-byen2cxt.js} +1 -1
- package/dist/{dispatcher-7b8e4z2j.js → dispatcher-hayexmkr.js} +20 -20
- package/dist/{docker-s84hjs91.js → docker-fdzyewpz.js} +2 -2
- package/dist/{dsh-adapter-2ezw3xea.js → dsh-adapter-46wawff9.js} +2 -2
- package/dist/{e2b-9gkdhm3c.js → e2b-222hsajc.js} +2 -2
- package/dist/{e2b-cjtf8k1z.js → e2b-fn3zacvt.js} +2 -2
- package/dist/{easter-egg-3d-e4qv8dxj.js → easter-egg-3d-8d1e0h4m.js} +1 -1
- package/dist/{event-bus-3z78ep5n.js → event-bus-pghg8hqy.js} +1 -1
- package/dist/{execute-0xpz8950.js → execute-cjrfxfnm.js} +1 -1
- package/dist/{external-channel-e2s028da.js → external-channel-xbmxcg09.js} +2 -2
- package/dist/{grok-adapter-ys7gaqee.js → grok-adapter-ag4wks2a.js} +10 -10
- package/dist/handlers-tzmevqb5.js +41 -0
- package/dist/{heartbeat-w3mntghj.js → heartbeat-psfvcgwp.js} +23 -23
- package/dist/{hook-g7kvv6x9.js → hook-8pz04697.js} +6 -6
- package/dist/{hook-3jjywnpb.js → hook-cyq4mryn.js} +8 -8
- package/dist/{http-tkp7d4kb.js → http-haj1nd6k.js} +921 -169
- package/dist/{index-q7m26kvs.js → index-jwt2bj6f.js} +265 -97
- package/dist/{index-3n8m9ea2.js → index-q52f47pe.js} +1 -1
- package/dist/{index-31ceyf42.js → index-syvhnvtw.js} +1 -1
- package/dist/index-y1ajx04a.js +43 -0
- package/dist/{index-1ef1myq2.js → index-z5h22pes.js} +20 -20
- package/dist/{index-xdaahthb.js → index-zyq8ct52.js} +20 -20
- package/dist/{keepalive-v4ta8wgg.js → keepalive-xdvsamvj.js} +4 -4
- package/dist/lead-hqg57kdp.js +79 -0
- package/dist/{maintenance-84n1ex21.js → maintenance-4y2xzh3a.js} +1 -1
- package/dist/{oauth-refresh-sweep-vjnnandw.js → oauth-refresh-sweep-jd4r7zbs.js} +9 -4
- package/dist/{onboard-sfmpe4np.js → onboard-18447q9q.js} +6 -6
- package/dist/{opencode-adapter-jmpst4q4.js → opencode-adapter-26b2frdf.js} +7 -7
- package/dist/{otel-impl-v6cc0zve.js → otel-impl-f9tgr8yw.js} +2 -2
- package/dist/{pi-mono-adapter-fk8fkcf6.js → pi-mono-adapter-qn6qbga1.js} +38 -17
- package/dist/{pricing-refresh-fsk1e1zc.js → pricing-refresh-dw2tgb53.js} +3 -3
- package/dist/{rbac-roles-prmkdnkw.js → rbac-roles-aygm2760.js} +2 -2
- package/dist/{rbac-roles-zxmxqa39.js → rbac-roles-s4nwj2m3.js} +1 -1
- package/dist/render-v2-yfvdfr0x.js +16 -0
- package/dist/{resume-frdtgfa0.js → resume-1bmvjbf3.js} +9 -9
- package/dist/{runtime-bt5pkpm1.js → runtime-f7tpzedy.js} +1 -1
- package/dist/{session-access-fpez4y5q.js → session-access-r5garej6.js} +4 -4
- package/dist/session-source-backfill-424wzv0r.js +110 -0
- package/dist/{setup-mq6m3m8q.js → setup-8tygvxkq.js} +1 -1
- package/dist/{virtual-modules-mnfbh5mr.js → virtual-modules-xsw323b3.js} +2 -2
- package/dist/{work-objects-kfahhjsq.js → work-objects-ggnghpgf.js} +23 -23
- package/dist/worker-sqjd1nqf.js +79 -0
- package/dist/{x-pbpezr0s.js → x-wxrzvzx8.js} +2 -2
- package/openapi.json +1324 -14
- package/package.json +5 -3
- package/src/assets/source-key.ts +95 -0
- package/src/be/automation-preflight.ts +2 -0
- package/src/be/db/api-keys.ts +89 -0
- package/src/be/db/session-labels.ts +28 -0
- package/src/be/db/session-moves.ts +5 -6
- package/src/be/db/session-tree.ts +153 -14
- package/src/be/db/tasks/read.ts +11 -0
- package/src/be/db/tasks/write.ts +6 -1
- package/src/be/db/workflow-runs.ts +219 -6
- package/src/be/db-retention.ts +61 -6
- package/src/be/db.ts +135 -132
- package/src/be/memory/providers/openai-embedding.ts +5 -1
- package/src/be/memory/task-learning.ts +155 -0
- package/src/be/migrations/244_api_key_validation.sql +4 -0
- package/src/be/migrations/245_script_executor.sql +3 -0
- package/src/be/oauth-refresh-sweep.ts +2 -1
- package/src/be/onboarding.ts +4 -0
- package/src/be/scripts/boot-reembed.ts +2 -0
- package/src/be/scripts/db.ts +14 -4
- package/src/be/scripts/embeddings.ts +2 -0
- package/src/be/scripts/run-saved.ts +1 -0
- package/src/be/scripts/typecheck.ts +10 -4
- package/src/be/seed-skills/index.ts +7 -0
- package/src/be/session-source-backfill.ts +163 -0
- package/src/be/swarm-config-guard.ts +1 -0
- package/src/comb/review-batch.ts +28 -1
- package/src/comb/templates.ts +7 -2
- package/src/commands/claude-managed-setup.ts +7 -1
- package/src/commands/credential-refresh.ts +76 -1
- package/src/commands/credential-wait.ts +2 -2
- package/src/commands/onboard/compose-generator.ts +5 -3
- package/src/commands/provider-credentials.ts +212 -171
- package/src/commands/runner.ts +25 -4
- package/src/extensions/apply-task-create.ts +8 -1
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/extensions/contract.ts +5 -0
- package/src/heartbeat/heartbeat.ts +14 -1
- package/src/heartbeat/script-runner-probe.ts +47 -0
- package/src/http/all-routes.ts +2 -0
- package/src/http/api-keys.ts +77 -9
- package/src/http/assets.ts +15 -0
- package/src/http/codex-oauth-accounts.ts +191 -0
- package/src/http/comb.ts +15 -2
- package/src/http/config.ts +1 -1
- package/src/http/core.ts +6 -0
- package/src/http/fleet-benchmark.ts +5 -2
- package/src/http/idempotency-key.ts +1 -1
- package/src/http/index.ts +14 -3
- package/src/http/integrations.ts +5 -1
- package/src/http/kv.ts +8 -3
- package/src/http/onboarding.ts +6 -1
- package/src/http/pages-public.ts +38 -5
- package/src/http/pool-keys.ts +354 -0
- package/src/http/scripts.ts +27 -0
- package/src/http/sessions.ts +37 -12
- package/src/http/stats.ts +1 -0
- package/src/http/status.ts +26 -12
- package/src/http/tasks.ts +3 -0
- package/src/http/used-by.ts +7 -2
- package/src/http/workflows.ts +88 -9
- package/src/http/x.ts +5 -0
- package/src/integrations/chat-sdk/inbound.ts +27 -0
- package/src/kv-reserved-namespaces.ts +19 -0
- package/src/prompts/base-prompt.ts +21 -1
- package/src/prompts/session-templates.ts +17 -3
- package/src/providers/acp-targets.ts +1 -1
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/claude-adapter.ts +5 -1
- package/src/providers/claude-managed-adapter.ts +16 -9
- package/src/providers/codex-adapter.ts +71 -11
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/pi-mono-adapter.ts +41 -11
- package/src/scheduler/scheduler.ts +5 -3
- 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 +16 -3
- package/src/scripts-runtime/types/stdlib.d.ts +10 -2
- package/src/scripts-runtime/types/swarm-extension.d.ts +17 -8
- package/src/scripts-runtime/types/swarm-sdk.d.ts +10 -2
- package/src/slack/assistant.ts +7 -0
- package/src/slack/channel-labels.ts +74 -0
- package/src/slack/handlers.ts +8 -0
- package/src/slack/inbound-files.ts +5 -2
- package/src/slack/thread-buffer.ts +4 -0
- package/src/tasks/sibling-awareness.ts +12 -3
- package/src/telemetry-context.ts +5 -6
- package/src/telemetry-identity.ts +3 -3
- package/src/tests/api-key-status-by-suffix.test.ts +242 -0
- package/src/tests/api-keys-validation.test.ts +170 -0
- package/src/tests/base-prompt.test.ts +70 -2
- package/src/tests/claude-managed-adapter.test.ts +46 -0
- package/src/tests/claude-routes-e2e.test.ts +7 -2
- package/src/tests/codex-credential-pools.test.ts +120 -0
- package/src/tests/codex-gateway-route.test.ts +165 -0
- package/src/tests/codex-oauth-accounts.test.ts +357 -0
- package/src/tests/comb-review-batch.test.ts +63 -2
- package/src/tests/community-linear-delivery-loop.test.ts +2099 -0
- package/src/tests/config-pool-keys.test.ts +533 -0
- package/src/tests/credential-check.test.ts +294 -4
- package/src/tests/credential-refresh.test.ts +172 -1
- package/src/tests/db-retention.test.ts +9 -3
- package/src/tests/db-task-read-contract.test.ts +1 -0
- package/src/tests/db-task-write-contract.test.ts +1 -3
- 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/extensions-pre-task.test.ts +53 -1
- package/src/tests/fetch-resolved-env.test.ts +16 -2
- package/src/tests/fixtures/extensions/record-task-create/hooks.ts +9 -0
- package/src/tests/fixtures/extensions/record-task-create/manifest.json +7 -0
- package/src/tests/fleet-benchmark.test.ts +8 -0
- package/src/tests/get-workflow-run-tool.test.ts +98 -0
- package/src/tests/heartbeat-checklist.test.ts +89 -0
- package/src/tests/heartbeat-script-runner-probe.test.ts +83 -0
- package/src/tests/integrations-http.test.ts +32 -2
- package/src/tests/internal-ai/complete-structured.test.ts +37 -1
- package/src/tests/internal-ai/credentials.test.ts +23 -0
- package/src/tests/kv-http.test.ts +32 -0
- package/src/tests/kv-tool.test.ts +25 -0
- package/src/tests/multi-runtime-registration.test.ts +12 -12
- package/src/tests/oauth-refresh-sweep.test.ts +34 -1
- package/src/tests/onboard-compose.test.ts +20 -0
- package/src/tests/onboarding.test.ts +108 -1
- package/src/tests/openai-embedding-credentials.test.ts +35 -0
- package/src/tests/opencode-adapter.test.ts +2 -1
- package/src/tests/openrouter-attribution-call-sites.test.ts +2 -2
- package/src/tests/org-website.test.ts +36 -0
- 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-native-mcp.test.ts +13 -1
- package/src/tests/pi-tool-exposure.test.ts +8 -8
- package/src/tests/probe-tool-schema-tokens.test.ts +144 -0
- package/src/tests/prompt-template-session.test.ts +4 -3
- package/src/tests/provider-key-probes.test.ts +181 -0
- package/src/tests/rbac-e2e-helpers.test.ts +88 -1
- package/src/tests/rbac-e2e-helpers.ts +36 -11
- package/src/tests/request-composition-derive.test.ts +329 -0
- package/src/tests/script-connections.test.ts +4 -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/script-runs-retention.test.ts +212 -0
- package/src/tests/scripts-external-api.test.ts +45 -0
- package/src/tests/sdk-allowlist.test.ts +42 -2
- package/src/tests/self-improvement.test.ts +4 -4
- package/src/tests/session-source-backfill.test.ts +429 -0
- package/src/tests/session-source-keys.test.ts +236 -0
- package/src/tests/session-source-labels.test.ts +419 -0
- package/src/tests/sessions-detail-batch.test.ts +2 -0
- package/src/tests/sessions-move.test.ts +131 -0
- package/src/tests/sessions-tree.test.ts +299 -2
- package/src/tests/slack-inbound-files.test.ts +103 -23
- package/src/tests/status.test.ts +101 -18
- package/src/tests/store-progress-learning.test.ts +288 -0
- package/src/tests/tasks-workflow-run-filter.test.ts +146 -0
- package/src/tests/test-net.ts +31 -0
- package/src/tests/transcription.test.ts +8 -0
- package/src/tests/ui-logs-parser.test.ts +180 -0
- package/src/tests/used-by-route.test.ts +11 -0
- package/src/tests/workflow-cascade-cancel.test.ts +172 -0
- package/src/tests/workflow-engine-v2.test.ts +116 -1
- package/src/tests/workflow-http-v2.test.ts +131 -0
- 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-step-order.test.ts +92 -0
- package/src/tests/workflow-sub-workflow.test.ts +30 -0
- package/src/tests/workflow-swarm-script.test.ts +34 -0
- package/src/tools/get-tasks.ts +8 -0
- package/src/tools/kv/kv-read-auth.ts +4 -2
- 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 +67 -1
- package/src/tools/task-action.ts +1 -1
- package/src/tools/workflows/get-workflow-run.ts +14 -4
- package/src/tools/workflows/trigger-workflow.ts +104 -10
- package/src/types.ts +36 -1
- package/src/utils/company-profile.ts +51 -1
- package/src/utils/credentials.ts +10 -4
- package/src/utils/internal-ai/complete-structured.ts +5 -0
- package/src/utils/internal-ai/credentials.ts +4 -3
- package/src/utils/provider-key-probes.ts +328 -0
- package/src/utils/transcription.ts +4 -1
- package/src/workflows/engine.ts +18 -16
- package/src/workflows/executors/sub-workflow.ts +8 -1
- 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/ai-toolbox.manifest.json +2 -2
- 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/researching/content.md +2 -0
- package/templates/skills/swarm-docs/config.json +14 -0
- package/templates/skills/swarm-docs/content.md +144 -0
- package/templates/skills/swarm-scripts/SKILL.md +1 -0
- package/templates/skills/swarm-scripts/content.md +1 -0
- package/dist/cli-7421jzsw.js +0 -84
- package/dist/cli-e45e4m6b.js +0 -413
- package/dist/cli-zw88zjba.js +0 -307
- package/dist/handlers-wy1dr6ja.js +0 -41
- package/dist/index-nmm79qgr.js +0 -43
- package/dist/lead-sawnyqxk.js +0 -79
- package/dist/render-v2-pjwxvnkw.js +0 -12
- package/dist/worker-v5e5rxj3.js +0 -79
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
1
|
+
import { describe, expect, spyOn, test } from "bun:test";
|
|
2
2
|
import { Type } from "typebox";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { SummaryWithRatingsSchema } from "../../be/memory/raters/llm.js";
|
|
@@ -501,6 +501,42 @@ describe("completeStructured", () => {
|
|
|
501
501
|
});
|
|
502
502
|
|
|
503
503
|
describe("defaultSpawnClaudeCli", () => {
|
|
504
|
+
for (const mirrored of [false, true]) {
|
|
505
|
+
test(`selects single credentials before CLI spawn (mirror=${mirrored})`, async () => {
|
|
506
|
+
const keys = [
|
|
507
|
+
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
508
|
+
"AGENT_SWARM_CLAUDE_OAUTH_TOKEN",
|
|
509
|
+
"ANTHROPIC_API_KEY",
|
|
510
|
+
];
|
|
511
|
+
const saved = Object.fromEntries(keys.map((key) => [key, process.env[key]]));
|
|
512
|
+
process.env.CLAUDE_CODE_OAUTH_TOKEN = mirrored ? "" : "example-first,example-second";
|
|
513
|
+
process.env.AGENT_SWARM_CLAUDE_OAUTH_TOKEN = "example-first,example-second";
|
|
514
|
+
process.env.ANTHROPIC_API_KEY = "example-first,example-second";
|
|
515
|
+
let capturedEnv: Record<string, string | undefined> | undefined;
|
|
516
|
+
const spawn = spyOn(Bun, "spawn").mockImplementation(((
|
|
517
|
+
...args: Parameters<typeof Bun.spawn>
|
|
518
|
+
) => {
|
|
519
|
+
const options = args[0] as Bun.SpawnOptions.OptionsObject;
|
|
520
|
+
capturedEnv = options.env;
|
|
521
|
+
throw new Error("test stops before CLI spawn");
|
|
522
|
+
}) as typeof Bun.spawn);
|
|
523
|
+
try {
|
|
524
|
+
await expect(defaultSpawnClaudeCli("prompt", "haiku")).rejects.toThrow(
|
|
525
|
+
"test stops before CLI spawn",
|
|
526
|
+
);
|
|
527
|
+
for (const key of ["CLAUDE_CODE_OAUTH_TOKEN", "ANTHROPIC_API_KEY"]) {
|
|
528
|
+
expect(["example-first", "example-second"]).toContain(capturedEnv?.[key]);
|
|
529
|
+
}
|
|
530
|
+
} finally {
|
|
531
|
+
spawn.mockRestore();
|
|
532
|
+
for (const [key, value] of Object.entries(saved)) {
|
|
533
|
+
if (value === undefined) delete process.env[key];
|
|
534
|
+
else process.env[key] = value;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
|
|
504
540
|
test("sets SKIP_SESSION_SUMMARY=1 in the child env (Stop-hook recursion guard)", async () => {
|
|
505
541
|
// Without this guard, the spawned `claude -p` summarizer session fires the
|
|
506
542
|
// same global Stop hook on exit, which spawns another summarizer claude,
|
|
@@ -25,6 +25,29 @@ function makeOpts(
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
describe("resolveCredential", () => {
|
|
28
|
+
for (const [keyVar, kind] of [
|
|
29
|
+
["OPENROUTER_API_KEY", "openrouter"],
|
|
30
|
+
["ANTHROPIC_API_KEY", "anthropic"],
|
|
31
|
+
["OPENAI_API_KEY", "openai"],
|
|
32
|
+
] as const) {
|
|
33
|
+
for (const source of ["env", "fallback"] as const) {
|
|
34
|
+
test(`${keyVar} selects one pool member from ${source}`, async () => {
|
|
35
|
+
const pool = "example-first, example-second";
|
|
36
|
+
const cred = await resolveCredential(
|
|
37
|
+
makeOpts({
|
|
38
|
+
env: source === "env" ? { [keyVar]: pool } : {},
|
|
39
|
+
_getEnvApiKey: (provider) =>
|
|
40
|
+
source === "fallback" && provider === kind ? pool : undefined,
|
|
41
|
+
}),
|
|
42
|
+
);
|
|
43
|
+
expect(cred?.kind).toBe(kind);
|
|
44
|
+
expect(
|
|
45
|
+
cred && "apiKey" in cred && ["example-first", "example-second"].includes(cred.apiKey),
|
|
46
|
+
).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
28
51
|
test("OPENROUTER_API_KEY wins", async () => {
|
|
29
52
|
const cred = await resolveCredential(makeOpts({ env: { OPENROUTER_API_KEY: "or-1" } }));
|
|
30
53
|
expect(cred).not.toBeNull();
|
|
@@ -362,6 +362,38 @@ describe("/api/kv REST — auth on writes", () => {
|
|
|
362
362
|
expect(intruderWrite.status).toBe(403);
|
|
363
363
|
});
|
|
364
364
|
|
|
365
|
+
test("session source labels are private: generic callers cannot read, list, or write them", async () => {
|
|
366
|
+
const namespace = "session-labels";
|
|
367
|
+
const key = encodeURIComponent("shared/slack/d0private1/");
|
|
368
|
+
await upsertKv({
|
|
369
|
+
namespace,
|
|
370
|
+
key: "shared/slack/d0private1/",
|
|
371
|
+
value: "DM · Lena",
|
|
372
|
+
valueType: "string",
|
|
373
|
+
});
|
|
374
|
+
for (const as of [agentId, leadAgentId]) {
|
|
375
|
+
expect((await authedFetch(`/api/kv/_/${namespace}/${key}`, { agentId: as })).status).toBe(
|
|
376
|
+
403,
|
|
377
|
+
);
|
|
378
|
+
expect((await authedFetch(`/api/kv/_/${namespace}`, { agentId: as })).status).toBe(403);
|
|
379
|
+
for (const method of ["PUT", "DELETE"]) {
|
|
380
|
+
const res = await authedFetch(`/api/kv/_/${namespace}/${key}`, {
|
|
381
|
+
method,
|
|
382
|
+
body:
|
|
383
|
+
method === "PUT" ? JSON.stringify({ value: "forged", valueType: "string" }) : undefined,
|
|
384
|
+
agentId: as,
|
|
385
|
+
});
|
|
386
|
+
expect(res.status).toBe(403);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
expect(
|
|
390
|
+
await getDbClient().get<{ value: string }>(
|
|
391
|
+
"SELECT value FROM kv_entries WHERE namespace = ? AND key = ?",
|
|
392
|
+
[namespace, "shared/slack/d0private1/"],
|
|
393
|
+
),
|
|
394
|
+
).toEqual({ value: "DM · Lena" });
|
|
395
|
+
});
|
|
396
|
+
|
|
365
397
|
test("403 when writing to task:page:* without an X-Page-Id header", async () => {
|
|
366
398
|
const ns = "task:page:abc";
|
|
367
399
|
const res = await authedFetch(`/api/kv/_/${encodeURIComponent(ns)}/k`, {
|
|
@@ -133,6 +133,31 @@ describe("kv MCP tools", () => {
|
|
|
133
133
|
).toBeNull();
|
|
134
134
|
});
|
|
135
135
|
|
|
136
|
+
test("kv-get, kv-list, and kv-set refuse the private session-labels namespace, even for lead", async () => {
|
|
137
|
+
const namespace = "session-labels";
|
|
138
|
+
const key = "shared/slack/d0private1/";
|
|
139
|
+
await getDbClient().run(
|
|
140
|
+
`INSERT INTO kv_entries (namespace, key, value, value_type, created_at, updated_at)
|
|
141
|
+
VALUES (?, ?, 'DM · Lena', 'string', 0, 0)`,
|
|
142
|
+
[namespace, key],
|
|
143
|
+
);
|
|
144
|
+
const tools = buildServer();
|
|
145
|
+
for (const result of [
|
|
146
|
+
await tools.get.handler({ namespace, key }, meta(lead)),
|
|
147
|
+
await tools.list.handler({ namespace }, meta(lead)),
|
|
148
|
+
await tools.set.handler({ namespace, key, value: "forged" }, meta(lead)),
|
|
149
|
+
] as StructuredResult<{ success: boolean; message: string }>[]) {
|
|
150
|
+
expect(result.structuredContent.success).toBe(false);
|
|
151
|
+
expect(result.structuredContent.message).toContain("reserved for session source labels");
|
|
152
|
+
}
|
|
153
|
+
expect(
|
|
154
|
+
await getDbClient().get("SELECT value FROM kv_entries WHERE namespace = ? AND key = ?", [
|
|
155
|
+
namespace,
|
|
156
|
+
key,
|
|
157
|
+
]),
|
|
158
|
+
).toEqual({ value: "DM · Lena" });
|
|
159
|
+
});
|
|
160
|
+
|
|
136
161
|
test("kv-get returns entry=null for missing keys", async () => {
|
|
137
162
|
const tools = buildServer();
|
|
138
163
|
const getRes = (await tools.get.handler({ key: "nope" }, meta(agentA))) as StructuredResult<{
|
|
@@ -2995,11 +2995,19 @@ describe("post-task credential recovery without a restart", () => {
|
|
|
2995
2995
|
ready: true,
|
|
2996
2996
|
lastRefreshAt: 0,
|
|
2997
2997
|
inFlight: false,
|
|
2998
|
+
fingerprint: null,
|
|
2999
|
+
rejectedKeys: 0,
|
|
2998
3000
|
};
|
|
2999
3001
|
const check = spyOn(CREDENTIAL_PROVIDER_CHECKERS, "claude");
|
|
3000
3002
|
const originalOAuth = process.env.CLAUDE_CODE_OAUTH_TOKEN;
|
|
3001
|
-
// The real Claude checker and live-test use OAuth presence, no upstream I/O.
|
|
3002
3003
|
process.env.CLAUDE_CODE_OAUTH_TOKEN = "test-credential-recovery";
|
|
3004
|
+
// The subscription live test calls Anthropic's OAuth profile: answer it locally.
|
|
3005
|
+
const realFetch = globalThis.fetch;
|
|
3006
|
+
globalThis.fetch = (async (input: string | URL | Request, init?: RequestInit) => {
|
|
3007
|
+
const url = input instanceof Request ? input.url : String(input);
|
|
3008
|
+
if (url.startsWith("https://api.anthropic.com/")) return new Response("{}");
|
|
3009
|
+
return realFetch(input, init);
|
|
3010
|
+
}) as typeof fetch;
|
|
3003
3011
|
try {
|
|
3004
3012
|
// A live provider swap produces a real not-ready post-task snapshot.
|
|
3005
3013
|
await refreshCredentialStatus(api, refresh, "claude", {}, 1);
|
|
@@ -3021,6 +3029,8 @@ describe("post-task credential recovery without a restart", () => {
|
|
|
3021
3029
|
await refreshCredentialStatus(api, refresh, "claude", {}, CREDENTIAL_RETRY_INTERVAL_MS + 1);
|
|
3022
3030
|
expect(check).toHaveBeenCalledTimes(2);
|
|
3023
3031
|
expect((await getAgentById(id))?.status).toBe("waiting_for_credentials");
|
|
3032
|
+
// Credentials resolve. A changed credential is checked at once, inside
|
|
3033
|
+
// the retry throttle. Same runner state, provider, and runtime identity.
|
|
3024
3034
|
const env = { CLAUDE_CODE_OAUTH_TOKEN: "test-credential-recovery" };
|
|
3025
3035
|
await refreshCredentialStatus(
|
|
3026
3036
|
api,
|
|
@@ -3029,17 +3039,6 @@ describe("post-task credential recovery without a restart", () => {
|
|
|
3029
3039
|
env,
|
|
3030
3040
|
2 * CREDENTIAL_RETRY_INTERVAL_MS,
|
|
3031
3041
|
);
|
|
3032
|
-
expect(check).toHaveBeenCalledTimes(2);
|
|
3033
|
-
expect((await getAgentById(id))?.status).toBe("waiting_for_credentials");
|
|
3034
|
-
|
|
3035
|
-
// Credentials resolve. Same runner state, provider, and runtime identity.
|
|
3036
|
-
await refreshCredentialStatus(
|
|
3037
|
-
api,
|
|
3038
|
-
refresh,
|
|
3039
|
-
"claude",
|
|
3040
|
-
env,
|
|
3041
|
-
2 * CREDENTIAL_RETRY_INTERVAL_MS + 1,
|
|
3042
|
-
);
|
|
3043
3042
|
expect(check).toHaveBeenCalledTimes(3);
|
|
3044
3043
|
expect((await getAgentById(id))?.status).toBe("idle");
|
|
3045
3044
|
expect((await getAgentById(id))?.credStatus?.ready).toBe(true);
|
|
@@ -3058,6 +3057,7 @@ describe("post-task credential recovery without a restart", () => {
|
|
|
3058
3057
|
);
|
|
3059
3058
|
expect(check).toHaveBeenCalledTimes(3);
|
|
3060
3059
|
} finally {
|
|
3060
|
+
globalThis.fetch = realFetch;
|
|
3061
3061
|
check.mockRestore();
|
|
3062
3062
|
if (originalOAuth === undefined) delete process.env.CLAUDE_CODE_OAUTH_TOKEN;
|
|
3063
3063
|
else process.env.CLAUDE_CODE_OAUTH_TOKEN = originalOAuth;
|
|
@@ -8,7 +8,11 @@ import {
|
|
|
8
8
|
storeOAuthTokens,
|
|
9
9
|
upsertOAuthApp,
|
|
10
10
|
} from "../be/db-queries/oauth";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
startOAuthRefreshSweep,
|
|
13
|
+
stopOAuthRefreshSweep,
|
|
14
|
+
sweepOAuthTokenRefresh,
|
|
15
|
+
} from "../be/oauth-refresh-sweep";
|
|
12
16
|
|
|
13
17
|
async function authorizationStatus(provider: string): Promise<string | undefined> {
|
|
14
18
|
const id = await getDefaultAuthorizationIdForProvider(provider);
|
|
@@ -242,3 +246,32 @@ describe("sweepOAuthTokenRefresh", () => {
|
|
|
242
246
|
expect((await getOAuthTokens("vendor_a"))?.accessToken).toBe("example-new-access-token");
|
|
243
247
|
});
|
|
244
248
|
});
|
|
249
|
+
|
|
250
|
+
describe("startOAuthRefreshSweep", () => {
|
|
251
|
+
const originalDisable = process.env.OAUTH_REFRESH_SWEEP_DISABLE;
|
|
252
|
+
|
|
253
|
+
afterAll(async () => {
|
|
254
|
+
await stopOAuthRefreshSweep();
|
|
255
|
+
if (originalDisable === undefined) {
|
|
256
|
+
delete process.env.OAUTH_REFRESH_SWEEP_DISABLE;
|
|
257
|
+
} else {
|
|
258
|
+
process.env.OAUTH_REFRESH_SWEEP_DISABLE = originalDisable;
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
for (const val of ["true", "1", "TRUE"]) {
|
|
263
|
+
test(`does not start when OAUTH_REFRESH_SWEEP_DISABLE=${val}`, async () => {
|
|
264
|
+
process.env.OAUTH_REFRESH_SWEEP_DISABLE = val;
|
|
265
|
+
const logs: string[] = [];
|
|
266
|
+
const origLog = console.log;
|
|
267
|
+
console.log = (...args: unknown[]) => logs.push(args.map(String).join(" "));
|
|
268
|
+
try {
|
|
269
|
+
startOAuthRefreshSweep();
|
|
270
|
+
} finally {
|
|
271
|
+
console.log = origLog;
|
|
272
|
+
await stopOAuthRefreshSweep();
|
|
273
|
+
}
|
|
274
|
+
expect(logs).toEqual(["[oauth-refresh-sweep] Disabled via OAUTH_REFRESH_SWEEP_DISABLE"]);
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
});
|
|
@@ -136,6 +136,26 @@ describe("generateCompose", () => {
|
|
|
136
136
|
expect(yaml).not.toContain("pull_policy: always");
|
|
137
137
|
});
|
|
138
138
|
|
|
139
|
+
test.each([
|
|
140
|
+
"docker",
|
|
141
|
+
"podman",
|
|
142
|
+
] as const)("%s agent services mount volumes where the worker reads and writes", (containerEngine) => {
|
|
143
|
+
const doc = Bun.YAML.parse(generateCompose({ ...devState, containerEngine })) as {
|
|
144
|
+
services: Record<string, { volumes?: string[] }>;
|
|
145
|
+
};
|
|
146
|
+
const agents = Object.entries(doc.services).filter(([name]) => name !== "swarm-api");
|
|
147
|
+
|
|
148
|
+
expect(agents.map(([name]) => name)).toEqual(["lead", "worker-coder-1", "worker-coder-2"]);
|
|
149
|
+
for (const [name, service] of agents) {
|
|
150
|
+
expect(service.volumes).toEqual([
|
|
151
|
+
"swarm_logs:/logs",
|
|
152
|
+
"swarm_shared:/workspace/shared",
|
|
153
|
+
`swarm_${name.replaceAll("-", "_")}:/workspace/personal`,
|
|
154
|
+
]);
|
|
155
|
+
}
|
|
156
|
+
expect(doc.services["swarm-api"]?.volumes).toEqual(["swarm_data:/app/data"]);
|
|
157
|
+
});
|
|
158
|
+
|
|
139
159
|
test("dev preset produces 3 agent services + 1 API service", () => {
|
|
140
160
|
const yaml = generateCompose(devState);
|
|
141
161
|
// Only count service definitions in the services section (before volumes:)
|
|
@@ -362,7 +362,14 @@ describe("onboarding state", () => {
|
|
|
362
362
|
signals: { providers: Array<Record<string, unknown>> };
|
|
363
363
|
};
|
|
364
364
|
expect(body.signals.providers).toEqual([
|
|
365
|
-
{
|
|
365
|
+
{
|
|
366
|
+
provider: "claude",
|
|
367
|
+
state: "verified",
|
|
368
|
+
workers: 2,
|
|
369
|
+
verifiedWorkers: 2,
|
|
370
|
+
error: null,
|
|
371
|
+
warning: null,
|
|
372
|
+
},
|
|
366
373
|
]);
|
|
367
374
|
expect(body.state.steps.ai).toMatchObject({
|
|
368
375
|
status: "done",
|
|
@@ -370,6 +377,57 @@ describe("onboarding state", () => {
|
|
|
370
377
|
});
|
|
371
378
|
});
|
|
372
379
|
|
|
380
|
+
test("GET carries the latest live test error and the pool warning", async () => {
|
|
381
|
+
const report = (liveTest: {
|
|
382
|
+
ok: boolean;
|
|
383
|
+
error: string | null;
|
|
384
|
+
latency_ms: number;
|
|
385
|
+
testedAt: number;
|
|
386
|
+
warning?: string;
|
|
387
|
+
}) => ({
|
|
388
|
+
ready: true,
|
|
389
|
+
missing: [],
|
|
390
|
+
satisfiedBy: "env" as const,
|
|
391
|
+
hint: null,
|
|
392
|
+
liveTest,
|
|
393
|
+
reportedAt: Date.now(),
|
|
394
|
+
reportKind: "boot" as const,
|
|
395
|
+
});
|
|
396
|
+
const failing = await createAgent({ name: "codex-worker", isLead: false, status: "idle" });
|
|
397
|
+
await setAgentHarnessProvider(failing.id, "codex");
|
|
398
|
+
await updateAgentCredStatus(
|
|
399
|
+
failing.id,
|
|
400
|
+
report({ ok: false, error: "HTTP 401: bad key", latency_ms: 5, testedAt: Date.now() }),
|
|
401
|
+
);
|
|
402
|
+
const warned = await createAgent({ name: "pi-worker", isLead: false, status: "idle" });
|
|
403
|
+
await setAgentHarnessProvider(warned.id, "pi");
|
|
404
|
+
await updateAgentCredStatus(
|
|
405
|
+
warned.id,
|
|
406
|
+
report({
|
|
407
|
+
ok: true,
|
|
408
|
+
error: null,
|
|
409
|
+
latency_ms: 5,
|
|
410
|
+
testedAt: Date.now(),
|
|
411
|
+
warning: "1 of 2 keys failed: key ending abcde (HTTP 401).",
|
|
412
|
+
}),
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
const body = (await (await request("/api/onboarding")).json()) as {
|
|
416
|
+
signals: { providers: Array<Record<string, unknown>> };
|
|
417
|
+
};
|
|
418
|
+
const byProvider = Object.fromEntries(body.signals.providers.map((p) => [p.provider, p]));
|
|
419
|
+
expect(byProvider.codex).toMatchObject({
|
|
420
|
+
state: "configured",
|
|
421
|
+
error: "HTTP 401: bad key",
|
|
422
|
+
warning: null,
|
|
423
|
+
});
|
|
424
|
+
expect(byProvider.pi).toMatchObject({
|
|
425
|
+
state: "verified",
|
|
426
|
+
error: null,
|
|
427
|
+
warning: "1 of 2 keys failed: key ending abcde (HTTP 401).",
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
|
|
373
431
|
test("GET derives integrations from environment configuration", async () => {
|
|
374
432
|
process.env.GITHUB_TOKEN = "example-github-token";
|
|
375
433
|
const body = (await (await request("/api/onboarding")).json()) as {
|
|
@@ -853,6 +911,55 @@ describe("onboarding memory probe", () => {
|
|
|
853
911
|
expect((await getState()).steps.memory.status).toBe("todo");
|
|
854
912
|
});
|
|
855
913
|
|
|
914
|
+
test("probes and saves the first member of a reused OpenAI pool", async () => {
|
|
915
|
+
const originalFetch = globalThis.fetch;
|
|
916
|
+
let authorization: string | null = null;
|
|
917
|
+
process.env.OPENAI_API_KEY = "example-first, example-second";
|
|
918
|
+
globalThis.fetch = (async (input, init) => {
|
|
919
|
+
const req = new Request(input, init);
|
|
920
|
+
if (new URL(req.url).hostname !== "api.openai.com") return originalFetch(input, init);
|
|
921
|
+
authorization = req.headers.get("authorization");
|
|
922
|
+
return embeddingResponse(512);
|
|
923
|
+
}) as typeof fetch;
|
|
924
|
+
try {
|
|
925
|
+
const response = await request("/api/onboarding/memory", {
|
|
926
|
+
method: "POST",
|
|
927
|
+
body: { preset: "openai", reuseKey: "OPENAI_API_KEY" },
|
|
928
|
+
});
|
|
929
|
+
expect(await response.json()).toMatchObject({ ok: true, dimensions: 512 });
|
|
930
|
+
expect(authorization).toBe("Bearer example-first");
|
|
931
|
+
const configs = await getSwarmConfigs({ scope: "global", key: "EMBEDDING_API_KEY" });
|
|
932
|
+
expect(configs[0]?.value).toBe("example-first");
|
|
933
|
+
} finally {
|
|
934
|
+
globalThis.fetch = originalFetch;
|
|
935
|
+
}
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
for (const keyVar of ["EMBEDDING_API_KEY", "OPENAI_API_KEY"]) {
|
|
939
|
+
test(`existing memory probes the first member of ${keyVar}`, async () => {
|
|
940
|
+
const originalFetch = globalThis.fetch;
|
|
941
|
+
let authorization: string | null = null;
|
|
942
|
+
process.env[keyVar] = "example-first, example-second";
|
|
943
|
+
process.env.EMBEDDING_API_BASE_URL = "https://api.openai.com/v1";
|
|
944
|
+
globalThis.fetch = (async (input, init) => {
|
|
945
|
+
const req = new Request(input, init);
|
|
946
|
+
if (new URL(req.url).hostname !== "api.openai.com") return originalFetch(input, init);
|
|
947
|
+
authorization = req.headers.get("authorization");
|
|
948
|
+
return embeddingResponse(512);
|
|
949
|
+
}) as typeof fetch;
|
|
950
|
+
try {
|
|
951
|
+
const response = await request("/api/onboarding/memory", {
|
|
952
|
+
method: "POST",
|
|
953
|
+
body: { preset: "existing" },
|
|
954
|
+
});
|
|
955
|
+
expect(await response.json()).toMatchObject({ ok: true, dimensions: 512 });
|
|
956
|
+
expect(authorization).toBe("Bearer example-first");
|
|
957
|
+
} finally {
|
|
958
|
+
globalThis.fetch = originalFetch;
|
|
959
|
+
}
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
|
|
856
963
|
test("does not send a reused OpenAI key to another host", async () => {
|
|
857
964
|
let hits = 0;
|
|
858
965
|
const fake = startEmbeddingServer(() => {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { OpenAIEmbeddingProvider } from "../be/memory/providers/openai-embedding";
|
|
3
|
+
|
|
4
|
+
for (const source of ["config", "EMBEDDING_API_KEY", "OPENAI_API_KEY"] as const) {
|
|
5
|
+
test(`embedding selects one key from ${source}`, async () => {
|
|
6
|
+
const originalFetch = globalThis.fetch;
|
|
7
|
+
const saved = {
|
|
8
|
+
EMBEDDING_API_KEY: process.env.EMBEDDING_API_KEY,
|
|
9
|
+
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
|
|
10
|
+
};
|
|
11
|
+
delete process.env.EMBEDDING_API_KEY;
|
|
12
|
+
delete process.env.OPENAI_API_KEY;
|
|
13
|
+
const pool = "example-first, example-second";
|
|
14
|
+
if (source !== "config") process.env[source] = pool;
|
|
15
|
+
let authorization: string | null = null;
|
|
16
|
+
globalThis.fetch = (async (input, init) => {
|
|
17
|
+
authorization = new Request(input, init).headers.get("authorization");
|
|
18
|
+
return Response.json({ data: [{ embedding: [0.5] }] });
|
|
19
|
+
}) as typeof fetch;
|
|
20
|
+
try {
|
|
21
|
+
const provider = new OpenAIEmbeddingProvider({
|
|
22
|
+
dimensions: 1,
|
|
23
|
+
...(source === "config" ? { apiKey: pool } : {}),
|
|
24
|
+
});
|
|
25
|
+
expect(await provider.embed("test input")).toEqual(new Float32Array([0.5]));
|
|
26
|
+
expect(["Bearer example-first", "Bearer example-second"]).toContain(authorization);
|
|
27
|
+
} finally {
|
|
28
|
+
globalThis.fetch = originalFetch;
|
|
29
|
+
for (const [key, value] of Object.entries(saved)) {
|
|
30
|
+
if (value === undefined) delete process.env[key];
|
|
31
|
+
else process.env[key] = value;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -675,7 +675,7 @@ describe("OpencodeSession — persisted rows render in the dashboard", () => {
|
|
|
675
675
|
const { items } = normalizeSessionLogs(rows);
|
|
676
676
|
|
|
677
677
|
const visible = items
|
|
678
|
-
.filter((item) => item.kind !== "lifecycle")
|
|
678
|
+
.filter((item) => item.kind !== "lifecycle" && item.kind !== "call_usage")
|
|
679
679
|
.map((item) =>
|
|
680
680
|
item.kind === "text"
|
|
681
681
|
? `${item.role} text: ${item.text}`
|
|
@@ -695,6 +695,7 @@ describe("OpencodeSession — persisted rows render in the dashboard", () => {
|
|
|
695
695
|
"system text: opencode error: provider overloaded",
|
|
696
696
|
]);
|
|
697
697
|
expect(items.some((item) => item.kind === "unknown")).toBe(false);
|
|
698
|
+
expect(items.filter((item) => item.kind === "call_usage")).toHaveLength(1);
|
|
698
699
|
});
|
|
699
700
|
});
|
|
700
701
|
|
|
@@ -164,12 +164,12 @@ describe("workflow raw-llm and validate nodes", () => {
|
|
|
164
164
|
});
|
|
165
165
|
});
|
|
166
166
|
|
|
167
|
-
describe("OpenRouter credential check (GET /
|
|
167
|
+
describe("OpenRouter credential check (GET /key)", () => {
|
|
168
168
|
for (const c of CASES) {
|
|
169
169
|
test(`${c.name}`, async () => {
|
|
170
170
|
setEnv(c.env);
|
|
171
171
|
await validateProviderCredentials("grok", { OPENROUTER_API_KEY: "example-or-key" });
|
|
172
|
-
expect(requests[0]?.url.endsWith("/
|
|
172
|
+
expect(requests[0]?.url.endsWith("/key")).toBe(true);
|
|
173
173
|
expectAttribution(requests[0]?.headers, c.attributed);
|
|
174
174
|
});
|
|
175
175
|
}
|
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
COMPANY_ACQUISITION_SOURCES,
|
|
6
6
|
COMPANY_TEAM_SIZES,
|
|
7
7
|
COMPANY_USE_CASES,
|
|
8
|
+
reportedUseCase,
|
|
9
|
+
reportedUserFunction,
|
|
8
10
|
} from "../utils/company-profile";
|
|
9
11
|
import {
|
|
10
12
|
isFilledOrgLogoUrl,
|
|
@@ -157,6 +159,40 @@ describe("company profile config guards", () => {
|
|
|
157
159
|
}
|
|
158
160
|
});
|
|
159
161
|
|
|
162
|
+
describe("profile values the telemetry catalog does not list yet", () => {
|
|
163
|
+
const input = {
|
|
164
|
+
orgId: "org_0123456789abcdef",
|
|
165
|
+
installationId: "install_0123456789abcdef",
|
|
166
|
+
isCloud: false,
|
|
167
|
+
isE2b: false,
|
|
168
|
+
swarmVersion: "1.0.0",
|
|
169
|
+
actor: { userId: "user-1" },
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
test("the config guards accept them", () => {
|
|
173
|
+
for (const value of ["content", "research", "seo", "admin"]) {
|
|
174
|
+
expect(validateConfigValue("SWARM_ORG_USE_CASE", value)).toBeNull();
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("stay out of the telemetry context, as if unset", () => {
|
|
179
|
+
for (const useCase of ["content", "research", "seo", "admin"]) {
|
|
180
|
+
expect(reportedUseCase(useCase)).toBeUndefined();
|
|
181
|
+
expect(buildContext({ ...input, useCase })).not.toHaveProperty("use_case");
|
|
182
|
+
}
|
|
183
|
+
for (const userFunction of ["marketing", "sales", "research"]) {
|
|
184
|
+
expect(reportedUserFunction(userFunction)).toBeUndefined();
|
|
185
|
+
expect(buildContext({ ...input, userFunction })).not.toHaveProperty("user_function");
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("the catalog values still report", () => {
|
|
190
|
+
const context = buildContext({ ...input, useCase: "docs", userFunction: "founder" });
|
|
191
|
+
expect(context.use_case).toBe("docs");
|
|
192
|
+
expect(context.user_function).toBe("founder");
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
160
196
|
describe("org_website in the telemetry context", () => {
|
|
161
197
|
const input = {
|
|
162
198
|
orgId: "org_0123456789abcdef",
|