@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,12 +1,17 @@
|
|
|
1
|
+
import type { AgentCredStatus } from "../types";
|
|
2
|
+
import { CREDENTIAL_POOL_VARS } from "../utils/credentials";
|
|
3
|
+
import { scrubSecrets } from "../utils/secret-scrubber";
|
|
1
4
|
import {
|
|
2
5
|
buildCredStatusReport,
|
|
3
6
|
isCredCheckDisabled,
|
|
7
|
+
type LiveKeyResult,
|
|
4
8
|
sendCredStatusReport,
|
|
5
9
|
shouldRefreshBedrockStatus,
|
|
6
10
|
} from "./provider-credentials";
|
|
7
11
|
import type { ApiConfig } from "./runner";
|
|
8
12
|
|
|
9
13
|
export const CREDENTIAL_RETRY_INTERVAL_MS = 30_000;
|
|
14
|
+
export const KEY_RECHECK_INTERVAL_MS = 10 * 60_000;
|
|
10
15
|
const BEDROCK_REFRESH_INTERVAL_MS = 5 * 60 * 1000;
|
|
11
16
|
|
|
12
17
|
export interface CredentialRefreshState {
|
|
@@ -15,6 +20,64 @@ export interface CredentialRefreshState {
|
|
|
15
20
|
ready: boolean | null;
|
|
16
21
|
lastRefreshAt: number;
|
|
17
22
|
inFlight: boolean;
|
|
23
|
+
fingerprint: string | null;
|
|
24
|
+
rejectedKeys: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function credentialFingerprint(env: Record<string, string | undefined>): string {
|
|
28
|
+
return Bun.hash(
|
|
29
|
+
JSON.stringify(
|
|
30
|
+
[...CREDENTIAL_POOL_VARS, "ANTHROPIC_AUTH_TOKEN", "ANTHROPIC_BASE_URL"].map(
|
|
31
|
+
(key) => env[key] ?? null,
|
|
32
|
+
),
|
|
33
|
+
),
|
|
34
|
+
).toString();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Failed keys worth the periodic recheck. A Claude subscription token is left
|
|
39
|
+
* out: its check calls an undocumented Anthropic account endpoint, so it runs
|
|
40
|
+
* only once per token value (boot or change). A failed test with no per-key
|
|
41
|
+
* results counts as one.
|
|
42
|
+
*/
|
|
43
|
+
export function recheckableFailures(liveTest: AgentCredStatus["liveTest"] | undefined): number {
|
|
44
|
+
const keys = liveTest?.keys;
|
|
45
|
+
if (keys?.length) {
|
|
46
|
+
return keys.filter((key) => !key.ok && key.keyType !== "CLAUDE_CODE_OAUTH_TOKEN").length;
|
|
47
|
+
}
|
|
48
|
+
return liveTest?.ok === false ? 1 : 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Report each pool separately. A telemetry failure must not stop credential checks. */
|
|
52
|
+
export async function reportKeyValidation(api: ApiConfig, keys: LiveKeyResult[]): Promise<void> {
|
|
53
|
+
for (const keyType of new Set(keys.map((key) => key.keyType))) {
|
|
54
|
+
try {
|
|
55
|
+
const response = await fetch(`${api.apiUrl}/api/keys/report-validation`, {
|
|
56
|
+
method: "POST",
|
|
57
|
+
headers: {
|
|
58
|
+
"Content-Type": "application/json",
|
|
59
|
+
Authorization: `Bearer ${api.apiKey}`,
|
|
60
|
+
"X-Agent-ID": api.agentId,
|
|
61
|
+
},
|
|
62
|
+
body: JSON.stringify({
|
|
63
|
+
keyType,
|
|
64
|
+
keys: keys
|
|
65
|
+
.filter((key) => key.keyType === keyType)
|
|
66
|
+
.map((key) => ({
|
|
67
|
+
keyIndex: key.index,
|
|
68
|
+
keySuffix: key.suffix,
|
|
69
|
+
ok: key.ok,
|
|
70
|
+
httpStatus: key.httpStatus,
|
|
71
|
+
})),
|
|
72
|
+
}),
|
|
73
|
+
signal: AbortSignal.timeout(5000),
|
|
74
|
+
});
|
|
75
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
76
|
+
} catch (err) {
|
|
77
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
78
|
+
console.warn(scrubSecrets(`[credentials] Failed to report key validation: ${message}`));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
18
81
|
}
|
|
19
82
|
|
|
20
83
|
/**
|
|
@@ -31,11 +94,17 @@ export async function refreshCredentialStatus(
|
|
|
31
94
|
): Promise<void> {
|
|
32
95
|
if (isCredCheckDisabled(env) || state.inFlight) return;
|
|
33
96
|
const providerChanged = state.harnessProvider !== harnessProvider;
|
|
97
|
+
const fingerprint = credentialFingerprint(env);
|
|
98
|
+
const fingerprintChanged = state.fingerprint !== fingerprint;
|
|
34
99
|
const retryDue =
|
|
35
100
|
state.ready !== true && now - state.lastRefreshAt >= CREDENTIAL_RETRY_INTERVAL_MS;
|
|
101
|
+
const keyRecheckDue =
|
|
102
|
+
state.rejectedKeys > 0 && now - state.lastRefreshAt >= KEY_RECHECK_INTERVAL_MS;
|
|
36
103
|
if (
|
|
37
104
|
!providerChanged &&
|
|
105
|
+
!fingerprintChanged &&
|
|
38
106
|
!retryDue &&
|
|
107
|
+
!keyRecheckDue &&
|
|
39
108
|
!shouldRefreshBedrockStatus({
|
|
40
109
|
harnessProvider,
|
|
41
110
|
env,
|
|
@@ -61,7 +130,13 @@ export async function refreshCredentialStatus(
|
|
|
61
130
|
missing: snapshot.missing,
|
|
62
131
|
credStatus: snapshot,
|
|
63
132
|
});
|
|
64
|
-
if (state.harnessProvider === harnessProvider)
|
|
133
|
+
if (state.harnessProvider === harnessProvider) {
|
|
134
|
+
state.ready = snapshot.ready;
|
|
135
|
+
state.fingerprint = fingerprint;
|
|
136
|
+
state.rejectedKeys = recheckableFailures(snapshot.liveTest);
|
|
137
|
+
const keys = snapshot.liveTest?.keys;
|
|
138
|
+
if (keys?.length) await reportKeyValidation(api, keys);
|
|
139
|
+
}
|
|
65
140
|
} finally {
|
|
66
141
|
// Errors propagate to the runner's non-fatal logger, leaving ready unknown
|
|
67
142
|
// so even a lost ready:true write is retried without a restart.
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* 3. Re-checks; if ready, returns.
|
|
13
13
|
* 4. Logs a `[boot] waiting for …` line and invokes `onTick(status)` so
|
|
14
14
|
* callers can report state to the API.
|
|
15
|
-
* 5. Sleeps with exponential backoff (2s
|
|
15
|
+
* 5. Sleeps with exponential backoff (2s to 10s, cap configurable).
|
|
16
16
|
* 6. If `BOOT_MAX_WAIT_SECONDS` is set and exceeded, throws a
|
|
17
17
|
* `BootMaxWaitExceededError` so the runner can exit with a distinct
|
|
18
18
|
* code. Default 0 = wait forever.
|
|
@@ -96,7 +96,7 @@ function resolveBackoff(
|
|
|
96
96
|
};
|
|
97
97
|
return {
|
|
98
98
|
initialMs: override?.initialMs ?? parsePositive(env.BOOT_INITIAL_BACKOFF_MS, 2000),
|
|
99
|
-
maxMs: override?.maxMs ?? parsePositive(env.BOOT_MAX_BACKOFF_MS,
|
|
99
|
+
maxMs: override?.maxMs ?? parsePositive(env.BOOT_MAX_BACKOFF_MS, 10000),
|
|
100
100
|
// 0 = wait forever — the runner can override with a finite ceiling per
|
|
101
101
|
// worker if monitoring wants a "config never arrived" signal.
|
|
102
102
|
maxWaitSeconds: override?.maxWaitSeconds ?? parsePositive(env.BOOT_MAX_WAIT_SECONDS, 0),
|
|
@@ -243,9 +243,11 @@ export function generateCompose(state: OnboardState): string {
|
|
|
243
243
|
lines.push(` - "${port}:3000"`);
|
|
244
244
|
lines.push("");
|
|
245
245
|
lines.push(" volumes:");
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
lines.push(
|
|
246
|
+
// Same targets as docker-compose.example.yml: the worker logs to /logs and
|
|
247
|
+
// keeps its clones and files under /workspace.
|
|
248
|
+
lines.push(" - swarm_logs:/logs");
|
|
249
|
+
lines.push(" - swarm_shared:/workspace/shared");
|
|
250
|
+
lines.push(` - swarm_${svc.sanitizedName}:/workspace/personal`);
|
|
249
251
|
if (state.provider === "bedrock" && state.awsProfile) {
|
|
250
252
|
lines.push(engine === "podman" ? AWS_PROFILE_VOLUME_SELINUX : AWS_PROFILE_VOLUME);
|
|
251
253
|
}
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
import {
|
|
22
|
-
type OpenAICompatibleProvider,
|
|
23
22
|
openAICompatibleKeyVars,
|
|
24
23
|
openAICompatibleProviders,
|
|
25
24
|
providerBaseUrl,
|
|
@@ -45,13 +44,36 @@ import type {
|
|
|
45
44
|
ProviderName,
|
|
46
45
|
ReasoningEffort,
|
|
47
46
|
} from "../types";
|
|
48
|
-
import { CLAUDE_CREDENTIALS_HINT } from "../utils/credentials";
|
|
49
47
|
import {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
CLAUDE_CREDENTIALS_HINT,
|
|
49
|
+
CREDENTIAL_POOL_VARS,
|
|
50
|
+
splitCredentialPool,
|
|
51
|
+
} from "../utils/credentials";
|
|
52
|
+
import { isEnvFlagEnabled } from "../utils/env-flag";
|
|
53
|
+
import {
|
|
54
|
+
checkAnthropicApiKey,
|
|
55
|
+
checkDeepSeekApiKey,
|
|
56
|
+
checkOpenAICompatibleProvider,
|
|
57
|
+
checkOpenAiApiKey,
|
|
58
|
+
checkOpenRouter,
|
|
59
|
+
checkXaiApiKey,
|
|
60
|
+
type LiveKeyResult,
|
|
61
|
+
type LiveValidationResult,
|
|
62
|
+
timedFetch,
|
|
63
|
+
} from "../utils/provider-key-probes";
|
|
53
64
|
import { scrubSecrets } from "../utils/secret-scrubber";
|
|
54
65
|
|
|
66
|
+
export {
|
|
67
|
+
checkAnthropicApiKey,
|
|
68
|
+
checkDeepSeekApiKey,
|
|
69
|
+
checkOpenAICompatibleProvider,
|
|
70
|
+
checkOpenAiApiKey,
|
|
71
|
+
checkOpenRouter,
|
|
72
|
+
checkXaiApiKey,
|
|
73
|
+
type LiveKeyResult,
|
|
74
|
+
type LiveValidationResult,
|
|
75
|
+
} from "../utils/provider-key-probes";
|
|
76
|
+
|
|
55
77
|
export type SupportedProvider =
|
|
56
78
|
| "claude"
|
|
57
79
|
| "claude-managed"
|
|
@@ -215,46 +237,8 @@ export async function checkProviderCredentials(
|
|
|
215
237
|
// Mirrors `checkProviderCredentials` but issues a real (cheapest possible)
|
|
216
238
|
// upstream call so the user can flip the `harness` milestone to `verified`.
|
|
217
239
|
//
|
|
218
|
-
//
|
|
219
|
-
//
|
|
220
|
-
// safe. All errors run through `scrubSecrets` so we never leak the user's
|
|
221
|
-
// own key shape back through the JSON response.
|
|
222
|
-
|
|
223
|
-
const LIVE_TEST_TIMEOUT_MS = 5_000;
|
|
224
|
-
|
|
225
|
-
export interface LiveValidationResult {
|
|
226
|
-
ok: boolean;
|
|
227
|
-
error?: string;
|
|
228
|
-
latency_ms: number;
|
|
229
|
-
/** No free live check exists for this credential (cloud routes): report presence only. */
|
|
230
|
-
skipped?: boolean;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
async function timedFetch(
|
|
234
|
-
url: string,
|
|
235
|
-
init: RequestInit,
|
|
236
|
-
): Promise<{ ok: boolean; status: number; bodyText: string; latency_ms: number }> {
|
|
237
|
-
const controller = new AbortController();
|
|
238
|
-
const timer = setTimeout(() => controller.abort(), LIVE_TEST_TIMEOUT_MS);
|
|
239
|
-
const start = Date.now();
|
|
240
|
-
try {
|
|
241
|
-
const res = await fetch(url, { ...init, signal: controller.signal });
|
|
242
|
-
let bodyText = "";
|
|
243
|
-
try {
|
|
244
|
-
bodyText = await res.text();
|
|
245
|
-
} catch {
|
|
246
|
-
// ignore body read errors — status alone is enough for ok/not-ok
|
|
247
|
-
}
|
|
248
|
-
return {
|
|
249
|
-
ok: res.ok,
|
|
250
|
-
status: res.status,
|
|
251
|
-
bodyText,
|
|
252
|
-
latency_ms: Date.now() - start,
|
|
253
|
-
};
|
|
254
|
-
} finally {
|
|
255
|
-
clearTimeout(timer);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
240
|
+
// Worker-only dispatch uses the shared probes in `provider-key-probes.ts`.
|
|
241
|
+
// The API imports those probes directly to avoid loading worker harnesses.
|
|
258
242
|
|
|
259
243
|
function asLiveError(err: unknown, latency_ms: number): LiveValidationResult {
|
|
260
244
|
const raw = err instanceof Error ? err.message : String(err);
|
|
@@ -265,6 +249,69 @@ function asLiveError(err: unknown, latency_ms: number): LiveValidationResult {
|
|
|
265
249
|
};
|
|
266
250
|
}
|
|
267
251
|
|
|
252
|
+
/**
|
|
253
|
+
* Run `check` on every member of a comma pool in parallel. The pool passes
|
|
254
|
+
* when one key works (the runner skips a rejected key); `warning` names the
|
|
255
|
+
* keys that failed, and the check fails only when every key fails.
|
|
256
|
+
*/
|
|
257
|
+
export async function checkEachKey(
|
|
258
|
+
keyType: string,
|
|
259
|
+
value: string,
|
|
260
|
+
check: (key: string) => Promise<LiveValidationResult>,
|
|
261
|
+
): Promise<LiveValidationResult> {
|
|
262
|
+
const members = splitCredentialPool(value);
|
|
263
|
+
if (members.length === 0) members.push(value.trim());
|
|
264
|
+
const results = await Promise.all(
|
|
265
|
+
members.map(async (member) => {
|
|
266
|
+
const startedAt = Date.now();
|
|
267
|
+
try {
|
|
268
|
+
return await check(member);
|
|
269
|
+
} catch (err) {
|
|
270
|
+
return asLiveError(err, Date.now() - startedAt);
|
|
271
|
+
}
|
|
272
|
+
}),
|
|
273
|
+
);
|
|
274
|
+
const latency_ms = Math.max(...results.map((result) => result.latency_ms));
|
|
275
|
+
if (results.every((result) => result.skipped)) return { ok: true, skipped: true, latency_ms };
|
|
276
|
+
const keys = results.map(
|
|
277
|
+
(result, index): LiveKeyResult => ({
|
|
278
|
+
keyType,
|
|
279
|
+
index,
|
|
280
|
+
suffix: members[index]!.slice(-5),
|
|
281
|
+
ok: result.ok,
|
|
282
|
+
...(result.error !== undefined ? { error: result.error } : {}),
|
|
283
|
+
...(result.httpStatus !== undefined ? { httpStatus: result.httpStatus } : {}),
|
|
284
|
+
latency_ms: result.latency_ms,
|
|
285
|
+
}),
|
|
286
|
+
);
|
|
287
|
+
const failed = keys.filter((key) => !key.ok);
|
|
288
|
+
if (failed.length === 0) return { ok: true, latency_ms, keys };
|
|
289
|
+
const failures = failed
|
|
290
|
+
.map(
|
|
291
|
+
(key) =>
|
|
292
|
+
`key ending ${key.suffix} (${key.httpStatus !== undefined ? `HTTP ${key.httpStatus}` : (key.error ?? "Unknown error")})`,
|
|
293
|
+
)
|
|
294
|
+
.join(", ");
|
|
295
|
+
if (failed.length < keys.length) {
|
|
296
|
+
return {
|
|
297
|
+
ok: true,
|
|
298
|
+
latency_ms,
|
|
299
|
+
keys,
|
|
300
|
+
warning: `${failed.length} of ${keys.length} keys failed: ${failures}.`,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
if (keys.length === 1) {
|
|
304
|
+
return {
|
|
305
|
+
ok: false,
|
|
306
|
+
error: failed[0]!.error,
|
|
307
|
+
httpStatus: failed[0]!.httpStatus,
|
|
308
|
+
latency_ms,
|
|
309
|
+
keys,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
return { ok: false, error: `All ${keys.length} keys failed: ${failures}.`, latency_ms, keys };
|
|
313
|
+
}
|
|
314
|
+
|
|
268
315
|
// ─── Per-endpoint live-call helpers ──────────────────────────────────────────
|
|
269
316
|
// Each helper accepts the auth material it needs and returns a normalized
|
|
270
317
|
// `LiveValidationResult`. The harness-level dispatcher below picks which
|
|
@@ -272,22 +319,10 @@ function asLiveError(err: unknown, latency_ms: number): LiveValidationResult {
|
|
|
272
319
|
// users no longer see "ANTHROPIC_API_KEY is not set" when their adapter is
|
|
273
320
|
// happily running off `CLAUDE_CODE_OAUTH_TOKEN`.
|
|
274
321
|
|
|
275
|
-
async function checkAnthropicApiKey(apiKey: string): Promise<LiveValidationResult> {
|
|
276
|
-
const r = await timedFetch("https://api.anthropic.com/v1/models", {
|
|
277
|
-
method: "GET",
|
|
278
|
-
headers: { "x-api-key": apiKey, "anthropic-version": "2023-06-01" },
|
|
279
|
-
});
|
|
280
|
-
if (r.ok) return { ok: true, latency_ms: r.latency_ms };
|
|
281
|
-
return {
|
|
282
|
-
ok: false,
|
|
283
|
-
error: scrubSecrets(`HTTP ${r.status}: ${r.bodyText.slice(0, 200)}`),
|
|
284
|
-
latency_ms: r.latency_ms,
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
|
|
288
322
|
/**
|
|
289
|
-
* OAuth credentials (
|
|
290
|
-
*
|
|
323
|
+
* Codex OAuth credentials (ChatGPT login) are treated as a presence check — we
|
|
324
|
+
* don't issue a live upstream call. (Claude subscription tokens get a real
|
|
325
|
+
* check through the route: see `claudeSubscriptionProvider`.)
|
|
291
326
|
*
|
|
292
327
|
* Why: OAuth flows include their own refresh logic (handled at adapter boot,
|
|
293
328
|
* not here), the OAuth-bearer-with-/v1/models contract isn't a stable public
|
|
@@ -299,70 +334,6 @@ function presenceCheckOk(): LiveValidationResult {
|
|
|
299
334
|
return { ok: true, latency_ms: 0 };
|
|
300
335
|
}
|
|
301
336
|
|
|
302
|
-
async function checkOpenAiApiKey(apiKey: string): Promise<LiveValidationResult> {
|
|
303
|
-
const r = await timedFetch("https://api.openai.com/v1/models", {
|
|
304
|
-
method: "GET",
|
|
305
|
-
headers: { Authorization: `Bearer ${apiKey}` },
|
|
306
|
-
});
|
|
307
|
-
if (r.ok) return { ok: true, latency_ms: r.latency_ms };
|
|
308
|
-
return {
|
|
309
|
-
ok: false,
|
|
310
|
-
error: scrubSecrets(`HTTP ${r.status}: ${r.bodyText.slice(0, 200)}`),
|
|
311
|
-
latency_ms: r.latency_ms,
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/** `GET /v1/models` is the call the Grok CLI makes first; it runs no inference. */
|
|
316
|
-
async function checkXaiApiKey(apiKey: string): Promise<LiveValidationResult> {
|
|
317
|
-
const r = await timedFetch("https://api.x.ai/v1/models", {
|
|
318
|
-
method: "GET",
|
|
319
|
-
headers: { Authorization: `Bearer ${apiKey}` },
|
|
320
|
-
});
|
|
321
|
-
if (r.ok) return { ok: true, latency_ms: r.latency_ms };
|
|
322
|
-
return {
|
|
323
|
-
ok: false,
|
|
324
|
-
error: scrubSecrets(`HTTP ${r.status}: ${r.bodyText.slice(0, 200)}`),
|
|
325
|
-
latency_ms: r.latency_ms,
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
async function checkOpenRouter(apiKey: string): Promise<LiveValidationResult> {
|
|
330
|
-
const url = `${getOpenRouterBaseUrl()}/models`;
|
|
331
|
-
const r = await timedFetch(url, {
|
|
332
|
-
method: "GET",
|
|
333
|
-
headers: { ...getOpenRouterAttributionHeaders(url), Authorization: `Bearer ${apiKey}` },
|
|
334
|
-
});
|
|
335
|
-
if (r.ok) return { ok: true, latency_ms: r.latency_ms };
|
|
336
|
-
return {
|
|
337
|
-
ok: false,
|
|
338
|
-
error: scrubSecrets(`HTTP ${r.status}: ${r.bodyText.slice(0, 200)}`),
|
|
339
|
-
latency_ms: r.latency_ms,
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* `GET <baseUrl>/models` with a Bearer key against an OpenAI-compatible
|
|
345
|
-
* registry provider (NaN, Helmcode). Lists models, runs no inference.
|
|
346
|
-
* `baseUrl` defaults to the registry entry; the probe passes the
|
|
347
|
-
* `<ID>_BASE_URL` override, and tests inject a fake server.
|
|
348
|
-
*/
|
|
349
|
-
export async function checkOpenAICompatibleProvider(
|
|
350
|
-
provider: OpenAICompatibleProvider,
|
|
351
|
-
apiKey: string,
|
|
352
|
-
baseUrl: string = provider.baseUrl,
|
|
353
|
-
): Promise<LiveValidationResult> {
|
|
354
|
-
const r = await timedFetch(`${baseUrl.replace(/\/+$/, "")}/models`, {
|
|
355
|
-
method: "GET",
|
|
356
|
-
headers: { Authorization: `Bearer ${apiKey}` },
|
|
357
|
-
});
|
|
358
|
-
if (r.ok) return { ok: true, latency_ms: r.latency_ms };
|
|
359
|
-
return {
|
|
360
|
-
ok: false,
|
|
361
|
-
error: scrubSecrets(`HTTP ${r.status}: ${r.bodyText.slice(0, 200)}`),
|
|
362
|
-
latency_ms: r.latency_ms,
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
|
|
366
337
|
/**
|
|
367
338
|
* Mirror of the disk check in `checkCodexCredentials` — true when the worker
|
|
368
339
|
* has a materialised `~/.codex/auth.json`, which is the canonical "logged in"
|
|
@@ -402,14 +373,15 @@ function parseCodexOAuthAccess(blob: string | undefined): string | null {
|
|
|
402
373
|
*
|
|
403
374
|
* | Harness | Accepted credentials (in resolution order) | Endpoint |
|
|
404
375
|
* |------------------|-------------------------------------------------------------------------|--------------------------------|
|
|
405
|
-
* | `claude` | default route: Foundry / Bedrock / Vertex → gateway (`ANTHROPIC_BASE_URL` + `ANTHROPIC_AUTH_TOKEN` or `ANTHROPIC_API_KEY`) → `CLAUDE_CODE_OAUTH_TOKEN` → `ANTHROPIC_API_KEY` | the route's own `/v1/models`; OAuth
|
|
376
|
+
* | `claude` | default route: Foundry / Bedrock / Vertex → gateway (`ANTHROPIC_BASE_URL` + `ANTHROPIC_AUTH_TOKEN` or `ANTHROPIC_API_KEY`) → `CLAUDE_CODE_OAUTH_TOKEN` → `ANTHROPIC_API_KEY` | the route's own `/v1/models`; OAuth: `/api/oauth/profile` (403 scope = ok); cloud routes skipped |
|
|
406
377
|
* | `claude-managed` | `ANTHROPIC_API_KEY` (managed agents always use API key + managed envs) | Anthropic `/v1/models` |
|
|
407
378
|
* | `codex` | `~/.codex/auth.json` (file) → `CODEX_OAUTH` (env OAuth) → `OPENAI_API_KEY` | OpenAI `/v1/models` (api-key path only) |
|
|
408
|
-
* | `opencode` | auth.json (file) → `OPENROUTER_API_KEY` → `ANTHROPIC_API_KEY` → `OPENAI_API_KEY` → each registry provider key (`NAN_API_KEY` or `HELMCODE_API_NAN`, `HELMCODE_API_KEY`) | file presence-only, otherwise provider's `/v1/models` |
|
|
379
|
+
* | `opencode` | auth.json (file) → `OPENROUTER_API_KEY` → `ANTHROPIC_API_KEY` → `OPENAI_API_KEY` → each registry provider key (`NAN_API_KEY` or `HELMCODE_API_NAN`, `HELMCODE_API_KEY`) | file presence-only, otherwise OpenRouter `/key` or the provider's `/v1/models` |
|
|
409
380
|
* | `pi` | auth.json (file) → provider API keys, including model-specific keys | file/model-specific key presence-only, otherwise provider's `/v1/models` |
|
|
410
381
|
* | `acp` | target-specific (the ACP target process owns its own auth) | presence-only (validated by the target process) |
|
|
411
382
|
* | `pi` (bedrock) | `MODEL_OVERRIDE=amazon-bedrock/*` → AWS SDK default credential chain | presence-only (real check is the worker-side Bedrock enumeration) |
|
|
412
383
|
* | `devin` | `DEVIN_API_KEY` (+ `DEVIN_API_BASE_URL` override) | `${baseUrl}/v3/self` |
|
|
384
|
+
* | `dsh` | `DEEPSEEK_API_KEY` → `OPENROUTER_API_KEY` | DeepSeek `/models`, or OpenRouter `/key` |
|
|
413
385
|
* | `amp` | `AMP_API_KEY` | `amp usage` (credit balance, no inference; 5s timeout) |
|
|
414
386
|
*
|
|
415
387
|
* Returns `{ok: true, latency_ms}` on 2xx, `{ok: false, error, latency_ms}`
|
|
@@ -429,8 +401,9 @@ export async function validateProviderCredentials(
|
|
|
429
401
|
case "claude": {
|
|
430
402
|
// The default route decides where the check goes: a gateway key is
|
|
431
403
|
// checked against the gateway only (scoped fetch), never against
|
|
432
|
-
// api.anthropic.com.
|
|
433
|
-
// free check and report
|
|
404
|
+
// api.anthropic.com. A subscription token is checked against
|
|
405
|
+
// `/api/oauth/profile`; cloud routes have no free check and report
|
|
406
|
+
// `configured` (no live test).
|
|
434
407
|
const route = deriveDefaultRoute("claude", env);
|
|
435
408
|
if (!route) {
|
|
436
409
|
return {
|
|
@@ -439,16 +412,40 @@ export async function validateProviderCredentials(
|
|
|
439
412
|
latency_ms: Date.now() - startedAt,
|
|
440
413
|
};
|
|
441
414
|
}
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
415
|
+
const checkRoute = async (
|
|
416
|
+
routeEnv: Record<string, string | undefined>,
|
|
417
|
+
): Promise<LiveValidationResult> => {
|
|
418
|
+
const start = Date.now();
|
|
419
|
+
const result = await validateRoute(route, routeEnv);
|
|
420
|
+
const latency_ms = Date.now() - start;
|
|
421
|
+
if (result.status === "verified") return { ok: true, latency_ms };
|
|
422
|
+
if (result.status === "configured") return { ok: true, skipped: true, latency_ms };
|
|
423
|
+
return {
|
|
424
|
+
ok: false,
|
|
425
|
+
error: scrubSecrets(result.reason),
|
|
426
|
+
httpStatus: result.httpStatus,
|
|
427
|
+
latency_ms,
|
|
428
|
+
};
|
|
429
|
+
};
|
|
430
|
+
const keyVar =
|
|
431
|
+
route.auth.kind === "subscription"
|
|
432
|
+
? "CLAUDE_CODE_OAUTH_TOKEN"
|
|
433
|
+
: "secretKey" in route.auth
|
|
434
|
+
? route.auth.secretKey
|
|
435
|
+
: undefined;
|
|
436
|
+
const value = keyVar ? env[keyVar] : undefined;
|
|
437
|
+
// Only pool vars are split: the runner never splits a gateway token.
|
|
438
|
+
if (keyVar && value && (CREDENTIAL_POOL_VARS as readonly string[]).includes(keyVar)) {
|
|
439
|
+
return checkEachKey(keyVar, value, (member) => checkRoute({ ...env, [keyVar]: member }));
|
|
440
|
+
}
|
|
441
|
+
return checkRoute(env);
|
|
447
442
|
}
|
|
448
443
|
case "claude-managed": {
|
|
449
444
|
// Managed agents always run with an API key — OAuth not supported on
|
|
450
445
|
// the managed-agents path today.
|
|
451
|
-
if (env.ANTHROPIC_API_KEY)
|
|
446
|
+
if (env.ANTHROPIC_API_KEY) {
|
|
447
|
+
return checkEachKey("ANTHROPIC_API_KEY", env.ANTHROPIC_API_KEY, checkAnthropicApiKey);
|
|
448
|
+
}
|
|
452
449
|
return {
|
|
453
450
|
ok: false,
|
|
454
451
|
error: "ANTHROPIC_API_KEY is not set.",
|
|
@@ -456,6 +453,22 @@ export async function validateProviderCredentials(
|
|
|
456
453
|
};
|
|
457
454
|
}
|
|
458
455
|
case "codex": {
|
|
456
|
+
// A gateway route is checked against the gateway only (scoped fetch
|
|
457
|
+
// to its own `/models`), never against api.openai.com, and wins over
|
|
458
|
+
// every OAuth / auth.json / OPENAI_API_KEY check below.
|
|
459
|
+
const gateway = deriveDefaultRoute("codex", env);
|
|
460
|
+
if (gateway) {
|
|
461
|
+
const result = await validateRoute(gateway, env);
|
|
462
|
+
const latency_ms = Date.now() - startedAt;
|
|
463
|
+
if (result.status === "verified") return { ok: true, latency_ms };
|
|
464
|
+
if (result.status === "configured") return { ok: true, skipped: true, latency_ms };
|
|
465
|
+
return {
|
|
466
|
+
ok: false,
|
|
467
|
+
error: scrubSecrets(result.reason),
|
|
468
|
+
httpStatus: result.httpStatus,
|
|
469
|
+
latency_ms,
|
|
470
|
+
};
|
|
471
|
+
}
|
|
459
472
|
// Resolution order matches `checkCodexCredentials`:
|
|
460
473
|
// 1) `~/.codex/auth.json` on disk (canonical state once `codex login`
|
|
461
474
|
// has run, or when the entrypoint pre-materialised it from
|
|
@@ -474,7 +487,9 @@ export async function validateProviderCredentials(
|
|
|
474
487
|
if (codexAuthFileExists(env)) return presenceCheckOk();
|
|
475
488
|
if (parseCodexOAuthAccess(env.CODEX_OAUTH)) return presenceCheckOk();
|
|
476
489
|
if (hasCodexOAuthPoolSlots(env)) return presenceCheckOk();
|
|
477
|
-
if (env.OPENAI_API_KEY)
|
|
490
|
+
if (env.OPENAI_API_KEY) {
|
|
491
|
+
return checkEachKey("OPENAI_API_KEY", env.OPENAI_API_KEY, checkOpenAiApiKey);
|
|
492
|
+
}
|
|
478
493
|
return {
|
|
479
494
|
ok: false,
|
|
480
495
|
error:
|
|
@@ -503,13 +518,21 @@ export async function validateProviderCredentials(
|
|
|
503
518
|
// registry providers in declaration order (NaN, canonical key or
|
|
504
519
|
// alias → Helmcode) at their `<ID>_BASE_URL`, so existing env
|
|
505
520
|
// combinations keep their probe.
|
|
506
|
-
if (env.OPENROUTER_API_KEY)
|
|
507
|
-
|
|
508
|
-
|
|
521
|
+
if (env.OPENROUTER_API_KEY) {
|
|
522
|
+
return checkEachKey("OPENROUTER_API_KEY", env.OPENROUTER_API_KEY, checkOpenRouter);
|
|
523
|
+
}
|
|
524
|
+
if (env.ANTHROPIC_API_KEY) {
|
|
525
|
+
return checkEachKey("ANTHROPIC_API_KEY", env.ANTHROPIC_API_KEY, checkAnthropicApiKey);
|
|
526
|
+
}
|
|
527
|
+
if (env.OPENAI_API_KEY) {
|
|
528
|
+
return checkEachKey("OPENAI_API_KEY", env.OPENAI_API_KEY, checkOpenAiApiKey);
|
|
529
|
+
}
|
|
509
530
|
for (const direct of openAICompatibleProviders()) {
|
|
510
531
|
const key = providerKeyFromEnv(direct, env);
|
|
511
532
|
if (key) {
|
|
512
|
-
return
|
|
533
|
+
return checkEachKey(direct.envVar, key, (member) =>
|
|
534
|
+
checkOpenAICompatibleProvider(direct, member, providerBaseUrl(direct, env)),
|
|
535
|
+
);
|
|
513
536
|
}
|
|
514
537
|
}
|
|
515
538
|
// Model-specific keys (e.g. GEMINI_API_KEY) can satisfy readiness
|
|
@@ -534,28 +557,36 @@ export async function validateProviderCredentials(
|
|
|
534
557
|
};
|
|
535
558
|
}
|
|
536
559
|
const baseUrl = env.DEVIN_API_BASE_URL ?? "https://api.devin.ai";
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
560
|
+
return checkEachKey("DEVIN_API_KEY", apiKey, async (member) => {
|
|
561
|
+
const r = await timedFetch(`${baseUrl.replace(/\/+$/, "")}/v3/self`, {
|
|
562
|
+
method: "GET",
|
|
563
|
+
headers: {
|
|
564
|
+
Authorization: `Bearer ${member}`,
|
|
565
|
+
"Content-Type": "application/json",
|
|
566
|
+
},
|
|
567
|
+
});
|
|
568
|
+
if (r.ok) return { ok: true, latency_ms: r.latency_ms };
|
|
569
|
+
return {
|
|
570
|
+
ok: false,
|
|
571
|
+
error: scrubSecrets(`HTTP ${r.status}: ${r.bodyText.slice(0, 200)}`),
|
|
572
|
+
httpStatus: r.status,
|
|
573
|
+
latency_ms: r.latency_ms,
|
|
574
|
+
};
|
|
543
575
|
});
|
|
544
|
-
|
|
576
|
+
}
|
|
577
|
+
case "dsh": {
|
|
578
|
+
// Same precedence as `checkDshCredentials`: the DeepSeek key, then OpenRouter.
|
|
579
|
+
const deepseekKey = env.DEEPSEEK_API_KEY?.trim();
|
|
580
|
+
if (deepseekKey) return checkEachKey("DEEPSEEK_API_KEY", deepseekKey, checkDeepSeekApiKey);
|
|
581
|
+
const openRouterKey = env.OPENROUTER_API_KEY?.trim();
|
|
582
|
+
if (openRouterKey)
|
|
583
|
+
return checkEachKey("OPENROUTER_API_KEY", openRouterKey, checkOpenRouter);
|
|
545
584
|
return {
|
|
546
585
|
ok: false,
|
|
547
|
-
error:
|
|
548
|
-
latency_ms:
|
|
586
|
+
error: "Set DEEPSEEK_API_KEY or OPENROUTER_API_KEY for dsh.",
|
|
587
|
+
latency_ms: Date.now() - startedAt,
|
|
549
588
|
};
|
|
550
589
|
}
|
|
551
|
-
case "dsh":
|
|
552
|
-
return checkDshCredentials(env).ready
|
|
553
|
-
? presenceCheckOk()
|
|
554
|
-
: {
|
|
555
|
-
ok: false,
|
|
556
|
-
error: "Set DEEPSEEK_API_KEY or OPENROUTER_API_KEY for dsh.",
|
|
557
|
-
latency_ms: Date.now() - startedAt,
|
|
558
|
-
};
|
|
559
590
|
case "amp": {
|
|
560
591
|
if (!checkAmpCredentials(env).ready) {
|
|
561
592
|
return {
|
|
@@ -565,14 +596,17 @@ export async function validateProviderCredentials(
|
|
|
565
596
|
};
|
|
566
597
|
}
|
|
567
598
|
// `amp usage` reads the credit balance and runs no inference.
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
599
|
+
return checkEachKey("AMP_API_KEY", env.AMP_API_KEY!, async (member) => {
|
|
600
|
+
const start = Date.now();
|
|
601
|
+
const live = await liveTestAmpCredentials({ ...env, AMP_API_KEY: member });
|
|
602
|
+
return live.ok
|
|
603
|
+
? { ok: true, latency_ms: Date.now() - start }
|
|
604
|
+
: {
|
|
605
|
+
ok: false,
|
|
606
|
+
error: live.error ?? "amp usage failed",
|
|
607
|
+
latency_ms: Date.now() - start,
|
|
608
|
+
};
|
|
609
|
+
});
|
|
576
610
|
}
|
|
577
611
|
case "cursor": {
|
|
578
612
|
const apiKey = env.CURSOR_API_KEY?.trim();
|
|
@@ -584,16 +618,21 @@ export async function validateProviderCredentials(
|
|
|
584
618
|
};
|
|
585
619
|
}
|
|
586
620
|
const { liveTestCursorKey } = await import("../providers/cursor-adapter");
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
621
|
+
return checkEachKey("CURSOR_API_KEY", apiKey, async (member) => {
|
|
622
|
+
const start = Date.now();
|
|
623
|
+
const result = await liveTestCursorKey(member);
|
|
624
|
+
return result.ok
|
|
625
|
+
? { ok: true, latency_ms: Date.now() - start }
|
|
626
|
+
: { ok: false, error: result.error, latency_ms: Date.now() - start };
|
|
627
|
+
});
|
|
591
628
|
}
|
|
592
629
|
case "grok": {
|
|
593
630
|
const apiKey = env.XAI_API_KEY?.trim();
|
|
594
|
-
if (apiKey) return
|
|
631
|
+
if (apiKey) return checkEachKey("XAI_API_KEY", apiKey, checkXaiApiKey);
|
|
595
632
|
const openRouterKey = env.OPENROUTER_API_KEY?.trim();
|
|
596
|
-
if (openRouterKey)
|
|
633
|
+
if (openRouterKey) {
|
|
634
|
+
return checkEachKey("OPENROUTER_API_KEY", openRouterKey, checkOpenRouter);
|
|
635
|
+
}
|
|
597
636
|
return {
|
|
598
637
|
ok: false,
|
|
599
638
|
error: "Set XAI_API_KEY or OPENROUTER_API_KEY for grok.",
|
|
@@ -626,7 +665,7 @@ export async function validateProviderCredentials(
|
|
|
626
665
|
* the dashboard surfaces "unreported".
|
|
627
666
|
*/
|
|
628
667
|
export function isCredCheckDisabled(env: NodeJS.ProcessEnv): boolean {
|
|
629
|
-
return
|
|
668
|
+
return isEnvFlagEnabled("CRED_CHECK_DISABLE", false, env);
|
|
630
669
|
}
|
|
631
670
|
|
|
632
671
|
/**
|
|
@@ -653,6 +692,8 @@ export async function buildCredStatusReport(
|
|
|
653
692
|
error: live.error ?? null,
|
|
654
693
|
latency_ms: live.latency_ms,
|
|
655
694
|
testedAt: Date.now(),
|
|
695
|
+
...(live.warning !== undefined ? { warning: live.warning } : {}),
|
|
696
|
+
...(live.keys !== undefined ? { keys: live.keys } : {}),
|
|
656
697
|
};
|
|
657
698
|
}
|
|
658
699
|
// Include the Bedrock enumeration block when the pi probe ran in Bedrock SDK
|