@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
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
completeTask,
|
|
7
7
|
createAgent,
|
|
8
8
|
createLogEntry,
|
|
9
|
+
createScheduledTask,
|
|
9
10
|
createTaskExtended,
|
|
10
11
|
ensureSlackDelegationActivation,
|
|
11
12
|
ensureSlackRenderV2Activation,
|
|
@@ -19,12 +20,14 @@ import {
|
|
|
19
20
|
getSlackTreeMessages,
|
|
20
21
|
getTaskById,
|
|
21
22
|
initDb,
|
|
23
|
+
insertTaskAttachment,
|
|
22
24
|
isPendingSlackMessage,
|
|
23
25
|
markTaskSlackReplySent,
|
|
24
26
|
startTask,
|
|
25
27
|
supersedeTask,
|
|
26
28
|
upsertSwarmConfig,
|
|
27
29
|
} from "../be/db";
|
|
30
|
+
import { createStandaloneScheduleTask } from "../scheduler/schedule-task";
|
|
28
31
|
import { getTaskLink, MAX_SECTION_LENGTH } from "../slack/blocks";
|
|
29
32
|
import {
|
|
30
33
|
_resetSlackRenderV2ForTests,
|
|
@@ -39,7 +42,7 @@ import {
|
|
|
39
42
|
} from "../slack/render-v2";
|
|
40
43
|
import { getAgentDisplayName, getAgentEmoji } from "../slack/responses";
|
|
41
44
|
import { slackContextKey } from "../tasks/context-key";
|
|
42
|
-
import type { AgentTask } from "../types";
|
|
45
|
+
import type { AgentTask, TaskAttachment } from "../types";
|
|
43
46
|
import { clearVolatileSecretsForTesting } from "../utils/secret-scrubber";
|
|
44
47
|
|
|
45
48
|
const TEST_DB_PATH = "./test-slack-render-v2.sqlite";
|
|
@@ -299,12 +302,83 @@ describe("Slack renderer v2", () => {
|
|
|
299
302
|
});
|
|
300
303
|
});
|
|
301
304
|
|
|
302
|
-
|
|
305
|
+
for (const delegationEnabled of [false, true]) {
|
|
306
|
+
test(`relays a deferred schedule continuation with delegation=${delegationEnabled}`, async () => {
|
|
307
|
+
process.env.SLACK_RENDER_V2_DELEGATION = String(delegationEnabled);
|
|
308
|
+
if (delegationEnabled) await ensureSlackDelegationActivation();
|
|
309
|
+
const lead = await createAgent({ name: "Continuation Lead", isLead: true, status: "idle" });
|
|
310
|
+
const { channelId, threadTs } = uniqueSlackAddress("C_CONTINUATION");
|
|
311
|
+
const ask = await createTaskExtended("answer when ready", {
|
|
312
|
+
agentId: lead.id,
|
|
313
|
+
source: "slack",
|
|
314
|
+
slackChannelId: channelId,
|
|
315
|
+
slackThreadTs: threadTs,
|
|
316
|
+
contextKey: slackContextKey({ channelId, threadTs }),
|
|
317
|
+
});
|
|
318
|
+
await startTask(ask.id);
|
|
319
|
+
await completeTask(ask.id, "Waiting for the result.", { addTags: ["deferred"] });
|
|
320
|
+
await backdateLastUpdated([ask.id], 20);
|
|
321
|
+
await processSlackRenderV2();
|
|
322
|
+
expect((await getSlackOutcomeMessage(ask.id))?.finalizedAt).toBeTruthy();
|
|
323
|
+
|
|
324
|
+
const schedule = await createScheduledTask({
|
|
325
|
+
name: "deferred-answer",
|
|
326
|
+
scheduleType: "one_time",
|
|
327
|
+
taskTemplate: "Deliver the result",
|
|
328
|
+
taskType: "deferred",
|
|
329
|
+
tags: ["deferred"],
|
|
330
|
+
parentTaskId: ask.id,
|
|
331
|
+
targetAgentId: lead.id,
|
|
332
|
+
createdByAgentId: lead.id,
|
|
333
|
+
nextRunAt: new Date().toISOString(),
|
|
334
|
+
});
|
|
335
|
+
const continuation = await createStandaloneScheduleTask(schedule);
|
|
336
|
+
expect(continuation.source).toBe("schedule");
|
|
337
|
+
expect(continuation.scheduleId).toBe(schedule.id);
|
|
338
|
+
expect(continuation.slackChannelId).toBe(channelId);
|
|
339
|
+
expect(continuation.slackThreadTs).toBe(threadTs);
|
|
340
|
+
await startTask(continuation.id);
|
|
341
|
+
await processSlackRenderV2();
|
|
342
|
+
expect(await getSlackOutcomeMessage(continuation.id)).toBeNull();
|
|
343
|
+
await completeTask(continuation.id, "Here is the complete answer.");
|
|
344
|
+
calls.length = 0;
|
|
345
|
+
|
|
346
|
+
await processSlackRenderV2();
|
|
347
|
+
await processSlackRenderV2();
|
|
348
|
+
|
|
349
|
+
expect((await getSlackOutcomeMessage(continuation.id))?.finalizedAt).toBeTruthy();
|
|
350
|
+
const streams = calls.filter((call) => call.method === "chat.startStream");
|
|
351
|
+
expect(streams).toHaveLength(1);
|
|
352
|
+
expect(streams[0]?.payload).toMatchObject({
|
|
353
|
+
channel: channelId,
|
|
354
|
+
thread_ts: threadTs,
|
|
355
|
+
markdown_text: "✅\n\nHere is the complete answer.",
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
test("does not relay an ordinary scheduled task without Slack context", async () => {
|
|
361
|
+
const schedule = await createScheduledTask({
|
|
362
|
+
name: "ordinary-maintenance",
|
|
363
|
+
scheduleType: "one_time",
|
|
364
|
+
taskTemplate: "Run maintenance",
|
|
365
|
+
taskType: "maintenance",
|
|
366
|
+
nextRunAt: new Date().toISOString(),
|
|
367
|
+
});
|
|
368
|
+
const task = await createStandaloneScheduleTask(schedule);
|
|
369
|
+
await startTask(task.id);
|
|
370
|
+
await completeTask(task.id, "Maintenance completed.");
|
|
371
|
+
await processSlackRenderV2();
|
|
372
|
+
expect(await getSlackOutcomeMessage(task.id)).toBeNull();
|
|
373
|
+
expect(calls).toHaveLength(0);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test("defaults on and accepts an explicit opt-out", () => {
|
|
303
377
|
const previous = process.env.SLACK_RENDER_V2;
|
|
304
378
|
delete process.env.SLACK_RENDER_V2;
|
|
305
|
-
expect(isSlackRenderV2Enabled()).toBe(false);
|
|
306
|
-
process.env.SLACK_RENDER_V2 = "true";
|
|
307
379
|
expect(isSlackRenderV2Enabled()).toBe(true);
|
|
380
|
+
process.env.SLACK_RENDER_V2 = "false";
|
|
381
|
+
expect(isSlackRenderV2Enabled()).toBe(false);
|
|
308
382
|
if (previous === undefined) delete process.env.SLACK_RENDER_V2;
|
|
309
383
|
else process.env.SLACK_RENDER_V2 = previous;
|
|
310
384
|
});
|
|
@@ -1183,6 +1257,95 @@ describe("Slack renderer v2", () => {
|
|
|
1183
1257
|
expect(calls.some((call) => call.method === "chat.appendStream")).toBe(false);
|
|
1184
1258
|
});
|
|
1185
1259
|
|
|
1260
|
+
test.each<{
|
|
1261
|
+
label: string;
|
|
1262
|
+
attachment: Partial<TaskAttachment> & Pick<TaskAttachment, "kind" | "name">;
|
|
1263
|
+
expected: string;
|
|
1264
|
+
}>([
|
|
1265
|
+
{
|
|
1266
|
+
label: "plain ASCII positive control",
|
|
1267
|
+
attachment: { kind: "agent-fs", name: "report.txt", path: "/reports/report.txt" },
|
|
1268
|
+
expected:
|
|
1269
|
+
"📎 [report.txt](https://files.example.test/file/~/org-1/drive-1/reports/report.txt)",
|
|
1270
|
+
},
|
|
1271
|
+
{
|
|
1272
|
+
label: "raw path spaces and parentheses",
|
|
1273
|
+
attachment: { kind: "agent-fs", name: "final report", path: "/shared reports/final (v1).md" },
|
|
1274
|
+
expected:
|
|
1275
|
+
"📎 [final report](https://files.example.test/file/~/org-1/drive-1/shared%20reports/final%20%28v1%29.md)",
|
|
1276
|
+
},
|
|
1277
|
+
{
|
|
1278
|
+
label: "already encoded positive control",
|
|
1279
|
+
attachment: { kind: "agent-fs", name: "report", path: "/reports/final%20%28v1%29.md" },
|
|
1280
|
+
expected:
|
|
1281
|
+
"📎 [report](https://files.example.test/file/~/org-1/drive-1/reports/final%20%28v1%29.md)",
|
|
1282
|
+
},
|
|
1283
|
+
{
|
|
1284
|
+
label: "label delimiters, backslashes, and whitespace",
|
|
1285
|
+
attachment: {
|
|
1286
|
+
kind: "url",
|
|
1287
|
+
name: " \t[report] (final)\\\r\n\t copy ",
|
|
1288
|
+
url: "https://example.test/report",
|
|
1289
|
+
},
|
|
1290
|
+
expected: "📎 [\\[report\\] \\(final\\)\\\\ copy](https://example.test/report)",
|
|
1291
|
+
},
|
|
1292
|
+
{
|
|
1293
|
+
label: "URL positive control with an IPv6 host",
|
|
1294
|
+
attachment: { kind: "url", name: "report", url: "http://[::1]/report.txt?q=a%20b&x=1#part" },
|
|
1295
|
+
expected: "📎 [report](http://[::1]/report.txt?q=a%20b&x=1#part)",
|
|
1296
|
+
},
|
|
1297
|
+
{
|
|
1298
|
+
label: "URL delimiters with existing escapes and query parameters",
|
|
1299
|
+
attachment: {
|
|
1300
|
+
kind: "url",
|
|
1301
|
+
name: "report",
|
|
1302
|
+
url: "https://example.test/a%20b) [c]<(d)>\\file?q=one two&x=1#part",
|
|
1303
|
+
},
|
|
1304
|
+
expected:
|
|
1305
|
+
"📎 [report](https://example.test/a%20b%29%20[c]%3C%28d%29%3E%5Cfile?q=one%20two&x=1#part)",
|
|
1306
|
+
},
|
|
1307
|
+
{
|
|
1308
|
+
label: "non-HTTP fallback stays omitted",
|
|
1309
|
+
attachment: { kind: "url", name: "report", url: "agent-fs:/reports/report.md" },
|
|
1310
|
+
expected: "",
|
|
1311
|
+
},
|
|
1312
|
+
])("streams safe attachment Markdown: $label", async ({ attachment, expected }) => {
|
|
1313
|
+
const originalHost = process.env.AGENT_FS_LIVE_URL;
|
|
1314
|
+
process.env.AGENT_FS_LIVE_URL = "https://files.example.test";
|
|
1315
|
+
try {
|
|
1316
|
+
const lead = await createAgent({ name: "Attachment Lead", isLead: true, status: "idle" });
|
|
1317
|
+
const { channelId, threadTs } = uniqueSlackAddress("C_OUTCOME_ATTACHMENT");
|
|
1318
|
+
const ask = await createTaskExtended("render attachment link", {
|
|
1319
|
+
agentId: lead.id,
|
|
1320
|
+
source: "slack",
|
|
1321
|
+
slackChannelId: channelId,
|
|
1322
|
+
slackThreadTs: threadTs,
|
|
1323
|
+
contextKey: slackContextKey({ channelId, threadTs }),
|
|
1324
|
+
});
|
|
1325
|
+
await insertTaskAttachment({
|
|
1326
|
+
...attachment,
|
|
1327
|
+
taskId: ask.id,
|
|
1328
|
+
agentId: lead.id,
|
|
1329
|
+
orgId: "org-1",
|
|
1330
|
+
driveId: "drive-1",
|
|
1331
|
+
isPrimary: true,
|
|
1332
|
+
});
|
|
1333
|
+
await startTask(ask.id);
|
|
1334
|
+
await ensureSlackThreadTree([ask.id]);
|
|
1335
|
+
await completeTask(ask.id, "Done");
|
|
1336
|
+
calls.length = 0;
|
|
1337
|
+
_resetSlackRenderV2ForTests();
|
|
1338
|
+
|
|
1339
|
+
await processSlackRenderV2();
|
|
1340
|
+
|
|
1341
|
+
const started = calls.find((call) => call.method === "chat.startStream");
|
|
1342
|
+
expect(started?.payload.markdown_text).toBe(`✅\n\nDone${expected ? `\n\n${expected}` : ""}`);
|
|
1343
|
+
} finally {
|
|
1344
|
+
if (originalHost === undefined) delete process.env.AGENT_FS_LIVE_URL;
|
|
1345
|
+
else process.env.AGENT_FS_LIVE_URL = originalHost;
|
|
1346
|
+
}
|
|
1347
|
+
});
|
|
1348
|
+
|
|
1186
1349
|
test("truncates oversized Markdown before a code fence and links the full task", async () => {
|
|
1187
1350
|
const lead = await createAgent({ name: "Overflow Lead", isLead: true, status: "idle" });
|
|
1188
1351
|
const { channelId, threadTs } = uniqueSlackAddress("C_OUTCOME_OVERFLOW");
|
|
@@ -1626,6 +1789,35 @@ describe("Slack renderer v2", () => {
|
|
|
1626
1789
|
expect(outcome?.finalizedAt).toBeDefined();
|
|
1627
1790
|
});
|
|
1628
1791
|
|
|
1792
|
+
test("keeps a deferral's human-facing line even when the agent already sent a slack-reply", async () => {
|
|
1793
|
+
const lead = await createAgent({ name: "Deferring Lead", isLead: true, status: "idle" });
|
|
1794
|
+
const { channelId, threadTs } = uniqueSlackAddress("C_RENDER_DEFER_AFTER_REPLY");
|
|
1795
|
+
const ask = await createTaskExtended("defer after replying by hand", {
|
|
1796
|
+
agentId: lead.id,
|
|
1797
|
+
source: "slack",
|
|
1798
|
+
slackChannelId: channelId,
|
|
1799
|
+
slackThreadTs: threadTs,
|
|
1800
|
+
contextKey: slackContextKey({ channelId, threadTs }),
|
|
1801
|
+
});
|
|
1802
|
+
await startTask(ask.id);
|
|
1803
|
+
await ensureSlackThreadTree([ask.id]);
|
|
1804
|
+
// Agent posts a hand-written slack-reply first (e.g. a transcription),
|
|
1805
|
+
// then defer-task completes the task with the engine-authored deferral
|
|
1806
|
+
// line and tags it "deferred" (mirrors defer-task.ts's completeTask call).
|
|
1807
|
+
await markTaskSlackReplySent(ask.id);
|
|
1808
|
+
const deferralLine =
|
|
1809
|
+
"Deferred until today 17:30:19 UTC ([715bf847](https://app.agent-swarm.dev/schedules/715bf847-fe3e-40e9-9fef-3297a62d9afd)) -> checking the new defer card";
|
|
1810
|
+
await completeTask(ask.id, deferralLine, { addTags: ["deferred"] });
|
|
1811
|
+
calls.length = 0;
|
|
1812
|
+
_resetSlackRenderV2ForTests();
|
|
1813
|
+
|
|
1814
|
+
await processSlackRenderV2();
|
|
1815
|
+
|
|
1816
|
+
const started = calls.find((call) => call.method === "chat.startStream");
|
|
1817
|
+
expect(started?.payload.markdown_text).toBe(`✅\n\n${deferralLine}`);
|
|
1818
|
+
expect(started?.payload.markdown_text).not.toBe(`✅ ${lead.name} completed`);
|
|
1819
|
+
});
|
|
1820
|
+
|
|
1629
1821
|
test("refreshes a stream started with stale content before finalizing it", async () => {
|
|
1630
1822
|
const lead = await createAgent({ name: "Refresh Lead", isLead: true, status: "idle" });
|
|
1631
1823
|
const { channelId, threadTs } = uniqueSlackAddress("C_RENDER_REFRESH_STALE");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
2
2
|
import { unlinkSync } from "node:fs";
|
|
3
|
-
import { closeDb, createAgent, createTaskExtended, initDb } from "../be/db";
|
|
4
|
-
import { routeMessage } from "../slack/router";
|
|
3
|
+
import { closeDb, createAgent, createTaskExtended, getAllAgents, initDb } from "../be/db";
|
|
4
|
+
import { broadcastMatches, routeMessage } from "../slack/router";
|
|
5
5
|
import type { Agent } from "../types";
|
|
6
6
|
|
|
7
7
|
const TEST_DB_PATH = "./test-slack-router.sqlite";
|
|
@@ -169,6 +169,21 @@ describe("Slack router — thread follow-up routing", () => {
|
|
|
169
169
|
// Lead should not be in the matches
|
|
170
170
|
expect(matches.every((m) => !m.agent.isLead)).toBe(true);
|
|
171
171
|
});
|
|
172
|
+
|
|
173
|
+
test("swarm#all includes offline non-lead agents", async () => {
|
|
174
|
+
const offlineWorker = await createAgent({
|
|
175
|
+
name: "offline-broadcast-worker",
|
|
176
|
+
isLead: false,
|
|
177
|
+
status: "offline",
|
|
178
|
+
capabilities: [],
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const matches = broadcastMatches(await getAllAgents());
|
|
182
|
+
|
|
183
|
+
expect(matches.map((match) => match.agent.id)).toContain(offlineWorker.id);
|
|
184
|
+
expect(matches.every((match) => !match.agent.isLead)).toBe(true);
|
|
185
|
+
expect(matches.every((match) => match.matchedText === "swarm#all")).toBe(true);
|
|
186
|
+
});
|
|
172
187
|
});
|
|
173
188
|
|
|
174
189
|
describe("no thread context behaves normally", () => {
|
|
@@ -105,6 +105,25 @@ describe("Slack thread steering", () => {
|
|
|
105
105
|
expect(await getChildTasks(leadTask.id)).toHaveLength(1);
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
+
test("unset configuration steers the lead with queued delivery", async () => {
|
|
109
|
+
delete process.env.SLACK_THREAD_STEERING;
|
|
110
|
+
delete process.env.SLACK_THREAD_STEERING_MODE;
|
|
111
|
+
const channelId = "C_DEFAULT_STEER";
|
|
112
|
+
const threadTs = "1500.0001";
|
|
113
|
+
const task = await createRunningSlackTask(leadId, channelId, threadTs);
|
|
114
|
+
const result = await requestSlackThreadSteering({
|
|
115
|
+
channelId,
|
|
116
|
+
threadTs,
|
|
117
|
+
message: "new context",
|
|
118
|
+
});
|
|
119
|
+
expect(result).toMatchObject({
|
|
120
|
+
task: { id: task.id },
|
|
121
|
+
result: { outcome: "queued", effectiveMode: "queue" },
|
|
122
|
+
});
|
|
123
|
+
expect(await getSteeringMessagesForTask(task.id)).toHaveLength(1);
|
|
124
|
+
expect(await getChildTasks(task.id)).toEqual([]);
|
|
125
|
+
});
|
|
126
|
+
|
|
108
127
|
test("lead mode sends one steering message to an in-progress lead and creates no task", async () => {
|
|
109
128
|
process.env.SLACK_THREAD_STEERING = "lead";
|
|
110
129
|
process.env.SLACK_THREAD_STEERING_MODE = "steer";
|
package/src/tests/status.test.ts
CHANGED
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
updateAgentCredStatus,
|
|
32
32
|
} from "../be/db";
|
|
33
33
|
import { storeOAuthTokens, upsertOAuthApp } from "../be/db-queries/oauth";
|
|
34
|
+
import { resetEmbeddingProvider } from "../be/memory";
|
|
34
35
|
import { validateProviderCredentials } from "../commands/provider-credentials";
|
|
35
36
|
import {
|
|
36
37
|
_resetTestConnectionCache,
|
|
@@ -123,6 +124,11 @@ function clearEnv() {
|
|
|
123
124
|
for (const k of ENV_KEYS_TO_RESET) {
|
|
124
125
|
delete process.env[k];
|
|
125
126
|
}
|
|
127
|
+
// The embedding provider memoizes its API key at construction (see
|
|
128
|
+
// OpenAIEmbeddingProvider); reset the singleton so embeddingsMilestone()
|
|
129
|
+
// (which now reads the live provider, not process.env directly) reflects
|
|
130
|
+
// whatever this test just set/cleared instead of a stale prior test's key.
|
|
131
|
+
resetEmbeddingProvider();
|
|
126
132
|
}
|
|
127
133
|
|
|
128
134
|
function restoreEnv() {
|
|
@@ -245,14 +251,20 @@ describe("setup milestones", () => {
|
|
|
245
251
|
hint: "Memory search is off. Set OPENAI_API_KEY (or EMBEDDING_API_KEY) on the API server to enable it; it is cheap.",
|
|
246
252
|
});
|
|
247
253
|
|
|
248
|
-
|
|
254
|
+
// The provider memoizes its API key at construction; in production a
|
|
255
|
+
// config reload resets it (see resetEmbeddingProvider in src/http/core.ts).
|
|
256
|
+
// Mirror that here after each direct env mutation.
|
|
257
|
+
process.env.OPENAI_API_KEY = "example-openai-embedding-key";
|
|
258
|
+
resetEmbeddingProvider();
|
|
249
259
|
expect(getMilestone(await buildStatusPayload(), "embeddings").state).toBe("configured");
|
|
250
260
|
|
|
251
261
|
process.env.EMBEDDING_API_KEY = "";
|
|
262
|
+
resetEmbeddingProvider();
|
|
252
263
|
expect(getMilestone(await buildStatusPayload(), "embeddings").state).toBe("unverified");
|
|
253
264
|
|
|
254
|
-
process.env.EMBEDDING_API_KEY = "dedicated-embedding-key";
|
|
265
|
+
process.env.EMBEDDING_API_KEY = "example-dedicated-embedding-key";
|
|
255
266
|
delete process.env.OPENAI_API_KEY;
|
|
267
|
+
resetEmbeddingProvider();
|
|
256
268
|
expect(getMilestone(await buildStatusPayload(), "embeddings").state).toBe("configured");
|
|
257
269
|
});
|
|
258
270
|
|
|
@@ -417,7 +429,7 @@ describe("setup milestones", () => {
|
|
|
417
429
|
const a = await buildStatusPayload();
|
|
418
430
|
expect(getMilestone(a, "github").state).toBe("unverified");
|
|
419
431
|
|
|
420
|
-
process.env.GITHUB_APP_PRIVATE_KEY = "
|
|
432
|
+
process.env.GITHUB_APP_PRIVATE_KEY = "[REDACTED:github_app_private_key]";
|
|
421
433
|
const b = await buildStatusPayload();
|
|
422
434
|
expect(getMilestone(b, "github").state).toBe("verified");
|
|
423
435
|
});
|
|
@@ -456,7 +468,7 @@ describe("setup milestones", () => {
|
|
|
456
468
|
scopes: "read",
|
|
457
469
|
});
|
|
458
470
|
await storeOAuthTokens("linear", {
|
|
459
|
-
accessToken: "lin-tok-xyz",
|
|
471
|
+
accessToken: "example-lin-tok-xyz",
|
|
460
472
|
refreshToken: "ref",
|
|
461
473
|
expiresAt: new Date(Date.now() + 3600_000).toISOString(),
|
|
462
474
|
scope: "read",
|
|
@@ -475,7 +487,7 @@ describe("setup milestones", () => {
|
|
|
475
487
|
// metadata intentionally omitted on first upsert
|
|
476
488
|
});
|
|
477
489
|
await storeOAuthTokens("jira", {
|
|
478
|
-
accessToken: "jira-tok",
|
|
490
|
+
accessToken: "example-jira-tok",
|
|
479
491
|
refreshToken: null,
|
|
480
492
|
expiresAt: new Date(Date.now() + 3600_000).toISOString(),
|
|
481
493
|
scope: null,
|
|
@@ -831,7 +843,7 @@ describe("validateProviderCredentials — error scrubbing", () => {
|
|
|
831
843
|
});
|
|
832
844
|
|
|
833
845
|
test("devin hits v3/self (not the deprecated v1 endpoint) and passes on 2xx", async () => {
|
|
834
|
-
process.env.DEVIN_API_KEY = "cog_fake-devin-key-1234";
|
|
846
|
+
process.env.DEVIN_API_KEY = "example-cog_fake-devin-key-1234";
|
|
835
847
|
let capturedUrl = "";
|
|
836
848
|
globalThis.fetch = (async (url) => {
|
|
837
849
|
capturedUrl = String(url);
|
|
@@ -851,7 +863,7 @@ describe("validateProviderCredentials — error scrubbing", () => {
|
|
|
851
863
|
});
|
|
852
864
|
|
|
853
865
|
test("scrubs api key from error message on 401 response", async () => {
|
|
854
|
-
const fakeKey = "
|
|
866
|
+
const fakeKey = "example-anthropic-key-do-not-leak";
|
|
855
867
|
process.env.ANTHROPIC_API_KEY = fakeKey;
|
|
856
868
|
globalThis.fetch = (async () =>
|
|
857
869
|
new Response(`Unauthorized: invalid key ${fakeKey}`, {
|
|
@@ -413,7 +413,7 @@ describe("task steering core", () => {
|
|
|
413
413
|
|
|
414
414
|
test("scrubs secrets before persisting the steering body", async () => {
|
|
415
415
|
const previous = process.env.OPENAI_API_KEY;
|
|
416
|
-
const secret = "
|
|
416
|
+
const secret = "example-steering-secret-value";
|
|
417
417
|
process.env.OPENAI_API_KEY = secret;
|
|
418
418
|
try {
|
|
419
419
|
const task = await runningTask("pi", "secret scrubbing");
|
|
@@ -48,12 +48,11 @@ function restoreEnv(name: string, value: string | undefined): void {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
|
-
*
|
|
52
|
-
* is simply the default: unset the flag for the duration of the callback.
|
|
51
|
+
* Exercise an explicit opt-out without depending on the deployment default.
|
|
53
52
|
*/
|
|
54
53
|
async function withSteeringDisabled<T>(run: () => Promise<T> | T): Promise<T> {
|
|
55
54
|
const previous = process.env.STEERING_ENABLED;
|
|
56
|
-
|
|
55
|
+
process.env.STEERING_ENABLED = "false";
|
|
57
56
|
try {
|
|
58
57
|
return await run();
|
|
59
58
|
} finally {
|
|
@@ -116,8 +115,7 @@ async function api(
|
|
|
116
115
|
const originalSteeringEnabled = process.env.STEERING_ENABLED;
|
|
117
116
|
|
|
118
117
|
beforeAll(async () => {
|
|
119
|
-
//
|
|
120
|
-
// default); withSteeringDisabled() drops the flag to exercise the default.
|
|
118
|
+
// Pin both branches explicitly so inherited configuration cannot affect them.
|
|
121
119
|
process.env.STEERING_ENABLED = "true";
|
|
122
120
|
await removeTestDb();
|
|
123
121
|
process.env.DATABASE_PATH = TEST_DB_PATH;
|
|
@@ -147,9 +145,9 @@ afterAll(async () => {
|
|
|
147
145
|
await removeTestDb();
|
|
148
146
|
});
|
|
149
147
|
|
|
150
|
-
describe("STEERING_ENABLED
|
|
151
|
-
test("steering
|
|
152
|
-
expect(isSteeringEnabled({})).toBe(
|
|
148
|
+
describe("STEERING_ENABLED defaults and overrides", () => {
|
|
149
|
+
test("steering defaults on and accepts explicit opt-outs", () => {
|
|
150
|
+
expect(isSteeringEnabled({})).toBe(true);
|
|
153
151
|
expect(isSteeringEnabled({ STEERING_ENABLED: "false" })).toBe(false);
|
|
154
152
|
expect(isSteeringEnabled({ STEERING_ENABLED: "0" })).toBe(false);
|
|
155
153
|
expect(isSteeringEnabled({ STEERING_ENABLED: "true" })).toBe(true);
|
|
@@ -233,7 +233,7 @@ describe("steering worker transport", () => {
|
|
|
233
233
|
|
|
234
234
|
test("scrubs provider errors before reporting an undeliverable reason", async () => {
|
|
235
235
|
const pending = pendingMessage();
|
|
236
|
-
const secret =
|
|
236
|
+
const secret = `sk-proj-${"steering".repeat(4)}`;
|
|
237
237
|
let reportedBody = "";
|
|
238
238
|
const fetchImpl = (async (input: string | URL | Request, init?: RequestInit) => {
|
|
239
239
|
if (String(input).includes("/api/steering-messages?")) {
|
|
@@ -280,7 +280,7 @@ describe("steering worker transport", () => {
|
|
|
280
280
|
expect(requested.outcome).toBe("queued");
|
|
281
281
|
|
|
282
282
|
await pollAndDispatchSteering(
|
|
283
|
-
{ apiUrl: baseUrl, apiKey: "test-key", agentId: agent.id },
|
|
283
|
+
{ apiUrl: baseUrl, apiKey: "example-test-key", agentId: agent.id },
|
|
284
284
|
task.id,
|
|
285
285
|
session(),
|
|
286
286
|
createSteeringDispatchState(),
|
|
@@ -327,7 +327,7 @@ describe("steering worker transport", () => {
|
|
|
327
327
|
// undeliverable — but the external-delivery flag short-circuits the poll.
|
|
328
328
|
const codexLikeSession = { ...session(), steeringDeliveredExternally: true };
|
|
329
329
|
await pollAndDispatchSteering(
|
|
330
|
-
{ apiUrl: baseUrl, apiKey: "test-key", agentId: agent.id },
|
|
330
|
+
{ apiUrl: baseUrl, apiKey: "example-test-key", agentId: agent.id },
|
|
331
331
|
task.id,
|
|
332
332
|
codexLikeSession,
|
|
333
333
|
createSteeringDispatchState(),
|
|
@@ -343,7 +343,7 @@ describe("steering worker transport", () => {
|
|
|
343
343
|
const previousBaseUrl = process.env.MCP_BASE_URL;
|
|
344
344
|
const previousApiKey = process.env.AGENT_SWARM_API_KEY;
|
|
345
345
|
process.env.MCP_BASE_URL = baseUrl;
|
|
346
|
-
process.env.AGENT_SWARM_API_KEY = "test-key";
|
|
346
|
+
process.env.AGENT_SWARM_API_KEY = "example-test-key";
|
|
347
347
|
try {
|
|
348
348
|
const agent = await createAgent({
|
|
349
349
|
name: "accept steering worker",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import type * as z from "zod";
|
|
4
|
+
import { registerStoreProgressTool } from "../tools/store-progress";
|
|
5
|
+
|
|
6
|
+
describe("store-progress output guidance", () => {
|
|
7
|
+
const server = new McpServer({ name: "output-guidance-test", version: "1.0.0" });
|
|
8
|
+
registerStoreProgressTool(server);
|
|
9
|
+
const tools = (
|
|
10
|
+
server as unknown as {
|
|
11
|
+
_registeredTools: Record<string, { inputSchema: z.ZodObject }>;
|
|
12
|
+
}
|
|
13
|
+
)._registeredTools;
|
|
14
|
+
const outputSchema = tools["store-progress"].inputSchema.shape.output;
|
|
15
|
+
|
|
16
|
+
test("the exposed output field carries the same budget and exceptions as the prompts", () => {
|
|
17
|
+
expect(outputSchema.description).toContain("Keep free-text output under 120 words by default");
|
|
18
|
+
expect(outputSchema.description).toContain("published verbatim in the thread's outcome card");
|
|
19
|
+
expect(outputSchema.description).toContain("every artifact link");
|
|
20
|
+
expect(outputSchema.description).toContain("Link documents instead of inlining them");
|
|
21
|
+
expect(outputSchema.description).toContain(
|
|
22
|
+
"requested depth, enumerated results, essential evidence",
|
|
23
|
+
);
|
|
24
|
+
expect(outputSchema.description).toContain("the task's outputSchema requires longer output");
|
|
25
|
+
expect(outputSchema.description).toContain("output must be JSON matching it");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("the style target does not reject longer requested or schema-constrained output", () => {
|
|
29
|
+
const longResult = "Required result. ".repeat(150);
|
|
30
|
+
for (const output of [longResult, JSON.stringify({ results: [longResult] })]) {
|
|
31
|
+
expect(outputSchema.parse(output)).toBe(output);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -111,38 +111,38 @@ describe("finalizeSwarmToolResult", () => {
|
|
|
111
111
|
});
|
|
112
112
|
|
|
113
113
|
test("secrets are scrubbed from message, details, and data at the egress point", async () => {
|
|
114
|
-
registerVolatileSecret("sk-super-secret-token-123", "TEST_TOKEN");
|
|
114
|
+
registerVolatileSecret("example-sk-super-secret-token-123", "TEST_TOKEN");
|
|
115
115
|
try {
|
|
116
116
|
const result = await finalizeSwarmToolResult("some-tool", {
|
|
117
117
|
ok: false,
|
|
118
|
-
message: "Auth failed with sk-super-secret-token-123",
|
|
119
|
-
details: "header was sk-super-secret-token-123",
|
|
120
|
-
data: { token: "sk-super-secret-token-123" },
|
|
118
|
+
message: "Auth failed with example-sk-super-secret-token-123",
|
|
119
|
+
details: "header was example-sk-super-secret-token-123",
|
|
120
|
+
data: { token: "example-sk-super-secret-token-123" },
|
|
121
121
|
});
|
|
122
122
|
const serialized = JSON.stringify(result);
|
|
123
|
-
expect(serialized).not.toContain("sk-super-secret-token-123");
|
|
123
|
+
expect(serialized).not.toContain("example-sk-super-secret-token-123");
|
|
124
124
|
} finally {
|
|
125
125
|
clearVolatileSecretsForTesting();
|
|
126
126
|
}
|
|
127
127
|
});
|
|
128
128
|
|
|
129
129
|
test("allowSecretEgress skips scrubbing for deliberate credential reveals only", async () => {
|
|
130
|
-
registerVolatileSecret("xsk_reveal_me_once_456", "TEST_REVEAL");
|
|
130
|
+
registerVolatileSecret("example-xsk_reveal_me_once_456", "TEST_REVEAL");
|
|
131
131
|
try {
|
|
132
132
|
const revealed = await finalizeSwarmToolResult("script-apis", {
|
|
133
133
|
ok: true,
|
|
134
134
|
message: "Endpoint created.",
|
|
135
|
-
details: "Bearer token (shown once — save it now): xsk_reveal_me_once_456",
|
|
136
|
-
data: { token: "xsk_reveal_me_once_456" },
|
|
135
|
+
details: "Bearer token (shown once — save it now): example-xsk_reveal_me_once_456",
|
|
136
|
+
data: { token: "example-xsk_reveal_me_once_456" },
|
|
137
137
|
allowSecretEgress: true,
|
|
138
138
|
});
|
|
139
|
-
expect(JSON.stringify(revealed)).toContain("xsk_reveal_me_once_456");
|
|
139
|
+
expect(JSON.stringify(revealed)).toContain("example-xsk_reveal_me_once_456");
|
|
140
140
|
|
|
141
141
|
const scrubbed = await finalizeSwarmToolResult("some-tool", {
|
|
142
142
|
ok: true,
|
|
143
|
-
message: "leaky xsk_reveal_me_once_456",
|
|
143
|
+
message: "leaky example-xsk_reveal_me_once_456",
|
|
144
144
|
});
|
|
145
|
-
expect(JSON.stringify(scrubbed)).not.toContain("xsk_reveal_me_once_456");
|
|
145
|
+
expect(JSON.stringify(scrubbed)).not.toContain("example-xsk_reveal_me_once_456");
|
|
146
146
|
} finally {
|
|
147
147
|
clearVolatileSecretsForTesting();
|
|
148
148
|
}
|
|
@@ -36,12 +36,14 @@ afterEach(() => {
|
|
|
36
36
|
|
|
37
37
|
describe("swarm_x MCP tool", () => {
|
|
38
38
|
test("routes composio requests with server-side auth and scrubbed output", async () => {
|
|
39
|
-
process.env.COMPOSIO_API_KEY = "ck_tool_secret_value";
|
|
39
|
+
process.env.COMPOSIO_API_KEY = "example-ck_tool_secret_value";
|
|
40
40
|
const fetchMock = mock(async (url: string | URL | Request, init?: RequestInit) => {
|
|
41
41
|
expect(String(url)).toBe("https://backend.composio.dev/api/v3.1/tools?limit=1");
|
|
42
42
|
expect(init?.method).toBe("GET");
|
|
43
|
-
expect((init?.headers as Record<string, string>)["x-api-key"]).toBe(
|
|
44
|
-
|
|
43
|
+
expect((init?.headers as Record<string, string>)["x-api-key"]).toBe(
|
|
44
|
+
"example-ck_tool_secret_value",
|
|
45
|
+
);
|
|
46
|
+
return jsonResponse({ ok: true, token: "example-ck_tool_secret_value" });
|
|
45
47
|
});
|
|
46
48
|
globalThis.fetch = fetchMock;
|
|
47
49
|
|
|
@@ -65,11 +67,11 @@ describe("swarm_x MCP tool", () => {
|
|
|
65
67
|
expect(JSON.stringify(result.structuredContent.response)).toContain(
|
|
66
68
|
"[REDACTED:COMPOSIO_API_KEY]",
|
|
67
69
|
);
|
|
68
|
-
expect(result.structuredContent.responseText).not.toContain("ck_tool_secret_value");
|
|
70
|
+
expect(result.structuredContent.responseText).not.toContain("example-ck_tool_secret_value");
|
|
69
71
|
});
|
|
70
72
|
|
|
71
73
|
test("rejects absolute composio paths", async () => {
|
|
72
|
-
process.env.COMPOSIO_API_KEY = "ck_tool_secret_value";
|
|
74
|
+
process.env.COMPOSIO_API_KEY = "example-ck_tool_secret_value";
|
|
73
75
|
const fetchMock = mock(async () => jsonResponse({ ok: true }));
|
|
74
76
|
globalThis.fetch = fetchMock;
|
|
75
77
|
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
type Manifest,
|
|
11
11
|
type PathRewrite,
|
|
12
12
|
parseSkillMd,
|
|
13
|
+
rewriteBundledFileComments,
|
|
13
14
|
rewriteSkillReferences,
|
|
14
15
|
sanitizeSyncedVia,
|
|
15
16
|
stableJson,
|
|
@@ -215,6 +216,24 @@ describe("ai-toolbox skill sync pure transforms", () => {
|
|
|
215
216
|
).toThrow("exceed 10485760 bytes total");
|
|
216
217
|
});
|
|
217
218
|
|
|
219
|
+
test("clarifies only the Codex helper comment and preserves executable sandbox settings", () => {
|
|
220
|
+
const comment =
|
|
221
|
+
"# CODEX_SANDBOX default: workspace-write read-only | workspace-write | danger-full-access";
|
|
222
|
+
const executable = `SANDBOX="\${CODEX_SANDBOX:-workspace-write}"\nargs+=( --dangerously-bypass-approvals-and-sandbox )\n`;
|
|
223
|
+
const source = `${comment}\n${executable}`;
|
|
224
|
+
expect(rewriteBundledFileComments("delegate-work", "scripts/codex-exec.sh", source)).toBe(
|
|
225
|
+
`# CODEX_SANDBOX default: workspace-write (passed to codex's -s option)\n${executable}`,
|
|
226
|
+
);
|
|
227
|
+
expect(rewriteBundledFileComments("other", "scripts/codex-exec.sh", source)).toBe(source);
|
|
228
|
+
expect(rewriteBundledFileComments("delegate-work", "scripts/other.sh", source)).toBe(source);
|
|
229
|
+
expect(() =>
|
|
230
|
+
rewriteBundledFileComments("delegate-work", "scripts/codex-exec.sh", executable),
|
|
231
|
+
).toThrow("expected one upstream sandbox default comment");
|
|
232
|
+
expect(() =>
|
|
233
|
+
rewriteBundledFileComments("delegate-work", "scripts/codex-exec.sh", `${comment}\n${source}`),
|
|
234
|
+
).toThrow("expected one upstream sandbox default comment");
|
|
235
|
+
});
|
|
236
|
+
|
|
218
237
|
test("reports executable text downgrades and rejects binary files", () => {
|
|
219
238
|
const executable = validateBundledFileBytes(
|
|
220
239
|
"fixture",
|
|
@@ -645,7 +645,7 @@ describe("Task Supersede + Resume", () => {
|
|
|
645
645
|
test("scrubs secret-shaped values from session-log summaries", async () => {
|
|
646
646
|
// GitHub PAT-shaped token — matches a structural pattern in
|
|
647
647
|
// scrubSecrets (regardless of env state).
|
|
648
|
-
const fakeToken = "
|
|
648
|
+
const fakeToken = `ghp_${"a".repeat(36)}`;
|
|
649
649
|
mockSessionLogs = [
|
|
650
650
|
{
|
|
651
651
|
createdAt: new Date().toISOString(),
|
|
@@ -73,11 +73,14 @@ async function pins(client: Client): Promise<string[]> {
|
|
|
73
73
|
.sort();
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
test("wire response is default-
|
|
76
|
+
test("wire response is default-on, per-task, additive, and fixed for one MCP session", async () => {
|
|
77
77
|
const task = await createTaskExtended("review fixture", { agentId, taskType: "review" });
|
|
78
78
|
const other = await createTaskExtended("other fixture", { agentId, taskType: "other" });
|
|
79
79
|
const foreign = await createTaskExtended("unowned", { taskType: "review" });
|
|
80
80
|
await configure("TASK_TOOL_MANIFESTS", '{"taskTypes":{"review":["script-run","get-tasks"]}}');
|
|
81
|
+
const defaultOn = await connect(task.id);
|
|
82
|
+
expect(await pins(defaultOn)).toEqual(["get-tasks", "script-run"]);
|
|
83
|
+
await configure("TASK_TOOL_PRELOAD_ENABLED", "false");
|
|
81
84
|
const off = await connect(task.id);
|
|
82
85
|
const baseline = (await off.listTools()).tools;
|
|
83
86
|
expect(baseline.some((tool) => tool.name === "script-run")).toBe(true);
|