@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
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// Generated by scripts/bundle-extension-types.ts. Do not edit.
|
|
2
2
|
// biome-ignore-all lint/suspicious/noTemplateCurlyInString: Type declarations contain literal template placeholders.
|
|
3
3
|
export const EXTENSION_TYPE_DEFINITIONS =
|
|
4
|
-
'// biome-ignore-all lint/suspicious/noConfusingVoidType: Extension handlers may explicitly return no result.\ndeclare module "swarm-extension" {\n export type CreateTaskOptions = {\n key?: string | undefined;\n agentId?: string | null | undefined;\n creatorAgentId?: string | undefined;\n source?:\n | "workflow"\n | "schedule"\n | "slack"\n | "agentmail"\n | "mcp"\n | "api"\n | "ui"\n | "github"\n | "gitlab"\n | "azure-devops"\n | "bitbucket"\n | "system"\n | "linear"\n | "jira"\n | "comb"\n | "chat"\n | undefined;\n routingReason?:\n | "skill"\n | "continuity"\n | "overflow"\n | "human_pinned"\n | "reroute_fault"\n | undefined;\n routingSource?: "declared" | "engine_default" | undefined;\n routingNote?: string | undefined;\n taskType?: string | undefined;\n tags?: string[] | undefined;\n priority?: number | undefined;\n dependsOn?: string[] | undefined;\n offeredTo?: string | undefined;\n status?: "draft" | "backlog" | "unassigned" | undefined;\n slackChannelId?: string | undefined;\n slackThreadTs?: string | undefined;\n slackTriggerMessageTs?: string | undefined;\n slackUserId?: string | undefined;\n overrideSlackContext?: boolean | undefined;\n vcsProvider?: "github" | "gitlab" | "azure-devops" | "bitbucket" | undefined;\n vcsRepo?: string | undefined;\n vcsEventType?: string | undefined;\n vcsNumber?: number | undefined;\n vcsCommentId?: number | undefined;\n vcsAuthor?: string | undefined;\n vcsUrl?: string | undefined;\n vcsInstallationId?: number | undefined;\n vcsNodeId?: string | undefined;\n agentmailInboxId?: string | undefined;\n agentmailMessageId?: string | undefined;\n agentmailThreadId?: string | undefined;\n mentionMessageId?: string | undefined;\n mentionChannelId?: string | undefined;\n dir?: string | undefined;\n parentTaskId?: string | undefined;\n model?: string | undefined;\n modelTier?: "smol" | "regular" | "smart" | "ultra" | undefined;\n effort?: "off" | "low" | "medium" | "high" | "xhigh" | "max" | undefined;\n profile?: string | undefined;\n scheduleId?: string | undefined;\n workflowRunId?: string | undefined;\n workflowRunStepId?: string | undefined;\n sourceTaskId?: string | undefined;\n outputSchema?: Record<string, unknown> | undefined;\n inheritParentOutputSchema?: boolean | undefined;\n inheritParentRequester?: boolean | undefined;\n inheritParentRoutingAffinity?: boolean | undefined;\n inheritParentFollowUpConfig?: boolean | undefined;\n followUpConfig?:\n | {\n disabled?: boolean | undefined;\n onCompleted?: string | undefined;\n onFailed?: string | undefined;\n }\n | undefined;\n requestedByUserId?: string | undefined;\n contextKey?: string | undefined;\n bypassTrackerContextDedup?: boolean | undefined;\n routingAffinity?:\n | {\n capabilities: string[];\n sourceAgentId?: string | undefined;\n role?: string | undefined;\n harnessProvider?:\n | "claude"\n | "codex"\n | "pi"\n | "devin"\n | "claude-managed"\n | "opencode"\n | "acp"\n | "dsh"\n | "amp"\n | "cursor"\n | "grok"\n | undefined;\n leadOnly?: boolean | undefined;\n }\n | undefined;\n };\n\n export type AgentTask = {\n id: string;\n key: string;\n agentId: string | null;\n task: string;\n status:\n | "draft"\n | "backlog"\n | "unassigned"\n | "offered"\n | "reviewing"\n | "pending"\n | "in_progress"\n | "paused"\n | "completed"\n | "failed"\n | "cancelled"\n | "superseded";\n source:\n | "workflow"\n | "schedule"\n | "slack"\n | "agentmail"\n | "mcp"\n | "api"\n | "ui"\n | "github"\n | "gitlab"\n | "azure-devops"\n | "bitbucket"\n | "system"\n | "linear"\n | "jira"\n | "comb"\n | "chat";\n tags: string[];\n priority: number;\n dependsOn: string[];\n createdAt: string;\n lastUpdatedAt: string;\n slackReplySent: boolean;\n wasPaused: boolean;\n creatorAgentId?: string | undefined;\n title?: string | undefined;\n routingReason?:\n | "skill"\n | "continuity"\n | "overflow"\n | "human_pinned"\n | "reroute_fault"\n | undefined;\n routingSource?: "declared" | "engine_default" | undefined;\n routingNote?: string | undefined;\n poolWakeAt?: string | undefined;\n taskType?: string | undefined;\n offeredTo?: string | undefined;\n offeredAt?: string | undefined;\n acceptedAt?: string | undefined;\n rejectionReason?: string | undefined;\n finishedAt?: string | undefined;\n notifiedAt?: string | undefined;\n failureReason?: string | undefined;\n output?: string | undefined;\n progress?: string | undefined;\n slackChannelId?: string | undefined;\n slackThreadTs?: string | undefined;\n slackTriggerMessageTs?: string | undefined;\n slackUserId?: string | undefined;\n slackProgressMessageTs?: string | undefined;\n slackTreeRootMessageTs?: string | undefined;\n vcsProvider?: "github" | "gitlab" | "azure-devops" | "bitbucket" | undefined;\n vcsRepo?: string | undefined;\n vcsEventType?: string | undefined;\n vcsNumber?: number | undefined;\n vcsCommentId?: number | undefined;\n vcsAuthor?: string | undefined;\n vcsUrl?: string | undefined;\n vcsInstallationId?: number | undefined;\n vcsNodeId?: string | undefined;\n agentmailInboxId?: string | undefined;\n agentmailMessageId?: string | undefined;\n agentmailThreadId?: string | undefined;\n mentionMessageId?: string | undefined;\n mentionChannelId?: string | undefined;\n dir?: string | undefined;\n parentTaskId?: string | undefined;\n claudeSessionId?: string | undefined;\n model?: string | undefined;\n modelTier?: "smol" | "regular" | "smart" | "ultra" | undefined;\n effort?: "off" | "low" | "medium" | "high" | "xhigh" | "max" | undefined;\n resolvedModel?: string | undefined;\n modelSource?: string | undefined;\n modelAlias?: string | undefined;\n resolvedEffort?: "off" | "low" | "medium" | "high" | "xhigh" | "max" | undefined;\n profile?: string | undefined;\n resolvedProfile?: string | undefined;\n scheduleId?: string | undefined;\n workflowRunId?: string | null | undefined;\n workflowRunStepId?: string | null | undefined;\n contextKey?: string | undefined;\n outputSchema?: Record<string, unknown> | undefined;\n followUpConfig?:\n | {\n disabled?: boolean | undefined;\n onCompleted?: string | undefined;\n onFailed?: string | undefined;\n }\n | undefined;\n deferredAt?: string | undefined;\n compactionCount?: number | undefined;\n peakContextPercent?: number | undefined;\n peakContextTokens?: number | undefined;\n contextWindowSize?: number | undefined;\n credentialKeySuffix?: string | undefined;\n credentialKeyType?: string | undefined;\n requestedByUserId?: string | undefined;\n swarmVersion?: string | undefined;\n provider?:\n | "claude"\n | "codex"\n | "pi"\n | "devin"\n | "claude-managed"\n | "opencode"\n | "acp"\n | "dsh"\n | "amp"\n | "cursor"\n | "grok"\n | undefined;\n providerMeta?: Record<string, unknown> | undefined;\n harnessVariant?: string | undefined;\n harnessVariantMeta?: Record<string, unknown> | undefined;\n totalCostUsd?: number | undefined;\n routingAffinity?:\n | {\n capabilities: string[];\n sourceAgentId?: string | undefined;\n role?: string | undefined;\n harnessProvider?:\n | "claude"\n | "codex"\n | "pi"\n | "devin"\n | "claude-managed"\n | "opencode"\n | "acp"\n | "dsh"\n | "amp"\n | "cursor"\n | "grok"\n | undefined;\n leadOnly?: boolean | undefined;\n }\n | undefined;\n routingAffinityInvalid?: boolean | undefined;\n };\n\n export type ActiveSession = {\n id: string;\n agentId: string;\n taskId: string | null;\n triggerType: string;\n inboxMessageId: string | null;\n taskDescription: string | null;\n runnerSessionId: string | null;\n providerSessionId: string | null;\n startedAt: string;\n lastHeartbeatAt: string;\n runtimeInstanceId?: string | null | undefined;\n };\n\n export type RequestInfo = {\n sessionId: string | undefined;\n agentId: string | undefined;\n runtimeInstanceId: string | undefined;\n sourceTaskId: string | undefined;\n contextKey: string | undefined;\n callOrigin: "mcp" | "extension" | "script-sdk";\n };\n\n import type { SwarmSdk } from "swarm-sdk";\n import type { z } from "zod";\n export type Runtime = "api" | "worker";\n export interface ExtensionScriptAsset {\n name: string;\n file: string;\n description: string;\n intent?: string;\n }\n export interface ExtensionScheduleAsset {\n name: string;\n description?: string;\n script: string;\n cronExpression?: string;\n intervalMs?: number;\n timezone?: string;\n args?: Record<string, unknown>;\n }\n export interface ExtensionWorkflowAsset {\n file: string;\n }\n export interface ExtensionSkillAsset {\n dir: string;\n }\n export interface ExtensionManifest {\n $schema?: string;\n name: string;\n description: string;\n version: string;\n runtime: Runtime;\n assets: {\n hooks: string;\n scripts?: readonly ExtensionScriptAsset[];\n schedules?: readonly ExtensionScheduleAsset[];\n workflows?: readonly ExtensionWorkflowAsset[];\n skills?: readonly ExtensionSkillAsset[];\n };\n homepage?: string;\n author?: string;\n }\n export interface HooksModule {\n default: SwarmExtension;\n config?: z.ZodTypeAny;\n }\n export interface ExtensionState {\n get<T = unknown>(key: string): Promise<T | null>;\n set<T>(key: string, value: T): Promise<void>;\n incr(key: string, by?: number): Promise<number>;\n del(key: string): Promise<void>;\n }\n export interface ExtensionLogger {\n debug(message: string, data?: unknown): void;\n info(message: string, data?: unknown): void;\n warn(message: string, data?: unknown): void;\n error(message: string, data?: unknown): void;\n }\n type ConfigFor<M extends ExtensionManifest> = M extends {\n config: infer C extends z.ZodTypeAny;\n }\n ? z.infer<C>\n : Record<string, unknown>;\n export interface ApiCtx<Config = Record<string, unknown>> {\n swarm: SwarmSdk;\n state: ExtensionState;\n config: Config;\n log: ExtensionLogger;\n signal: AbortSignal;\n event: {\n name: keyof SwarmEventMap;\n at: string;\n extension: {\n id: string;\n version: number;\n };\n };\n }\n export interface WorkerCtx<Config = Record<string, unknown>>\n extends Omit<ApiCtx<Config>, "swarm"> {\n swarm: SwarmSdk;\n worker: {\n agentId: string;\n taskId: string;\n harness: string;\n };\n }\n export type CtxFor<M extends ExtensionManifest> = M["runtime"] extends "api"\n ? ApiCtx<ConfigFor<M>>\n : WorkerCtx<ConfigFor<M>>;\n export type PreResult<Modify> =\n | void\n | {\n action: "continue";\n }\n | {\n action: "modify";\n data: Modify;\n }\n | {\n action: "block";\n reason: string;\n };\n export type TaskCreateOrigin =\n | "rest"\n | "app"\n | "mcp"\n | "slack"\n | "schedule"\n | "workflow"\n | "webhook"\n | "followUp"\n | `extension:${string}`;\n export interface TaskCreateEvent {\n options: CreateTaskOptions;\n description: string;\n origin: TaskCreateOrigin;\n requestInfo?: RequestInfo;\n }\n /** A pre.task.create hook blocked a task before it was created. */\n export interface TaskBlockedEvent {\n /** The extension that blocked, or whose model/agent rewrite was rejected. */\n extension: {\n id: string;\n name: string;\n };\n /** The scrubbed block reason. */\n reason: string;\n origin: TaskCreateOrigin;\n source?: AgentTask["source"];\n requester: {\n userId?: string;\n agentId?: string;\n slackUserId?: string;\n };\n context?: {\n contextKey?: string;\n scheduleId?: string;\n workflowRunId?: string;\n workflowRunStepId?: string;\n parentTaskId?: string;\n vcsRepo?: string;\n vcsNumber?: number;\n slackChannelId?: string;\n slackThreadTs?: string;\n };\n /** The original task description, scrubbed and cut to 500 characters. */\n description: string;\n }\n export type TaskCreateModify = Partial<\n Pick<\n CreateTaskOptions,\n | "agentId"\n | "creatorAgentId"\n | "source"\n | "taskType"\n | "tags"\n | "priority"\n | "dependsOn"\n | "offeredTo"\n | "vcsProvider"\n | "vcsRepo"\n | "vcsEventType"\n | "vcsNumber"\n | "vcsCommentId"\n | "vcsAuthor"\n | "vcsUrl"\n | "vcsInstallationId"\n | "vcsNodeId"\n | "agentmailInboxId"\n | "agentmailMessageId"\n | "agentmailThreadId"\n | "mentionMessageId"\n | "mentionChannelId"\n | "dir"\n | "model"\n | "modelTier"\n | "effort"\n | "outputSchema"\n | "followUpConfig"\n | "bypassTrackerContextDedup"\n >\n > & {\n description?: string;\n };\n export interface TaskFollowUpEvent {\n completedTask: AgentTask;\n status: "completed" | "failed";\n output?: string;\n failureReason?: string;\n workerAgentId: string;\n leadAgentId: string;\n summary: string;\n }\n export type TaskFollowUpModify = Partial<\n Pick<CreateTaskOptions, "agentId" | "priority" | "followUpConfig">\n > & {\n description?: string;\n };\n export interface SlackRouteEvent {\n channelId: string;\n userId: string;\n text: string;\n threadTs?: string;\n botMentioned: boolean;\n threadContext?: {\n channelId: string;\n threadTs: string;\n };\n }\n export interface SlackRouteModify {\n target:\n | {\n kind: "agent";\n agentId: string;\n }\n | {\n kind: "lead";\n }\n | {\n kind: "broadcast";\n };\n }\n /** A Chat SDK message after its thread buffer flushed, before routing. */\n export interface ChatRouteEvent {\n /** Adapter name, such as `telegram` or `slack`. */\n adapter: string;\n channelId: string;\n /** The Chat SDK thread id, prefixed with the adapter name. */\n threadId: string;\n /** The first buffered sender. */\n userId: string;\n userName?: string;\n /** The buffered messages, joined. */\n text: string;\n /** True for an @mention and for a reply to one of the bot\'s messages. */\n botMentioned: boolean;\n isDirectMessage: boolean;\n /** Platform chat type, such as Telegram `private`, `group`, `supergroup` or `channel`. */\n chatType?: string;\n /** Set for a Telegram supergroup with topics. */\n isForum?: boolean;\n /** A buffered message replies to one of the bot\'s own messages. */\n replyToBot: boolean;\n /** Set when earlier swarm tasks exist in this thread. */\n threadContext?: {\n adapter: string;\n threadId: string;\n };\n }\n export interface ChatRouteModify {\n target:\n | {\n kind: "agent";\n agentId: string;\n }\n | {\n kind: "lead";\n }\n | {\n kind: "broadcast";\n }\n | {\n kind: "workflow";\n workflowId: string;\n };\n }\n export type HeartbeatClassification = "no-session" | "stale-session" | "fresh-stalled";\n export type HeartbeatAction = "supersede-resume" | "fail" | "record";\n export interface HeartbeatRemediateEvent {\n task: AgentTask;\n session?: ActiveSession;\n classification: HeartbeatClassification;\n proposedAction: HeartbeatAction;\n reason: string;\n taskAgeMs: number;\n sessionHeartbeatAgeMs?: number;\n }\n export interface HeartbeatRemediateModify {\n proposedAction: HeartbeatAction;\n }\n export interface ToolCallEvent {\n tool: string;\n args: unknown;\n requestInfo: RequestInfo;\n }\n export interface ToolCallModify {\n args: unknown;\n }\n export interface TaskEvent {\n task: AgentTask;\n }\n export interface TaskCompletedEvent extends TaskEvent {\n output: string;\n }\n export interface TaskFailedEvent extends TaskEvent {\n failureReason: string;\n }\n export interface TaskSupersededEvent extends TaskEvent {\n supersededBy: string;\n }\n export interface TaskProgressEvent extends TaskEvent {\n progress: string;\n }\n export interface SlackMessageEvent {\n channelId: string;\n userId: string;\n text: string;\n threadTs?: string;\n taskId?: string;\n }\n /** A Chat SDK message the swarm received or sent. */\n export interface ChatMessageEvent {\n adapter: string;\n channelId: string;\n threadId: string;\n /** The sender, for an inbound message. */\n userId?: string;\n text: string;\n direction: "inbound" | "outbound";\n taskId?: string;\n messageId?: string;\n }\n /**\n * A person answered an approval request. Fires for workflow and standalone\n * requests. Timeouts and cancellations do not emit it today.\n */\n export interface ApprovalResolvedEvent {\n requestId: string;\n status: "approved" | "rejected" | "timeout" | "cancelled";\n responses: Record<string, unknown> | null;\n /** Set when a workflow human-in-the-loop step asked. Absent for a standalone request. */\n workflowRunId?: string;\n workflowRunStepId?: string;\n /** The task whose agent asked, for a standalone request. */\n sourceTaskId?: string;\n }\n /** A task was refused because a daily spend budget is used up. */\n export interface TaskBudgetRefusedEvent extends TaskEvent {\n /** The agent the task was refused for. */\n agentId: string;\n /** Which budget refused it. The matching spend and budget fields are set. */\n cause: "agent" | "global" | "user";\n agentSpendUsd?: number;\n agentBudgetUsd?: number;\n globalSpendUsd?: number;\n globalBudgetUsd?: number;\n userSpendUsd?: number;\n userBudgetUsd?: number;\n /** ISO 8601 time the daily budget resets. */\n resetAt: string;\n }\n /** An inbound AgentMail message. */\n export interface EmailReceivedEvent {\n inboxId: string;\n /** The From header as sent. */\n from: string;\n subject: string;\n /** The text body, or the HTML when there is no text, cut to 500 characters with `...` appended. */\n body: string;\n threadId: string;\n messageId: string;\n }\n /** An inbound Kapso (WhatsApp) message. */\n export interface KapsoMessageEvent {\n phoneNumberId: string;\n conversationId?: string;\n messageId: string;\n from?: string;\n /** Kapso message type, such as `text` or `image`. */\n type?: string;\n /** The text, or a placeholder naming the type for a non-text message. */\n text: string;\n }\n export type VcsProvider = "github" | "gitlab" | "azure-devops" | "bitbucket";\n /**\n * The provider object the event is about, as the provider names it. GitLab\n * calls a pull request a merge request, and a comment a `note`.\n */\n export type VcsEventKind =\n | "pull_request"\n | "merge_request"\n | "issue"\n | "issue_comment"\n | "note"\n | "pull_request_review"\n | "pipeline";\n /**\n * A webhook event from a connected code host. `action` is the provider\'s own\n * value: `opened`, `closed`, `synchronize`, `merge`, `created`, `commented`, or,\n * for a pipeline, its status such as `success` or `failed`. Fields a kind does\n * not carry are absent.\n */\n export interface VcsEvent {\n provider: VcsProvider;\n kind: VcsEventKind;\n action: string;\n /** `owner/name` on GitHub and GitLab, `workspace/repo_slug` on Bitbucket, the repository URL on Azure DevOps. */\n repo: string;\n /** The PR, merge request, or issue number. Absent for a pipeline outside a merge request. */\n number?: number;\n title?: string;\n body?: string | null;\n /** Login of the user behind the event. Set for pull requests, merge requests, and Azure DevOps comments. */\n author?: string;\n url?: string;\n merged?: boolean;\n changedFiles?: number;\n /** The review verdict, on a pull request review. */\n reviewState?: string;\n }\n export interface ToolCallCompletedEvent extends ToolCallEvent {\n result: unknown;\n durationMs: number;\n }\n export interface SwarmEventMap {\n "pre.task.create": {\n event: TaskCreateEvent;\n modify: TaskCreateModify;\n result: PreResult<TaskCreateModify>;\n };\n "pre.task.followUp": {\n event: TaskFollowUpEvent;\n modify: TaskFollowUpModify;\n result: PreResult<TaskFollowUpModify>;\n };\n "pre.slack.route": {\n event: SlackRouteEvent;\n modify: SlackRouteModify;\n result: PreResult<SlackRouteModify>;\n };\n "pre.chat.route": {\n event: ChatRouteEvent;\n modify: ChatRouteModify;\n result: PreResult<ChatRouteModify>;\n };\n "pre.heartbeat.remediate": {\n event: HeartbeatRemediateEvent;\n modify: HeartbeatRemediateModify;\n result: PreResult<HeartbeatRemediateModify>;\n };\n "pre.tool.call": {\n event: ToolCallEvent;\n modify: ToolCallModify;\n result: PreResult<ToolCallModify>;\n };\n "post.task.created": {\n event: TaskEvent;\n modify: never;\n result: void;\n };\n "post.task.blocked": {\n event: TaskBlockedEvent;\n modify: never;\n result: void;\n };\n "post.task.completed": {\n event: TaskCompletedEvent;\n modify: never;\n result: void;\n };\n "post.task.failed": {\n event: TaskFailedEvent;\n modify: never;\n result: void;\n };\n "post.task.cancelled": {\n event: TaskEvent;\n modify: never;\n result: void;\n };\n "post.task.superseded": {\n event: TaskSupersededEvent;\n modify: never;\n result: void;\n };\n "post.task.progress": {\n event: TaskProgressEvent;\n modify: never;\n result: void;\n };\n "post.slack.message": {\n event: SlackMessageEvent;\n modify: never;\n result: void;\n };\n "post.chat.message": {\n event: ChatMessageEvent;\n modify: never;\n result: void;\n };\n "post.approval.resolved": {\n event: ApprovalResolvedEvent;\n modify: never;\n result: void;\n };\n "post.task.budgetRefused": {\n event: TaskBudgetRefusedEvent;\n modify: never;\n result: void;\n };\n "post.email.received": {\n event: EmailReceivedEvent;\n modify: never;\n result: void;\n };\n "post.kapso.message": {\n event: KapsoMessageEvent;\n modify: never;\n result: void;\n };\n "post.vcs.event": {\n event: VcsEvent;\n modify: never;\n result: void;\n };\n "post.tool.call": {\n event: ToolCallCompletedEvent;\n modify: never;\n result: void;\n };\n }\n export interface ExtensionApi<\n M extends ExtensionManifest = ExtensionManifest & {\n runtime: "api";\n },\n > {\n on<E extends keyof SwarmEventMap>(\n event: E,\n handler: (\n event: SwarmEventMap[E]["event"],\n ctx: CtxFor<M>,\n ) => Promise<SwarmEventMap[E]["result"]> | SwarmEventMap[E]["result"],\n opts?: {\n priority?: number;\n },\n ): void;\n }\n export type SwarmExtension<\n M extends ExtensionManifest = ExtensionManifest & {\n runtime: "api";\n },\n > = (api: ExtensionApi<M>) => void;\n export { block, modify } from "./contract-runtime";\n}\n';
|
|
4
|
+
'// biome-ignore-all lint/suspicious/noConfusingVoidType: Extension handlers may explicitly return no result.\ndeclare module "swarm-extension" {\n export type CreateTaskOptions = {\n key?: string | undefined;\n agentId?: string | null | undefined;\n creatorAgentId?: string | undefined;\n source?:\n | "workflow"\n | "schedule"\n | "slack"\n | "agentmail"\n | "github"\n | "gitlab"\n | "azure-devops"\n | "bitbucket"\n | "linear"\n | "jira"\n | "mcp"\n | "api"\n | "ui"\n | "system"\n | "comb"\n | "chat"\n | undefined;\n routingReason?:\n | "skill"\n | "continuity"\n | "overflow"\n | "human_pinned"\n | "reroute_fault"\n | undefined;\n routingSource?: "declared" | "engine_default" | undefined;\n routingNote?: string | undefined;\n taskType?: string | undefined;\n tags?: string[] | undefined;\n priority?: number | undefined;\n dependsOn?: string[] | undefined;\n offeredTo?: string | undefined;\n status?: "draft" | "backlog" | "unassigned" | undefined;\n slackChannelId?: string | undefined;\n slackThreadTs?: string | undefined;\n slackTriggerMessageTs?: string | undefined;\n slackUserId?: string | undefined;\n overrideSlackContext?: boolean | undefined;\n vcsProvider?: "github" | "gitlab" | "azure-devops" | "bitbucket" | undefined;\n vcsRepo?: string | undefined;\n vcsEventType?: string | undefined;\n vcsNumber?: number | undefined;\n vcsCommentId?: number | undefined;\n vcsAuthor?: string | undefined;\n vcsUrl?: string | undefined;\n vcsInstallationId?: number | undefined;\n vcsNodeId?: string | undefined;\n agentmailInboxId?: string | undefined;\n agentmailMessageId?: string | undefined;\n agentmailThreadId?: string | undefined;\n mentionMessageId?: string | undefined;\n mentionChannelId?: string | undefined;\n dir?: string | undefined;\n parentTaskId?: string | undefined;\n model?: string | undefined;\n modelTier?: "smol" | "regular" | "smart" | "ultra" | undefined;\n effort?: "off" | "low" | "medium" | "high" | "xhigh" | "max" | undefined;\n profile?: string | undefined;\n scheduleId?: string | undefined;\n workflowRunId?: string | undefined;\n workflowRunStepId?: string | undefined;\n sourceTaskId?: string | undefined;\n outputSchema?: Record<string, unknown> | undefined;\n inheritParentOutputSchema?: boolean | undefined;\n inheritParentRequester?: boolean | undefined;\n inheritParentRoutingAffinity?: boolean | undefined;\n inheritParentFollowUpConfig?: boolean | undefined;\n followUpConfig?:\n | {\n disabled?: boolean | undefined;\n onCompleted?: string | undefined;\n onFailed?: string | undefined;\n }\n | undefined;\n requestedByUserId?: string | undefined;\n contextKey?: string | undefined;\n bypassTrackerContextDedup?: boolean | undefined;\n routingAffinity?:\n | {\n capabilities: string[];\n sourceAgentId?: string | undefined;\n role?: string | undefined;\n harnessProvider?:\n | "claude"\n | "codex"\n | "pi"\n | "devin"\n | "claude-managed"\n | "opencode"\n | "acp"\n | "dsh"\n | "amp"\n | "cursor"\n | "grok"\n | undefined;\n leadOnly?: boolean | undefined;\n }\n | undefined;\n };\n\n export type AgentTask = {\n id: string;\n key: string;\n agentId: string | null;\n task: string;\n status:\n | "draft"\n | "backlog"\n | "unassigned"\n | "offered"\n | "reviewing"\n | "pending"\n | "in_progress"\n | "paused"\n | "completed"\n | "failed"\n | "cancelled"\n | "superseded";\n source:\n | "workflow"\n | "schedule"\n | "slack"\n | "agentmail"\n | "github"\n | "gitlab"\n | "azure-devops"\n | "bitbucket"\n | "linear"\n | "jira"\n | "mcp"\n | "api"\n | "ui"\n | "system"\n | "comb"\n | "chat";\n tags: string[];\n priority: number;\n dependsOn: string[];\n createdAt: string;\n lastUpdatedAt: string;\n slackReplySent: boolean;\n wasPaused: boolean;\n creatorAgentId?: string | undefined;\n title?: string | undefined;\n routingReason?:\n | "skill"\n | "continuity"\n | "overflow"\n | "human_pinned"\n | "reroute_fault"\n | undefined;\n routingSource?: "declared" | "engine_default" | undefined;\n routingNote?: string | undefined;\n poolWakeAt?: string | undefined;\n taskType?: string | undefined;\n offeredTo?: string | undefined;\n offeredAt?: string | undefined;\n acceptedAt?: string | undefined;\n rejectionReason?: string | undefined;\n finishedAt?: string | undefined;\n notifiedAt?: string | undefined;\n failureReason?: string | undefined;\n output?: string | undefined;\n progress?: string | undefined;\n slackChannelId?: string | undefined;\n slackThreadTs?: string | undefined;\n slackTriggerMessageTs?: string | undefined;\n slackUserId?: string | undefined;\n slackProgressMessageTs?: string | undefined;\n slackTreeRootMessageTs?: string | undefined;\n vcsProvider?: "github" | "gitlab" | "azure-devops" | "bitbucket" | undefined;\n vcsRepo?: string | undefined;\n vcsEventType?: string | undefined;\n vcsNumber?: number | undefined;\n vcsCommentId?: number | undefined;\n vcsAuthor?: string | undefined;\n vcsUrl?: string | undefined;\n vcsInstallationId?: number | undefined;\n vcsNodeId?: string | undefined;\n agentmailInboxId?: string | undefined;\n agentmailMessageId?: string | undefined;\n agentmailThreadId?: string | undefined;\n mentionMessageId?: string | undefined;\n mentionChannelId?: string | undefined;\n dir?: string | undefined;\n parentTaskId?: string | undefined;\n claudeSessionId?: string | undefined;\n model?: string | undefined;\n modelTier?: "smol" | "regular" | "smart" | "ultra" | undefined;\n effort?: "off" | "low" | "medium" | "high" | "xhigh" | "max" | undefined;\n resolvedModel?: string | undefined;\n modelSource?: string | undefined;\n modelAlias?: string | undefined;\n resolvedEffort?: "off" | "low" | "medium" | "high" | "xhigh" | "max" | undefined;\n profile?: string | undefined;\n resolvedProfile?: string | undefined;\n scheduleId?: string | undefined;\n workflowRunId?: string | null | undefined;\n workflowRunStepId?: string | null | undefined;\n contextKey?: string | undefined;\n outputSchema?: Record<string, unknown> | undefined;\n followUpConfig?:\n | {\n disabled?: boolean | undefined;\n onCompleted?: string | undefined;\n onFailed?: string | undefined;\n }\n | undefined;\n deferredAt?: string | undefined;\n compactionCount?: number | undefined;\n peakContextPercent?: number | undefined;\n peakContextTokens?: number | undefined;\n contextWindowSize?: number | undefined;\n credentialKeySuffix?: string | undefined;\n credentialKeyType?: string | undefined;\n requestedByUserId?: string | undefined;\n swarmVersion?: string | undefined;\n provider?:\n | "claude"\n | "codex"\n | "pi"\n | "devin"\n | "claude-managed"\n | "opencode"\n | "acp"\n | "dsh"\n | "amp"\n | "cursor"\n | "grok"\n | undefined;\n providerMeta?: Record<string, unknown> | undefined;\n harnessVariant?: string | undefined;\n harnessVariantMeta?: Record<string, unknown> | undefined;\n totalCostUsd?: number | undefined;\n routingAffinity?:\n | {\n capabilities: string[];\n sourceAgentId?: string | undefined;\n role?: string | undefined;\n harnessProvider?:\n | "claude"\n | "codex"\n | "pi"\n | "devin"\n | "claude-managed"\n | "opencode"\n | "acp"\n | "dsh"\n | "amp"\n | "cursor"\n | "grok"\n | undefined;\n leadOnly?: boolean | undefined;\n }\n | undefined;\n routingAffinityInvalid?: boolean | undefined;\n };\n\n export type ActiveSession = {\n id: string;\n agentId: string;\n taskId: string | null;\n triggerType: string;\n inboxMessageId: string | null;\n taskDescription: string | null;\n runnerSessionId: string | null;\n providerSessionId: string | null;\n startedAt: string;\n lastHeartbeatAt: string;\n runtimeInstanceId?: string | null | undefined;\n };\n\n export type RequestInfo = {\n sessionId: string | undefined;\n agentId: string | undefined;\n runtimeInstanceId: string | undefined;\n sourceTaskId: string | undefined;\n contextKey: string | undefined;\n callOrigin: "mcp" | "extension" | "script-sdk";\n };\n\n import type { SwarmSdk } from "swarm-sdk";\n import type { z } from "zod";\n export type Runtime = "api" | "worker";\n export interface ExtensionScriptAsset {\n name: string;\n file: string;\n description: string;\n intent?: string;\n }\n export interface ExtensionScheduleAsset {\n name: string;\n description?: string;\n script: string;\n cronExpression?: string;\n intervalMs?: number;\n timezone?: string;\n args?: Record<string, unknown>;\n }\n export interface ExtensionWorkflowAsset {\n file: string;\n }\n export interface ExtensionSkillAsset {\n dir: string;\n }\n export interface ExtensionManifest {\n $schema?: string;\n name: string;\n description: string;\n version: string;\n runtime: Runtime;\n assets: {\n hooks: string;\n scripts?: readonly ExtensionScriptAsset[];\n schedules?: readonly ExtensionScheduleAsset[];\n workflows?: readonly ExtensionWorkflowAsset[];\n skills?: readonly ExtensionSkillAsset[];\n };\n homepage?: string;\n author?: string;\n }\n export interface HooksModule {\n default: SwarmExtension;\n config?: z.ZodTypeAny;\n }\n export interface ExtensionState {\n get<T = unknown>(key: string): Promise<T | null>;\n set<T>(key: string, value: T): Promise<void>;\n incr(key: string, by?: number): Promise<number>;\n del(key: string): Promise<void>;\n }\n export interface ExtensionLogger {\n debug(message: string, data?: unknown): void;\n info(message: string, data?: unknown): void;\n warn(message: string, data?: unknown): void;\n error(message: string, data?: unknown): void;\n }\n type ConfigFor<M extends ExtensionManifest> = M extends {\n config: infer C extends z.ZodTypeAny;\n }\n ? z.infer<C>\n : Record<string, unknown>;\n export interface ApiCtx<Config = Record<string, unknown>> {\n swarm: SwarmSdk;\n state: ExtensionState;\n config: Config;\n log: ExtensionLogger;\n signal: AbortSignal;\n event: {\n name: keyof SwarmEventMap;\n at: string;\n extension: {\n id: string;\n version: number;\n };\n };\n }\n export interface WorkerCtx<Config = Record<string, unknown>>\n extends Omit<ApiCtx<Config>, "swarm"> {\n swarm: SwarmSdk;\n worker: {\n agentId: string;\n taskId: string;\n harness: string;\n };\n }\n export type CtxFor<M extends ExtensionManifest> = M["runtime"] extends "api"\n ? ApiCtx<ConfigFor<M>>\n : WorkerCtx<ConfigFor<M>>;\n export type PreResult<Modify> =\n | void\n | {\n action: "continue";\n }\n | {\n action: "modify";\n data: Modify;\n }\n | {\n action: "block";\n reason: string;\n };\n export type TaskCreateOrigin =\n | "rest"\n | "app"\n | "mcp"\n | "slack"\n | "schedule"\n | "workflow"\n | "webhook"\n | "followUp"\n | `extension:${string}`;\n export interface TaskCreateEvent {\n options: CreateTaskOptions;\n description: string;\n origin: TaskCreateOrigin;\n requestInfo?: RequestInfo;\n /** Slack ingress metadata; not persisted or writable through modify. */\n slack?: {\n trigger: "mention" | "thread-buffer";\n messages: {\n ts: string;\n userId: string;\n text: string;\n }[];\n };\n }\n /** A pre.task.create hook blocked a task before it was created. */\n export interface TaskBlockedEvent {\n /** The extension that blocked, or whose model/agent rewrite was rejected. */\n extension: {\n id: string;\n name: string;\n };\n /** The scrubbed block reason. */\n reason: string;\n origin: TaskCreateOrigin;\n source?: AgentTask["source"];\n requester: {\n userId?: string;\n agentId?: string;\n slackUserId?: string;\n };\n context?: {\n contextKey?: string;\n scheduleId?: string;\n workflowRunId?: string;\n workflowRunStepId?: string;\n parentTaskId?: string;\n vcsRepo?: string;\n vcsNumber?: number;\n slackChannelId?: string;\n slackThreadTs?: string;\n };\n /** The original task description, scrubbed and cut to 500 characters. */\n description: string;\n }\n export type TaskCreateModify = Partial<\n Pick<\n CreateTaskOptions,\n | "agentId"\n | "creatorAgentId"\n | "source"\n | "taskType"\n | "tags"\n | "priority"\n | "dependsOn"\n | "offeredTo"\n | "vcsProvider"\n | "vcsRepo"\n | "vcsEventType"\n | "vcsNumber"\n | "vcsCommentId"\n | "vcsAuthor"\n | "vcsUrl"\n | "vcsInstallationId"\n | "vcsNodeId"\n | "agentmailInboxId"\n | "agentmailMessageId"\n | "agentmailThreadId"\n | "mentionMessageId"\n | "mentionChannelId"\n | "dir"\n | "model"\n | "modelTier"\n | "effort"\n | "outputSchema"\n | "followUpConfig"\n | "bypassTrackerContextDedup"\n >\n > & {\n description?: string;\n };\n export interface TaskFollowUpEvent {\n completedTask: AgentTask;\n status: "completed" | "failed";\n output?: string;\n failureReason?: string;\n workerAgentId: string;\n leadAgentId: string;\n summary: string;\n }\n export type TaskFollowUpModify = Partial<\n Pick<CreateTaskOptions, "agentId" | "priority" | "followUpConfig">\n > & {\n description?: string;\n };\n export interface SlackRouteEvent {\n channelId: string;\n userId: string;\n text: string;\n threadTs?: string;\n botMentioned: boolean;\n threadContext?: {\n channelId: string;\n threadTs: string;\n };\n }\n export interface SlackRouteModify {\n target:\n | {\n kind: "agent";\n agentId: string;\n }\n | {\n kind: "lead";\n }\n | {\n kind: "broadcast";\n };\n }\n /** A Chat SDK message after its thread buffer flushed, before routing. */\n export interface ChatRouteEvent {\n /** Adapter name, such as `telegram` or `slack`. */\n adapter: string;\n channelId: string;\n /** The Chat SDK thread id, prefixed with the adapter name. */\n threadId: string;\n /** The first buffered sender. */\n userId: string;\n userName?: string;\n /** The buffered messages, joined. */\n text: string;\n /** True for an @mention and for a reply to one of the bot\'s messages. */\n botMentioned: boolean;\n isDirectMessage: boolean;\n /** Platform chat type, such as Telegram `private`, `group`, `supergroup` or `channel`. */\n chatType?: string;\n /** Set for a Telegram supergroup with topics. */\n isForum?: boolean;\n /** A buffered message replies to one of the bot\'s own messages. */\n replyToBot: boolean;\n /** Set when earlier swarm tasks exist in this thread. */\n threadContext?: {\n adapter: string;\n threadId: string;\n };\n }\n export interface ChatRouteModify {\n target:\n | {\n kind: "agent";\n agentId: string;\n }\n | {\n kind: "lead";\n }\n | {\n kind: "broadcast";\n }\n | {\n kind: "workflow";\n workflowId: string;\n };\n }\n export type HeartbeatClassification = "no-session" | "stale-session" | "fresh-stalled";\n export type HeartbeatAction = "supersede-resume" | "fail" | "record";\n export interface HeartbeatRemediateEvent {\n task: AgentTask;\n session?: ActiveSession;\n classification: HeartbeatClassification;\n proposedAction: HeartbeatAction;\n reason: string;\n taskAgeMs: number;\n sessionHeartbeatAgeMs?: number;\n }\n export interface HeartbeatRemediateModify {\n proposedAction: HeartbeatAction;\n }\n export interface ToolCallEvent {\n tool: string;\n args: unknown;\n requestInfo: RequestInfo;\n }\n export interface ToolCallModify {\n args: unknown;\n }\n export interface TaskEvent {\n task: AgentTask;\n }\n export interface TaskCompletedEvent extends TaskEvent {\n output: string;\n }\n export interface TaskFailedEvent extends TaskEvent {\n failureReason: string;\n }\n export interface TaskSupersededEvent extends TaskEvent {\n supersededBy: string;\n }\n export interface TaskProgressEvent extends TaskEvent {\n progress: string;\n }\n export interface SlackMessageEvent {\n channelId: string;\n userId: string;\n text: string;\n threadTs?: string;\n taskId?: string;\n }\n /** A Chat SDK message the swarm received or sent. */\n export interface ChatMessageEvent {\n adapter: string;\n channelId: string;\n threadId: string;\n /** The sender, for an inbound message. */\n userId?: string;\n text: string;\n direction: "inbound" | "outbound";\n taskId?: string;\n messageId?: string;\n }\n /**\n * A person answered an approval request. Fires for workflow and standalone\n * requests. Timeouts and cancellations do not emit it today.\n */\n export interface ApprovalResolvedEvent {\n requestId: string;\n status: "approved" | "rejected" | "timeout" | "cancelled";\n responses: Record<string, unknown> | null;\n /** Set when a workflow human-in-the-loop step asked. Absent for a standalone request. */\n workflowRunId?: string;\n workflowRunStepId?: string;\n /** The task whose agent asked, for a standalone request. */\n sourceTaskId?: string;\n }\n /** A task was refused because a daily spend budget is used up. */\n export interface TaskBudgetRefusedEvent extends TaskEvent {\n /** The agent the task was refused for. */\n agentId: string;\n /** Which budget refused it. The matching spend and budget fields are set. */\n cause: "agent" | "global" | "user";\n agentSpendUsd?: number;\n agentBudgetUsd?: number;\n globalSpendUsd?: number;\n globalBudgetUsd?: number;\n userSpendUsd?: number;\n userBudgetUsd?: number;\n /** ISO 8601 time the daily budget resets. */\n resetAt: string;\n }\n /** An inbound AgentMail message. */\n export interface EmailReceivedEvent {\n inboxId: string;\n /** The From header as sent. */\n from: string;\n subject: string;\n /** The text body, or the HTML when there is no text, cut to 500 characters with `...` appended. */\n body: string;\n threadId: string;\n messageId: string;\n }\n /** An inbound Kapso (WhatsApp) message. */\n export interface KapsoMessageEvent {\n phoneNumberId: string;\n conversationId?: string;\n messageId: string;\n from?: string;\n /** Kapso message type, such as `text` or `image`. */\n type?: string;\n /** The text, or a placeholder naming the type for a non-text message. */\n text: string;\n }\n export type VcsProvider = "github" | "gitlab" | "azure-devops" | "bitbucket";\n /**\n * The provider object the event is about, as the provider names it. GitLab\n * calls a pull request a merge request, and a comment a `note`.\n */\n export type VcsEventKind =\n | "pull_request"\n | "merge_request"\n | "issue"\n | "issue_comment"\n | "note"\n | "pull_request_review"\n | "pipeline";\n /**\n * A webhook event from a connected code host. `action` is the provider\'s own\n * value: `opened`, `closed`, `synchronize`, `merge`, `created`, `commented`, or,\n * for a pipeline, its status such as `success` or `failed`. Fields a kind does\n * not carry are absent.\n */\n export interface VcsEvent {\n provider: VcsProvider;\n kind: VcsEventKind;\n action: string;\n /** `owner/name` on GitHub and GitLab, `workspace/repo_slug` on Bitbucket, the repository URL on Azure DevOps. */\n repo: string;\n /** The PR, merge request, or issue number. Absent for a pipeline outside a merge request. */\n number?: number;\n title?: string;\n body?: string | null;\n /** Login of the user behind the event. Set for pull requests, merge requests, and Azure DevOps comments. */\n author?: string;\n url?: string;\n merged?: boolean;\n changedFiles?: number;\n /** The review verdict, on a pull request review. */\n reviewState?: string;\n }\n export interface ToolCallCompletedEvent extends ToolCallEvent {\n result: unknown;\n durationMs: number;\n }\n export interface SwarmEventMap {\n "pre.task.create": {\n event: TaskCreateEvent;\n modify: TaskCreateModify;\n result: PreResult<TaskCreateModify>;\n };\n "pre.task.followUp": {\n event: TaskFollowUpEvent;\n modify: TaskFollowUpModify;\n result: PreResult<TaskFollowUpModify>;\n };\n "pre.slack.route": {\n event: SlackRouteEvent;\n modify: SlackRouteModify;\n result: PreResult<SlackRouteModify>;\n };\n "pre.chat.route": {\n event: ChatRouteEvent;\n modify: ChatRouteModify;\n result: PreResult<ChatRouteModify>;\n };\n "pre.heartbeat.remediate": {\n event: HeartbeatRemediateEvent;\n modify: HeartbeatRemediateModify;\n result: PreResult<HeartbeatRemediateModify>;\n };\n "pre.tool.call": {\n event: ToolCallEvent;\n modify: ToolCallModify;\n result: PreResult<ToolCallModify>;\n };\n "post.task.created": {\n event: TaskEvent;\n modify: never;\n result: void;\n };\n "post.task.blocked": {\n event: TaskBlockedEvent;\n modify: never;\n result: void;\n };\n "post.task.completed": {\n event: TaskCompletedEvent;\n modify: never;\n result: void;\n };\n "post.task.failed": {\n event: TaskFailedEvent;\n modify: never;\n result: void;\n };\n "post.task.cancelled": {\n event: TaskEvent;\n modify: never;\n result: void;\n };\n "post.task.superseded": {\n event: TaskSupersededEvent;\n modify: never;\n result: void;\n };\n "post.task.progress": {\n event: TaskProgressEvent;\n modify: never;\n result: void;\n };\n "post.slack.message": {\n event: SlackMessageEvent;\n modify: never;\n result: void;\n };\n "post.chat.message": {\n event: ChatMessageEvent;\n modify: never;\n result: void;\n };\n "post.approval.resolved": {\n event: ApprovalResolvedEvent;\n modify: never;\n result: void;\n };\n "post.task.budgetRefused": {\n event: TaskBudgetRefusedEvent;\n modify: never;\n result: void;\n };\n "post.email.received": {\n event: EmailReceivedEvent;\n modify: never;\n result: void;\n };\n "post.kapso.message": {\n event: KapsoMessageEvent;\n modify: never;\n result: void;\n };\n "post.vcs.event": {\n event: VcsEvent;\n modify: never;\n result: void;\n };\n "post.tool.call": {\n event: ToolCallCompletedEvent;\n modify: never;\n result: void;\n };\n }\n export interface ExtensionApi<\n M extends ExtensionManifest = ExtensionManifest & {\n runtime: "api";\n },\n > {\n on<E extends keyof SwarmEventMap>(\n event: E,\n handler: (\n event: SwarmEventMap[E]["event"],\n ctx: CtxFor<M>,\n ) => Promise<SwarmEventMap[E]["result"]> | SwarmEventMap[E]["result"],\n opts?: {\n priority?: number;\n },\n ): void;\n }\n export type SwarmExtension<\n M extends ExtensionManifest = ExtensionManifest & {\n runtime: "api";\n },\n > = (api: ExtensionApi<M>) => void;\n export { block, modify } from "./contract-runtime";\n}\n';
|
|
@@ -117,6 +117,11 @@ export interface TaskCreateEvent {
|
|
|
117
117
|
description: string;
|
|
118
118
|
origin: TaskCreateOrigin;
|
|
119
119
|
requestInfo?: RequestInfo;
|
|
120
|
+
/** Slack ingress metadata; not persisted or writable through modify. */
|
|
121
|
+
slack?: {
|
|
122
|
+
trigger: "mention" | "thread-buffer";
|
|
123
|
+
messages: { ts: string; userId: string; text: string }[];
|
|
124
|
+
};
|
|
120
125
|
}
|
|
121
126
|
|
|
122
127
|
/** A pre.task.create hook blocked a task before it was created. */
|
package/src/http/all-routes.ts
CHANGED
|
@@ -15,9 +15,11 @@ import "./apps";
|
|
|
15
15
|
import "./assets";
|
|
16
16
|
import "./budgets";
|
|
17
17
|
import "./codex-oauth-device";
|
|
18
|
+
import "./codex-oauth-accounts";
|
|
18
19
|
import "./codex-oauth-keep-warm";
|
|
19
20
|
import "./comb";
|
|
20
21
|
import "./config";
|
|
22
|
+
import "./pool-keys";
|
|
21
23
|
import "./context";
|
|
22
24
|
import "./core";
|
|
23
25
|
import "./db-query";
|
package/src/http/api-keys.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
recordKeyRateLimitWindows,
|
|
11
11
|
recordKeySeatMismatch,
|
|
12
12
|
recordKeyUsage,
|
|
13
|
+
recordKeyValidation,
|
|
13
14
|
setApiKeyName,
|
|
14
15
|
setApiKeyPlan,
|
|
15
16
|
} from "../be/db";
|
|
@@ -79,6 +80,39 @@ const reportRateLimit = route({
|
|
|
79
80
|
auth: { apiKey: true },
|
|
80
81
|
});
|
|
81
82
|
|
|
83
|
+
const reportValidation = route({
|
|
84
|
+
method: "post",
|
|
85
|
+
path: "/api/keys/report-validation",
|
|
86
|
+
pattern: ["api", "keys", "report-validation"],
|
|
87
|
+
summary: "Record live validation results for a credential pool",
|
|
88
|
+
tags: ["API Keys"],
|
|
89
|
+
body: z.object({
|
|
90
|
+
keyType: z.string(),
|
|
91
|
+
keys: z
|
|
92
|
+
.array(
|
|
93
|
+
z.object({
|
|
94
|
+
keyIndex: z.number().int().min(0),
|
|
95
|
+
keySuffix: z.string().min(1).max(10),
|
|
96
|
+
ok: z.boolean(),
|
|
97
|
+
httpStatus: z.number().int().optional(),
|
|
98
|
+
}),
|
|
99
|
+
)
|
|
100
|
+
.min(1)
|
|
101
|
+
.max(100),
|
|
102
|
+
scope: z.string().optional(),
|
|
103
|
+
scopeId: z.string().optional(),
|
|
104
|
+
}),
|
|
105
|
+
responses: {
|
|
106
|
+
200: { description: "Validation recorded", schema: successMessageSchema },
|
|
107
|
+
400: { description: "Validation error" },
|
|
108
|
+
401: { description: "Unauthorized" },
|
|
109
|
+
},
|
|
110
|
+
auth: { apiKey: true },
|
|
111
|
+
rbac: {
|
|
112
|
+
ungated: "worker credential telemetry, same posture as POST /api/keys/report-rate-limit",
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
|
|
82
116
|
const reportAuthFailure = route({
|
|
83
117
|
method: "post",
|
|
84
118
|
path: "/api/keys/report-auth-failure",
|
|
@@ -223,6 +257,7 @@ const getAvailable = route({
|
|
|
223
257
|
schema: z.object({
|
|
224
258
|
success: z.literal(true),
|
|
225
259
|
availableIndices: z.array(z.number().int()),
|
|
260
|
+
rejectedIndices: z.array(z.number().int()).optional(),
|
|
226
261
|
totalKeys: z.number().int(),
|
|
227
262
|
/** Indices excluded only by an active model-scoped window block. Present only when `model` was passed. */
|
|
228
263
|
modelBlockedIndices: z.array(z.number().int()).optional(),
|
|
@@ -273,6 +308,8 @@ const ApiKeyStatusSchema = z.object({
|
|
|
273
308
|
/** Auth failures in a row since the last success or clear. */
|
|
274
309
|
consecutiveAuthFailures: z.number().int(),
|
|
275
310
|
lastAuthFailureAt: z.string().nullable(),
|
|
311
|
+
lastValidationError: z.string().nullable(),
|
|
312
|
+
lastValidationFailedAt: z.string().nullable(),
|
|
276
313
|
/** Derived, readable view of any rejected model-scoped window (Fable/Opus/Sonnet) on this key. */
|
|
277
314
|
modelLimits: z.array(
|
|
278
315
|
z.object({
|
|
@@ -555,6 +592,21 @@ export async function handleApiKeys(
|
|
|
555
592
|
return true;
|
|
556
593
|
}
|
|
557
594
|
|
|
595
|
+
// POST /api/keys/report-validation
|
|
596
|
+
if (reportValidation.match(req.method, pathSegments)) {
|
|
597
|
+
const parsed = await reportValidation.parse(req, res, pathSegments, queryParams);
|
|
598
|
+
if (!parsed) return true;
|
|
599
|
+
|
|
600
|
+
const { keyType, keys, scope, scopeId } = parsed.body;
|
|
601
|
+
try {
|
|
602
|
+
await recordKeyValidation(keyType, keys, scope, scopeId ?? null);
|
|
603
|
+
reportValidation.respond(res, 200, { success: true, message: "Key validation recorded" });
|
|
604
|
+
} catch (err) {
|
|
605
|
+
jsonError(res, err instanceof Error ? err.message : "Failed to record key validation", 500);
|
|
606
|
+
}
|
|
607
|
+
return true;
|
|
608
|
+
}
|
|
609
|
+
|
|
558
610
|
// POST /api/keys/report-auth-failure
|
|
559
611
|
if (reportAuthFailure.match(req.method, pathSegments)) {
|
|
560
612
|
const parsed = await reportAuthFailure.parse(req, res, pathSegments, queryParams);
|
|
@@ -650,6 +702,7 @@ export async function handleApiKeys(
|
|
|
650
702
|
getAvailable.respond(res, 200, {
|
|
651
703
|
success: true,
|
|
652
704
|
availableIndices: result.availableIndices,
|
|
705
|
+
rejectedIndices: result.rejectedIndices,
|
|
653
706
|
totalKeys,
|
|
654
707
|
authFailureFence: result.authFailureFence,
|
|
655
708
|
...(model !== undefined
|
package/src/http/assets.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { getApp } from "../apps/store";
|
|
4
|
+
import { normalizeAssetKey } from "../assets/key";
|
|
5
|
+
import { sourceMoveError, sourceOfKey } from "../assets/source-key";
|
|
4
6
|
import { auditAssetKeys } from "../be/asset-key-audit";
|
|
5
7
|
import { AssetKeyAuthorizationError, authorizeAssetKeyWrite } from "../be/asset-key-auth";
|
|
6
8
|
import { resolveHttpAuditUserId } from "../be/audit-user";
|
|
@@ -15,6 +17,7 @@ import {
|
|
|
15
17
|
upsertAssetKeyMapping,
|
|
16
18
|
} from "../be/db";
|
|
17
19
|
import { getScriptById } from "../be/scripts/db";
|
|
20
|
+
import { getSessionRoot } from "../be/session-shares";
|
|
18
21
|
import { can, type RbacResource } from "../rbac";
|
|
19
22
|
import {
|
|
20
23
|
type AssetEntityType,
|
|
@@ -334,6 +337,18 @@ export async function handleAssets(
|
|
|
334
337
|
);
|
|
335
338
|
return true;
|
|
336
339
|
}
|
|
340
|
+
const destination = normalizeAssetKey(parsed.body.key);
|
|
341
|
+
if (sourceOfKey(destination)) {
|
|
342
|
+
// The session root's own source key is the only allowed source key
|
|
343
|
+
// (Undo). A root we cannot load allows none.
|
|
344
|
+
const rootId = task.parentTaskId ? (await getSessionRoot(task.id))?.id : task.id;
|
|
345
|
+
const root = rootId === task.id ? task : rootId ? await getTaskById(rootId) : null;
|
|
346
|
+
const sourceError = sourceMoveError(destination, root ?? { id: task.id });
|
|
347
|
+
if (sourceError) {
|
|
348
|
+
jsonError(res, sourceError, 400);
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
337
352
|
// Only the tasks checked here move: a subtask created after this read stays put.
|
|
338
353
|
const tasks = parsed.body.cascade ? await getRootTaskChain(task.id) : [task];
|
|
339
354
|
for (const target of tasks) {
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { decryptSecret, getEncryptionKey } from "../be/crypto";
|
|
4
|
+
import { deleteKv, deleteSwarmConfigByKey, getDbClient, getKeyStatuses, getKv } from "../be/db";
|
|
5
|
+
import { retirePoolKeyStatus } from "../be/db/api-keys";
|
|
6
|
+
import { deriveCodexKeySuffix } from "../providers/codex-oauth/auth-json";
|
|
7
|
+
import { decodeJwt, extractChatgptPlanType } from "../providers/codex-oauth/flow";
|
|
8
|
+
import { MAX_CODEX_OAUTH_SLOT } from "../providers/codex-oauth/storage";
|
|
9
|
+
import {
|
|
10
|
+
CODEX_AUTH_FAILURE_BENCH_THRESHOLD,
|
|
11
|
+
CODEX_AUTH_WATCH_NAMESPACE,
|
|
12
|
+
codexAuthBenchMarkerKey,
|
|
13
|
+
} from "../utils/codex-auth-failure";
|
|
14
|
+
import { ensureConfigAdmin, requestSecretReadScope, runConfigWriteSideEffects } from "./config";
|
|
15
|
+
import { route } from "./route-def";
|
|
16
|
+
import { jsonError } from "./utils";
|
|
17
|
+
|
|
18
|
+
export const CodexAccountSchema = z.object({
|
|
19
|
+
slot: z.number().int().min(0),
|
|
20
|
+
configKey: z.string(),
|
|
21
|
+
legacy: z.boolean(),
|
|
22
|
+
suffix: z.string().nullable(),
|
|
23
|
+
plan: z.string().nullable(),
|
|
24
|
+
email: z.string().nullable(),
|
|
25
|
+
status: z.enum(["ok", "needs_login", "unchecked"]),
|
|
26
|
+
});
|
|
27
|
+
export type CodexAccount = z.infer<typeof CodexAccountSchema>;
|
|
28
|
+
|
|
29
|
+
export const CodexAccountsResponseSchema = z.object({ accounts: z.array(CodexAccountSchema) });
|
|
30
|
+
export type CodexAccountsResponse = z.infer<typeof CodexAccountsResponseSchema>;
|
|
31
|
+
|
|
32
|
+
const listAccounts = route({
|
|
33
|
+
method: "get",
|
|
34
|
+
path: "/api/codex-oauth/accounts",
|
|
35
|
+
pattern: ["api", "codex-oauth", "accounts"],
|
|
36
|
+
summary: "List stored Codex ChatGPT accounts",
|
|
37
|
+
tags: ["Codex OAuth"],
|
|
38
|
+
responses: {
|
|
39
|
+
200: { description: "Stored account metadata", schema: CodexAccountsResponseSchema },
|
|
40
|
+
403: { description: "Caller cannot read all secrets" },
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const deleteAccount = route({
|
|
45
|
+
method: "delete",
|
|
46
|
+
path: "/api/codex-oauth/accounts/{slot}",
|
|
47
|
+
pattern: ["api", "codex-oauth", "accounts", null],
|
|
48
|
+
summary: "Remove a stored Codex ChatGPT account",
|
|
49
|
+
tags: ["Codex OAuth"],
|
|
50
|
+
rbac: { permission: "config.delete.any" },
|
|
51
|
+
params: z.object({ slot: z.coerce.number().int().min(0).max(MAX_CODEX_OAUTH_SLOT) }),
|
|
52
|
+
responses: {
|
|
53
|
+
200: { description: "Remaining accounts", schema: CodexAccountsResponseSchema },
|
|
54
|
+
403: { description: "Config administration and permission to read all secrets required" },
|
|
55
|
+
404: { description: "Account slot does not exist" },
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
type AccountRow = { key: string; value: string; encrypted: number };
|
|
60
|
+
|
|
61
|
+
async function accountRows(): Promise<Array<AccountRow & { slot: number; legacy: boolean }>> {
|
|
62
|
+
const rows = await getDbClient().query<AccountRow>(
|
|
63
|
+
"SELECT key, value, encrypted FROM swarm_config WHERE scope = 'global' AND (key = 'codex_oauth' OR key GLOB 'codex_oauth_[0-9]*')",
|
|
64
|
+
);
|
|
65
|
+
const hasSlotZero = rows.some((row) => row.key === "codex_oauth_0");
|
|
66
|
+
return rows
|
|
67
|
+
.flatMap((row) => {
|
|
68
|
+
if (row.key === "codex_oauth") {
|
|
69
|
+
return hasSlotZero ? [] : [{ ...row, slot: 0, legacy: true }];
|
|
70
|
+
}
|
|
71
|
+
const match = /^codex_oauth_(0|[1-9]\d*)$/.exec(row.key);
|
|
72
|
+
if (!match) return [];
|
|
73
|
+
const slot = Number(match[1]);
|
|
74
|
+
return slot <= MAX_CODEX_OAUTH_SLOT ? [{ ...row, slot, legacy: false }] : [];
|
|
75
|
+
})
|
|
76
|
+
.sort((a, b) => a.slot - b.slot);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const AccountCredentialsSchema = z.object({
|
|
80
|
+
access: z.string().min(1),
|
|
81
|
+
refresh: z.string().min(1),
|
|
82
|
+
expires: z.number().finite(),
|
|
83
|
+
accountId: z.string().min(1),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
function readCredentials(row: AccountRow): z.infer<typeof AccountCredentialsSchema> | null {
|
|
87
|
+
try {
|
|
88
|
+
const value = row.encrypted ? decryptSecret(row.value, getEncryptionKey()) : row.value;
|
|
89
|
+
return AccountCredentialsSchema.parse(JSON.parse(value));
|
|
90
|
+
} catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function accountSuffix(row: AccountRow): string | null {
|
|
96
|
+
const credentials = readCredentials(row);
|
|
97
|
+
return credentials ? deriveCodexKeySuffix(credentials.access, credentials.accountId) : null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function accountsResponse(): Promise<CodexAccountsResponse> {
|
|
101
|
+
const rows = await accountRows();
|
|
102
|
+
const statuses = await getKeyStatuses("CODEX_OAUTH", "global", null);
|
|
103
|
+
const accounts: CodexAccount[] = [];
|
|
104
|
+
for (const row of rows) {
|
|
105
|
+
const account: CodexAccount = {
|
|
106
|
+
slot: row.slot,
|
|
107
|
+
configKey: row.key,
|
|
108
|
+
legacy: row.legacy,
|
|
109
|
+
suffix: null,
|
|
110
|
+
plan: null,
|
|
111
|
+
email: null,
|
|
112
|
+
status: "needs_login",
|
|
113
|
+
};
|
|
114
|
+
const credentials = readCredentials(row);
|
|
115
|
+
if (!credentials) {
|
|
116
|
+
accounts.push(account);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
account.suffix = deriveCodexKeySuffix(credentials.access, credentials.accountId);
|
|
120
|
+
account.plan = extractChatgptPlanType(credentials.access);
|
|
121
|
+
const profile = decodeJwt(credentials.access)?.["https://api.openai.com/profile"];
|
|
122
|
+
if (profile && typeof profile === "object" && "email" in profile) {
|
|
123
|
+
account.email = typeof profile.email === "string" && profile.email ? profile.email : null;
|
|
124
|
+
}
|
|
125
|
+
const status = statuses.find((entry) => entry.keySuffix === account.suffix);
|
|
126
|
+
const marker = await getKv(CODEX_AUTH_WATCH_NAMESPACE, codexAuthBenchMarkerKey(account.suffix));
|
|
127
|
+
const statusBenched =
|
|
128
|
+
status &&
|
|
129
|
+
status.consecutiveAuthFailures >= CODEX_AUTH_FAILURE_BENCH_THRESHOLD &&
|
|
130
|
+
status.status === "rate_limited";
|
|
131
|
+
account.status = marker || statusBenched ? "needs_login" : status ? "ok" : "unchecked";
|
|
132
|
+
accounts.push(account);
|
|
133
|
+
}
|
|
134
|
+
return { accounts };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Both responses list account emails and plans, so both routes need this gate. */
|
|
138
|
+
async function ensureSecretRead(req: IncomingMessage, res: ServerResponse): Promise<boolean> {
|
|
139
|
+
if ((await requestSecretReadScope(req)) === "all") return true;
|
|
140
|
+
jsonError(res, "Reading Codex accounts requires access to all secrets", 403);
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export async function handleCodexOAuthAccounts(
|
|
145
|
+
req: IncomingMessage,
|
|
146
|
+
res: ServerResponse,
|
|
147
|
+
pathSegments: string[],
|
|
148
|
+
queryParams: URLSearchParams,
|
|
149
|
+
): Promise<boolean> {
|
|
150
|
+
if (listAccounts.match(req.method, pathSegments)) {
|
|
151
|
+
const parsed = await listAccounts.parse(req, res, pathSegments, queryParams);
|
|
152
|
+
if (!parsed) return true;
|
|
153
|
+
if (!(await ensureSecretRead(req, res))) return true;
|
|
154
|
+
listAccounts.respond(res, 200, await accountsResponse());
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (deleteAccount.match(req.method, pathSegments)) {
|
|
159
|
+
const parsed = await deleteAccount.parse(req, res, pathSegments, queryParams);
|
|
160
|
+
if (!parsed) return true;
|
|
161
|
+
if (!(await ensureConfigAdmin(req, res, "config.delete.any"))) return true;
|
|
162
|
+
if (!(await ensureSecretRead(req, res))) return true;
|
|
163
|
+
const deletedKey = await getDbClient().transaction(async () => {
|
|
164
|
+
const rows = await accountRows();
|
|
165
|
+
const account = rows.find((row) => row.slot === parsed.params.slot);
|
|
166
|
+
if (!account || !(await deleteSwarmConfigByKey("global", null, account.key))) return null;
|
|
167
|
+
// A new sign-in takes the first free slot, so it must not inherit this
|
|
168
|
+
// account's auth bench (keyed by slot) or its bench marker.
|
|
169
|
+
const suffix = accountSuffix(account);
|
|
170
|
+
if (suffix) {
|
|
171
|
+
const remaining = rows.flatMap((row) => {
|
|
172
|
+
const keySuffix = row === account ? null : accountSuffix(row);
|
|
173
|
+
return keySuffix ? [{ keySuffix, keyIndex: row.slot }] : [];
|
|
174
|
+
});
|
|
175
|
+
await retirePoolKeyStatus("CODEX_OAUTH", suffix, remaining);
|
|
176
|
+
if (!remaining.some((row) => row.keySuffix === suffix)) {
|
|
177
|
+
await deleteKv(CODEX_AUTH_WATCH_NAMESPACE, codexAuthBenchMarkerKey(suffix));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return account.key;
|
|
181
|
+
});
|
|
182
|
+
if (deletedKey === null) {
|
|
183
|
+
jsonError(res, "Codex account slot does not exist", 404);
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
await runConfigWriteSideEffects("global", deletedKey, "");
|
|
187
|
+
deleteAccount.respond(res, 200, await accountsResponse());
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
return false;
|
|
191
|
+
}
|
package/src/http/comb.ts
CHANGED
|
@@ -2,7 +2,12 @@ import type { IncomingMessage, ServerResponse } from "node:http";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { resolveHttpAuditUserId } from "../be/audit-user";
|
|
4
4
|
import { getCombConfig } from "../comb/config";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
REVIEW_BATCH_MAX,
|
|
7
|
+
REVIEW_NOTE_MAX,
|
|
8
|
+
ReviewBatchError,
|
|
9
|
+
sendReviewBatch,
|
|
10
|
+
} from "../comb/review-batch";
|
|
6
11
|
import { can } from "../rbac";
|
|
7
12
|
import { TaskCreationBlockedError } from "../tasks/errors";
|
|
8
13
|
import { scrubSecrets } from "../utils/secret-scrubber";
|
|
@@ -24,6 +29,12 @@ const resultSchema = z.object({
|
|
|
24
29
|
skipped: z.array(skippedSchema),
|
|
25
30
|
/** Comments of an earlier send whose missing "sent" reply this batch posted again. */
|
|
26
31
|
repaired: z.array(z.object({ id: z.string(), taskId: z.string() })),
|
|
32
|
+
/**
|
|
33
|
+
* With a new task only. `online`: a lead got it. `offline`: every lead is offline,
|
|
34
|
+
* so the task waits for one. `none`: no lead, so the task waits
|
|
35
|
+
* unassigned in the pool.
|
|
36
|
+
*/
|
|
37
|
+
lead: z.enum(["online", "offline", "none"]).optional(),
|
|
27
38
|
});
|
|
28
39
|
|
|
29
40
|
const batchErrorSchema = z.object({ error: z.string(), skipped: z.array(skippedSchema) });
|
|
@@ -41,7 +52,7 @@ const reviewBatchRoute = route({
|
|
|
41
52
|
pattern: ["api", "comb", "review-batches"],
|
|
42
53
|
summary: "Send agent-fs comments to the swarm as one lead task",
|
|
43
54
|
description:
|
|
44
|
-
"Comb's 'Send to swarm'. The server reads each comment again from agent-fs with its bootstrap key, skips replies, resolved comments, and comments already sent, and creates ONE task for the lead. Each sent comment gets a `[comb:sent task=<id>]` reply from the swarm service account. A comment is sent at most once. When an earlier send lost its reply, the batch posts that reply again (`repaired`). Answers 404 while COMB_ENABLED is off.",
|
|
55
|
+
"Comb's 'Send to swarm'. The server reads each comment again from agent-fs with its bootstrap key, skips replies, resolved comments, and comments already sent, and creates ONE task for the lead. Each sent comment gets a `[comb:sent task=<id>]` reply from the swarm service account. A comment is sent at most once. An optional `note` from the sender goes into the task text. When an earlier send lost its reply, the batch posts that reply again (`repaired`). Answers 404 while COMB_ENABLED is off.",
|
|
45
56
|
tags: ["Comb"],
|
|
46
57
|
body: z.object({
|
|
47
58
|
orgId: z.string().min(1),
|
|
@@ -54,6 +65,8 @@ const reviewBatchRoute = route({
|
|
|
54
65
|
.min(1)
|
|
55
66
|
.max(1024)
|
|
56
67
|
.refine(isDrivePath, "scopePath must start with '/' and have no '.' or '..' segment"),
|
|
68
|
+
/** The sender's note for the lead about the whole batch. */
|
|
69
|
+
note: z.string().max(REVIEW_NOTE_MAX).optional(),
|
|
57
70
|
}),
|
|
58
71
|
responses: {
|
|
59
72
|
200: {
|
package/src/http/config.ts
CHANGED
|
@@ -67,7 +67,7 @@ export function readsRepoSecrets(
|
|
|
67
67
|
}).allow;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
async function requestSecretReadScope(
|
|
70
|
+
export async function requestSecretReadScope(
|
|
71
71
|
req: IncomingMessage,
|
|
72
72
|
resolvedScope?: { agentId?: string; repoId?: string },
|
|
73
73
|
): Promise<"all" | "self" | "none"> {
|
package/src/http/core.ts
CHANGED
|
@@ -95,6 +95,12 @@ import {
|
|
|
95
95
|
*/
|
|
96
96
|
const injectedEnvOriginals = new Map<string, { original: string | undefined; injected: string }>();
|
|
97
97
|
|
|
98
|
+
/** Read the deployment value without a stored config's temporary env injection. */
|
|
99
|
+
export function getDeploymentEnvValue(key: string): string | undefined {
|
|
100
|
+
const tracked = injectedEnvOriginals.get(key);
|
|
101
|
+
return tracked && process.env[key] === tracked.injected ? tracked.original : process.env[key];
|
|
102
|
+
}
|
|
103
|
+
|
|
98
104
|
export async function loadGlobalConfigsIntoEnv(override = false): Promise<string[]> {
|
|
99
105
|
const globalConfigs = await getInjectableGlobalConfigs();
|
|
100
106
|
const updated: string[] = [];
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
localTasksPerWeek,
|
|
12
12
|
} from "../fleet-benchmark";
|
|
13
13
|
import { isTelemetryEnabled } from "../telemetry";
|
|
14
|
-
import { COMPANY_TEAM_SIZES, COMPANY_USE_CASES } from "../utils/company-profile";
|
|
14
|
+
import { COMPANY_TEAM_SIZES, COMPANY_USE_CASES, reportedUseCase } from "../utils/company-profile";
|
|
15
15
|
import { normalizeOrgWebsite } from "../utils/org-website";
|
|
16
16
|
import { globalConfig } from "./global-config";
|
|
17
17
|
import { rejectGuest } from "./request-principal";
|
|
@@ -188,6 +188,9 @@ export async function buildFleetBenchmarkResponse(
|
|
|
188
188
|
normalizeOrgWebsite(website) && team.success && use.success
|
|
189
189
|
? { teamSize: team.data, useCase: use.data }
|
|
190
190
|
: null;
|
|
191
|
+
// The proxy answers 400 for a use case its catalog does not list yet, so
|
|
192
|
+
// that use case compares against the whole fleet.
|
|
193
|
+
const proxyPeerGroup = peerGroup && reportedUseCase(peerGroup.useCase) ? peerGroup : null;
|
|
191
194
|
const empty = {
|
|
192
195
|
level: peerGroup ? ("full" as const) : ("basic" as const),
|
|
193
196
|
peerGroup: null,
|
|
@@ -215,7 +218,7 @@ export async function buildFleetBenchmarkResponse(
|
|
|
215
218
|
if (finishedTasks < FLEET_MIN_FINISHED_TASKS)
|
|
216
219
|
return { ...empty, state: "not_enough_tasks", local };
|
|
217
220
|
|
|
218
|
-
const fleet = await fleetBenchmarks(now.getTime(),
|
|
221
|
+
const fleet = await fleetBenchmarks(now.getTime(), proxyPeerGroup);
|
|
219
222
|
const responsePeerGroup =
|
|
220
223
|
peerGroup && fleet?.peer_group
|
|
221
224
|
? {
|
package/src/http/index.ts
CHANGED
|
@@ -76,6 +76,7 @@ import { handleApprovalRequests } from "./approval-requests";
|
|
|
76
76
|
import { handleApps } from "./apps";
|
|
77
77
|
import { handleAssets } from "./assets";
|
|
78
78
|
import { handleBudgets } from "./budgets";
|
|
79
|
+
import { handleCodexOAuthAccounts } from "./codex-oauth-accounts";
|
|
79
80
|
import { handleCodexOAuthDevice } from "./codex-oauth-device";
|
|
80
81
|
import { handleCodexOAuthKeepWarm } from "./codex-oauth-keep-warm";
|
|
81
82
|
import { handleComb } from "./comb";
|
|
@@ -115,6 +116,7 @@ import { handlePageProxy } from "./page-proxy";
|
|
|
115
116
|
import { handlePages } from "./pages";
|
|
116
117
|
import { handlePagesPublic } from "./pages-public";
|
|
117
118
|
import { handlePoll } from "./poll";
|
|
119
|
+
import { handlePoolKeys } from "./pool-keys";
|
|
118
120
|
import { handlePricing } from "./pricing";
|
|
119
121
|
import { handlePromptTemplates } from "./prompt-templates";
|
|
120
122
|
import { handleRanking } from "./ranking";
|
|
@@ -360,6 +362,7 @@ const httpServer = createHttpServer(async (req, res) => {
|
|
|
360
362
|
() => handleStatus(req, res, pathSegments, queryParams),
|
|
361
363
|
() => handleOnboarding(req, res, pathSegments, queryParams),
|
|
362
364
|
() => handleCodexOAuthDevice(req, res, pathSegments, queryParams),
|
|
365
|
+
() => handleCodexOAuthAccounts(req, res, pathSegments, queryParams),
|
|
363
366
|
() => handleActiveSessions(req, res, pathSegments, queryParams, myAgentId),
|
|
364
367
|
() => handlePricing(req, res, pathSegments, queryParams, myAgentId),
|
|
365
368
|
() => handleSchedules(req, res, pathSegments, queryParams, myAgentId),
|
|
@@ -367,6 +370,7 @@ const httpServer = createHttpServer(async (req, res) => {
|
|
|
367
370
|
() => handleWorkflowEvents(req, res, pathSegments, queryParams),
|
|
368
371
|
() => handleApprovalRequests(req, res, pathSegments, queryParams),
|
|
369
372
|
() => handleApps(req, res, pathSegments, queryParams, myAgentId),
|
|
373
|
+
() => handlePoolKeys(req, res, pathSegments, queryParams),
|
|
370
374
|
() => handleConfig(req, res, pathSegments, queryParams),
|
|
371
375
|
() => handleFs(req, res, pathSegments, queryParams, myAgentId),
|
|
372
376
|
() => handleComb(req, res, pathSegments, queryParams, myAgentId),
|
|
@@ -898,6 +902,13 @@ httpServer
|
|
|
898
902
|
.catch((err) => {
|
|
899
903
|
console.error("[boot-scrub-sweep] startup scrub failed (non-fatal):", err);
|
|
900
904
|
});
|
|
905
|
+
|
|
906
|
+
// Source key backfill is idempotent, resumable, and non-fatal.
|
|
907
|
+
import("../be/session-source-backfill")
|
|
908
|
+
.then(({ runSessionSourceBackfill }) => runSessionSourceBackfill())
|
|
909
|
+
.catch((err) => {
|
|
910
|
+
console.error("[session-source-backfill] startup run failed (non-fatal):", err);
|
|
911
|
+
});
|
|
901
912
|
})
|
|
902
913
|
.on("error", (err) => {
|
|
903
914
|
console.error("HTTP Server Error:", err);
|
package/src/http/integrations.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
registerTelegramWebhook,
|
|
9
9
|
} from "../integrations/chat-sdk/status";
|
|
10
10
|
import { buildSlackManifest } from "../slack/manifest";
|
|
11
|
+
import { selectCredential } from "../utils/credentials";
|
|
11
12
|
import { ensureHttpPermission } from "./config";
|
|
12
13
|
import { resolveConfigValue, resolveMcpBaseUrl } from "./config-values";
|
|
13
14
|
import { route } from "./route-def";
|
|
@@ -231,7 +232,10 @@ export function createIntegrationsHandler(deps: TestConnectionDeps = {}) {
|
|
|
231
232
|
}
|
|
232
233
|
|
|
233
234
|
try {
|
|
234
|
-
|
|
235
|
+
// A comma pool is one key per call, as the runner does.
|
|
236
|
+
const client = buildClient(
|
|
237
|
+
selectCredential(apiKey, undefined, "ANTHROPIC_API_KEY").selected,
|
|
238
|
+
);
|
|
235
239
|
const agent = await client.beta.agents.retrieve(agentId);
|
|
236
240
|
// `agent.model` is `BetaManagedAgentsModelConfig` ({id, speed}). Flatten
|
|
237
241
|
// to a string so the UI can render it directly without type guards.
|