@desplega.ai/agent-swarm 2.5.4 → 2.6.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 +2 -2
- package/dist/{acp-adapter-ek38cwsv.js → acp-adapter-dgfakfpw.js} +9 -9
- package/dist/{actions-92mepq03.js → actions-fbqma8aa.js} +21 -21
- package/dist/{amp-adapter-9gaxg015.js → amp-adapter-en0jy6hs.js} +4 -4
- package/dist/{app-ctn8gsgx.js → app-k8a5cgpp.js} +3 -3
- package/dist/{assistant-zyr5kz47.js → assistant-a16433gr.js} +29 -25
- package/dist/{boot-reembed-dwzfttdr.js → boot-reembed-6c1sjfnr.js} +1 -1
- package/dist/{boot-reembed-w75maga2.js → boot-reembed-f4nn561n.js} +1 -1
- package/dist/{boot-scrub-sweep-e9kq43kf.js → boot-scrub-sweep-d4w9gajf.js} +4 -4
- package/dist/{boot-seeds-x28v3fhb.js → boot-seeds-9bmkspyw.js} +4 -4
- package/dist/{claude-adapter-f0afqnne.js → claude-adapter-nys1kjxa.js} +9 -9
- package/dist/{claude-managed-adapter-x81a7r65.js → claude-managed-adapter-55207973.js} +2 -2
- package/dist/{claude-managed-setup-bqzwjd9x.js → claude-managed-setup-3cm35gey.js} +10 -3
- package/dist/{claude-sdk-session-7b7whkaq.js → claude-sdk-session-nw5yd8cp.js} +11 -11
- package/dist/{cli-c61mbdds.js → cli-03gg9pd1.js} +4 -4
- package/dist/{cli-kxpfx2xr.js → cli-0z1xjzwa.js} +2 -2
- package/dist/cli-1tem6ns9.js +894 -0
- package/dist/{cli-jbnn4vxb.js → cli-24j1jeeb.js} +3 -3
- package/dist/{cli-pzatvw94.js → cli-2sjhpsns.js} +2 -2
- package/dist/{cli-dew6x255.js → cli-39aet655.js} +2 -2
- package/dist/{cli-g1cw7xtt.js → cli-39rrw8yp.js} +1 -1
- package/dist/{cli-x0f2v69c.js → cli-3aqd4jaa.js} +12 -7
- package/dist/{cli-gbfxp48k.js → cli-3fwqy71j.js} +15 -15
- package/dist/{cli-7p4jxgtt.js → cli-3gfvmzht.js} +6 -6
- package/dist/{cli-03vpq45j.js → cli-3ndmpxxv.js} +2 -2
- package/dist/{cli-asq5ydm4.js → cli-4hxrf0p4.js} +131 -50
- package/dist/{cli-mz9afxxf.js → cli-4rf0f5vg.js} +15 -3
- package/dist/{cli-k4tjsdx9.js → cli-5rj83bam.js} +272 -195
- package/dist/{cli-sbwsxvtd.js → cli-5sckpqcx.js} +2 -2
- package/dist/{cli-cz5nmdxt.js → cli-683y0b3a.js} +2 -2
- package/dist/{cli-ethgy7ha.js → cli-6y51ryva.js} +2 -2
- package/dist/{cli-n4c8e3s5.js → cli-708wdy32.js} +1 -1
- package/dist/{cli-pasyvjmm.js → cli-737pyqxr.js} +1 -1
- package/dist/{cli-gx4rm0qx.js → cli-7enjwya9.js} +1 -1
- package/dist/{cli-6x9ma5w5.js → cli-803188pb.js} +1 -1
- package/dist/{cli-hh5we352.js → cli-9abdgn5t.js} +7 -7
- package/dist/{cli-ff1tm9n9.js → cli-a2rr0fmg.js} +1 -1
- package/dist/{cli-c952c7ac.js → cli-a34pr3w1.js} +2 -2
- package/dist/{cli-s682zgqz.js → cli-abkm71vr.js} +1 -1
- package/dist/{cli-mmhj8vd7.js → cli-ag2yrnt5.js} +11 -2
- package/dist/{cli-bvpgr9ny.js → cli-aj8djyqt.js} +5 -5
- package/dist/{cli-gwhjmh8g.js → cli-art7c1zh.js} +5 -5
- package/dist/{cli-5bnsc0e1.js → cli-az25fn5f.js} +4 -4
- package/dist/{cli-9s05422w.js → cli-bqztfyve.js} +1 -1
- package/dist/{cli-bn0rewsy.js → cli-c081bj3b.js} +2 -2
- package/dist/{cli-1r5ad7j5.js → cli-c2habvsf.js} +12 -12
- package/dist/{cli-64mh9mf3.js → cli-ca6banwz.js} +69 -17
- package/dist/{cli-25n3mv5d.js → cli-cz37jqss.js} +5 -5
- package/dist/{cli-6n8axjv1.js → cli-d2dpf95n.js} +2 -2
- package/dist/{cli-gn5af0x5.js → cli-emhjcj2y.js} +11 -11
- package/dist/{cli-6a1xg1p7.js → cli-esakngh8.js} +2 -2
- package/dist/{cli-ha9rmwgq.js → cli-fb8m699c.js} +2 -2
- package/dist/{cli-4b2z6n0m.js → cli-ffvsj2qw.js} +17 -8
- package/dist/{cli-abs8ndtr.js → cli-g20jjwt4.js} +26 -26
- package/dist/{cli-jb30dzvk.js → cli-g8ka5j4j.js} +446 -164
- package/dist/{cli-19202kew.js → cli-gf95q3bm.js} +5 -5
- package/dist/{cli-eftzyh9y.js → cli-gnsb1zxa.js} +2 -2
- package/dist/{cli-nwyw3zhg.js → cli-gxee0bqa.js} +1 -1
- package/dist/{cli-e42vss7b.js → cli-hfeb68z4.js} +5 -5
- package/dist/{cli-g9vd9ece.js → cli-k21gx5ht.js} +14 -16
- package/dist/{cli-tsma7p8v.js → cli-kf4jree9.js} +2 -2
- package/dist/{cli-dabehxhp.js → cli-kt907da6.js} +1 -1
- package/dist/{cli-wx2nj19m.js → cli-m5a9m0ak.js} +6 -6
- package/dist/{cli-621ce7kw.js → cli-m70bajzr.js} +3 -3
- package/dist/{cli-9j129brq.js → cli-mtnj4fwa.js} +6 -6
- package/dist/{cli-xzfq4at3.js → cli-mvjz5an3.js} +15 -11
- package/dist/{cli-277c31dw.js → cli-n7asmce6.js} +225 -3
- package/dist/{cli-xtrz6bzs.js → cli-nn6vx2mq.js} +1 -1
- package/dist/{cli-2655k16q.js → cli-nq677csy.js} +1 -1
- package/dist/{cli-0shx9j93.js → cli-pfk92eqz.js} +2 -2
- package/dist/{cli-wghjkr0e.js → cli-sqma6vw1.js} +10 -7
- package/dist/{cli-nzfnt2gs.js → cli-tf1n0r0b.js} +1 -1
- package/dist/{cli-qqzmjxkf.js → cli-v49fkghp.js} +11 -6
- package/dist/{cli-x77gen2p.js → cli-w2656ka1.js} +7 -7
- package/dist/{cli-5kqnemen.js → cli-yvy48785.js} +1 -1
- package/dist/{cli-m4ecdxvz.js → cli-zqne1qcj.js} +7 -7
- package/dist/cli.js +17 -17
- package/dist/{codex-adapter-wch12q99.js → codex-adapter-d95q4qsn.js} +7 -7
- package/dist/{codex-login-rn7c5gxt.js → codex-login-8cg0vhf2.js} +1 -1
- package/dist/{codex-session-runner-41pfcr95.js → codex-session-runner-gdwd8h0c.js} +8 -8
- package/dist/{commands-9849dkj6.js → commands-jqmgk54b.js} +2 -2
- package/dist/{controller-ja0sd95p.js → controller-z3jq589f.js} +4 -4
- package/dist/{controllers-connect-xp05dj6x.js → controllers-connect-r2za39ef.js} +23 -23
- package/dist/{cursor-adapter-623qxqew.js → cursor-adapter-4mr4jgkw.js} +2 -2
- package/dist/{db-47vj40dy.js → db-ppv2tze7.js} +3 -3
- package/dist/{db-w759qmx0.js → db-w36wwb5g.js} +4 -2
- package/dist/{devin-adapter-4ycy4vrf.js → devin-adapter-byen2cxt.js} +1 -1
- package/dist/{dispatcher-7b8e4z2j.js → dispatcher-pv5h77vf.js} +20 -20
- package/dist/{docker-s84hjs91.js → docker-fdzyewpz.js} +2 -2
- package/dist/{dsh-adapter-2ezw3xea.js → dsh-adapter-d4pdpb3r.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-4sp4dvmc.js} +1 -1
- package/dist/{event-bus-3z78ep5n.js → event-bus-pghg8hqy.js} +1 -1
- package/dist/{execute-0xpz8950.js → execute-jev839y1.js} +1 -1
- package/dist/{external-channel-e2s028da.js → external-channel-y6nkzhnn.js} +2 -2
- package/dist/{grok-adapter-ys7gaqee.js → grok-adapter-t08b9ntm.js} +10 -10
- package/dist/handlers-eqsnmv95.js +41 -0
- package/dist/{heartbeat-w3mntghj.js → heartbeat-vn31xwdk.js} +23 -23
- package/dist/{hook-g7kvv6x9.js → hook-6d0zbvvt.js} +6 -6
- package/dist/{hook-3jjywnpb.js → hook-cpjs8q51.js} +8 -8
- package/dist/{http-tkp7d4kb.js → http-9c6medda.js} +856 -99
- package/dist/{index-xdaahthb.js → index-38t35h0n.js} +20 -20
- package/dist/{index-3n8m9ea2.js → index-g6k9tctj.js} +1 -1
- package/dist/{index-1ef1myq2.js → index-k0yt1tvq.js} +20 -20
- package/dist/{index-31ceyf42.js → index-pkma1yf8.js} +1 -1
- package/dist/{index-q7m26kvs.js → index-qhjf2htm.js} +13 -10
- package/dist/index-yqgbfgaj.js +43 -0
- package/dist/{keepalive-v4ta8wgg.js → keepalive-em0117bp.js} +4 -4
- package/dist/lead-23e7z59t.js +79 -0
- package/dist/{maintenance-84n1ex21.js → maintenance-kbme3qy9.js} +1 -1
- package/dist/{oauth-refresh-sweep-vjnnandw.js → oauth-refresh-sweep-xysj4yse.js} +3 -3
- package/dist/{onboard-sfmpe4np.js → onboard-p6q0vz97.js} +3 -3
- package/dist/{opencode-adapter-jmpst4q4.js → opencode-adapter-esv3k9yn.js} +7 -7
- package/dist/{otel-impl-v6cc0zve.js → otel-impl-54qhqh1z.js} +2 -2
- package/dist/{pi-mono-adapter-fk8fkcf6.js → pi-mono-adapter-mymbwhj0.js} +10 -10
- package/dist/{pricing-refresh-fsk1e1zc.js → pricing-refresh-yb75wv33.js} +3 -3
- package/dist/{rbac-roles-prmkdnkw.js → rbac-roles-1vp2mw4p.js} +2 -2
- package/dist/{rbac-roles-zxmxqa39.js → rbac-roles-dexhc9x8.js} +1 -1
- package/dist/render-v2-tjay5hhf.js +16 -0
- package/dist/{resume-frdtgfa0.js → resume-8ayf37g5.js} +9 -9
- package/dist/{runtime-bt5pkpm1.js → runtime-hqe5cqcd.js} +1 -1
- package/dist/{session-access-fpez4y5q.js → session-access-fdhp987s.js} +4 -4
- package/dist/session-source-backfill-qgtc86z1.js +110 -0
- package/dist/{setup-mq6m3m8q.js → setup-4rrmqcq3.js} +1 -1
- package/dist/{virtual-modules-mnfbh5mr.js → virtual-modules-52km00d6.js} +2 -2
- package/dist/{work-objects-kfahhjsq.js → work-objects-p28fgd70.js} +23 -23
- package/dist/worker-e3w7vye1.js +79 -0
- package/dist/{x-pbpezr0s.js → x-wxrzvzx8.js} +2 -2
- package/openapi.json +1274 -32
- package/package.json +1 -1
- package/src/assets/source-key.ts +95 -0
- package/src/be/db/api-keys.ts +80 -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 +183 -1
- package/src/be/db-retention.ts +61 -6
- package/src/be/db.ts +59 -114
- package/src/be/memory/providers/openai-embedding.ts +5 -1
- package/src/be/migrations/244_api_key_validation.sql +4 -0
- package/src/be/onboarding.ts +4 -0
- package/src/be/scripts/typecheck.ts +1 -1
- 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/provider-credentials.ts +210 -170
- 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/http/all-routes.ts +2 -0
- package/src/http/api-keys.ts +53 -0
- 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/index.ts +11 -0
- package/src/http/integrations.ts +5 -1
- package/src/http/kv.ts +8 -3
- package/src/http/onboarding.ts +6 -1
- package/src/http/pool-keys.ts +354 -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 +78 -9
- package/src/integrations/chat-sdk/inbound.ts +27 -0
- package/src/kv-reserved-namespaces.ts +19 -0
- 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/scheduler/scheduler.ts +3 -2
- package/src/scripts-runtime/swarm-sdk.ts +7 -1
- package/src/scripts-runtime/types/stdlib.d.ts +1 -1
- package/src/scripts-runtime/types/swarm-extension.d.ts +17 -8
- package/src/scripts-runtime/types/swarm-sdk.d.ts +1 -1
- 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-keys-validation.test.ts +170 -0
- 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/config-pool-keys.test.ts +533 -0
- package/src/tests/credential-check.test.ts +290 -2
- 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/extensions-pre-task.test.ts +53 -1
- 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/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/onboarding.test.ts +108 -1
- package/src/tests/openai-embedding-credentials.test.ts +35 -0
- package/src/tests/openrouter-attribution-call-sites.test.ts +2 -2
- package/src/tests/org-website.test.ts +36 -0
- 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/script-connections.test.ts +2 -0
- package/src/tests/script-runs-retention.test.ts +212 -0
- package/src/tests/sdk-allowlist.test.ts +39 -0
- 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/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/used-by-route.test.ts +11 -0
- package/src/tests/workflow-cascade-cancel.test.ts +172 -0
- package/src/tests/workflow-http-v2.test.ts +131 -0
- package/src/tests/workflow-step-order.test.ts +92 -0
- package/src/tests/workflow-sub-workflow.test.ts +30 -0
- package/src/tools/get-tasks.ts +8 -0
- package/src/tools/kv/kv-read-auth.ts +4 -2
- package/src/tools/task-action.ts +1 -1
- package/src/tools/workflows/get-workflow-run.ts +14 -4
- package/src/types.ts +24 -0
- package/src/utils/company-profile.ts +51 -1
- package/src/utils/credentials.ts +5 -2
- 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/executors/sub-workflow.ts +8 -1
- package/templates/ai-toolbox.manifest.json +2 -2
- package/templates/skills/researching/content.md +2 -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
|
@@ -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,35 @@ 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 {
|
|
53
|
+
checkAnthropicApiKey,
|
|
54
|
+
checkDeepSeekApiKey,
|
|
55
|
+
checkOpenAICompatibleProvider,
|
|
56
|
+
checkOpenAiApiKey,
|
|
57
|
+
checkOpenRouter,
|
|
58
|
+
checkXaiApiKey,
|
|
59
|
+
type LiveKeyResult,
|
|
60
|
+
type LiveValidationResult,
|
|
61
|
+
timedFetch,
|
|
62
|
+
} from "../utils/provider-key-probes";
|
|
53
63
|
import { scrubSecrets } from "../utils/secret-scrubber";
|
|
54
64
|
|
|
65
|
+
export {
|
|
66
|
+
checkAnthropicApiKey,
|
|
67
|
+
checkDeepSeekApiKey,
|
|
68
|
+
checkOpenAICompatibleProvider,
|
|
69
|
+
checkOpenAiApiKey,
|
|
70
|
+
checkOpenRouter,
|
|
71
|
+
checkXaiApiKey,
|
|
72
|
+
type LiveKeyResult,
|
|
73
|
+
type LiveValidationResult,
|
|
74
|
+
} from "../utils/provider-key-probes";
|
|
75
|
+
|
|
55
76
|
export type SupportedProvider =
|
|
56
77
|
| "claude"
|
|
57
78
|
| "claude-managed"
|
|
@@ -215,46 +236,8 @@ export async function checkProviderCredentials(
|
|
|
215
236
|
// Mirrors `checkProviderCredentials` but issues a real (cheapest possible)
|
|
216
237
|
// upstream call so the user can flip the `harness` milestone to `verified`.
|
|
217
238
|
//
|
|
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
|
-
}
|
|
239
|
+
// Worker-only dispatch uses the shared probes in `provider-key-probes.ts`.
|
|
240
|
+
// The API imports those probes directly to avoid loading worker harnesses.
|
|
258
241
|
|
|
259
242
|
function asLiveError(err: unknown, latency_ms: number): LiveValidationResult {
|
|
260
243
|
const raw = err instanceof Error ? err.message : String(err);
|
|
@@ -265,6 +248,69 @@ function asLiveError(err: unknown, latency_ms: number): LiveValidationResult {
|
|
|
265
248
|
};
|
|
266
249
|
}
|
|
267
250
|
|
|
251
|
+
/**
|
|
252
|
+
* Run `check` on every member of a comma pool in parallel. The pool passes
|
|
253
|
+
* when one key works (the runner skips a rejected key); `warning` names the
|
|
254
|
+
* keys that failed, and the check fails only when every key fails.
|
|
255
|
+
*/
|
|
256
|
+
export async function checkEachKey(
|
|
257
|
+
keyType: string,
|
|
258
|
+
value: string,
|
|
259
|
+
check: (key: string) => Promise<LiveValidationResult>,
|
|
260
|
+
): Promise<LiveValidationResult> {
|
|
261
|
+
const members = splitCredentialPool(value);
|
|
262
|
+
if (members.length === 0) members.push(value.trim());
|
|
263
|
+
const results = await Promise.all(
|
|
264
|
+
members.map(async (member) => {
|
|
265
|
+
const startedAt = Date.now();
|
|
266
|
+
try {
|
|
267
|
+
return await check(member);
|
|
268
|
+
} catch (err) {
|
|
269
|
+
return asLiveError(err, Date.now() - startedAt);
|
|
270
|
+
}
|
|
271
|
+
}),
|
|
272
|
+
);
|
|
273
|
+
const latency_ms = Math.max(...results.map((result) => result.latency_ms));
|
|
274
|
+
if (results.every((result) => result.skipped)) return { ok: true, skipped: true, latency_ms };
|
|
275
|
+
const keys = results.map(
|
|
276
|
+
(result, index): LiveKeyResult => ({
|
|
277
|
+
keyType,
|
|
278
|
+
index,
|
|
279
|
+
suffix: members[index]!.slice(-5),
|
|
280
|
+
ok: result.ok,
|
|
281
|
+
...(result.error !== undefined ? { error: result.error } : {}),
|
|
282
|
+
...(result.httpStatus !== undefined ? { httpStatus: result.httpStatus } : {}),
|
|
283
|
+
latency_ms: result.latency_ms,
|
|
284
|
+
}),
|
|
285
|
+
);
|
|
286
|
+
const failed = keys.filter((key) => !key.ok);
|
|
287
|
+
if (failed.length === 0) return { ok: true, latency_ms, keys };
|
|
288
|
+
const failures = failed
|
|
289
|
+
.map(
|
|
290
|
+
(key) =>
|
|
291
|
+
`key ending ${key.suffix} (${key.httpStatus !== undefined ? `HTTP ${key.httpStatus}` : (key.error ?? "Unknown error")})`,
|
|
292
|
+
)
|
|
293
|
+
.join(", ");
|
|
294
|
+
if (failed.length < keys.length) {
|
|
295
|
+
return {
|
|
296
|
+
ok: true,
|
|
297
|
+
latency_ms,
|
|
298
|
+
keys,
|
|
299
|
+
warning: `${failed.length} of ${keys.length} keys failed: ${failures}.`,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
if (keys.length === 1) {
|
|
303
|
+
return {
|
|
304
|
+
ok: false,
|
|
305
|
+
error: failed[0]!.error,
|
|
306
|
+
httpStatus: failed[0]!.httpStatus,
|
|
307
|
+
latency_ms,
|
|
308
|
+
keys,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
return { ok: false, error: `All ${keys.length} keys failed: ${failures}.`, latency_ms, keys };
|
|
312
|
+
}
|
|
313
|
+
|
|
268
314
|
// ─── Per-endpoint live-call helpers ──────────────────────────────────────────
|
|
269
315
|
// Each helper accepts the auth material it needs and returns a normalized
|
|
270
316
|
// `LiveValidationResult`. The harness-level dispatcher below picks which
|
|
@@ -272,22 +318,10 @@ function asLiveError(err: unknown, latency_ms: number): LiveValidationResult {
|
|
|
272
318
|
// users no longer see "ANTHROPIC_API_KEY is not set" when their adapter is
|
|
273
319
|
// happily running off `CLAUDE_CODE_OAUTH_TOKEN`.
|
|
274
320
|
|
|
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
321
|
/**
|
|
289
|
-
* OAuth credentials (
|
|
290
|
-
*
|
|
322
|
+
* Codex OAuth credentials (ChatGPT login) are treated as a presence check — we
|
|
323
|
+
* don't issue a live upstream call. (Claude subscription tokens get a real
|
|
324
|
+
* check through the route: see `claudeSubscriptionProvider`.)
|
|
291
325
|
*
|
|
292
326
|
* Why: OAuth flows include their own refresh logic (handled at adapter boot,
|
|
293
327
|
* not here), the OAuth-bearer-with-/v1/models contract isn't a stable public
|
|
@@ -299,70 +333,6 @@ function presenceCheckOk(): LiveValidationResult {
|
|
|
299
333
|
return { ok: true, latency_ms: 0 };
|
|
300
334
|
}
|
|
301
335
|
|
|
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
336
|
/**
|
|
367
337
|
* Mirror of the disk check in `checkCodexCredentials` — true when the worker
|
|
368
338
|
* has a materialised `~/.codex/auth.json`, which is the canonical "logged in"
|
|
@@ -402,14 +372,15 @@ function parseCodexOAuthAccess(blob: string | undefined): string | null {
|
|
|
402
372
|
*
|
|
403
373
|
* | Harness | Accepted credentials (in resolution order) | Endpoint |
|
|
404
374
|
* |------------------|-------------------------------------------------------------------------|--------------------------------|
|
|
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
|
|
375
|
+
* | `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
376
|
* | `claude-managed` | `ANTHROPIC_API_KEY` (managed agents always use API key + managed envs) | Anthropic `/v1/models` |
|
|
407
377
|
* | `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` |
|
|
378
|
+
* | `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
379
|
* | `pi` | auth.json (file) → provider API keys, including model-specific keys | file/model-specific key presence-only, otherwise provider's `/v1/models` |
|
|
410
380
|
* | `acp` | target-specific (the ACP target process owns its own auth) | presence-only (validated by the target process) |
|
|
411
381
|
* | `pi` (bedrock) | `MODEL_OVERRIDE=amazon-bedrock/*` → AWS SDK default credential chain | presence-only (real check is the worker-side Bedrock enumeration) |
|
|
412
382
|
* | `devin` | `DEVIN_API_KEY` (+ `DEVIN_API_BASE_URL` override) | `${baseUrl}/v3/self` |
|
|
383
|
+
* | `dsh` | `DEEPSEEK_API_KEY` → `OPENROUTER_API_KEY` | DeepSeek `/models`, or OpenRouter `/key` |
|
|
413
384
|
* | `amp` | `AMP_API_KEY` | `amp usage` (credit balance, no inference; 5s timeout) |
|
|
414
385
|
*
|
|
415
386
|
* Returns `{ok: true, latency_ms}` on 2xx, `{ok: false, error, latency_ms}`
|
|
@@ -429,8 +400,9 @@ export async function validateProviderCredentials(
|
|
|
429
400
|
case "claude": {
|
|
430
401
|
// The default route decides where the check goes: a gateway key is
|
|
431
402
|
// checked against the gateway only (scoped fetch), never against
|
|
432
|
-
// api.anthropic.com.
|
|
433
|
-
// free check and report
|
|
403
|
+
// api.anthropic.com. A subscription token is checked against
|
|
404
|
+
// `/api/oauth/profile`; cloud routes have no free check and report
|
|
405
|
+
// `configured` (no live test).
|
|
434
406
|
const route = deriveDefaultRoute("claude", env);
|
|
435
407
|
if (!route) {
|
|
436
408
|
return {
|
|
@@ -439,16 +411,40 @@ export async function validateProviderCredentials(
|
|
|
439
411
|
latency_ms: Date.now() - startedAt,
|
|
440
412
|
};
|
|
441
413
|
}
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
414
|
+
const checkRoute = async (
|
|
415
|
+
routeEnv: Record<string, string | undefined>,
|
|
416
|
+
): Promise<LiveValidationResult> => {
|
|
417
|
+
const start = Date.now();
|
|
418
|
+
const result = await validateRoute(route, routeEnv);
|
|
419
|
+
const latency_ms = Date.now() - start;
|
|
420
|
+
if (result.status === "verified") return { ok: true, latency_ms };
|
|
421
|
+
if (result.status === "configured") return { ok: true, skipped: true, latency_ms };
|
|
422
|
+
return {
|
|
423
|
+
ok: false,
|
|
424
|
+
error: scrubSecrets(result.reason),
|
|
425
|
+
httpStatus: result.httpStatus,
|
|
426
|
+
latency_ms,
|
|
427
|
+
};
|
|
428
|
+
};
|
|
429
|
+
const keyVar =
|
|
430
|
+
route.auth.kind === "subscription"
|
|
431
|
+
? "CLAUDE_CODE_OAUTH_TOKEN"
|
|
432
|
+
: "secretKey" in route.auth
|
|
433
|
+
? route.auth.secretKey
|
|
434
|
+
: undefined;
|
|
435
|
+
const value = keyVar ? env[keyVar] : undefined;
|
|
436
|
+
// Only pool vars are split: the runner never splits a gateway token.
|
|
437
|
+
if (keyVar && value && (CREDENTIAL_POOL_VARS as readonly string[]).includes(keyVar)) {
|
|
438
|
+
return checkEachKey(keyVar, value, (member) => checkRoute({ ...env, [keyVar]: member }));
|
|
439
|
+
}
|
|
440
|
+
return checkRoute(env);
|
|
447
441
|
}
|
|
448
442
|
case "claude-managed": {
|
|
449
443
|
// Managed agents always run with an API key — OAuth not supported on
|
|
450
444
|
// the managed-agents path today.
|
|
451
|
-
if (env.ANTHROPIC_API_KEY)
|
|
445
|
+
if (env.ANTHROPIC_API_KEY) {
|
|
446
|
+
return checkEachKey("ANTHROPIC_API_KEY", env.ANTHROPIC_API_KEY, checkAnthropicApiKey);
|
|
447
|
+
}
|
|
452
448
|
return {
|
|
453
449
|
ok: false,
|
|
454
450
|
error: "ANTHROPIC_API_KEY is not set.",
|
|
@@ -456,6 +452,22 @@ export async function validateProviderCredentials(
|
|
|
456
452
|
};
|
|
457
453
|
}
|
|
458
454
|
case "codex": {
|
|
455
|
+
// A gateway route is checked against the gateway only (scoped fetch
|
|
456
|
+
// to its own `/models`), never against api.openai.com, and wins over
|
|
457
|
+
// every OAuth / auth.json / OPENAI_API_KEY check below.
|
|
458
|
+
const gateway = deriveDefaultRoute("codex", env);
|
|
459
|
+
if (gateway) {
|
|
460
|
+
const result = await validateRoute(gateway, env);
|
|
461
|
+
const latency_ms = Date.now() - startedAt;
|
|
462
|
+
if (result.status === "verified") return { ok: true, latency_ms };
|
|
463
|
+
if (result.status === "configured") return { ok: true, skipped: true, latency_ms };
|
|
464
|
+
return {
|
|
465
|
+
ok: false,
|
|
466
|
+
error: scrubSecrets(result.reason),
|
|
467
|
+
httpStatus: result.httpStatus,
|
|
468
|
+
latency_ms,
|
|
469
|
+
};
|
|
470
|
+
}
|
|
459
471
|
// Resolution order matches `checkCodexCredentials`:
|
|
460
472
|
// 1) `~/.codex/auth.json` on disk (canonical state once `codex login`
|
|
461
473
|
// has run, or when the entrypoint pre-materialised it from
|
|
@@ -474,7 +486,9 @@ export async function validateProviderCredentials(
|
|
|
474
486
|
if (codexAuthFileExists(env)) return presenceCheckOk();
|
|
475
487
|
if (parseCodexOAuthAccess(env.CODEX_OAUTH)) return presenceCheckOk();
|
|
476
488
|
if (hasCodexOAuthPoolSlots(env)) return presenceCheckOk();
|
|
477
|
-
if (env.OPENAI_API_KEY)
|
|
489
|
+
if (env.OPENAI_API_KEY) {
|
|
490
|
+
return checkEachKey("OPENAI_API_KEY", env.OPENAI_API_KEY, checkOpenAiApiKey);
|
|
491
|
+
}
|
|
478
492
|
return {
|
|
479
493
|
ok: false,
|
|
480
494
|
error:
|
|
@@ -503,13 +517,21 @@ export async function validateProviderCredentials(
|
|
|
503
517
|
// registry providers in declaration order (NaN, canonical key or
|
|
504
518
|
// alias → Helmcode) at their `<ID>_BASE_URL`, so existing env
|
|
505
519
|
// combinations keep their probe.
|
|
506
|
-
if (env.OPENROUTER_API_KEY)
|
|
507
|
-
|
|
508
|
-
|
|
520
|
+
if (env.OPENROUTER_API_KEY) {
|
|
521
|
+
return checkEachKey("OPENROUTER_API_KEY", env.OPENROUTER_API_KEY, checkOpenRouter);
|
|
522
|
+
}
|
|
523
|
+
if (env.ANTHROPIC_API_KEY) {
|
|
524
|
+
return checkEachKey("ANTHROPIC_API_KEY", env.ANTHROPIC_API_KEY, checkAnthropicApiKey);
|
|
525
|
+
}
|
|
526
|
+
if (env.OPENAI_API_KEY) {
|
|
527
|
+
return checkEachKey("OPENAI_API_KEY", env.OPENAI_API_KEY, checkOpenAiApiKey);
|
|
528
|
+
}
|
|
509
529
|
for (const direct of openAICompatibleProviders()) {
|
|
510
530
|
const key = providerKeyFromEnv(direct, env);
|
|
511
531
|
if (key) {
|
|
512
|
-
return
|
|
532
|
+
return checkEachKey(direct.envVar, key, (member) =>
|
|
533
|
+
checkOpenAICompatibleProvider(direct, member, providerBaseUrl(direct, env)),
|
|
534
|
+
);
|
|
513
535
|
}
|
|
514
536
|
}
|
|
515
537
|
// Model-specific keys (e.g. GEMINI_API_KEY) can satisfy readiness
|
|
@@ -534,28 +556,36 @@ export async function validateProviderCredentials(
|
|
|
534
556
|
};
|
|
535
557
|
}
|
|
536
558
|
const baseUrl = env.DEVIN_API_BASE_URL ?? "https://api.devin.ai";
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
559
|
+
return checkEachKey("DEVIN_API_KEY", apiKey, async (member) => {
|
|
560
|
+
const r = await timedFetch(`${baseUrl.replace(/\/+$/, "")}/v3/self`, {
|
|
561
|
+
method: "GET",
|
|
562
|
+
headers: {
|
|
563
|
+
Authorization: `Bearer ${member}`,
|
|
564
|
+
"Content-Type": "application/json",
|
|
565
|
+
},
|
|
566
|
+
});
|
|
567
|
+
if (r.ok) return { ok: true, latency_ms: r.latency_ms };
|
|
568
|
+
return {
|
|
569
|
+
ok: false,
|
|
570
|
+
error: scrubSecrets(`HTTP ${r.status}: ${r.bodyText.slice(0, 200)}`),
|
|
571
|
+
httpStatus: r.status,
|
|
572
|
+
latency_ms: r.latency_ms,
|
|
573
|
+
};
|
|
543
574
|
});
|
|
544
|
-
|
|
575
|
+
}
|
|
576
|
+
case "dsh": {
|
|
577
|
+
// Same precedence as `checkDshCredentials`: the DeepSeek key, then OpenRouter.
|
|
578
|
+
const deepseekKey = env.DEEPSEEK_API_KEY?.trim();
|
|
579
|
+
if (deepseekKey) return checkEachKey("DEEPSEEK_API_KEY", deepseekKey, checkDeepSeekApiKey);
|
|
580
|
+
const openRouterKey = env.OPENROUTER_API_KEY?.trim();
|
|
581
|
+
if (openRouterKey)
|
|
582
|
+
return checkEachKey("OPENROUTER_API_KEY", openRouterKey, checkOpenRouter);
|
|
545
583
|
return {
|
|
546
584
|
ok: false,
|
|
547
|
-
error:
|
|
548
|
-
latency_ms:
|
|
585
|
+
error: "Set DEEPSEEK_API_KEY or OPENROUTER_API_KEY for dsh.",
|
|
586
|
+
latency_ms: Date.now() - startedAt,
|
|
549
587
|
};
|
|
550
588
|
}
|
|
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
589
|
case "amp": {
|
|
560
590
|
if (!checkAmpCredentials(env).ready) {
|
|
561
591
|
return {
|
|
@@ -565,14 +595,17 @@ export async function validateProviderCredentials(
|
|
|
565
595
|
};
|
|
566
596
|
}
|
|
567
597
|
// `amp usage` reads the credit balance and runs no inference.
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
598
|
+
return checkEachKey("AMP_API_KEY", env.AMP_API_KEY!, async (member) => {
|
|
599
|
+
const start = Date.now();
|
|
600
|
+
const live = await liveTestAmpCredentials({ ...env, AMP_API_KEY: member });
|
|
601
|
+
return live.ok
|
|
602
|
+
? { ok: true, latency_ms: Date.now() - start }
|
|
603
|
+
: {
|
|
604
|
+
ok: false,
|
|
605
|
+
error: live.error ?? "amp usage failed",
|
|
606
|
+
latency_ms: Date.now() - start,
|
|
607
|
+
};
|
|
608
|
+
});
|
|
576
609
|
}
|
|
577
610
|
case "cursor": {
|
|
578
611
|
const apiKey = env.CURSOR_API_KEY?.trim();
|
|
@@ -584,16 +617,21 @@ export async function validateProviderCredentials(
|
|
|
584
617
|
};
|
|
585
618
|
}
|
|
586
619
|
const { liveTestCursorKey } = await import("../providers/cursor-adapter");
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
620
|
+
return checkEachKey("CURSOR_API_KEY", apiKey, async (member) => {
|
|
621
|
+
const start = Date.now();
|
|
622
|
+
const result = await liveTestCursorKey(member);
|
|
623
|
+
return result.ok
|
|
624
|
+
? { ok: true, latency_ms: Date.now() - start }
|
|
625
|
+
: { ok: false, error: result.error, latency_ms: Date.now() - start };
|
|
626
|
+
});
|
|
591
627
|
}
|
|
592
628
|
case "grok": {
|
|
593
629
|
const apiKey = env.XAI_API_KEY?.trim();
|
|
594
|
-
if (apiKey) return
|
|
630
|
+
if (apiKey) return checkEachKey("XAI_API_KEY", apiKey, checkXaiApiKey);
|
|
595
631
|
const openRouterKey = env.OPENROUTER_API_KEY?.trim();
|
|
596
|
-
if (openRouterKey)
|
|
632
|
+
if (openRouterKey) {
|
|
633
|
+
return checkEachKey("OPENROUTER_API_KEY", openRouterKey, checkOpenRouter);
|
|
634
|
+
}
|
|
597
635
|
return {
|
|
598
636
|
ok: false,
|
|
599
637
|
error: "Set XAI_API_KEY or OPENROUTER_API_KEY for grok.",
|
|
@@ -653,6 +691,8 @@ export async function buildCredStatusReport(
|
|
|
653
691
|
error: live.error ?? null,
|
|
654
692
|
latency_ms: live.latency_ms,
|
|
655
693
|
testedAt: Date.now(),
|
|
694
|
+
...(live.warning !== undefined ? { warning: live.warning } : {}),
|
|
695
|
+
...(live.keys !== undefined ? { keys: live.keys } : {}),
|
|
656
696
|
};
|
|
657
697
|
}
|
|
658
698
|
// Include the Bedrock enumeration block when the pi probe ran in Bedrock SDK
|
package/src/commands/runner.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, statSync } from "node:fs";
|
|
2
2
|
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import { deriveDefaultRoute } from "@desplega/model-routing";
|
|
3
4
|
import type { TemplateResponse } from "../../templates/schema.ts";
|
|
4
5
|
import {
|
|
5
6
|
type Attributes,
|
|
@@ -118,7 +119,13 @@ import {
|
|
|
118
119
|
prependContextPreamble,
|
|
119
120
|
} from "./context-preamble.ts";
|
|
120
121
|
import { reportCredentialOutcomeThenFinish } from "./credential-outcome-report.ts";
|
|
121
|
-
import {
|
|
122
|
+
import {
|
|
123
|
+
type CredentialRefreshState,
|
|
124
|
+
credentialFingerprint,
|
|
125
|
+
recheckableFailures,
|
|
126
|
+
refreshCredentialStatus,
|
|
127
|
+
reportKeyValidation,
|
|
128
|
+
} from "./credential-refresh.ts";
|
|
122
129
|
import {
|
|
123
130
|
awaitCredentials,
|
|
124
131
|
BootMaxWaitExceededError,
|
|
@@ -831,6 +838,8 @@ export async function exitAfterTasksIfFinished(
|
|
|
831
838
|
*/
|
|
832
839
|
export interface ResolvedEnvResult {
|
|
833
840
|
env: Record<string, string | undefined>;
|
|
841
|
+
/** Full credential pools for live validation. Keep this environment in process only. */
|
|
842
|
+
credentialEnv: Record<string, string | undefined>;
|
|
834
843
|
credentialSelections: CredentialSelection[];
|
|
835
844
|
/** Raw config-row value, kept separate from the merged environment so an
|
|
836
845
|
* actual worker environment variable retains precedence. */
|
|
@@ -1049,6 +1058,7 @@ export async function fetchResolvedEnv(
|
|
|
1049
1058
|
});
|
|
1050
1059
|
const effectiveModel = modelSelection.model || (env.MODEL_OVERRIDE as string | undefined) || "";
|
|
1051
1060
|
|
|
1061
|
+
const credentialEnv = { ...env };
|
|
1052
1062
|
const credentialSelections = await resolveCredentialPools(env, {
|
|
1053
1063
|
apiUrl,
|
|
1054
1064
|
apiKey,
|
|
@@ -1065,7 +1075,7 @@ export async function fetchResolvedEnv(
|
|
|
1065
1075
|
enforceModelCapacity: sessionContext?.enforceModelCapacity,
|
|
1066
1076
|
});
|
|
1067
1077
|
|
|
1068
|
-
return { env, credentialSelections, resolvedProvider, scriptsOnlyConfigValue };
|
|
1078
|
+
return { env, credentialEnv, credentialSelections, resolvedProvider, scriptsOnlyConfigValue };
|
|
1069
1079
|
}
|
|
1070
1080
|
|
|
1071
1081
|
/**
|
|
@@ -4319,7 +4329,9 @@ async function spawnProviderProcess(
|
|
|
4319
4329
|
// config-store slot — gates whether `codexSlot` is set below (see
|
|
4320
4330
|
// `CodexOAuthCredentialInfo`).
|
|
4321
4331
|
let oauthIsPoolBacked = false;
|
|
4322
|
-
|
|
4332
|
+
// A gateway route uses no OAuth: no pool slot is picked, so no auth.json is
|
|
4333
|
+
// materialised for a session whose traffic goes to the gateway.
|
|
4334
|
+
if (adapter.name === "codex" && !deriveDefaultRoute("codex", freshEnv)) {
|
|
4323
4335
|
const oauthInfo = await resolveCodexOAuthCredentialInfo(opts.apiUrl, opts.apiKey);
|
|
4324
4336
|
oauthSelection = oauthInfo?.selection;
|
|
4325
4337
|
oauthIsPoolBacked = oauthInfo?.isPoolBacked ?? false;
|
|
@@ -6019,7 +6031,10 @@ export async function runAgent(config: RunnerConfig, opts: RunnerOptions) {
|
|
|
6019
6031
|
ready: null,
|
|
6020
6032
|
lastRefreshAt: 0,
|
|
6021
6033
|
inFlight: false,
|
|
6034
|
+
fingerprint: null,
|
|
6035
|
+
rejectedKeys: 0,
|
|
6022
6036
|
};
|
|
6037
|
+
let latestCredentialEnv: Record<string, string | undefined> | undefined;
|
|
6023
6038
|
|
|
6024
6039
|
// Throttle for live HARNESS_PROVIDER reconciliation. Each reconciliation
|
|
6025
6040
|
// calls `fetchResolvedEnv` which also re-resolves credential pools — we
|
|
@@ -6413,6 +6428,10 @@ export async function runAgent(config: RunnerConfig, opts: RunnerOptions) {
|
|
|
6413
6428
|
credentialRefreshState.harnessProvider = state.harnessProvider;
|
|
6414
6429
|
credentialRefreshState.ready = bootCredSnapshot?.ready ?? true;
|
|
6415
6430
|
credentialRefreshState.lastRefreshAt = Date.now();
|
|
6431
|
+
credentialRefreshState.fingerprint = credentialFingerprint(process.env);
|
|
6432
|
+
credentialRefreshState.rejectedKeys = recheckableFailures(bootCredSnapshot?.liveTest);
|
|
6433
|
+
const keys = bootCredSnapshot?.liveTest?.keys;
|
|
6434
|
+
if (keys?.length) await reportKeyValidation(apiConfig, keys);
|
|
6416
6435
|
} catch (error) {
|
|
6417
6436
|
console.error(`[${role}] Failed to report credential readiness after recovery: ${error}`);
|
|
6418
6437
|
process.exit(1);
|
|
@@ -7015,9 +7034,11 @@ export async function runAgent(config: RunnerConfig, opts: RunnerOptions) {
|
|
|
7015
7034
|
try {
|
|
7016
7035
|
const {
|
|
7017
7036
|
env: freshEnv,
|
|
7037
|
+
credentialEnv,
|
|
7018
7038
|
resolvedProvider,
|
|
7019
7039
|
scriptsOnlyConfigValue,
|
|
7020
7040
|
} = await fetchResolvedEnv(apiUrl, apiKey, agentId);
|
|
7041
|
+
latestCredentialEnv = credentialEnv;
|
|
7021
7042
|
const nextScriptsOnly = resolveScriptsOnlyMode({
|
|
7022
7043
|
env: process.env.SCRIPTS_ONLY_MCP,
|
|
7023
7044
|
configValue: scriptsOnlyConfigValue,
|
|
@@ -7049,7 +7070,7 @@ export async function runAgent(config: RunnerConfig, opts: RunnerOptions) {
|
|
|
7049
7070
|
apiConfig,
|
|
7050
7071
|
credentialRefreshState,
|
|
7051
7072
|
state.harnessProvider,
|
|
7052
|
-
process.env,
|
|
7073
|
+
latestCredentialEnv ?? process.env,
|
|
7053
7074
|
).catch((err) =>
|
|
7054
7075
|
console.warn(`[${role}] cred_status post_task refresh failed (non-fatal): ${err}`),
|
|
7055
7076
|
);
|
|
@@ -3,7 +3,12 @@ import type { RequestInfo } from "../tools/utils";
|
|
|
3
3
|
import { type CreateTaskOptions, CreateTaskOptionsSchema } from "../types";
|
|
4
4
|
import { scrubSecrets } from "../utils/secret-scrubber";
|
|
5
5
|
import { workflowEventBus } from "../workflows/event-bus";
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
TaskBlockedEvent,
|
|
8
|
+
TaskCreateEvent,
|
|
9
|
+
TaskCreateModify,
|
|
10
|
+
TaskCreateOrigin,
|
|
11
|
+
} from "./contract";
|
|
7
12
|
import { dispatchPre, extensionIdForAgent, listRegistered } from "./dispatcher";
|
|
8
13
|
|
|
9
14
|
const MODIFIABLE_OPTION_KEYS = [
|
|
@@ -50,6 +55,7 @@ type ApplyPreTaskCreateArgs = {
|
|
|
50
55
|
options: CreateTaskOptions;
|
|
51
56
|
origin: TaskCreateOrigin;
|
|
52
57
|
requestInfo?: RequestInfo;
|
|
58
|
+
slack?: TaskCreateEvent["slack"];
|
|
53
59
|
/** The caller's escape hatch for an uncatalogued `model`; the harness check ignores it. */
|
|
54
60
|
allowCustomModel?: boolean;
|
|
55
61
|
};
|
|
@@ -141,6 +147,7 @@ export async function applyPreTaskCreate(
|
|
|
141
147
|
options: args.options,
|
|
142
148
|
origin,
|
|
143
149
|
requestInfo: args.requestInfo,
|
|
150
|
+
...(args.slack ? { slack: args.slack } : {}),
|
|
144
151
|
},
|
|
145
152
|
{
|
|
146
153
|
skipExtensionId: creatingExtensionId,
|