@desplega.ai/agent-swarm 1.149.1 → 1.151.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 +18 -3
- package/dist/{acp-adapter-vhkzfpjg.js → acp-adapter-ex8hzjfc.js} +5 -5
- package/dist/{actions-9d2694yk.js → actions-2bgpyhww.js} +39 -11
- package/dist/{app-fq2ggynt.js → app-1706zhr1.js} +7 -7
- package/dist/{artifact-gj9arwrr.js → artifact-td4q4dch.js} +1 -1
- package/dist/{assistant-edb6n9nn.js → assistant-bavke5p7.js} +44 -20
- package/dist/{azure-openai-responses-cqar64rc.js → azure-openai-responses-f38x2avq.js} +13 -13
- package/dist/{boot-reembed-msweabbp.js → boot-reembed-3cmrrmpa.js} +9 -9
- package/dist/{boot-reembed-q5wd3tha.js → boot-reembed-nafcmmmc.js} +14 -16
- package/dist/{boot-scrub-logs-3g1az933.js → boot-scrub-logs-cd6a2sq0.js} +5 -5
- package/dist/{claude-adapter-v3rnzsdc.js → claude-adapter-wfp2gzvz.js} +15 -16
- package/dist/{claude-managed-adapter-qvsmh6mv.js → claude-managed-adapter-dsmkcrjy.js} +3 -3
- package/dist/{claude-sdk-session-qxrpsna1.js → claude-sdk-session-bht9jjak.js} +15 -16
- package/dist/{cli-5m0qmvdp.js → cli-0qb1z6s2.js} +1 -1
- package/dist/{cli-9d8ntetj.js → cli-2658f6at.js} +1 -1
- package/dist/{cli-7evafqvy.js → cli-2tbvf1z1.js} +85 -69
- package/dist/{cli-k351hv5k.js → cli-3v9xhjcn.js} +24 -12
- package/dist/{cli-1ebpar1q.js → cli-4wppfkcm.js} +350 -20
- package/dist/{cli-4260r1tg.js → cli-5jd2z7av.js} +97 -84
- package/dist/{cli-cv7s5zdq.js → cli-5tpgq60f.js} +9865 -715
- package/dist/{cli-6skz3rk8.js → cli-7tgcstn2.js} +9 -4
- package/dist/{cli-01g3x1ca.js → cli-9004vfdf.js} +108 -56
- package/dist/{cli-1c94wvcw.js → cli-a1z3h107.js} +124 -99
- package/dist/{cli-9br8ypfv.js → cli-czjtgfys.js} +2 -2
- package/dist/{cli-5my3bjsd.js → cli-d3p82erb.js} +7 -7
- package/dist/{cli-e29gmdmw.js → cli-dv6cdjs2.js} +2 -1
- package/dist/{cli-wby1xnnb.js → cli-f1wky6zp.js} +1 -1
- package/dist/{cli-zvz62chv.js → cli-fm4ytsdk.js} +4 -4
- package/dist/{cli-tg8qj8ee.js → cli-frcta2w4.js} +6 -6
- package/dist/{cli-7xx10h0a.js → cli-g69fdc9g.js} +2 -2
- package/dist/{cli-cmp8g3sw.js → cli-gbfwcd3k.js} +44 -9
- package/dist/{handlers-nsg0bcye.js → cli-gjyvhmmh.js} +97 -45
- package/dist/{cli-08b07b4r.js → cli-ht5rs63n.js} +1 -1
- package/dist/{cli-jecc01cb.js → cli-j4nv2wqy.js} +2 -4
- package/dist/{cli-cd60fv6j.js → cli-j9xczhaq.js} +1 -1
- package/dist/cli-pf9q3696.js +172 -0
- package/dist/{cli-xm87hzbw.js → cli-pgj4a5fb.js} +1 -1
- package/dist/{cli-jezx9b9c.js → cli-ppm4975n.js} +9 -3
- package/dist/{cli-3z634pqe.js → cli-q88st9aj.js} +2 -2
- package/dist/{cli-3sysyjve.js → cli-r2q6pevn.js} +142 -49
- package/dist/{cli-07jd68a1.js → cli-re8z5p8p.js} +1 -1
- package/dist/{cli-qgd4sqz5.js → cli-s14agm5s.js} +121 -14
- package/dist/{cli-svbrpf5q.js → cli-sjaae6ff.js} +9872 -424
- package/dist/{cli-73gq0zxn.js → cli-vwm416m6.js} +13 -13
- package/dist/{cli-4wtq5jjv.js → cli-wbyk465z.js} +1 -1
- package/dist/{cli-3apbzgyk.js → cli-z61ykvam.js} +19 -14
- package/dist/{cli-e06a8a7y.js → cli-zkxymas5.js} +4 -3
- package/dist/cli.js +26 -25
- package/dist/{codex-adapter-jyw7a91z.js → codex-adapter-jjjk8w4m.js} +8 -9
- package/dist/{codex-hook-hfrt9shr.js → codex-hook-8jexc34x.js} +3 -3
- package/dist/{codex-session-runner-bmngj0s5.js → codex-session-runner-nd6ayxkx.js} +8 -9
- package/dist/{commands-n755g391.js → commands-9v4psyq1.js} +5 -5
- package/dist/{db-s1cgh5gv.js → db-wnt58tpe.js} +5 -5
- package/dist/dispatcher-05jh1aqk.js +74 -0
- package/dist/{e2b-p04vmtx0.js → e2b-413b8t4v.js} +1 -1
- package/dist/{google-generative-ai-tsskx7te.js → google-generative-ai-ndjmj5fa.js} +7 -7
- package/dist/{google-vertex-ppw8ydpa.js → google-vertex-fr665tqs.js} +10 -10
- package/dist/handlers-tw90pkh5.js +65 -0
- package/dist/{hook-jf0xe8zh.js → hook-123e2scb.js} +11 -12
- package/dist/{hook-h9cqrm8q.js → hook-sxe3r349.js} +11 -12
- package/dist/{http-nc6pydw5.js → http-ecsar8ez.js} +1442 -581
- package/dist/{index-ezxkds2n.js → index-16js521z.js} +335 -107
- package/dist/{index-769nc012.js → index-hw26vj40.js} +25 -19
- package/dist/{index-0wdy6gwc.js → index-nnrbpcjp.js} +25 -17
- package/dist/index-ymn7em0p.js +25 -0
- package/dist/{index-x1zcj09z.js → index-zj518gf9.js} +26 -20
- package/dist/{keepalive-nesm3xb6.js → keepalive-asqtcm4y.js} +7 -7
- package/dist/{lead-bwy6t2za.js → lead-xbfr7fvt.js} +40 -48
- package/dist/{maintenance-8w393zf0.js → maintenance-qma55qvw.js} +9 -9
- package/dist/{oauth-refresh-sweep-sh8xjy08.js → oauth-refresh-sweep-3s04c6qw.js} +6 -6
- package/dist/{onboard-25ps29h1.js → onboard-drskd386.js} +13 -4
- package/dist/{openai-codex-responses-g7w48y30.js → openai-codex-responses-wy6a43wd.js} +13 -13
- package/dist/{openai-responses-nmeyptke.js → openai-responses-6xwf7bzg.js} +19 -19
- package/dist/{opencode-adapter-dce4wskd.js → opencode-adapter-28axqa09.js} +4 -4
- package/dist/{otel-impl-b93gxnz5.js → otel-impl-943c96er.js} +1 -1
- package/dist/{pi-mono-adapter-zvshk5cj.js → pi-mono-adapter-jcqt71cg.js} +17 -19
- package/dist/{pricing-refresh-5v56x7gb.js → pricing-refresh-mx51az05.js} +7 -7
- package/dist/{rbac-roles-x55cnezn.js → rbac-roles-78baecqd.js} +13 -9
- package/dist/{rbac-roles-0p0s6pbj.js → rbac-roles-wcmrg6sv.js} +7 -7
- package/dist/{render-v2-hyd45w8b.js → render-v2-8zjntgf5.js} +7 -7
- package/dist/{seed-pricing-49gbgn96.js → seed-pricing-jqjs6cws.js} +6 -6
- package/dist/{setup-qvq64vzq.js → setup-4pks0vfk.js} +2 -2
- package/dist/work-objects-wangt792.js +165 -0
- package/dist/{worker-scj60cxg.js → worker-p2mr422x.js} +40 -48
- package/openapi.json +1365 -268
- package/package.json +4 -3
- package/src/agentmail/handlers.ts +95 -66
- package/src/be/budget-refusal-notify.ts +1 -0
- package/src/be/db/tasks/read.ts +3 -0
- package/src/be/db/tasks/write.ts +13 -1
- package/src/be/db-client.ts +5 -0
- package/src/be/db.ts +15 -5
- package/src/be/extensions/db.ts +461 -0
- package/src/be/extensions/validate.ts +107 -0
- package/src/be/memory/boot-reembed.ts +17 -12
- package/src/be/memory/index.ts +12 -0
- package/src/be/memory/providers/openai-embedding.ts +4 -0
- package/src/be/memory/types.ts +2 -0
- package/src/be/migrations/153_deferred_task_wait_members.sql +25 -0
- package/src/be/migrations/154_routing_source.sql +3 -0
- package/src/be/migrations/155_extensions.sql +66 -0
- package/src/be/migrations/156_inbox_item_state_read_at.sql +7 -0
- package/src/be/rbac-roles.ts +3 -2
- package/src/be/scripts/typecheck.ts +59 -41
- package/src/be/seed/schedules-seeder.ts +25 -2
- package/src/be/seed/workflows-seeder.ts +17 -0
- package/src/be/seed-scripts/catalog/delegate.ts +3 -1
- package/src/be/seed-skills/bundled-files.generated.json +1 -1
- package/src/be/seed-skills/index.ts +7 -0
- package/src/be/steering.ts +1 -0
- package/src/be/swarm-config-guard.ts +3 -1
- package/src/commands/credential-refresh.ts +70 -0
- package/src/commands/onboard/compose-generator.ts +14 -2
- package/src/commands/onboard/env-generator.ts +7 -0
- package/src/commands/provider-credentials.ts +2 -25
- package/src/commands/runner.ts +24 -57
- package/src/extensions/apply-task-create.ts +135 -0
- package/src/extensions/bundle-path.ts +10 -0
- package/src/extensions/contract-runtime.ts +7 -0
- package/src/extensions/contract-types.generated.ts +4 -0
- package/src/extensions/contract.ts +270 -0
- package/src/extensions/ctx.ts +186 -0
- package/src/extensions/dispatcher.ts +479 -0
- package/src/extensions/identity.ts +44 -0
- package/src/extensions/lifecycle.ts +281 -0
- package/src/extensions/loader.ts +207 -0
- package/src/extensions/post-bridge.ts +161 -0
- package/src/github/handlers.ts +185 -140
- package/src/gitlab/handlers.ts +97 -77
- package/src/heartbeat/heartbeat.ts +155 -83
- package/src/http/agents.ts +1 -1
- package/src/http/all-routes.ts +1 -0
- package/src/http/approval-requests.ts +1 -0
- package/src/http/apps.ts +14 -9
- package/src/http/config.ts +4 -1
- package/src/http/core.ts +38 -2
- package/src/http/extensions.ts +547 -0
- package/src/http/inbox-state.ts +2 -0
- package/src/http/index.ts +41 -3
- package/src/http/mcp-bridge.ts +96 -62
- package/src/http/mcp.ts +1 -1
- package/src/http/script-runs.ts +5 -0
- package/src/http/status.ts +6 -4
- package/src/http/tasks.ts +51 -30
- package/src/integrations/kapso/inbound.ts +15 -10
- package/src/jira/sync.ts +26 -16
- package/src/linear/sync.ts +26 -16
- package/src/prompts/session-templates.ts +18 -13
- package/src/providers/claude-adapter.ts +21 -8
- package/src/rbac/admission.ts +1 -1
- package/src/rbac/elevated-agents.ts +24 -0
- package/src/rbac/legacy-policy.ts +35 -6
- package/src/rbac/permissions.ts +8 -0
- package/src/scheduler/deferred-task-waits.ts +138 -27
- package/src/scheduler/schedule-task.ts +54 -24
- package/src/scheduler/scheduler.ts +108 -125
- package/src/scripts-runtime/executors/native.ts +5 -1
- package/src/scripts-runtime/executors/types.ts +5 -0
- package/src/scripts-runtime/import-allowlist.ts +79 -19
- package/src/scripts-runtime/sdk-allowlist.ts +4 -0
- package/src/scripts-runtime/swarm-config.ts +8 -0
- package/src/scripts-runtime/swarm-sdk.ts +3 -0
- package/src/scripts-runtime/types/stdlib.d.ts +16 -3
- package/src/scripts-runtime/types/swarm-extension.d.ts +562 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +16 -3
- package/src/server.ts +6 -0
- package/src/slack/HEURISTICS.md +3 -1
- package/src/slack/actions.ts +21 -16
- package/src/slack/app.ts +2 -0
- package/src/slack/assistant.ts +4 -12
- package/src/slack/blocks.ts +1 -1
- package/src/slack/handlers.ts +107 -19
- package/src/slack/inbound-files.ts +9 -5
- package/src/slack/render-v2.ts +188 -38
- package/src/slack/router.ts +13 -3
- package/src/slack/steering.ts +3 -3
- package/src/slack/thread-buffer.ts +66 -23
- package/src/slack/work-objects.test.ts +252 -0
- package/src/slack/work-objects.ts +118 -0
- package/src/tasks/errors.ts +9 -0
- package/src/tasks/sibling-awareness.ts +35 -1
- package/src/tasks/worker-follow-up.ts +67 -11
- package/src/tests/acp-adapter.test.ts +10 -10
- package/src/tests/agent-fs-live-url.test.ts +40 -0
- package/src/tests/agent-fs-provision-seeder.test.ts +12 -12
- package/src/tests/api-key-tracking.test.ts +18 -18
- package/src/tests/api-key.test.ts +4 -2
- package/src/tests/apps-rbac.test.ts +1 -1
- package/src/tests/apps-spike5.test.ts +1 -1
- package/src/tests/apps-sync-engine.test.ts +8 -8
- package/src/tests/apps-user-config.test.ts +1 -1
- package/src/tests/artifact-sdk.test.ts +8 -8
- package/src/tests/asset-key-mcp.test.ts +2 -0
- package/src/tests/automation-preflight-alert.test.ts +1 -1
- package/src/tests/base-prompt.test.ts +7 -6
- package/src/tests/budget-user-scope.test.ts +1 -1
- package/src/tests/budgets-routes.test.ts +1 -1
- package/src/tests/claude-adapter-binary.test.ts +22 -40
- package/src/tests/claude-adapter-otel.test.ts +13 -7
- package/src/tests/claude-adapter.test.ts +8 -6
- package/src/tests/claude-hook-credentials.test.ts +3 -3
- package/src/tests/claude-managed-adapter.test.ts +4 -4
- package/src/tests/claude-managed-setup.test.ts +3 -3
- package/src/tests/claude-sdk-lifecycle.test.ts +1 -1
- package/src/tests/claude-sdk-transport.test.ts +6 -6
- package/src/tests/claude-stop-hook.test.ts +8 -8
- package/src/tests/claude-worker-parity.test.ts +3 -3
- package/src/tests/codex-adapter-pool-auth-revalidate.test.ts +5 -5
- package/src/tests/codex-adapter.test.ts +7 -5
- package/src/tests/codex-app-server.test.ts +2 -2
- package/src/tests/codex-login.test.ts +15 -15
- package/src/tests/codex-oauth-credential-info.test.ts +4 -4
- package/src/tests/codex-oauth-keep-warm.test.ts +4 -4
- package/src/tests/codex-oauth-refresh-lock.test.ts +1 -1
- package/src/tests/codex-oauth-storage.test.ts +7 -7
- package/src/tests/codex-pool.test.ts +1 -1
- package/src/tests/codex-steering-hook.test.ts +1 -1
- package/src/tests/codex-swarm-events.test.ts +1 -1
- package/src/tests/config-api-only-keys.test.ts +1 -1
- package/src/tests/config-session-auth.test.ts +2 -2
- package/src/tests/connection-embedded-auth.test.ts +20 -18
- package/src/tests/core-auth.test.ts +1 -1
- package/src/tests/create-page-tool.test.ts +3 -3
- package/src/tests/credential-refresh.test.ts +115 -0
- package/src/tests/credential-wait.test.ts +2 -2
- package/src/tests/credentials.test.ts +6 -4
- package/src/tests/db-queries-oauth.test.ts +40 -40
- package/src/tests/defer-task.test.ts +73 -8
- package/src/tests/deferred-task-wait-migration.test.ts +64 -0
- package/src/tests/deferred-task-wake.test.ts +349 -8
- package/src/tests/devin-adapter.test.ts +1 -1
- package/src/tests/devin-api.test.ts +1 -1
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/e2b-dispatch.test.ts +21 -19
- package/src/tests/e2e-claude-transport.test.ts +3 -3
- package/src/tests/e2e-redact.test.ts +6 -6
- package/src/tests/ensure-token.test.ts +68 -68
- package/src/tests/entrypoint-api-readiness.test.ts +1 -1
- package/src/tests/entrypoint-codex-oauth-seed.test.ts +13 -13
- package/src/tests/env-flag.test.ts +1 -0
- package/src/tests/events-http.test.ts +1 -1
- package/src/tests/extensions-db.test.ts +216 -0
- package/src/tests/extensions-dispatcher.test.ts +275 -0
- package/src/tests/extensions-example-heartbeat-remediate.test.ts +69 -0
- package/src/tests/extensions-example-mcp-install.test.ts +63 -0
- package/src/tests/extensions-example-no-exclamation-marks.test.ts +72 -0
- package/src/tests/extensions-example-rewrite-and-suppress.test.ts +70 -0
- package/src/tests/extensions-example-route-slack-channel.test.ts +217 -0
- package/src/tests/extensions-heartbeat.test.ts +291 -0
- package/src/tests/extensions-http.test.ts +431 -0
- package/src/tests/extensions-lead-equivalence.test.ts +49 -0
- package/src/tests/extensions-lifecycle.test.ts +209 -0
- package/src/tests/extensions-loader.test.ts +95 -0
- package/src/tests/extensions-mcp-tools.test.ts +245 -0
- package/src/tests/extensions-pre-task.test.ts +487 -0
- package/src/tests/extensions-run-context.test.ts +41 -0
- package/src/tests/extensions-tool-call.test.ts +288 -0
- package/src/tests/extensions-validate.test.ts +196 -0
- package/src/tests/fixtures/extensions/bad-import/hooks.ts +8 -0
- package/src/tests/fixtures/extensions/bad-import/manifest.json +9 -0
- package/src/tests/fixtures/extensions/bad-return-shape/hooks.ts +10 -0
- package/src/tests/fixtures/extensions/bad-return-shape/manifest.json +9 -0
- package/src/tests/fixtures/extensions/block-tasks-from-source/hooks.ts +22 -0
- package/src/tests/fixtures/extensions/block-tasks-from-source/manifest.json +9 -0
- package/src/tests/fixtures/extensions/ignore-channel/hooks.ts +22 -0
- package/src/tests/fixtures/extensions/ignore-channel/manifest.json +9 -0
- package/src/tests/fixtures/extensions/load.ts +10 -0
- package/src/tests/fixtures/extensions/minimal/hooks.ts +7 -0
- package/src/tests/fixtures/extensions/minimal/manifest.json +9 -0
- package/src/tests/fixtures/extensions/never-fail-long-tasks/hooks.ts +11 -0
- package/src/tests/fixtures/extensions/never-fail-long-tasks/manifest.json +9 -0
- package/src/tests/fixtures/extensions/no-exclamation-marks/hooks.ts +23 -0
- package/src/tests/fixtures/extensions/no-exclamation-marks/manifest.json +9 -0
- package/src/tests/fixtures/extensions/post-logger/hooks.ts +9 -0
- package/src/tests/fixtures/extensions/post-logger/manifest.json +9 -0
- package/src/tests/fixtures/extensions/priority-a/hooks.ts +7 -0
- package/src/tests/fixtures/extensions/priority-a/manifest.json +9 -0
- package/src/tests/fixtures/extensions/priority-b/hooks.ts +7 -0
- package/src/tests/fixtures/extensions/priority-b/manifest.json +9 -0
- package/src/tests/fixtures/extensions/record-only-on-tag/hooks.ts +16 -0
- package/src/tests/fixtures/extensions/record-only-on-tag/manifest.json +9 -0
- package/src/tests/fixtures/extensions/reserved-assets/hooks.ts +5 -0
- package/src/tests/fixtures/extensions/reserved-assets/manifest.json +10 -0
- package/src/tests/fixtures/extensions/rewrite-progress-prefix/hooks.ts +22 -0
- package/src/tests/fixtures/extensions/rewrite-progress-prefix/manifest.json +9 -0
- package/src/tests/fixtures/extensions/rewrite-task-priority/hooks.ts +7 -0
- package/src/tests/fixtures/extensions/rewrite-task-priority/manifest.json +9 -0
- package/src/tests/fixtures/extensions/route-channel-to-agent/hooks.ts +26 -0
- package/src/tests/fixtures/extensions/route-channel-to-agent/manifest.json +9 -0
- package/src/tests/fixtures/extensions/slow/hooks.ts +10 -0
- package/src/tests/fixtures/extensions/slow/manifest.json +9 -0
- package/src/tests/fixtures/extensions/suppress-lead-follow-up/hooks.ts +12 -0
- package/src/tests/fixtures/extensions/suppress-lead-follow-up/manifest.json +9 -0
- package/src/tests/fixtures/extensions/throws/hooks.ts +12 -0
- package/src/tests/fixtures/extensions/throws/manifest.json +9 -0
- package/src/tests/fixtures/extensions/worker-runtime/hooks.ts +5 -0
- package/src/tests/fixtures/extensions/worker-runtime/manifest.json +9 -0
- package/src/tests/fs-routes.test.ts +1 -1
- package/src/tests/gitlab-auth.test.ts +10 -10
- package/src/tests/harness-provider-resolution.test.ts +8 -5
- package/src/tests/heartbeat-reroute-decision.test.ts +1 -0
- package/src/tests/helper-credentials.test.ts +137 -0
- package/src/tests/http/context-routes.test.ts +2 -2
- package/src/tests/http-api-integration.test.ts +5 -2
- package/src/tests/http-users.test.ts +1 -1
- package/src/tests/identity-refresh.test.ts +2 -2
- package/src/tests/internal-ai/complete-structured.test.ts +2 -2
- package/src/tests/internal-ai/credentials.test.ts +19 -17
- package/src/tests/jira-metadata.test.ts +1 -1
- package/src/tests/jira-oauth.test.ts +5 -5
- package/src/tests/jira-sync.test.ts +1 -1
- package/src/tests/jira-webhook-lifecycle.test.ts +3 -3
- package/src/tests/jira-webhook.test.ts +2 -2
- package/src/tests/kapso-inbound.test.ts +2 -2
- package/src/tests/kv-http.test.ts +1 -1
- package/src/tests/kv-namespace-resolution.test.ts +1 -1
- package/src/tests/kv-page-proxy.test.ts +2 -2
- package/src/tests/launch-password-rejection.test.ts +2 -2
- package/src/tests/linear-webhook.test.ts +1 -1
- package/src/tests/mcp-oauth-dcr-reuse.test.ts +6 -6
- package/src/tests/mcp-oauth-ensure-token.test.ts +28 -22
- package/src/tests/mcp-oauth-manual-client.test.ts +16 -12
- package/src/tests/mcp-oauth-queries.test.ts +35 -32
- package/src/tests/mcp-oauth-resolve-secrets.test.ts +8 -8
- package/src/tests/mcp-oauth-wrapper.test.ts +20 -20
- package/src/tests/mcp-user-route.test.ts +1 -1
- package/src/tests/memory-edges.test.ts +1 -1
- package/src/tests/memory-edit-authorization.test.ts +32 -0
- package/src/tests/memory-rate-endpoint.test.ts +1 -1
- package/src/tests/memory-rate-tool.test.ts +2 -2
- package/src/tests/memory-rater-e2e.test.ts +1 -1
- package/src/tests/memory-rater-llm-summarizer.test.ts +3 -3
- package/src/tests/memory-rater-llm.test.ts +1 -1
- package/src/tests/model-control.test.ts +3 -0
- package/src/tests/multi-runtime-enabled-stats.test.ts +2 -2
- package/src/tests/multi-runtime-registration.test.ts +111 -17
- package/src/tests/oauth-access-token-tool.test.ts +18 -18
- package/src/tests/oauth-callback-flow.test.ts +8 -8
- package/src/tests/oauth-credential-bindings.test.ts +29 -27
- package/src/tests/oauth-keepalive.test.ts +10 -10
- package/src/tests/oauth-presets.test.ts +13 -5
- package/src/tests/oauth-refresh-failure.test.ts +9 -9
- package/src/tests/oauth-refresh-sweep.test.ts +20 -13
- package/src/tests/oauth-wrapper.test.ts +1 -1
- package/src/tests/onboard-compose.test.ts +47 -3
- package/src/tests/onboard-env.test.ts +26 -26
- package/src/tests/onboard-non-interactive.test.ts +4 -4
- package/src/tests/opencode-adapter.test.ts +1 -1
- package/src/tests/opencode-plugin.test.ts +2 -2
- package/src/tests/otel-session-cost-metrics.test.ts +1 -1
- package/src/tests/page-proxy-authed.test.ts +2 -2
- package/src/tests/page-proxy.test.ts +2 -2
- package/src/tests/page-session.test.ts +7 -7
- package/src/tests/page-viewer-auth.test.ts +2 -2
- package/src/tests/pages-authed-mode.test.ts +1 -1
- package/src/tests/pages-http.test.ts +1 -1
- package/src/tests/pages-password-mode.test.ts +1 -1
- package/src/tests/pages-public-authed-401.test.ts +1 -1
- package/src/tests/pages-public-html.test.ts +2 -2
- package/src/tests/pages-view-count.test.ts +1 -1
- package/src/tests/pi-mono-adapter.test.ts +10 -10
- package/src/tests/pi-mono-extension.test.ts +2 -2
- package/src/tests/preload.ts +5 -0
- package/src/tests/pricing-routes.test.ts +1 -1
- package/src/tests/profile-sync-warning.test.ts +8 -8
- package/src/tests/profile-sync.test.ts +8 -8
- package/src/tests/prompt-template-session.test.ts +36 -5
- package/src/tests/provider-process-group.test.ts +95 -4
- package/src/tests/providers/pi-cost.test.ts +1 -1
- package/src/tests/queue-stall-alarm.test.ts +1 -1
- package/src/tests/rbac-admission.test.ts +19 -4
- package/src/tests/rbac-charact-http.test.ts +1 -1
- package/src/tests/rbac-e2e-helpers.ts +1 -1
- package/src/tests/rbac-engine.test.ts +39 -0
- package/src/tests/realtime-room-auth.test.ts +1 -1
- package/src/tests/realtime-room-surfaces.test.ts +1 -1
- package/src/tests/reload-config.test.ts +45 -1
- package/src/tests/routing-affinity-inventory.test.ts +1 -1
- package/src/tests/routing-reason-contract.test.ts +76 -3
- package/src/tests/routing-reason-inventory.test.ts +11 -6
- package/src/tests/routing-source.test.ts +53 -0
- package/src/tests/runner-agent-fs-provision-retry.test.ts +1 -1
- package/src/tests/runner-context-preamble.test.ts +1 -1
- package/src/tests/runner-deployment-resume.test.ts +1 -1
- package/src/tests/runner-fallback-output.test.ts +1 -1
- package/src/tests/runner-identity-refresh.test.ts +1 -1
- package/src/tests/runner-skills-refresh.test.ts +1 -1
- package/src/tests/schedule-target-type.test.ts +3 -3
- package/src/tests/scheduled-tasks.test.ts +14 -5
- package/src/tests/scheduler-occurrence.test.ts +347 -0
- package/src/tests/script-apis-mcp.test.ts +1 -1
- package/src/tests/script-connections-http.test.ts +61 -58
- package/src/tests/script-connections-mcp.test.ts +1 -1
- package/src/tests/script-connections.test.ts +3 -1
- package/src/tests/script-runs-http.test.ts +15 -7
- package/src/tests/script-workflows-runtime-e2e.test.ts +1 -1
- package/src/tests/scripts-boot-reembed.test.ts +4 -0
- package/src/tests/scripts-embeddings.test.ts +4 -0
- package/src/tests/scripts-external-api.test.ts +1 -1
- package/src/tests/scripts-http.test.ts +1 -1
- package/src/tests/scripts-import-allowlist.test.ts +44 -1
- package/src/tests/scripts-mcp-e2e.test.ts +81 -1
- package/src/tests/scripts-only-gating.test.ts +4 -2
- package/src/tests/scripts-retention.test.ts +1 -1
- package/src/tests/scripts-runtime-identity.test.ts +2 -2
- package/src/tests/scripts-runtime-secret-egress.test.ts +1 -1
- package/src/tests/scripts-runtime.test.ts +6 -4
- package/src/tests/scripts-typecheck.test.ts +21 -0
- package/src/tests/sdk-allowlist.test.ts +1 -1
- package/src/tests/secret-scrubber.test.ts +38 -31
- package/src/tests/seed-registry.test.ts +46 -8
- package/src/tests/seed-schedules.test.ts +44 -9
- package/src/tests/seed-workflows.test.ts +21 -9
- package/src/tests/send-task-output-schema.test.ts +9 -1
- package/src/tests/send-task-requested-by.test.ts +1 -0
- package/src/tests/send-task-slack-routing-guard.test.ts +2 -0
- package/src/tests/session-costs-codex-recompute.test.ts +1 -1
- package/src/tests/session-costs-golden.test.ts +2 -2
- package/src/tests/session-costs-model-key-normalize.test.ts +2 -2
- package/src/tests/session-costs-recompute-all-providers.test.ts +1 -1
- package/src/tests/session-tokens.test.ts +1 -1
- package/src/tests/shutdown-error-scrubbing.test.ts +1 -1
- package/src/tests/slack-blocks.test.ts +12 -1
- package/src/tests/slack-file-tools-attachments.test.ts +1 -1
- package/src/tests/slack-inbound-files.test.ts +222 -86
- package/src/tests/slack-reaction-config.test.ts +5 -4
- package/src/tests/slack-read-boundaries.test.ts +1 -1
- package/src/tests/slack-render-v2.test.ts +196 -4
- package/src/tests/slack-router.test.ts +17 -2
- package/src/tests/slack-steering.test.ts +19 -0
- package/src/tests/status.test.ts +19 -7
- package/src/tests/steering-core.test.ts +1 -1
- package/src/tests/steering-enabled.test.ts +6 -8
- package/src/tests/steering-transport.test.ts +4 -4
- package/src/tests/store-progress-output-guidance.test.ts +34 -0
- package/src/tests/swarm-tool-result-gate.test.ts +11 -11
- package/src/tests/swarm-x-tool.test.ts +7 -5
- package/src/tests/sync-ai-toolbox-skills.test.ts +19 -0
- package/src/tests/task-supersede-resume.test.ts +1 -1
- package/src/tests/task-tool-preload-mcp.test.ts +4 -1
- package/src/tests/task-tools-ctx.test.ts +2 -0
- package/src/tests/tasks-requested-by-filter.test.ts +2 -2
- package/src/tests/telemetry-init.test.ts +11 -3
- package/src/tests/tool-annotations.test.ts +1 -1
- package/src/tests/tool-output-agent-id.test.ts +1 -0
- package/src/tests/tool-registrar-no-input.test.ts +42 -1
- package/src/tests/tracker-fold.test.ts +7 -7
- package/src/tests/tracker-tools.test.ts +3 -3
- package/src/tests/ui-followup-lead-delegation.test.ts +1 -0
- package/src/tests/unified-oauth-migration.test.ts +5 -5
- package/src/tests/user-token-rest-auth.test.ts +1 -1
- package/src/tests/user-token-routes.test.ts +1 -1
- package/src/tests/whoami-route.test.ts +1 -1
- package/src/tests/workflow-agent-task.test.ts +15 -13
- package/src/tests/workflow-ctx.test.ts +1 -1
- package/src/tests/workflow-e2e.test.ts +1 -1
- package/src/tests/workflow-engine-v2.test.ts +3 -1
- package/src/tests/workflow-executors.test.ts +2 -2
- package/src/tests/workflow-http-v2.test.ts +8 -6
- package/src/tests/workflow-input-redaction.test.ts +7 -5
- package/src/tests/workflow-integration-io.test.ts +2 -2
- package/src/tests/workflow-llm.test.ts +5 -5
- package/src/tests/workflow-registry.test.ts +1 -1
- package/src/tests/workflow-swarm-script.test.ts +1 -1
- package/src/tests/workflow-triggers-v2.test.ts +23 -21
- package/src/tests/x-composio.test.ts +9 -7
- package/src/tests/x402-client.test.ts +1 -1
- package/src/tests/x402-config.test.ts +28 -41
- package/src/tools/defer-task.ts +156 -24
- package/src/tools/extension-common.ts +84 -0
- package/src/tools/extension-install.ts +65 -0
- package/src/tools/extension-list.ts +66 -0
- package/src/tools/script-common.ts +43 -18
- package/src/tools/send-task.ts +87 -105
- package/src/tools/skills/skill-publish.ts +1 -0
- package/src/tools/store-progress.ts +1 -1
- package/src/tools/task-action.ts +43 -22
- package/src/tools/task-tool-ctx.ts +2 -0
- package/src/tools/templates.ts +2 -1
- package/src/tools/tool-config.ts +3 -1
- package/src/tools/utils.ts +140 -16
- package/src/types.ts +144 -0
- package/src/utils/constants.ts +7 -1
- package/src/utils/credentials.test.ts +16 -16
- package/src/utils/multi-runtime.ts +2 -2
- package/src/utils/steering-enabled.ts +4 -4
- package/src/workflows/executors/agent-task.ts +18 -3
- package/templates/ai-toolbox.manifest.json +7 -1
- package/templates/extensions/README.md +17 -0
- package/templates/extensions/notify-on-complete/README.md +11 -0
- package/templates/extensions/notify-on-complete/hooks.ts +61 -0
- package/templates/extensions/notify-on-complete/manifest.json +7 -0
- package/templates/extensions/require-ticket-ref/README.md +11 -0
- package/templates/extensions/require-ticket-ref/hooks.ts +30 -0
- package/templates/extensions/require-ticket-ref/manifest.json +7 -0
- package/templates/extensions/require-verification-note/README.md +11 -0
- package/templates/extensions/require-verification-note/hooks.ts +32 -0
- package/templates/extensions/require-verification-note/manifest.json +7 -0
- package/templates/schedules/daily-hn-briefing/config.json +1 -1
- package/templates/schedules/gtm-weekly-review/config.json +1 -1
- package/templates/schedules/weekly-code-health-reports/config.json +1 -1
- package/templates/schedules/weekly-dependabot-triage/config.json +1 -1
- package/templates/schedules/weekly-dora-metrics/config.json +1 -1
- package/templates/schedules/weekly-harness-upgrade-check/config.json +1 -1
- package/templates/skills/delegate-work/files/scripts/codex-exec.sh +1 -1
- package/templates/skills/scheduling/content.md +2 -0
- package/templates/skills/serply-search/SKILL.md +95 -0
- package/templates/skills/serply-search/config.json +13 -0
- package/templates/skills/serply-search/content.md +90 -0
- package/templates/skills/swarm-extensions/config.json +14 -0
- package/templates/skills/swarm-extensions/content.md +120 -0
- package/templates/skills/swarm-scripts/SKILL.md +2 -2
- package/templates/skills/swarm-scripts/content.md +2 -2
- package/templates/workflows/claude-code-changelog-watch/config.json +1 -1
- package/templates/workflows/gsc-topic-miner/config.json +1 -1
- package/templates/workflows/linear-drain-loop/config.json +1 -1
- package/templates/workflows/llm-safe-release-context/config.json +1 -1
- package/templates/workflows/ralph-loop/config.json +1 -1
- package/dist/cli-7fehefdf.js +0 -562
- package/dist/cli-ayvy5kde.js +0 -164
- package/dist/cli-ddfw1mc4.js +0 -4322
- package/dist/cli-hzqmgcm2.js +0 -3066
- package/dist/cli-j4d4f6hv.js +0 -338
- package/dist/cli-qgm2pfpq.js +0 -6377
- package/dist/cli-sdzq01t9.js +0 -823
- package/dist/cli-xvkmedgn.js +0 -332
- package/dist/cli-y3pq4v9g.js +0 -664
- package/dist/{anthropic-messages-vs2rabzp.js → anthropic-messages-2y8nktar.js} +19 -19
- package/dist/{claude-managed-setup-f5pmsyrp.js → claude-managed-setup-48dwg9bv.js} +3 -3
- package/dist/{cli-81r683bv.js → cli-ch38q5z4.js} +0 -0
- package/dist/{cli-6664w7y4.js → cli-kj76p1ez.js} +8 -8
- package/dist/{cli-c71mg51n.js → cli-r2fpe7cx.js} +5 -5
- package/dist/{cli-4xz3pmrq.js → cli-y6x0gqtg.js} +16 -16
- package/dist/{index-aa3sa6s8.js → index-2wsdsnmm.js} +1 -1
- package/dist/{mistral-conversations-4ryk7h83.js → mistral-conversations-qhr84ph3.js} +9 -9
- package/dist/{openai-completions-0bqmsajw.js → openai-completions-32rdwmze.js} +19 -19
- package/dist/{openrouter-images-qx18fyp8.js → openrouter-images-9f9ek4rw.js} +3 -3
- package/dist/{pi-messages-79jvzz1d.js → pi-messages-b1mbr6gp.js} +4 -4
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* handlers on both sides of MULTI_RUNTIME_ENABLED.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
6
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, spyOn, test } from "bun:test";
|
|
7
7
|
import { unlink } from "node:fs/promises";
|
|
8
8
|
import { createServer as createHttpServer, type Server } from "node:http";
|
|
9
9
|
import {
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
getActiveTaskCount,
|
|
16
16
|
getAgentById,
|
|
17
17
|
getDbClient,
|
|
18
|
+
getIdleWorkersWithCapacity,
|
|
18
19
|
getSwarmConfigs,
|
|
19
20
|
getTaskById,
|
|
20
21
|
hasCapacity,
|
|
@@ -22,6 +23,7 @@ import {
|
|
|
22
23
|
releaseStaleOfferedTasksForOfflineAgents,
|
|
23
24
|
startTask,
|
|
24
25
|
updateAgentStatus,
|
|
26
|
+
updateAgentStatusFromCapacity,
|
|
25
27
|
upsertSwarmConfig,
|
|
26
28
|
} from "../be/db";
|
|
27
29
|
import {
|
|
@@ -32,8 +34,16 @@ import {
|
|
|
32
34
|
getRuntimeInstanceById,
|
|
33
35
|
hasReadyLiveRuntime,
|
|
34
36
|
} from "../be/multi-runtime";
|
|
37
|
+
import {
|
|
38
|
+
CREDENTIAL_RETRY_INTERVAL_MS,
|
|
39
|
+
type CredentialRefreshState,
|
|
40
|
+
refreshCredentialStatus,
|
|
41
|
+
} from "../commands/credential-refresh";
|
|
35
42
|
import { retryBootStep } from "../commands/credential-wait";
|
|
36
|
-
import {
|
|
43
|
+
import {
|
|
44
|
+
CREDENTIAL_PROVIDER_CHECKERS,
|
|
45
|
+
sendCredStatusReport,
|
|
46
|
+
} from "../commands/provider-credentials";
|
|
37
47
|
import { runHeartbeatSweep } from "../heartbeat/heartbeat";
|
|
38
48
|
import { handleActiveSessions } from "../http/active-sessions";
|
|
39
49
|
import { handleAgentRegister, handleAgentsRest } from "../http/agents";
|
|
@@ -49,7 +59,7 @@ import { listenOnFreePort } from "./test-net";
|
|
|
49
59
|
|
|
50
60
|
const TEST_DB_PATH = "./test-multi-runtime-registration.sqlite";
|
|
51
61
|
let baseUrl = "";
|
|
52
|
-
const API_KEY = "test-multi-runtime-key";
|
|
62
|
+
const API_KEY = "example-test-multi-runtime-key";
|
|
53
63
|
|
|
54
64
|
const LEAD_ID = "44444444-4444-4444-4444-444444444444";
|
|
55
65
|
|
|
@@ -295,7 +305,7 @@ afterAll(async () => {
|
|
|
295
305
|
});
|
|
296
306
|
|
|
297
307
|
beforeEach(async () => {
|
|
298
|
-
|
|
308
|
+
process.env.MULTI_RUNTIME_ENABLED = "false";
|
|
299
309
|
await getDbClient().run("DELETE FROM runtime_instances");
|
|
300
310
|
await getDbClient().run("DELETE FROM active_sessions");
|
|
301
311
|
// Pool sweeps cap how many tasks they assign per tick, so leftovers from an
|
|
@@ -532,7 +542,7 @@ describe("disable / legacy restoration", () => {
|
|
|
532
542
|
expect((await getAgentById(id))?.maxTasks).toBe(3);
|
|
533
543
|
expect((await getAgentMaxTasksConfig(id))?.value).toBe("3");
|
|
534
544
|
|
|
535
|
-
|
|
545
|
+
process.env.MULTI_RUNTIME_ENABLED = "false";
|
|
536
546
|
const { status } = await register(id, { maxTasks: 8 });
|
|
537
547
|
expect(status).toBe(200);
|
|
538
548
|
expect((await getAgentById(id))?.maxTasks).toBe(8);
|
|
@@ -794,7 +804,7 @@ describe("GET /api/agents/{id}/runtime-instances", () => {
|
|
|
794
804
|
|
|
795
805
|
await listRuntimes(id);
|
|
796
806
|
// Rows written while the flag was on stay readable after a rollback.
|
|
797
|
-
|
|
807
|
+
process.env.MULTI_RUNTIME_ENABLED = "false";
|
|
798
808
|
const { status, body } = await listRuntimes(id);
|
|
799
809
|
expect(status).toBe(200);
|
|
800
810
|
expect(body.runtimeInstances).toHaveLength(1);
|
|
@@ -859,7 +869,7 @@ describe("runtime-aware close", () => {
|
|
|
859
869
|
const rA = crypto.randomUUID();
|
|
860
870
|
await register(id, { maxTasks: 1, runtimeInstanceId: rA });
|
|
861
871
|
|
|
862
|
-
|
|
872
|
+
process.env.MULTI_RUNTIME_ENABLED = "false";
|
|
863
873
|
expect(await closeRuntime(id, rA)).toBe(204);
|
|
864
874
|
// Runtime rows stay inert while the flag is off.
|
|
865
875
|
expect((await getAgentById(id))?.status).toBe("offline");
|
|
@@ -980,7 +990,7 @@ describe("runtime liveness via ping", () => {
|
|
|
980
990
|
await register(id, { maxTasks: 1, runtimeInstanceId: rA });
|
|
981
991
|
const before = await getRuntimeInstanceById(rA);
|
|
982
992
|
|
|
983
|
-
|
|
993
|
+
process.env.MULTI_RUNTIME_ENABLED = "false";
|
|
984
994
|
await Bun.sleep(10);
|
|
985
995
|
expect(await pingAgent(id, rA)).toBe(204);
|
|
986
996
|
expect((await getRuntimeInstanceById(rA))?.lastSeenAt).toBe(before?.lastSeenAt ?? "");
|
|
@@ -1181,7 +1191,7 @@ describe("stale runtime liveness", () => {
|
|
|
1181
1191
|
await register(id, { maxTasks: 1, runtimeInstanceId: rA });
|
|
1182
1192
|
|
|
1183
1193
|
// Flag off: close takes the legacy path and leaves the row active.
|
|
1184
|
-
|
|
1194
|
+
process.env.MULTI_RUNTIME_ENABLED = "false";
|
|
1185
1195
|
await closeRuntime(id, rA);
|
|
1186
1196
|
expect((await getRuntimeInstanceById(rA))?.status).toBe("active");
|
|
1187
1197
|
expect((await getAgentById(id))?.status).toBe("offline");
|
|
@@ -1376,7 +1386,7 @@ describe("rollback to legacy mode", () => {
|
|
|
1376
1386
|
await register(id, { maxTasks: 1, runtimeInstanceId: rA });
|
|
1377
1387
|
|
|
1378
1388
|
// Operator rolls back; workers stop refreshing their rows.
|
|
1379
|
-
|
|
1389
|
+
process.env.MULTI_RUNTIME_ENABLED = "false";
|
|
1380
1390
|
await makeRuntimeStale(rA);
|
|
1381
1391
|
|
|
1382
1392
|
const result = await expireStaleRuntimeInstances();
|
|
@@ -1392,7 +1402,7 @@ describe("rollback to legacy mode", () => {
|
|
|
1392
1402
|
const rA = crypto.randomUUID();
|
|
1393
1403
|
await register(id, { maxTasks: 1, runtimeInstanceId: rA });
|
|
1394
1404
|
|
|
1395
|
-
|
|
1405
|
+
process.env.MULTI_RUNTIME_ENABLED = "false";
|
|
1396
1406
|
await makeRuntimeStale(rA);
|
|
1397
1407
|
await expireStaleRuntimeInstances();
|
|
1398
1408
|
expect((await getAgentById(id))?.status).toBe("idle");
|
|
@@ -1459,7 +1469,7 @@ describe("flag re-enable cycle preserves live sessions", () => {
|
|
|
1459
1469
|
|
|
1460
1470
|
// Operator disables the flag: nothing refreshes runtime rows, but the
|
|
1461
1471
|
// worker keeps executing and heartbeating its session.
|
|
1462
|
-
|
|
1472
|
+
process.env.MULTI_RUNTIME_ENABLED = "false";
|
|
1463
1473
|
await makeRuntimeStale(rt);
|
|
1464
1474
|
|
|
1465
1475
|
// Re-enable past the stale window; the first sweep runs expiry.
|
|
@@ -1487,7 +1497,7 @@ describe("flag re-enable cycle preserves live sessions", () => {
|
|
|
1487
1497
|
await startTask(task.id);
|
|
1488
1498
|
await startSessionFor(id, task.id, rt);
|
|
1489
1499
|
|
|
1490
|
-
|
|
1500
|
+
process.env.MULTI_RUNTIME_ENABLED = "false";
|
|
1491
1501
|
await makeRuntimeStale(rt);
|
|
1492
1502
|
// Sessions heartbeat on tool activity only: a long model call or shell
|
|
1493
1503
|
// command can be quiet past the runtime cutoff while the worker is fine.
|
|
@@ -1516,7 +1526,7 @@ describe("flag re-enable cycle preserves live sessions", () => {
|
|
|
1516
1526
|
await startTask(task.id);
|
|
1517
1527
|
await startSessionFor(id, task.id, rt);
|
|
1518
1528
|
|
|
1519
|
-
|
|
1529
|
+
process.env.MULTI_RUNTIME_ENABLED = "false";
|
|
1520
1530
|
await makeRuntimeStale(rt);
|
|
1521
1531
|
await makeSessionStale(task.id, 20);
|
|
1522
1532
|
// The task itself progressed recently (store-progress traffic).
|
|
@@ -1541,7 +1551,7 @@ describe("flag re-enable cycle preserves live sessions", () => {
|
|
|
1541
1551
|
await startTask(task.id);
|
|
1542
1552
|
await startSessionFor(id, task.id, rt);
|
|
1543
1553
|
|
|
1544
|
-
|
|
1554
|
+
process.env.MULTI_RUNTIME_ENABLED = "false";
|
|
1545
1555
|
await makeRuntimeStale(rt);
|
|
1546
1556
|
// The process died: session heartbeat AND task progress both went stale.
|
|
1547
1557
|
await makeSessionStale(task.id, 30);
|
|
@@ -1568,7 +1578,7 @@ describe("flag re-enable cycle preserves live sessions", () => {
|
|
|
1568
1578
|
await startTask(task.id);
|
|
1569
1579
|
await startSessionFor(id, task.id, rt);
|
|
1570
1580
|
|
|
1571
|
-
|
|
1581
|
+
process.env.MULTI_RUNTIME_ENABLED = "false";
|
|
1572
1582
|
await makeRuntimeStale(rt);
|
|
1573
1583
|
|
|
1574
1584
|
const result = await expireStaleRuntimeInstances();
|
|
@@ -1762,7 +1772,7 @@ describe("sweep coverage and rollback inertness", () => {
|
|
|
1762
1772
|
await register(id, { maxTasks: 1, runtimeInstanceId: rt });
|
|
1763
1773
|
|
|
1764
1774
|
// Rolled back; the legacy worker stops refreshing its retained row.
|
|
1765
|
-
|
|
1775
|
+
process.env.MULTI_RUNTIME_ENABLED = "false";
|
|
1766
1776
|
await makeRuntimeStale(rt);
|
|
1767
1777
|
const task = await createTaskExtended("pool-work-legacy");
|
|
1768
1778
|
|
|
@@ -2840,3 +2850,87 @@ describe("MCP task-action accept requires a live runtime", () => {
|
|
|
2840
2850
|
expect((await getTaskById(task.id))?.agentId).toBe(id);
|
|
2841
2851
|
});
|
|
2842
2852
|
});
|
|
2853
|
+
|
|
2854
|
+
describe("post-task credential recovery without a restart", () => {
|
|
2855
|
+
for (const multiRuntime of [false, true]) {
|
|
2856
|
+
test(`parked worker recovers with multi-runtime ${multiRuntime ? "on" : "off"}`, async () => {
|
|
2857
|
+
process.env.MULTI_RUNTIME_ENABLED = String(multiRuntime);
|
|
2858
|
+
const id = await makeAgent(1);
|
|
2859
|
+
const runtimeInstanceId = crypto.randomUUID();
|
|
2860
|
+
await register(id, { maxTasks: 1, runtimeInstanceId });
|
|
2861
|
+
const api = { apiUrl: baseUrl, apiKey: API_KEY, agentId: id, runtimeInstanceId };
|
|
2862
|
+
const refresh: CredentialRefreshState = {
|
|
2863
|
+
harnessProvider: "acp",
|
|
2864
|
+
ready: true,
|
|
2865
|
+
lastRefreshAt: 0,
|
|
2866
|
+
inFlight: false,
|
|
2867
|
+
};
|
|
2868
|
+
const check = spyOn(CREDENTIAL_PROVIDER_CHECKERS, "claude");
|
|
2869
|
+
const originalOAuth = process.env.CLAUDE_CODE_OAUTH_TOKEN;
|
|
2870
|
+
// The real Claude checker and live-test use OAuth presence, no upstream I/O.
|
|
2871
|
+
process.env.CLAUDE_CODE_OAUTH_TOKEN = "test-credential-recovery";
|
|
2872
|
+
try {
|
|
2873
|
+
// A live provider swap produces a real not-ready post-task snapshot.
|
|
2874
|
+
await refreshCredentialStatus(api, refresh, "claude", {}, 1);
|
|
2875
|
+
expect((await getAgentById(id))?.status).toBe("waiting_for_credentials");
|
|
2876
|
+
expect((await getAgentById(id))?.credStatus?.reportKind).toBe("post_task");
|
|
2877
|
+
expect((await getIdleWorkersWithCapacity()).some((agent) => agent.id === id)).toBe(false);
|
|
2878
|
+
if (multiRuntime) {
|
|
2879
|
+
expect((await getRuntimeInstanceById(runtimeInstanceId))?.credentialReady).toBe(false);
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2882
|
+
// Ping and completion/capacity reconciliation cannot recover it.
|
|
2883
|
+
await pingAgent(id, runtimeInstanceId);
|
|
2884
|
+
await updateAgentStatusFromCapacity(id);
|
|
2885
|
+
expect((await getAgentById(id))?.status).toBe("waiting_for_credentials");
|
|
2886
|
+
await refreshCredentialStatus(api, refresh, "claude", {}, CREDENTIAL_RETRY_INTERVAL_MS);
|
|
2887
|
+
expect(check).toHaveBeenCalledTimes(1);
|
|
2888
|
+
|
|
2889
|
+
// A still-missing snapshot stays parked, and starts a new throttle window.
|
|
2890
|
+
await refreshCredentialStatus(api, refresh, "claude", {}, CREDENTIAL_RETRY_INTERVAL_MS + 1);
|
|
2891
|
+
expect(check).toHaveBeenCalledTimes(2);
|
|
2892
|
+
expect((await getAgentById(id))?.status).toBe("waiting_for_credentials");
|
|
2893
|
+
const env = { CLAUDE_CODE_OAUTH_TOKEN: "test-credential-recovery" };
|
|
2894
|
+
await refreshCredentialStatus(
|
|
2895
|
+
api,
|
|
2896
|
+
refresh,
|
|
2897
|
+
"claude",
|
|
2898
|
+
env,
|
|
2899
|
+
2 * CREDENTIAL_RETRY_INTERVAL_MS,
|
|
2900
|
+
);
|
|
2901
|
+
expect(check).toHaveBeenCalledTimes(2);
|
|
2902
|
+
expect((await getAgentById(id))?.status).toBe("waiting_for_credentials");
|
|
2903
|
+
|
|
2904
|
+
// Credentials resolve. Same runner state, provider, and runtime identity.
|
|
2905
|
+
await refreshCredentialStatus(
|
|
2906
|
+
api,
|
|
2907
|
+
refresh,
|
|
2908
|
+
"claude",
|
|
2909
|
+
env,
|
|
2910
|
+
2 * CREDENTIAL_RETRY_INTERVAL_MS + 1,
|
|
2911
|
+
);
|
|
2912
|
+
expect(check).toHaveBeenCalledTimes(3);
|
|
2913
|
+
expect((await getAgentById(id))?.status).toBe("idle");
|
|
2914
|
+
expect((await getAgentById(id))?.credStatus?.ready).toBe(true);
|
|
2915
|
+
expect((await getAgentById(id))?.credentialMissing).toBeNull();
|
|
2916
|
+
expect((await getIdleWorkersWithCapacity()).some((agent) => agent.id === id)).toBe(true);
|
|
2917
|
+
if (multiRuntime) {
|
|
2918
|
+
expect((await getRuntimeInstanceById(runtimeInstanceId))?.credentialReady).toBe(true);
|
|
2919
|
+
expect(await countActiveRuntimeInstancesForAgent(id)).toBe(1);
|
|
2920
|
+
}
|
|
2921
|
+
await refreshCredentialStatus(
|
|
2922
|
+
api,
|
|
2923
|
+
refresh,
|
|
2924
|
+
"claude",
|
|
2925
|
+
env,
|
|
2926
|
+
10 * CREDENTIAL_RETRY_INTERVAL_MS,
|
|
2927
|
+
);
|
|
2928
|
+
expect(check).toHaveBeenCalledTimes(3);
|
|
2929
|
+
} finally {
|
|
2930
|
+
check.mockRestore();
|
|
2931
|
+
if (originalOAuth === undefined) delete process.env.CLAUDE_CODE_OAUTH_TOKEN;
|
|
2932
|
+
else process.env.CLAUDE_CODE_OAUTH_TOKEN = originalOAuth;
|
|
2933
|
+
}
|
|
2934
|
+
});
|
|
2935
|
+
}
|
|
2936
|
+
});
|
|
@@ -23,7 +23,7 @@ const originalFetch = globalThis.fetch;
|
|
|
23
23
|
|
|
24
24
|
const testApp = {
|
|
25
25
|
clientId: "client-id",
|
|
26
|
-
clientSecret: "client-secret",
|
|
26
|
+
clientSecret: "example-client-secret",
|
|
27
27
|
authorizeUrl: "https://example.com/oauth/authorize",
|
|
28
28
|
tokenUrl: "https://example.com/oauth/token",
|
|
29
29
|
redirectUri: "http://localhost:3013/callback",
|
|
@@ -69,8 +69,8 @@ afterAll(async () => {
|
|
|
69
69
|
describe("resolveOAuthAccessToken", () => {
|
|
70
70
|
test("registered MCP tool returns a seeded provider token", async () => {
|
|
71
71
|
await storeOAuthTokens("custom-provider", {
|
|
72
|
-
accessToken: "mcp-tool-access-token-plain-value",
|
|
73
|
-
refreshToken: "mcp-tool-refresh-token",
|
|
72
|
+
accessToken: "example-mcp-tool-access-token-plain-value",
|
|
73
|
+
refreshToken: "example-mcp-tool-refresh-token",
|
|
74
74
|
expiresAt: new Date(Date.now() + 3600_000).toISOString(),
|
|
75
75
|
});
|
|
76
76
|
const server = new McpServer({ name: "oauth-access-token-test", version: "1.0.0" });
|
|
@@ -112,7 +112,7 @@ describe("resolveOAuthAccessToken", () => {
|
|
|
112
112
|
expect(result.structuredContent).toMatchObject({
|
|
113
113
|
success: true,
|
|
114
114
|
provider: "custom-provider",
|
|
115
|
-
accessToken: "mcp-tool-access-token-plain-value",
|
|
115
|
+
accessToken: "example-mcp-tool-access-token-plain-value",
|
|
116
116
|
expiresAt: expect.any(String),
|
|
117
117
|
tokenType: "Bearer",
|
|
118
118
|
});
|
|
@@ -123,7 +123,7 @@ describe("resolveOAuthAccessToken", () => {
|
|
|
123
123
|
// details carries the actual token payload (allowSecretEgress: true means
|
|
124
124
|
// the central scrubber does not redact it, since the tool's whole purpose
|
|
125
125
|
// is handing over the plaintext token).
|
|
126
|
-
expect(result.structuredContent.details).toBe("mcp-tool-access-token-plain-value");
|
|
126
|
+
expect(result.structuredContent.details).toBe("example-mcp-tool-access-token-plain-value");
|
|
127
127
|
|
|
128
128
|
// content[0].text is composed as message + "\n\n" + details, so both
|
|
129
129
|
// channels (text for pi/opencode/claude-managed, structuredContent for
|
|
@@ -132,7 +132,7 @@ describe("resolveOAuthAccessToken", () => {
|
|
|
132
132
|
expect(result.content).toHaveLength(1);
|
|
133
133
|
expect(result.content[0].type).toBe("text");
|
|
134
134
|
expect(result.content[0].text).toBe(
|
|
135
|
-
`custom-provider OAuth access token resolved; expires at ${expiresAt}.\n\
|
|
135
|
+
`custom-provider OAuth access token resolved; expires at ${expiresAt}.\n\nexample-mcp-tool-access-token-plain-value`,
|
|
136
136
|
);
|
|
137
137
|
});
|
|
138
138
|
|
|
@@ -170,10 +170,10 @@ describe("resolveOAuthAccessToken", () => {
|
|
|
170
170
|
});
|
|
171
171
|
|
|
172
172
|
test("returns a fresh access token and registers it for scrubber redaction", async () => {
|
|
173
|
-
const accessToken = "linear-access-token-plain-value-1234567890";
|
|
173
|
+
const accessToken = "example-linear-access-token-plain-value-1234567890";
|
|
174
174
|
await storeOAuthTokens("linear", {
|
|
175
175
|
accessToken,
|
|
176
|
-
refreshToken: "linear-refresh-token",
|
|
176
|
+
refreshToken: "example-linear-refresh-token",
|
|
177
177
|
expiresAt: new Date(Date.now() + 3600_000).toISOString(),
|
|
178
178
|
});
|
|
179
179
|
|
|
@@ -192,21 +192,21 @@ describe("resolveOAuthAccessToken", () => {
|
|
|
192
192
|
|
|
193
193
|
test("supports any configured OAuth provider slug", async () => {
|
|
194
194
|
await storeOAuthTokens("custom-provider", {
|
|
195
|
-
accessToken: "custom-provider-access-token-plain-value",
|
|
196
|
-
refreshToken: "custom-provider-refresh-token",
|
|
195
|
+
accessToken: "example-custom-provider-access-token-plain-value",
|
|
196
|
+
refreshToken: "example-custom-provider-refresh-token",
|
|
197
197
|
expiresAt: new Date(Date.now() + 3600_000).toISOString(),
|
|
198
198
|
});
|
|
199
199
|
|
|
200
200
|
const result = await resolveOAuthAccessToken("custom-provider");
|
|
201
201
|
|
|
202
202
|
expect(result.provider).toBe("custom-provider");
|
|
203
|
-
expect(result.accessToken).toBe("custom-provider-access-token-plain-value");
|
|
203
|
+
expect(result.accessToken).toBe("example-custom-provider-access-token-plain-value");
|
|
204
204
|
});
|
|
205
205
|
|
|
206
206
|
test("refreshes Jira before returning a near-expiry token", async () => {
|
|
207
207
|
await storeOAuthTokens("jira", {
|
|
208
|
-
accessToken: "old-jira-access-token",
|
|
209
|
-
refreshToken: "old-jira-refresh-token",
|
|
208
|
+
accessToken: "example-old-jira-access-token",
|
|
209
|
+
refreshToken: "example-old-jira-refresh-token",
|
|
210
210
|
expiresAt: new Date(Date.now() + 60_000).toISOString(),
|
|
211
211
|
});
|
|
212
212
|
|
|
@@ -214,10 +214,10 @@ describe("resolveOAuthAccessToken", () => {
|
|
|
214
214
|
Promise.resolve(
|
|
215
215
|
new Response(
|
|
216
216
|
JSON.stringify({
|
|
217
|
-
access_token: "new-jira-access-token-plain-value",
|
|
217
|
+
access_token: "example-new-jira-access-token-plain-value",
|
|
218
218
|
token_type: "Bearer",
|
|
219
219
|
expires_in: 3600,
|
|
220
|
-
refresh_token: "new-jira-refresh-token",
|
|
220
|
+
refresh_token: "example-new-jira-refresh-token",
|
|
221
221
|
}),
|
|
222
222
|
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
223
223
|
),
|
|
@@ -228,13 +228,13 @@ describe("resolveOAuthAccessToken", () => {
|
|
|
228
228
|
const result = await resolveOAuthAccessToken("jira");
|
|
229
229
|
|
|
230
230
|
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
|
231
|
-
expect(result.accessToken).toBe("new-jira-access-token-plain-value");
|
|
232
|
-
expect((await getOAuthTokens("jira"))?.refreshToken).toBe("new-jira-refresh-token");
|
|
231
|
+
expect(result.accessToken).toBe("example-new-jira-access-token-plain-value");
|
|
232
|
+
expect((await getOAuthTokens("jira"))?.refreshToken).toBe("example-new-jira-refresh-token");
|
|
233
233
|
});
|
|
234
234
|
|
|
235
235
|
test("rejects a near-expiry token when no refresh token is available", async () => {
|
|
236
236
|
await storeOAuthTokens("jira", {
|
|
237
|
-
accessToken: "stale-jira-access-token",
|
|
237
|
+
accessToken: "example-stale-jira-access-token",
|
|
238
238
|
refreshToken: null,
|
|
239
239
|
expiresAt: new Date(Date.now() + 60_000).toISOString(),
|
|
240
240
|
});
|
|
@@ -85,10 +85,10 @@ beforeAll(async () => {
|
|
|
85
85
|
if (url.pathname === "/token") {
|
|
86
86
|
lastTokenBody = await req.text();
|
|
87
87
|
return Response.json({
|
|
88
|
-
access_token: "mock-access-token",
|
|
88
|
+
access_token: "example-mock-access-token",
|
|
89
89
|
token_type: "Bearer",
|
|
90
90
|
expires_in: 3600,
|
|
91
|
-
refresh_token: "mock-refresh-token",
|
|
91
|
+
refresh_token: "example-mock-refresh-token",
|
|
92
92
|
scope: "read write",
|
|
93
93
|
});
|
|
94
94
|
}
|
|
@@ -98,7 +98,7 @@ beforeAll(async () => {
|
|
|
98
98
|
// fabricated one.
|
|
99
99
|
lastTokenBody = await req.text();
|
|
100
100
|
return Response.json({
|
|
101
|
-
access_token: "mock-access-token",
|
|
101
|
+
access_token: "example-mock-access-token",
|
|
102
102
|
token_type: "bearer",
|
|
103
103
|
scope: "read",
|
|
104
104
|
});
|
|
@@ -183,8 +183,8 @@ describe("static OAuth callback + multi-authorization flow", () => {
|
|
|
183
183
|
expect(supportAuth.accountEmail).toBe("connected@example.test");
|
|
184
184
|
// Tokens are encrypted at rest but decrypt to the mock values on read.
|
|
185
185
|
expect(supportAuth.tokensEncrypted).toBe(true);
|
|
186
|
-
expect(supportAuth.accessToken).toBe("mock-access-token");
|
|
187
|
-
expect(supportAuth.refreshToken).toBe("mock-refresh-token");
|
|
186
|
+
expect(supportAuth.accessToken).toBe("example-mock-access-token");
|
|
187
|
+
expect(supportAuth.refreshToken).toBe("example-mock-refresh-token");
|
|
188
188
|
|
|
189
189
|
// Second label: "sales" — first authorization must be untouched.
|
|
190
190
|
const sales = await buildAuthorizationUrl(config, { appId, label: "sales", flow: "generic" });
|
|
@@ -383,7 +383,7 @@ describe("static OAuth callback + multi-authorization flow", () => {
|
|
|
383
383
|
tokenUrl: `${providerBase}/token`,
|
|
384
384
|
scopes: "read",
|
|
385
385
|
dcrClientId: "mcp-client",
|
|
386
|
-
dcrClientSecret: "mcp-secret",
|
|
386
|
+
dcrClientSecret: "example-mcp-secret",
|
|
387
387
|
redirectUri: `${appBase}/api/oauth/callback`,
|
|
388
388
|
});
|
|
389
389
|
|
|
@@ -464,7 +464,7 @@ describe("step-4 security hardening", () => {
|
|
|
464
464
|
try {
|
|
465
465
|
const identity = await captureIdentity({
|
|
466
466
|
userinfoUrl: "http://127.0.0.1/userinfo",
|
|
467
|
-
accessToken: "live-bearer",
|
|
467
|
+
accessToken: "example-live-bearer",
|
|
468
468
|
});
|
|
469
469
|
expect(identity).toBeNull();
|
|
470
470
|
} finally {
|
|
@@ -476,7 +476,7 @@ describe("step-4 security hardening", () => {
|
|
|
476
476
|
test("captureIdentity does not follow a redirect to a private host", async () => {
|
|
477
477
|
const identity = await captureIdentity({
|
|
478
478
|
userinfoUrl: `${providerBase}/userinfo-redirect`,
|
|
479
|
-
accessToken: "live-bearer",
|
|
479
|
+
accessToken: "example-live-bearer",
|
|
480
480
|
});
|
|
481
481
|
// The 302 is not followed (redirect: manual → not .ok), so no identity.
|
|
482
482
|
expect(identity).toBeNull();
|
|
@@ -144,7 +144,7 @@ describe("OAuth credential bindings", () => {
|
|
|
144
144
|
|
|
145
145
|
await upsertOAuthApp("phase2-roundtrip", testApp("phase2-roundtrip"));
|
|
146
146
|
await storeOAuthTokens("phase2-roundtrip", {
|
|
147
|
-
accessToken: "roundtrip-access",
|
|
147
|
+
accessToken: "example-roundtrip-access",
|
|
148
148
|
expiresAt: new Date(Date.now() + 60 * 60 * 1000).toISOString(),
|
|
149
149
|
});
|
|
150
150
|
const authorizationId = (await getOAuthTokens("phase2-roundtrip"))!.id;
|
|
@@ -170,8 +170,8 @@ describe("OAuth credential bindings", () => {
|
|
|
170
170
|
test("OAuth binding resolves through the stored access token", async () => {
|
|
171
171
|
await upsertOAuthApp("phase2-resolve", testApp("phase2-resolve"));
|
|
172
172
|
await storeOAuthTokens("phase2-resolve", {
|
|
173
|
-
accessToken: "stored-oauth-access",
|
|
174
|
-
refreshToken: "stored-oauth-refresh",
|
|
173
|
+
accessToken: "example-stored-oauth-access",
|
|
174
|
+
refreshToken: "example-stored-oauth-refresh",
|
|
175
175
|
expiresAt: new Date(Date.now() + 60 * 60 * 1000).toISOString(),
|
|
176
176
|
});
|
|
177
177
|
await upsertCredentialBinding({
|
|
@@ -188,7 +188,7 @@ describe("OAuth credential bindings", () => {
|
|
|
188
188
|
expect(bindings).toContainEqual(
|
|
189
189
|
expect.objectContaining({
|
|
190
190
|
configKey: "PHASE2_RESOLVE_OAUTH",
|
|
191
|
-
value: "stored-oauth-access",
|
|
191
|
+
value: "example-stored-oauth-access",
|
|
192
192
|
}),
|
|
193
193
|
);
|
|
194
194
|
});
|
|
@@ -196,8 +196,8 @@ describe("OAuth credential bindings", () => {
|
|
|
196
196
|
test("expiring OAuth binding token is refreshed before resolution", async () => {
|
|
197
197
|
await upsertOAuthApp("phase2-refresh", testApp("phase2-refresh"));
|
|
198
198
|
await storeOAuthTokens("phase2-refresh", {
|
|
199
|
-
accessToken: "old-access-token",
|
|
200
|
-
refreshToken: "old-refresh-token",
|
|
199
|
+
accessToken: "example-old-access-token",
|
|
200
|
+
refreshToken: "example-old-refresh-token",
|
|
201
201
|
expiresAt: new Date(Date.now() + 60 * 1000).toISOString(),
|
|
202
202
|
});
|
|
203
203
|
await upsertCredentialBinding({
|
|
@@ -215,13 +215,13 @@ describe("OAuth credential bindings", () => {
|
|
|
215
215
|
}
|
|
216
216
|
expect(init?.method).toBe("POST");
|
|
217
217
|
expect(String(init?.body)).toContain("grant_type=refresh_token");
|
|
218
|
-
expect(String(init?.body)).toContain("refresh_token=old-refresh-token");
|
|
218
|
+
expect(String(init?.body)).toContain("refresh_token=example-old-refresh-token");
|
|
219
219
|
return Promise.resolve(
|
|
220
220
|
Response.json({
|
|
221
|
-
access_token: "new-access-token",
|
|
221
|
+
access_token: "example-new-access-token",
|
|
222
222
|
token_type: "Bearer",
|
|
223
223
|
expires_in: 3600,
|
|
224
|
-
refresh_token: "new-refresh-token",
|
|
224
|
+
refresh_token: "example-new-refresh-token",
|
|
225
225
|
}),
|
|
226
226
|
);
|
|
227
227
|
});
|
|
@@ -233,10 +233,12 @@ describe("OAuth credential bindings", () => {
|
|
|
233
233
|
expect(bindings).toContainEqual(
|
|
234
234
|
expect.objectContaining({
|
|
235
235
|
configKey: "PHASE2_REFRESH_OAUTH",
|
|
236
|
-
value: "new-access-token",
|
|
236
|
+
value: "example-new-access-token",
|
|
237
237
|
}),
|
|
238
238
|
);
|
|
239
|
-
expect((await getOAuthTokens("phase2-refresh"))?.refreshToken).toBe(
|
|
239
|
+
expect((await getOAuthTokens("phase2-refresh"))?.refreshToken).toBe(
|
|
240
|
+
"example-new-refresh-token",
|
|
241
|
+
);
|
|
240
242
|
});
|
|
241
243
|
|
|
242
244
|
test("basic tokenAuthStyle + json tokenBodyFormat reach the token endpoint (Notion-style)", async () => {
|
|
@@ -245,8 +247,8 @@ describe("OAuth credential bindings", () => {
|
|
|
245
247
|
metadata: JSON.stringify({ tokenAuthStyle: "basic", tokenBodyFormat: "json" }),
|
|
246
248
|
});
|
|
247
249
|
await storeOAuthTokens("phase2-basic", {
|
|
248
|
-
accessToken: "old-basic-access",
|
|
249
|
-
refreshToken: "old-basic-refresh",
|
|
250
|
+
accessToken: "example-old-basic-access",
|
|
251
|
+
refreshToken: "example-old-basic-refresh",
|
|
250
252
|
expiresAt: new Date(Date.now() + 60 * 1000).toISOString(),
|
|
251
253
|
});
|
|
252
254
|
await upsertCredentialBinding({
|
|
@@ -272,16 +274,16 @@ describe("OAuth credential bindings", () => {
|
|
|
272
274
|
expect(headers.get("content-type")).toBe("application/json");
|
|
273
275
|
const body = JSON.parse(String(init?.body)) as Record<string, string>;
|
|
274
276
|
expect(body.grant_type).toBe("refresh_token");
|
|
275
|
-
expect(body.refresh_token).toBe("old-basic-refresh");
|
|
277
|
+
expect(body.refresh_token).toBe("example-old-basic-refresh");
|
|
276
278
|
// Basic auth carries the client credentials — they must NOT be in the body
|
|
277
279
|
expect(body.client_id).toBeUndefined();
|
|
278
280
|
expect(body.client_secret).toBeUndefined();
|
|
279
281
|
return Promise.resolve(
|
|
280
282
|
Response.json({
|
|
281
|
-
access_token: "new-basic-access",
|
|
283
|
+
access_token: "example-new-basic-access",
|
|
282
284
|
token_type: "Bearer",
|
|
283
285
|
expires_in: 3600,
|
|
284
|
-
refresh_token: "new-basic-refresh",
|
|
286
|
+
refresh_token: "example-new-basic-refresh",
|
|
285
287
|
}),
|
|
286
288
|
);
|
|
287
289
|
});
|
|
@@ -293,7 +295,7 @@ describe("OAuth credential bindings", () => {
|
|
|
293
295
|
expect(bindings).toContainEqual(
|
|
294
296
|
expect.objectContaining({
|
|
295
297
|
configKey: "PHASE2_BASIC_OAUTH",
|
|
296
|
-
value: "new-basic-access",
|
|
298
|
+
value: "example-new-basic-access",
|
|
297
299
|
}),
|
|
298
300
|
);
|
|
299
301
|
});
|
|
@@ -323,7 +325,7 @@ describe("OAuth credential bindings", () => {
|
|
|
323
325
|
action: "oauth-app-upsert",
|
|
324
326
|
provider: "jira",
|
|
325
327
|
clientId: "jira-client",
|
|
326
|
-
clientSecret: "jira-secret",
|
|
328
|
+
clientSecret: "example-jira-secret",
|
|
327
329
|
authorizeUrl: "https://oauth.example.test/authorize",
|
|
328
330
|
tokenUrl: "https://oauth.example.test/token",
|
|
329
331
|
scopes: [],
|
|
@@ -345,7 +347,7 @@ describe("OAuth credential bindings", () => {
|
|
|
345
347
|
action: "oauth-app-upsert",
|
|
346
348
|
provider: "phase2-tool-unsafe",
|
|
347
349
|
clientId: "unsafe-client",
|
|
348
|
-
clientSecret: "unsafe-secret",
|
|
350
|
+
clientSecret: "example-unsafe-secret",
|
|
349
351
|
authorizeUrl: "https://oauth.example.test/authorize",
|
|
350
352
|
tokenUrl: "http://127.0.0.1/token",
|
|
351
353
|
scopes: [],
|
|
@@ -362,7 +364,7 @@ describe("OAuth credential bindings", () => {
|
|
|
362
364
|
action: "oauth-app-upsert",
|
|
363
365
|
provider: "phase2-tool-safe",
|
|
364
366
|
clientId: "safe-client",
|
|
365
|
-
clientSecret: "safe-secret",
|
|
367
|
+
clientSecret: "example-safe-secret",
|
|
366
368
|
authorizeUrl: "https://oauth.example.test/authorize",
|
|
367
369
|
tokenUrl: "https://oauth.example.test/token",
|
|
368
370
|
scopes: ["read"],
|
|
@@ -377,8 +379,8 @@ describe("OAuth credential bindings", () => {
|
|
|
377
379
|
test("failed OAuth token refresh skips only that binding, others still resolve", async () => {
|
|
378
380
|
await upsertOAuthApp("phase2-broken", testApp("phase2-broken"));
|
|
379
381
|
await storeOAuthTokens("phase2-broken", {
|
|
380
|
-
accessToken: "stale-access-token",
|
|
381
|
-
refreshToken: "stale-refresh-token",
|
|
382
|
+
accessToken: "example-stale-access-token",
|
|
383
|
+
refreshToken: "example-stale-refresh-token",
|
|
382
384
|
expiresAt: new Date(Date.now() + 60 * 1000).toISOString(),
|
|
383
385
|
});
|
|
384
386
|
await upsertCredentialBinding({
|
|
@@ -419,7 +421,7 @@ describe("OAuth credential bindings", () => {
|
|
|
419
421
|
test("revoked OAuth token skips binding resolution and list reports revoked", async () => {
|
|
420
422
|
await upsertOAuthApp("phase2-missing", testApp("phase2-missing"));
|
|
421
423
|
await storeOAuthTokens("phase2-missing", {
|
|
422
|
-
accessToken: "soon-deleted",
|
|
424
|
+
accessToken: "example-soon-deleted",
|
|
423
425
|
expiresAt: new Date(Date.now() + 60 * 60 * 1000).toISOString(),
|
|
424
426
|
});
|
|
425
427
|
const authorizationId = (await getOAuthTokens("phase2-missing"))!.id;
|
|
@@ -465,10 +467,10 @@ describe("OAuth credential bindings", () => {
|
|
|
465
467
|
expect(String(init?.body)).toContain("grant_type=authorization_code");
|
|
466
468
|
expect(String(init?.body)).toContain("code=callback-code");
|
|
467
469
|
return Response.json({
|
|
468
|
-
access_token: "callback-access-token",
|
|
470
|
+
access_token: "example-callback-access-token",
|
|
469
471
|
token_type: "Bearer",
|
|
470
472
|
expires_in: 3600,
|
|
471
|
-
refresh_token: "callback-refresh-token",
|
|
473
|
+
refresh_token: "example-callback-refresh-token",
|
|
472
474
|
scope: "read write",
|
|
473
475
|
});
|
|
474
476
|
}) as typeof fetch;
|
|
@@ -483,8 +485,8 @@ describe("OAuth credential bindings", () => {
|
|
|
483
485
|
expect(res.status).toBe(200);
|
|
484
486
|
expect(await res.text()).toContain("You can close this tab.");
|
|
485
487
|
const tokens = await getOAuthTokens(provider);
|
|
486
|
-
expect(tokens?.accessToken).toBe("callback-access-token");
|
|
487
|
-
expect(tokens?.refreshToken).toBe("callback-refresh-token");
|
|
488
|
+
expect(tokens?.accessToken).toBe("example-callback-access-token");
|
|
489
|
+
expect(tokens?.refreshToken).toBe("example-callback-refresh-token");
|
|
488
490
|
} finally {
|
|
489
491
|
server.close();
|
|
490
492
|
}
|
|
@@ -22,7 +22,7 @@ function restoreSlackAlertsChannel(): void {
|
|
|
22
22
|
|
|
23
23
|
const testApp = {
|
|
24
24
|
clientId: "test-client-id",
|
|
25
|
-
clientSecret: "test-client-secret",
|
|
25
|
+
clientSecret: "example-test-client-secret",
|
|
26
26
|
authorizeUrl: "https://example.com/oauth/authorize",
|
|
27
27
|
tokenUrl: "https://example.com/oauth/token",
|
|
28
28
|
redirectUri: "http://localhost:3013/callback",
|
|
@@ -78,13 +78,13 @@ describe("OAuth keepalive", () => {
|
|
|
78
78
|
|
|
79
79
|
test("stopOAuthKeepalive waits for in-flight Jira refresh persistence", async () => {
|
|
80
80
|
await storeOAuthTokens("linear", {
|
|
81
|
-
accessToken: "linear-access",
|
|
82
|
-
refreshToken: "linear-refresh",
|
|
81
|
+
accessToken: "example-linear-access",
|
|
82
|
+
refreshToken: "example-linear-refresh",
|
|
83
83
|
expiresAt: new Date(Date.now() + 60 * 60 * 1000).toISOString(),
|
|
84
84
|
});
|
|
85
85
|
await storeOAuthTokens("jira", {
|
|
86
|
-
accessToken: "old-jira-access",
|
|
87
|
-
refreshToken: "old-jira-refresh",
|
|
86
|
+
accessToken: "example-old-jira-access",
|
|
87
|
+
refreshToken: "example-old-jira-refresh",
|
|
88
88
|
expiresAt: new Date(Date.now() + 60 * 1000).toISOString(),
|
|
89
89
|
});
|
|
90
90
|
|
|
@@ -102,10 +102,10 @@ describe("OAuth keepalive", () => {
|
|
|
102
102
|
await tokenResponseReady;
|
|
103
103
|
return new Response(
|
|
104
104
|
JSON.stringify({
|
|
105
|
-
access_token: "new-jira-access",
|
|
105
|
+
access_token: "example-new-jira-access",
|
|
106
106
|
token_type: "Bearer",
|
|
107
107
|
expires_in: 3600,
|
|
108
|
-
refresh_token: "new-jira-refresh",
|
|
108
|
+
refresh_token: "example-new-jira-refresh",
|
|
109
109
|
}),
|
|
110
110
|
{ status: 200, headers: { "Content-Type": "application/json" } },
|
|
111
111
|
);
|
|
@@ -121,7 +121,7 @@ describe("OAuth keepalive", () => {
|
|
|
121
121
|
|
|
122
122
|
await Promise.resolve();
|
|
123
123
|
expect(stopResolved).toBe(false);
|
|
124
|
-
expect((await getOAuthTokens("jira"))?.refreshToken).toBe("old-jira-refresh");
|
|
124
|
+
expect((await getOAuthTokens("jira"))?.refreshToken).toBe("example-old-jira-refresh");
|
|
125
125
|
|
|
126
126
|
releaseTokenResponse();
|
|
127
127
|
await stopPromise;
|
|
@@ -129,7 +129,7 @@ describe("OAuth keepalive", () => {
|
|
|
129
129
|
|
|
130
130
|
expect(stopResolved).toBe(true);
|
|
131
131
|
const tokens = await getOAuthTokens("jira");
|
|
132
|
-
expect(tokens?.accessToken).toBe("new-jira-access");
|
|
133
|
-
expect(tokens?.refreshToken).toBe("new-jira-refresh");
|
|
132
|
+
expect(tokens?.accessToken).toBe("example-new-jira-access");
|
|
133
|
+
expect(tokens?.refreshToken).toBe("example-new-jira-refresh");
|
|
134
134
|
});
|
|
135
135
|
});
|