@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
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { decryptSecret, getEncryptionKey } from "../be/crypto";
|
|
4
|
+
import { deleteKv, deleteSwarmConfigByKey, getDbClient, getKeyStatuses, getKv } from "../be/db";
|
|
5
|
+
import { retirePoolKeyStatus } from "../be/db/api-keys";
|
|
6
|
+
import { deriveCodexKeySuffix } from "../providers/codex-oauth/auth-json";
|
|
7
|
+
import { decodeJwt, extractChatgptPlanType } from "../providers/codex-oauth/flow";
|
|
8
|
+
import { MAX_CODEX_OAUTH_SLOT } from "../providers/codex-oauth/storage";
|
|
9
|
+
import {
|
|
10
|
+
CODEX_AUTH_FAILURE_BENCH_THRESHOLD,
|
|
11
|
+
CODEX_AUTH_WATCH_NAMESPACE,
|
|
12
|
+
codexAuthBenchMarkerKey,
|
|
13
|
+
} from "../utils/codex-auth-failure";
|
|
14
|
+
import { ensureConfigAdmin, requestSecretReadScope, runConfigWriteSideEffects } from "./config";
|
|
15
|
+
import { route } from "./route-def";
|
|
16
|
+
import { jsonError } from "./utils";
|
|
17
|
+
|
|
18
|
+
export const CodexAccountSchema = z.object({
|
|
19
|
+
slot: z.number().int().min(0),
|
|
20
|
+
configKey: z.string(),
|
|
21
|
+
legacy: z.boolean(),
|
|
22
|
+
suffix: z.string().nullable(),
|
|
23
|
+
plan: z.string().nullable(),
|
|
24
|
+
email: z.string().nullable(),
|
|
25
|
+
status: z.enum(["ok", "needs_login", "unchecked"]),
|
|
26
|
+
});
|
|
27
|
+
export type CodexAccount = z.infer<typeof CodexAccountSchema>;
|
|
28
|
+
|
|
29
|
+
export const CodexAccountsResponseSchema = z.object({ accounts: z.array(CodexAccountSchema) });
|
|
30
|
+
export type CodexAccountsResponse = z.infer<typeof CodexAccountsResponseSchema>;
|
|
31
|
+
|
|
32
|
+
const listAccounts = route({
|
|
33
|
+
method: "get",
|
|
34
|
+
path: "/api/codex-oauth/accounts",
|
|
35
|
+
pattern: ["api", "codex-oauth", "accounts"],
|
|
36
|
+
summary: "List stored Codex ChatGPT accounts",
|
|
37
|
+
tags: ["Codex OAuth"],
|
|
38
|
+
responses: {
|
|
39
|
+
200: { description: "Stored account metadata", schema: CodexAccountsResponseSchema },
|
|
40
|
+
403: { description: "Caller cannot read all secrets" },
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const deleteAccount = route({
|
|
45
|
+
method: "delete",
|
|
46
|
+
path: "/api/codex-oauth/accounts/{slot}",
|
|
47
|
+
pattern: ["api", "codex-oauth", "accounts", null],
|
|
48
|
+
summary: "Remove a stored Codex ChatGPT account",
|
|
49
|
+
tags: ["Codex OAuth"],
|
|
50
|
+
rbac: { permission: "config.delete.any" },
|
|
51
|
+
params: z.object({ slot: z.coerce.number().int().min(0).max(MAX_CODEX_OAUTH_SLOT) }),
|
|
52
|
+
responses: {
|
|
53
|
+
200: { description: "Remaining accounts", schema: CodexAccountsResponseSchema },
|
|
54
|
+
403: { description: "Config administration and permission to read all secrets required" },
|
|
55
|
+
404: { description: "Account slot does not exist" },
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
type AccountRow = { key: string; value: string; encrypted: number };
|
|
60
|
+
|
|
61
|
+
async function accountRows(): Promise<Array<AccountRow & { slot: number; legacy: boolean }>> {
|
|
62
|
+
const rows = await getDbClient().query<AccountRow>(
|
|
63
|
+
"SELECT key, value, encrypted FROM swarm_config WHERE scope = 'global' AND (key = 'codex_oauth' OR key GLOB 'codex_oauth_[0-9]*')",
|
|
64
|
+
);
|
|
65
|
+
const hasSlotZero = rows.some((row) => row.key === "codex_oauth_0");
|
|
66
|
+
return rows
|
|
67
|
+
.flatMap((row) => {
|
|
68
|
+
if (row.key === "codex_oauth") {
|
|
69
|
+
return hasSlotZero ? [] : [{ ...row, slot: 0, legacy: true }];
|
|
70
|
+
}
|
|
71
|
+
const match = /^codex_oauth_(0|[1-9]\d*)$/.exec(row.key);
|
|
72
|
+
if (!match) return [];
|
|
73
|
+
const slot = Number(match[1]);
|
|
74
|
+
return slot <= MAX_CODEX_OAUTH_SLOT ? [{ ...row, slot, legacy: false }] : [];
|
|
75
|
+
})
|
|
76
|
+
.sort((a, b) => a.slot - b.slot);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const AccountCredentialsSchema = z.object({
|
|
80
|
+
access: z.string().min(1),
|
|
81
|
+
refresh: z.string().min(1),
|
|
82
|
+
expires: z.number().finite(),
|
|
83
|
+
accountId: z.string().min(1),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
function readCredentials(row: AccountRow): z.infer<typeof AccountCredentialsSchema> | null {
|
|
87
|
+
try {
|
|
88
|
+
const value = row.encrypted ? decryptSecret(row.value, getEncryptionKey()) : row.value;
|
|
89
|
+
return AccountCredentialsSchema.parse(JSON.parse(value));
|
|
90
|
+
} catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function accountSuffix(row: AccountRow): string | null {
|
|
96
|
+
const credentials = readCredentials(row);
|
|
97
|
+
return credentials ? deriveCodexKeySuffix(credentials.access, credentials.accountId) : null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function accountsResponse(): Promise<CodexAccountsResponse> {
|
|
101
|
+
const rows = await accountRows();
|
|
102
|
+
const statuses = await getKeyStatuses("CODEX_OAUTH", "global", null);
|
|
103
|
+
const accounts: CodexAccount[] = [];
|
|
104
|
+
for (const row of rows) {
|
|
105
|
+
const account: CodexAccount = {
|
|
106
|
+
slot: row.slot,
|
|
107
|
+
configKey: row.key,
|
|
108
|
+
legacy: row.legacy,
|
|
109
|
+
suffix: null,
|
|
110
|
+
plan: null,
|
|
111
|
+
email: null,
|
|
112
|
+
status: "needs_login",
|
|
113
|
+
};
|
|
114
|
+
const credentials = readCredentials(row);
|
|
115
|
+
if (!credentials) {
|
|
116
|
+
accounts.push(account);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
account.suffix = deriveCodexKeySuffix(credentials.access, credentials.accountId);
|
|
120
|
+
account.plan = extractChatgptPlanType(credentials.access);
|
|
121
|
+
const profile = decodeJwt(credentials.access)?.["https://api.openai.com/profile"];
|
|
122
|
+
if (profile && typeof profile === "object" && "email" in profile) {
|
|
123
|
+
account.email = typeof profile.email === "string" && profile.email ? profile.email : null;
|
|
124
|
+
}
|
|
125
|
+
const status = statuses.find((entry) => entry.keySuffix === account.suffix);
|
|
126
|
+
const marker = await getKv(CODEX_AUTH_WATCH_NAMESPACE, codexAuthBenchMarkerKey(account.suffix));
|
|
127
|
+
const statusBenched =
|
|
128
|
+
status &&
|
|
129
|
+
status.consecutiveAuthFailures >= CODEX_AUTH_FAILURE_BENCH_THRESHOLD &&
|
|
130
|
+
status.status === "rate_limited";
|
|
131
|
+
account.status = marker || statusBenched ? "needs_login" : status ? "ok" : "unchecked";
|
|
132
|
+
accounts.push(account);
|
|
133
|
+
}
|
|
134
|
+
return { accounts };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Both responses list account emails and plans, so both routes need this gate. */
|
|
138
|
+
async function ensureSecretRead(req: IncomingMessage, res: ServerResponse): Promise<boolean> {
|
|
139
|
+
if ((await requestSecretReadScope(req)) === "all") return true;
|
|
140
|
+
jsonError(res, "Reading Codex accounts requires access to all secrets", 403);
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export async function handleCodexOAuthAccounts(
|
|
145
|
+
req: IncomingMessage,
|
|
146
|
+
res: ServerResponse,
|
|
147
|
+
pathSegments: string[],
|
|
148
|
+
queryParams: URLSearchParams,
|
|
149
|
+
): Promise<boolean> {
|
|
150
|
+
if (listAccounts.match(req.method, pathSegments)) {
|
|
151
|
+
const parsed = await listAccounts.parse(req, res, pathSegments, queryParams);
|
|
152
|
+
if (!parsed) return true;
|
|
153
|
+
if (!(await ensureSecretRead(req, res))) return true;
|
|
154
|
+
listAccounts.respond(res, 200, await accountsResponse());
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (deleteAccount.match(req.method, pathSegments)) {
|
|
159
|
+
const parsed = await deleteAccount.parse(req, res, pathSegments, queryParams);
|
|
160
|
+
if (!parsed) return true;
|
|
161
|
+
if (!(await ensureConfigAdmin(req, res, "config.delete.any"))) return true;
|
|
162
|
+
if (!(await ensureSecretRead(req, res))) return true;
|
|
163
|
+
const deletedKey = await getDbClient().transaction(async () => {
|
|
164
|
+
const rows = await accountRows();
|
|
165
|
+
const account = rows.find((row) => row.slot === parsed.params.slot);
|
|
166
|
+
if (!account || !(await deleteSwarmConfigByKey("global", null, account.key))) return null;
|
|
167
|
+
// A new sign-in takes the first free slot, so it must not inherit this
|
|
168
|
+
// account's auth bench (keyed by slot) or its bench marker.
|
|
169
|
+
const suffix = accountSuffix(account);
|
|
170
|
+
if (suffix) {
|
|
171
|
+
const remaining = rows.flatMap((row) => {
|
|
172
|
+
const keySuffix = row === account ? null : accountSuffix(row);
|
|
173
|
+
return keySuffix ? [{ keySuffix, keyIndex: row.slot }] : [];
|
|
174
|
+
});
|
|
175
|
+
await retirePoolKeyStatus("CODEX_OAUTH", suffix, remaining);
|
|
176
|
+
if (!remaining.some((row) => row.keySuffix === suffix)) {
|
|
177
|
+
await deleteKv(CODEX_AUTH_WATCH_NAMESPACE, codexAuthBenchMarkerKey(suffix));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return account.key;
|
|
181
|
+
});
|
|
182
|
+
if (deletedKey === null) {
|
|
183
|
+
jsonError(res, "Codex account slot does not exist", 404);
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
await runConfigWriteSideEffects("global", deletedKey, "");
|
|
187
|
+
deleteAccount.respond(res, 200, await accountsResponse());
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
return false;
|
|
191
|
+
}
|
package/src/http/comb.ts
CHANGED
|
@@ -2,7 +2,12 @@ import type { IncomingMessage, ServerResponse } from "node:http";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { resolveHttpAuditUserId } from "../be/audit-user";
|
|
4
4
|
import { getCombConfig } from "../comb/config";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
REVIEW_BATCH_MAX,
|
|
7
|
+
REVIEW_NOTE_MAX,
|
|
8
|
+
ReviewBatchError,
|
|
9
|
+
sendReviewBatch,
|
|
10
|
+
} from "../comb/review-batch";
|
|
6
11
|
import { can } from "../rbac";
|
|
7
12
|
import { TaskCreationBlockedError } from "../tasks/errors";
|
|
8
13
|
import { scrubSecrets } from "../utils/secret-scrubber";
|
|
@@ -24,6 +29,12 @@ const resultSchema = z.object({
|
|
|
24
29
|
skipped: z.array(skippedSchema),
|
|
25
30
|
/** Comments of an earlier send whose missing "sent" reply this batch posted again. */
|
|
26
31
|
repaired: z.array(z.object({ id: z.string(), taskId: z.string() })),
|
|
32
|
+
/**
|
|
33
|
+
* With a new task only. `online`: a lead got it. `offline`: every lead is offline,
|
|
34
|
+
* so the task waits for one. `none`: no lead, so the task waits
|
|
35
|
+
* unassigned in the pool.
|
|
36
|
+
*/
|
|
37
|
+
lead: z.enum(["online", "offline", "none"]).optional(),
|
|
27
38
|
});
|
|
28
39
|
|
|
29
40
|
const batchErrorSchema = z.object({ error: z.string(), skipped: z.array(skippedSchema) });
|
|
@@ -41,7 +52,7 @@ const reviewBatchRoute = route({
|
|
|
41
52
|
pattern: ["api", "comb", "review-batches"],
|
|
42
53
|
summary: "Send agent-fs comments to the swarm as one lead task",
|
|
43
54
|
description:
|
|
44
|
-
"Comb's 'Send to swarm'. The server reads each comment again from agent-fs with its bootstrap key, skips replies, resolved comments, and comments already sent, and creates ONE task for the lead. Each sent comment gets a `[comb:sent task=<id>]` reply from the swarm service account. A comment is sent at most once. When an earlier send lost its reply, the batch posts that reply again (`repaired`). Answers 404 while COMB_ENABLED is off.",
|
|
55
|
+
"Comb's 'Send to swarm'. The server reads each comment again from agent-fs with its bootstrap key, skips replies, resolved comments, and comments already sent, and creates ONE task for the lead. Each sent comment gets a `[comb:sent task=<id>]` reply from the swarm service account. A comment is sent at most once. An optional `note` from the sender goes into the task text. When an earlier send lost its reply, the batch posts that reply again (`repaired`). Answers 404 while COMB_ENABLED is off.",
|
|
45
56
|
tags: ["Comb"],
|
|
46
57
|
body: z.object({
|
|
47
58
|
orgId: z.string().min(1),
|
|
@@ -54,6 +65,8 @@ const reviewBatchRoute = route({
|
|
|
54
65
|
.min(1)
|
|
55
66
|
.max(1024)
|
|
56
67
|
.refine(isDrivePath, "scopePath must start with '/' and have no '.' or '..' segment"),
|
|
68
|
+
/** The sender's note for the lead about the whole batch. */
|
|
69
|
+
note: z.string().max(REVIEW_NOTE_MAX).optional(),
|
|
57
70
|
}),
|
|
58
71
|
responses: {
|
|
59
72
|
200: {
|
package/src/http/config.ts
CHANGED
|
@@ -67,7 +67,7 @@ export function readsRepoSecrets(
|
|
|
67
67
|
}).allow;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
async function requestSecretReadScope(
|
|
70
|
+
export async function requestSecretReadScope(
|
|
71
71
|
req: IncomingMessage,
|
|
72
72
|
resolvedScope?: { agentId?: string; repoId?: string },
|
|
73
73
|
): Promise<"all" | "self" | "none"> {
|
package/src/http/core.ts
CHANGED
|
@@ -95,6 +95,12 @@ import {
|
|
|
95
95
|
*/
|
|
96
96
|
const injectedEnvOriginals = new Map<string, { original: string | undefined; injected: string }>();
|
|
97
97
|
|
|
98
|
+
/** Read the deployment value without a stored config's temporary env injection. */
|
|
99
|
+
export function getDeploymentEnvValue(key: string): string | undefined {
|
|
100
|
+
const tracked = injectedEnvOriginals.get(key);
|
|
101
|
+
return tracked && process.env[key] === tracked.injected ? tracked.original : process.env[key];
|
|
102
|
+
}
|
|
103
|
+
|
|
98
104
|
export async function loadGlobalConfigsIntoEnv(override = false): Promise<string[]> {
|
|
99
105
|
const globalConfigs = await getInjectableGlobalConfigs();
|
|
100
106
|
const updated: string[] = [];
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
localTasksPerWeek,
|
|
12
12
|
} from "../fleet-benchmark";
|
|
13
13
|
import { isTelemetryEnabled } from "../telemetry";
|
|
14
|
-
import { COMPANY_TEAM_SIZES, COMPANY_USE_CASES } from "../utils/company-profile";
|
|
14
|
+
import { COMPANY_TEAM_SIZES, COMPANY_USE_CASES, reportedUseCase } from "../utils/company-profile";
|
|
15
15
|
import { normalizeOrgWebsite } from "../utils/org-website";
|
|
16
16
|
import { globalConfig } from "./global-config";
|
|
17
17
|
import { rejectGuest } from "./request-principal";
|
|
@@ -188,6 +188,9 @@ export async function buildFleetBenchmarkResponse(
|
|
|
188
188
|
normalizeOrgWebsite(website) && team.success && use.success
|
|
189
189
|
? { teamSize: team.data, useCase: use.data }
|
|
190
190
|
: null;
|
|
191
|
+
// The proxy answers 400 for a use case its catalog does not list yet, so
|
|
192
|
+
// that use case compares against the whole fleet.
|
|
193
|
+
const proxyPeerGroup = peerGroup && reportedUseCase(peerGroup.useCase) ? peerGroup : null;
|
|
191
194
|
const empty = {
|
|
192
195
|
level: peerGroup ? ("full" as const) : ("basic" as const),
|
|
193
196
|
peerGroup: null,
|
|
@@ -215,7 +218,7 @@ export async function buildFleetBenchmarkResponse(
|
|
|
215
218
|
if (finishedTasks < FLEET_MIN_FINISHED_TASKS)
|
|
216
219
|
return { ...empty, state: "not_enough_tasks", local };
|
|
217
220
|
|
|
218
|
-
const fleet = await fleetBenchmarks(now.getTime(),
|
|
221
|
+
const fleet = await fleetBenchmarks(now.getTime(), proxyPeerGroup);
|
|
219
222
|
const responsePeerGroup =
|
|
220
223
|
peerGroup && fleet?.peer_group
|
|
221
224
|
? {
|
|
@@ -8,7 +8,7 @@ import { getRequestAuth } from "../utils/request-auth-context";
|
|
|
8
8
|
import { jsonError } from "./utils";
|
|
9
9
|
|
|
10
10
|
/** Visible ASCII, at most 255 characters: room for a UUID, a ULID or a client's own scheme. */
|
|
11
|
-
const IDEMPOTENCY_KEY_PATTERN = /^[\x21-\x7E]{1,255}$/;
|
|
11
|
+
export const IDEMPOTENCY_KEY_PATTERN = /^[\x21-\x7E]{1,255}$/;
|
|
12
12
|
|
|
13
13
|
/** Set on a response answered from an earlier accepted request. */
|
|
14
14
|
export const IDEMPOTENT_REPLAYED_HEADER = "Idempotent-Replayed";
|
package/src/http/index.ts
CHANGED
|
@@ -76,6 +76,7 @@ import { handleApprovalRequests } from "./approval-requests";
|
|
|
76
76
|
import { handleApps } from "./apps";
|
|
77
77
|
import { handleAssets } from "./assets";
|
|
78
78
|
import { handleBudgets } from "./budgets";
|
|
79
|
+
import { handleCodexOAuthAccounts } from "./codex-oauth-accounts";
|
|
79
80
|
import { handleCodexOAuthDevice } from "./codex-oauth-device";
|
|
80
81
|
import { handleCodexOAuthKeepWarm } from "./codex-oauth-keep-warm";
|
|
81
82
|
import { handleComb } from "./comb";
|
|
@@ -115,6 +116,7 @@ import { handlePageProxy } from "./page-proxy";
|
|
|
115
116
|
import { handlePages } from "./pages";
|
|
116
117
|
import { handlePagesPublic } from "./pages-public";
|
|
117
118
|
import { handlePoll } from "./poll";
|
|
119
|
+
import { handlePoolKeys } from "./pool-keys";
|
|
118
120
|
import { handlePricing } from "./pricing";
|
|
119
121
|
import { handlePromptTemplates } from "./prompt-templates";
|
|
120
122
|
import { handleRanking } from "./ranking";
|
|
@@ -360,6 +362,7 @@ const httpServer = createHttpServer(async (req, res) => {
|
|
|
360
362
|
() => handleStatus(req, res, pathSegments, queryParams),
|
|
361
363
|
() => handleOnboarding(req, res, pathSegments, queryParams),
|
|
362
364
|
() => handleCodexOAuthDevice(req, res, pathSegments, queryParams),
|
|
365
|
+
() => handleCodexOAuthAccounts(req, res, pathSegments, queryParams),
|
|
363
366
|
() => handleActiveSessions(req, res, pathSegments, queryParams, myAgentId),
|
|
364
367
|
() => handlePricing(req, res, pathSegments, queryParams, myAgentId),
|
|
365
368
|
() => handleSchedules(req, res, pathSegments, queryParams, myAgentId),
|
|
@@ -367,6 +370,7 @@ const httpServer = createHttpServer(async (req, res) => {
|
|
|
367
370
|
() => handleWorkflowEvents(req, res, pathSegments, queryParams),
|
|
368
371
|
() => handleApprovalRequests(req, res, pathSegments, queryParams),
|
|
369
372
|
() => handleApps(req, res, pathSegments, queryParams, myAgentId),
|
|
373
|
+
() => handlePoolKeys(req, res, pathSegments, queryParams),
|
|
370
374
|
() => handleConfig(req, res, pathSegments, queryParams),
|
|
371
375
|
() => handleFs(req, res, pathSegments, queryParams, myAgentId),
|
|
372
376
|
() => handleComb(req, res, pathSegments, queryParams, myAgentId),
|
|
@@ -513,7 +517,7 @@ async function shutdown() {
|
|
|
513
517
|
await stopChatSdk();
|
|
514
518
|
|
|
515
519
|
// Stop OAuth keepalive
|
|
516
|
-
if (
|
|
520
|
+
if (!isEnvFlagEnabled("OAUTH_KEEPALIVE_DISABLE", false)) {
|
|
517
521
|
const { stopOAuthKeepalive } = await import("../oauth/keepalive");
|
|
518
522
|
await stopOAuthKeepalive();
|
|
519
523
|
}
|
|
@@ -843,7 +847,7 @@ httpServer
|
|
|
843
847
|
startRuntimeSupervisor();
|
|
844
848
|
|
|
845
849
|
// Start OAuth token keepalive (proactive refresh to prevent expiry)
|
|
846
|
-
if (
|
|
850
|
+
if (!isEnvFlagEnabled("OAUTH_KEEPALIVE_DISABLE", false)) {
|
|
847
851
|
const { startOAuthKeepalive } = await import("../oauth/keepalive");
|
|
848
852
|
startOAuthKeepalive();
|
|
849
853
|
}
|
|
@@ -851,7 +855,7 @@ httpServer
|
|
|
851
855
|
// Start generic OAuth token refresh sweep (all oauth_apps providers,
|
|
852
856
|
// 15-min tick, first run ~1 min after boot). Complements the tracker-only
|
|
853
857
|
// keepalive above — see src/be/oauth-refresh-sweep.ts.
|
|
854
|
-
if (
|
|
858
|
+
if (!isEnvFlagEnabled("OAUTH_REFRESH_SWEEP_DISABLE", false)) {
|
|
855
859
|
const { startOAuthRefreshSweep } = await import("../be/oauth-refresh-sweep");
|
|
856
860
|
startOAuthRefreshSweep();
|
|
857
861
|
}
|
|
@@ -898,6 +902,13 @@ httpServer
|
|
|
898
902
|
.catch((err) => {
|
|
899
903
|
console.error("[boot-scrub-sweep] startup scrub failed (non-fatal):", err);
|
|
900
904
|
});
|
|
905
|
+
|
|
906
|
+
// Source key backfill is idempotent, resumable, and non-fatal.
|
|
907
|
+
import("../be/session-source-backfill")
|
|
908
|
+
.then(({ runSessionSourceBackfill }) => runSessionSourceBackfill())
|
|
909
|
+
.catch((err) => {
|
|
910
|
+
console.error("[session-source-backfill] startup run failed (non-fatal):", err);
|
|
911
|
+
});
|
|
901
912
|
})
|
|
902
913
|
.on("error", (err) => {
|
|
903
914
|
console.error("HTTP Server Error:", err);
|
package/src/http/integrations.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
registerTelegramWebhook,
|
|
9
9
|
} from "../integrations/chat-sdk/status";
|
|
10
10
|
import { buildSlackManifest } from "../slack/manifest";
|
|
11
|
+
import { selectCredential } from "../utils/credentials";
|
|
11
12
|
import { ensureHttpPermission } from "./config";
|
|
12
13
|
import { resolveConfigValue, resolveMcpBaseUrl } from "./config-values";
|
|
13
14
|
import { route } from "./route-def";
|
|
@@ -231,7 +232,10 @@ export function createIntegrationsHandler(deps: TestConnectionDeps = {}) {
|
|
|
231
232
|
}
|
|
232
233
|
|
|
233
234
|
try {
|
|
234
|
-
|
|
235
|
+
// A comma pool is one key per call, as the runner does.
|
|
236
|
+
const client = buildClient(
|
|
237
|
+
selectCredential(apiKey, undefined, "ANTHROPIC_API_KEY").selected,
|
|
238
|
+
);
|
|
235
239
|
const agent = await client.beta.agents.retrieve(agentId);
|
|
236
240
|
// `agent.model` is `BetaManagedAgentsModelConfig` ({id, speed}). Flatten
|
|
237
241
|
// to a string so the UI can render it directly without type guards.
|
package/src/http/kv.ts
CHANGED
|
@@ -12,7 +12,11 @@ import {
|
|
|
12
12
|
upsertKv,
|
|
13
13
|
} from "../be/db";
|
|
14
14
|
import { mcpOverflowAuthError } from "../kv-overflow";
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
privateNamespaceError,
|
|
17
|
+
reservedNamespaceError,
|
|
18
|
+
reservedRoomKeyError,
|
|
19
|
+
} from "../kv-reserved-namespaces";
|
|
16
20
|
import { hasKvViewArgs, resolveKvView } from "../kv-view";
|
|
17
21
|
import { can } from "../rbac";
|
|
18
22
|
import { agentContextKey, pageContextKey } from "../tasks/context-key";
|
|
@@ -405,8 +409,9 @@ function authorizeRead(
|
|
|
405
409
|
namespace: string,
|
|
406
410
|
ctx: AuthCtx,
|
|
407
411
|
): { status: number; message: string } | null {
|
|
408
|
-
const
|
|
409
|
-
|
|
412
|
+
const readErr =
|
|
413
|
+
privateNamespaceError(namespace) ?? mcpOverflowAuthError(namespace, ctx.callerAgentId);
|
|
414
|
+
return readErr ? { status: 403, message: readErr } : null;
|
|
410
415
|
}
|
|
411
416
|
|
|
412
417
|
function encodeValueOrError(
|
package/src/http/onboarding.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
} from "../be/onboarding";
|
|
22
22
|
import { assertUrlSafe, publicEndpointSsrfOptions } from "../oauth/mcp-wrapper";
|
|
23
23
|
import { ProviderNameSchema } from "../types";
|
|
24
|
+
import { splitCredentialPool } from "../utils/credentials";
|
|
24
25
|
import { getOpenRouterAttributionHeaders } from "../utils/openrouter-base-url";
|
|
25
26
|
import { ensureConfigAdmin } from "./config";
|
|
26
27
|
import { scheduleIntegrationsReload } from "./core";
|
|
@@ -36,11 +37,14 @@ async function buildSignals(state: OnboardingState): Promise<OnboardingSignals>
|
|
|
36
37
|
const provider = ProviderNameSchema.safeParse(entry.provider);
|
|
37
38
|
if (!provider.success) continue;
|
|
38
39
|
const rollup = await rollupCredStatusForProvider(provider.data);
|
|
40
|
+
const live = rollup.latestLiveTest;
|
|
39
41
|
providers.push({
|
|
40
42
|
provider: provider.data,
|
|
41
43
|
state: rollup.state,
|
|
42
44
|
workers: rollup.workers,
|
|
43
45
|
verifiedWorkers: rollup.verifiedWorkers,
|
|
46
|
+
error: live && !live.ok ? (live.error ?? null) : null,
|
|
47
|
+
warning: live?.ok ? (live.warning ?? null) : null,
|
|
44
48
|
});
|
|
45
49
|
}
|
|
46
50
|
|
|
@@ -241,7 +245,8 @@ async function handleMemoryProbe(body: OnboardingMemoryRequest): Promise<Onboard
|
|
|
241
245
|
body.preset === "existing"
|
|
242
246
|
? (process.env.EMBEDDING_API_KEY ?? process.env.OPENAI_API_KEY)
|
|
243
247
|
: undefined;
|
|
244
|
-
|
|
248
|
+
// Test and save the same pool member.
|
|
249
|
+
const apiKey = splitCredentialPool(explicitKey ?? reusedKey ?? implicitKey ?? "")[0];
|
|
245
250
|
|
|
246
251
|
if (!apiKey) {
|
|
247
252
|
return {
|
package/src/http/pages-public.ts
CHANGED
|
@@ -308,13 +308,45 @@ function getAppBaseUrl(): string {
|
|
|
308
308
|
return getAppUrl(LOCAL_PAGE_APP_URL);
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
+
// CSP host-source without a path: optional http(s) scheme, a host that may
|
|
312
|
+
// start with a `*.` wildcard label, and an optional port (digits or `*`).
|
|
313
|
+
// Bare `*`, keywords (`'none'`, `'self'`) and anything carrying `;` or a path
|
|
314
|
+
// are rejected so a bad env value cannot widen or break the policy.
|
|
315
|
+
const FRAME_ANCESTOR_SOURCE_RE =
|
|
316
|
+
/^(?:https?:\/\/)?(?:\*\.)?[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*(?::(?:\d{1,5}|\*))?$/i;
|
|
317
|
+
|
|
318
|
+
let cachedFrameAncestors: { raw: string; origins: string[] } | null = null;
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Parse `PAGES_FRAME_ANCESTORS`: extra origins allowed to iframe PUBLIC pages
|
|
322
|
+
* (for example a marketing site embedding a chart). Comma or space separated.
|
|
323
|
+
* Invalid entries are dropped with one warning per distinct env value; the
|
|
324
|
+
* parse is cached on the raw string so a config reload picks up changes
|
|
325
|
+
* without re-warning on every request.
|
|
326
|
+
*/
|
|
327
|
+
export function getPageFrameAncestors(): string[] {
|
|
328
|
+
const raw = process.env.PAGES_FRAME_ANCESTORS ?? "";
|
|
329
|
+
if (cachedFrameAncestors?.raw === raw) return cachedFrameAncestors.origins;
|
|
330
|
+
const origins: string[] = [];
|
|
331
|
+
for (const entry of raw.split(/[\s,]+/)) {
|
|
332
|
+
const value = entry.replace(/\/+$/, "");
|
|
333
|
+
if (!value) continue;
|
|
334
|
+
if (FRAME_ANCESTOR_SOURCE_RE.test(value)) origins.push(value);
|
|
335
|
+
else console.warn(`[pages] ignoring invalid PAGES_FRAME_ANCESTORS entry: ${entry}`);
|
|
336
|
+
}
|
|
337
|
+
cachedFrameAncestors = { raw, origins };
|
|
338
|
+
return origins;
|
|
339
|
+
}
|
|
340
|
+
|
|
311
341
|
/**
|
|
312
342
|
* Build the `Content-Security-Policy` for the served HTML. Allows inline
|
|
313
343
|
* scripts (required for `BROWSER_SDK_JS`) but locks down everything else to
|
|
314
344
|
* `'self'`. The SPA iframes the page in step-6 with `sandbox="allow-scripts
|
|
315
|
-
* allow-forms"`; the CSP is a defence-in-depth layer.
|
|
345
|
+
* allow-forms"`; the CSP is a defence-in-depth layer. `PAGES_FRAME_ANCESTORS`
|
|
346
|
+
* applies only to public pages: authed and password pages stay embeddable by
|
|
347
|
+
* the app origins alone.
|
|
316
348
|
*/
|
|
317
|
-
function buildCsp(): string {
|
|
349
|
+
function buildCsp(page: Page): string {
|
|
318
350
|
// `frame-ancestors` lists every origin allowed to iframe `/p/:id`. We must
|
|
319
351
|
// include the SPA origin(s). Configured app URLs may be comma-separated so
|
|
320
352
|
// portless dev (`https://ui.swarm.localhost`), a Vite port (`http://localhost:5274`),
|
|
@@ -333,7 +365,8 @@ function buildCsp(): string {
|
|
|
333
365
|
"https://*.localhost",
|
|
334
366
|
"http://*.localhost",
|
|
335
367
|
];
|
|
336
|
-
const
|
|
368
|
+
const embedders = page.authMode === "public" ? getPageFrameAncestors() : [];
|
|
369
|
+
const ancestors = Array.from(new Set([...configured, ...devFallbacks, ...embedders]));
|
|
337
370
|
// Allow Tailwind Play CDN (`cdn.tailwindcss.com`) for scripts, Google
|
|
338
371
|
// Fonts (`fonts.googleapis.com` stylesheets + `fonts.gstatic.com` font
|
|
339
372
|
// files) for the swarm default typography, and same-origin /@swarm/api/*
|
|
@@ -609,7 +642,7 @@ export async function handlePagesPublic(
|
|
|
609
642
|
const headers: Record<string, string | string[]> = {
|
|
610
643
|
"Content-Type": "text/html; charset=utf-8",
|
|
611
644
|
"Cache-Control": "no-store",
|
|
612
|
-
"Content-Security-Policy": buildCsp(),
|
|
645
|
+
"Content-Security-Policy": buildCsp(page),
|
|
613
646
|
"X-Content-Type-Options": "nosniff",
|
|
614
647
|
};
|
|
615
648
|
if (inlineSetCookie.length > 0) headers["Set-Cookie"] = inlineSetCookie;
|
|
@@ -642,7 +675,7 @@ export async function handlePagesPublic(
|
|
|
642
675
|
const headers: Record<string, string | string[]> = {
|
|
643
676
|
"Content-Type": "text/html; charset=utf-8",
|
|
644
677
|
"Cache-Control": "no-store",
|
|
645
|
-
"Content-Security-Policy": buildCsp(),
|
|
678
|
+
"Content-Security-Policy": buildCsp(page),
|
|
646
679
|
// Defence-in-depth: prevent MIME sniffing and clickjacking outside the SPA.
|
|
647
680
|
"X-Content-Type-Options": "nosniff",
|
|
648
681
|
};
|