@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
|
@@ -40,7 +40,7 @@ function callSendTask(
|
|
|
40
40
|
function structuredOf(result: CallToolResult) {
|
|
41
41
|
return result.structuredContent as {
|
|
42
42
|
success: boolean;
|
|
43
|
-
task?: { id: string; outputSchema?: Record<string, unknown
|
|
43
|
+
task?: { id: string; outputSchema?: Record<string, unknown>; routingSource?: string };
|
|
44
44
|
message: string;
|
|
45
45
|
};
|
|
46
46
|
}
|
|
@@ -168,6 +168,7 @@ describe("send-task: outputSchema propagation", () => {
|
|
|
168
168
|
const created = await getTaskById(s.task!.id);
|
|
169
169
|
expect(created?.outputSchema).toEqual(schema);
|
|
170
170
|
expect(created?.routingReason).toBeUndefined();
|
|
171
|
+
expect(created?.routingSource).toBeUndefined();
|
|
171
172
|
});
|
|
172
173
|
|
|
173
174
|
test("a provided outputSchema is persisted when the task is offered to an agent", async () => {
|
|
@@ -184,6 +185,7 @@ describe("send-task: outputSchema propagation", () => {
|
|
|
184
185
|
task: "offered task with a schema",
|
|
185
186
|
agentId: worker.id,
|
|
186
187
|
routingReason: "human_pinned",
|
|
188
|
+
routingNote: "This test explicitly selects the target worker",
|
|
187
189
|
offerMode: true,
|
|
188
190
|
outputSchema: schema,
|
|
189
191
|
allowDuplicate: true,
|
|
@@ -214,6 +216,7 @@ describe("send-task: outputSchema propagation", () => {
|
|
|
214
216
|
task: "directly assigned task with a schema",
|
|
215
217
|
agentId: worker.id,
|
|
216
218
|
routingReason: "human_pinned",
|
|
219
|
+
routingNote: "This test explicitly selects the target worker",
|
|
217
220
|
outputSchema: schema,
|
|
218
221
|
allowDuplicate: true,
|
|
219
222
|
},
|
|
@@ -224,6 +227,8 @@ describe("send-task: outputSchema propagation", () => {
|
|
|
224
227
|
const created = await getTaskById(s.task!.id);
|
|
225
228
|
expect(created?.outputSchema).toEqual(schema);
|
|
226
229
|
expect(created?.routingReason).toBe("human_pinned");
|
|
230
|
+
expect(created?.routingSource).toBe("declared");
|
|
231
|
+
expect(s.task?.routingSource).toBe("declared");
|
|
227
232
|
});
|
|
228
233
|
|
|
229
234
|
test("implicit parent routing stamps continuity while pool creation omits a reason", async () => {
|
|
@@ -249,6 +254,8 @@ describe("send-task: outputSchema propagation", () => {
|
|
|
249
254
|
const continuedTask = await getTaskById(structuredOf(continued).task!.id);
|
|
250
255
|
expect(continuedTask?.agentId).toBe(worker.id);
|
|
251
256
|
expect(continuedTask?.routingReason).toBe("continuity");
|
|
257
|
+
expect(continuedTask?.routingSource).toBe("engine_default");
|
|
258
|
+
expect(structuredOf(continued).task?.routingSource).toBe("engine_default");
|
|
252
259
|
|
|
253
260
|
const pooled = await callSendTask(
|
|
254
261
|
server,
|
|
@@ -278,6 +285,7 @@ describe("send-task: outputSchema propagation", () => {
|
|
|
278
285
|
);
|
|
279
286
|
const created = await getTaskById(structuredOf(result).task!.id);
|
|
280
287
|
expect(created?.routingNote).toBe("matches the required runtime specialization");
|
|
288
|
+
expect(created?.routingSource).toBe("declared");
|
|
281
289
|
});
|
|
282
290
|
});
|
|
283
291
|
|
|
@@ -152,6 +152,7 @@ describe("send-task: requestedByUserId inheritance", () => {
|
|
|
152
152
|
task: "worker direct assign — inherit user",
|
|
153
153
|
agentId: WORKER_ID,
|
|
154
154
|
routingReason: "human_pinned",
|
|
155
|
+
routingNote: "This test explicitly selects the target worker",
|
|
155
156
|
allowDuplicate: true,
|
|
156
157
|
},
|
|
157
158
|
LEAD_ID,
|
|
@@ -276,6 +276,7 @@ describe("send-task: Slack-routing coherence guard", () => {
|
|
|
276
276
|
task: "direct assign with wrong channel",
|
|
277
277
|
agentId: WORKER_ID,
|
|
278
278
|
routingReason: "human_pinned",
|
|
279
|
+
routingNote: "This test explicitly selects the target worker",
|
|
279
280
|
slackChannelId: "C_WRONG_DIRECT",
|
|
280
281
|
slackThreadTs: "555.555",
|
|
281
282
|
allowDuplicate: true,
|
|
@@ -301,6 +302,7 @@ describe("send-task: Slack-routing coherence guard", () => {
|
|
|
301
302
|
task: "offer with wrong channel",
|
|
302
303
|
agentId: WORKER_ID,
|
|
303
304
|
routingReason: "human_pinned",
|
|
305
|
+
routingNote: "This test explicitly selects the target worker",
|
|
304
306
|
offerMode: true,
|
|
305
307
|
slackChannelId: "C_WRONG_OFFER",
|
|
306
308
|
slackThreadTs: "666.666",
|
|
@@ -22,7 +22,7 @@ import { getPathSegments, parseQueryParams } from "../http/utils";
|
|
|
22
22
|
import { listenOnFreePort } from "./test-net";
|
|
23
23
|
|
|
24
24
|
const TEST_DB_PATH = "./test-session-costs-codex-recompute.sqlite";
|
|
25
|
-
const API_KEY = "test-codex-recompute-secret";
|
|
25
|
+
const API_KEY = "example-test-codex-recompute-secret";
|
|
26
26
|
|
|
27
27
|
async function removeDbFiles(path: string): Promise<void> {
|
|
28
28
|
for (const suffix of ["", "-wal", "-shm"]) {
|
|
@@ -21,8 +21,8 @@ import { getPathSegments, parseQueryParams } from "../http/utils";
|
|
|
21
21
|
import type { PricingProvider, PricingTokenClass } from "../types";
|
|
22
22
|
import { listenOnFreePort } from "./test-net";
|
|
23
23
|
|
|
24
|
-
const TEST_DB_PATH = "./test-session-costs-golden.sqlite";
|
|
25
|
-
const API_KEY = "test-session-costs-golden";
|
|
24
|
+
const TEST_DB_PATH = "./example-test-session-costs-golden.sqlite";
|
|
25
|
+
const API_KEY = "example-test-session-costs-golden";
|
|
26
26
|
const RATE_EFFECTIVE_AT = 1_800_000_000_000;
|
|
27
27
|
const FIXTURE_CREATED_AT = RATE_EFFECTIVE_AT + 1;
|
|
28
28
|
const EPSILON = 1e-9;
|
|
@@ -19,8 +19,8 @@ import { handleSessionData } from "../http/session-data";
|
|
|
19
19
|
import { getPathSegments, parseQueryParams } from "../http/utils";
|
|
20
20
|
import { listenOnFreePort } from "./test-net";
|
|
21
21
|
|
|
22
|
-
const TEST_DB_PATH = "./test-model-key-normalize.sqlite";
|
|
23
|
-
const API_KEY = "test-model-key-normalize";
|
|
22
|
+
const TEST_DB_PATH = "./example-test-model-key-normalize.sqlite";
|
|
23
|
+
const API_KEY = "example-test-model-key-normalize";
|
|
24
24
|
|
|
25
25
|
async function removeDbFiles(path: string): Promise<void> {
|
|
26
26
|
for (const suffix of ["", "-wal", "-shm"]) {
|
|
@@ -17,7 +17,7 @@ import { getPathSegments, parseQueryParams } from "../http/utils";
|
|
|
17
17
|
import { listenOnFreePort } from "./test-net";
|
|
18
18
|
|
|
19
19
|
const TEST_DB_PATH = "./test-recompute-all-providers.sqlite";
|
|
20
|
-
const API_KEY = "test-recompute-all";
|
|
20
|
+
const API_KEY = "example-test-recompute-all";
|
|
21
21
|
|
|
22
22
|
async function removeDbFiles(path: string): Promise<void> {
|
|
23
23
|
for (const suffix of ["", "-wal", "-shm"]) {
|
|
@@ -23,7 +23,7 @@ import { handleSessions } from "../http/sessions";
|
|
|
23
23
|
import { getPathSegments, parseQueryParams } from "../http/utils";
|
|
24
24
|
import { listenOnFreePort } from "./test-net";
|
|
25
25
|
|
|
26
|
-
const API_KEY = "test-session-token-key";
|
|
26
|
+
const API_KEY = "example-test-session-token-key";
|
|
27
27
|
|
|
28
28
|
function createTestServer(apiKey: string): Server {
|
|
29
29
|
return createHttpServer(async (req: IncomingMessage, res: ServerResponse) => {
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
* rather than executed.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
const SECRET = "
|
|
21
|
+
const SECRET = "example-shutdown-token-value";
|
|
22
22
|
|
|
23
23
|
const shutdownSourcePath = `${import.meta.dir}/../http/index.ts`;
|
|
24
24
|
const shutdownSource = await Bun.file(shutdownSourcePath).text();
|
|
@@ -298,8 +298,10 @@ describe("buildAssignmentSummaryBlocks", () => {
|
|
|
298
298
|
failed: [{ agentName: "Delta", reason: "error" }],
|
|
299
299
|
});
|
|
300
300
|
|
|
301
|
+
expect(blocks).toHaveLength(1);
|
|
302
|
+
expect(blocks[0].text.text).not.toBe("");
|
|
301
303
|
expect(blocks[0].text.text).toContain("⚠️");
|
|
302
|
-
expect(blocks[0].text.text).toContain("Could not assign");
|
|
304
|
+
expect(blocks[0].text.text).toContain("Could not assign to:");
|
|
303
305
|
});
|
|
304
306
|
});
|
|
305
307
|
|
|
@@ -463,6 +465,15 @@ function makeTaskId(prefix: string): string {
|
|
|
463
465
|
}
|
|
464
466
|
|
|
465
467
|
describe("buildTreeBlocks", () => {
|
|
468
|
+
test("empty tree omits the section with empty text", () => {
|
|
469
|
+
const blocks = buildTreeBlocks([]);
|
|
470
|
+
|
|
471
|
+
expect(blocks).toHaveLength(0);
|
|
472
|
+
expect(blocks.every((block) => block.type !== "section" || block.text.text.length > 0)).toBe(
|
|
473
|
+
true,
|
|
474
|
+
);
|
|
475
|
+
});
|
|
476
|
+
|
|
466
477
|
test("single root, no children", () => {
|
|
467
478
|
const root: TreeNode = {
|
|
468
479
|
taskId: makeTaskId("aaaa0001"),
|
|
@@ -21,7 +21,7 @@ import type { SlackFile } from "../slack/files";
|
|
|
21
21
|
import { createSlackTaskWithFiles, fetchSlackFiles } from "../slack/inbound-files";
|
|
22
22
|
|
|
23
23
|
const TEST_DB_PATH = "./test-slack-file-tools-attachments.sqlite";
|
|
24
|
-
const BOT_TOKEN = "
|
|
24
|
+
const BOT_TOKEN = "example-slack-file-tools-token";
|
|
25
25
|
const AGENT_ID = "aaaaaaaa-0000-4000-8000-00000000f001";
|
|
26
26
|
const OTHER_ID = "aaaaaaaa-0000-4000-8000-00000000f002";
|
|
27
27
|
const PNG = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 9, 9]);
|
|
@@ -6,7 +6,17 @@
|
|
|
6
6
|
* task unclaimable until its attachments exist, and both Slack ingress
|
|
7
7
|
* handlers end to end against a real DB and the local file provider.
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
afterAll,
|
|
11
|
+
afterEach,
|
|
12
|
+
beforeAll,
|
|
13
|
+
beforeEach,
|
|
14
|
+
describe,
|
|
15
|
+
expect,
|
|
16
|
+
mock,
|
|
17
|
+
spyOn,
|
|
18
|
+
test,
|
|
19
|
+
} from "bun:test";
|
|
10
20
|
import { mkdtemp, rm, unlink } from "node:fs/promises";
|
|
11
21
|
import { tmpdir } from "node:os";
|
|
12
22
|
import { join } from "node:path";
|
|
@@ -16,13 +26,17 @@ import {
|
|
|
16
26
|
createTaskExtended,
|
|
17
27
|
getDbClient,
|
|
18
28
|
getLogsByTaskId,
|
|
29
|
+
getSlackTasksInThread,
|
|
30
|
+
getSteeringMessagesForTask,
|
|
19
31
|
getTaskAttachments,
|
|
20
32
|
getTaskById,
|
|
21
33
|
initDb,
|
|
22
34
|
promoteAbandonedDraftTasks,
|
|
35
|
+
startTask,
|
|
23
36
|
} from "../be/db";
|
|
24
37
|
import { MAX_TASK_ATTACHMENT_BYTES } from "../be/task-attachment-store";
|
|
25
38
|
import { getFileStorageProvider, resetFileStorageProviderForTests } from "../fs/registry";
|
|
39
|
+
import * as slackAppModule from "../slack/app";
|
|
26
40
|
import { createAssistant } from "../slack/assistant";
|
|
27
41
|
import * as slackEnrichModule from "../slack/enrich";
|
|
28
42
|
import type { SlackFile } from "../slack/files";
|
|
@@ -34,10 +48,10 @@ import {
|
|
|
34
48
|
notifySlackFileFailures,
|
|
35
49
|
setDraftLeaseRefreshMsForTests,
|
|
36
50
|
} from "../slack/inbound-files";
|
|
37
|
-
import { instantFlush } from "../slack/thread-buffer";
|
|
51
|
+
import { getBufferMessageCount, instantFlush } from "../slack/thread-buffer";
|
|
38
52
|
|
|
39
53
|
const TEST_DB_PATH = "./test-slack-inbound-files.sqlite";
|
|
40
|
-
const BOT_TOKEN = "
|
|
54
|
+
const BOT_TOKEN = "example-slack-inbound-files-token";
|
|
41
55
|
const PNG_BYTES = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 1, 2, 3]);
|
|
42
56
|
|
|
43
57
|
let slackFiles: ReturnType<typeof Bun.serve>;
|
|
@@ -51,6 +65,7 @@ const previousEnv = {
|
|
|
51
65
|
SLACK_RENDER_V2: process.env.SLACK_RENDER_V2,
|
|
52
66
|
STEERING_ENABLED: process.env.STEERING_ENABLED,
|
|
53
67
|
SLACK_THREAD_STEERING: process.env.SLACK_THREAD_STEERING,
|
|
68
|
+
SLACK_THREAD_FOLLOWUP_REQUIRE_MENTION: process.env.SLACK_THREAD_FOLLOWUP_REQUIRE_MENTION,
|
|
54
69
|
};
|
|
55
70
|
|
|
56
71
|
const HTML_FILE = new TextEncoder().encode("<html><body>quarterly report</body></html>");
|
|
@@ -708,113 +723,234 @@ describe("Slack ingress with files", () => {
|
|
|
708
723
|
expect(await getTaskAttachments(followUp!.id)).toHaveLength(1);
|
|
709
724
|
});
|
|
710
725
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
726
|
+
describe("buffered follow-ups with files", () => {
|
|
727
|
+
let earlier: Awaited<ReturnType<typeof createTaskExtended>>;
|
|
728
|
+
let client: ReturnType<typeof slackClient>;
|
|
729
|
+
const getApp = spyOn(slackAppModule, "getSlackApp");
|
|
730
|
+
const channelId = "C0BUFFERFILES";
|
|
731
|
+
const threadTs = "1950000000.000001";
|
|
732
|
+
const key = `${channelId}:${threadTs}`;
|
|
733
|
+
|
|
734
|
+
beforeEach(async () => {
|
|
735
|
+
const lead = await createAgent({
|
|
736
|
+
name: "lead",
|
|
737
|
+
isLead: true,
|
|
738
|
+
status: "busy",
|
|
739
|
+
harnessProvider: "pi",
|
|
740
|
+
});
|
|
741
|
+
earlier = await createTaskExtended("earlier work", {
|
|
742
|
+
agentId: lead.id,
|
|
743
|
+
source: "slack",
|
|
744
|
+
slackChannelId: channelId,
|
|
745
|
+
slackThreadTs: threadTs,
|
|
746
|
+
});
|
|
747
|
+
await startTask(earlier.id);
|
|
748
|
+
process.env.ADDITIVE_SLACK = "true";
|
|
749
|
+
process.env.STEERING_ENABLED = "true";
|
|
750
|
+
process.env.SLACK_THREAD_STEERING = "lead";
|
|
751
|
+
process.env.SLACK_THREAD_FOLLOWUP_REQUIRE_MENTION = "false";
|
|
752
|
+
client = slackClient();
|
|
753
|
+
getApp.mockReturnValue({ client } as never);
|
|
754
|
+
});
|
|
723
755
|
|
|
724
|
-
|
|
756
|
+
afterEach(async () => {
|
|
757
|
+
await instantFlush(key);
|
|
758
|
+
process.env.ADDITIVE_SLACK = "false";
|
|
759
|
+
restoreSteeringEnv();
|
|
760
|
+
const requireMention = previousEnv.SLACK_THREAD_FOLLOWUP_REQUIRE_MENTION;
|
|
761
|
+
if (requireMention === undefined) delete process.env.SLACK_THREAD_FOLLOWUP_REQUIRE_MENTION;
|
|
762
|
+
else process.env.SLACK_THREAD_FOLLOWUP_REQUIRE_MENTION = requireMention;
|
|
763
|
+
getApp.mockReturnValue(null);
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
afterAll(() => getApp.mockRestore());
|
|
767
|
+
|
|
768
|
+
async function sendBuffered(text: string, files: SlackFile[] = []) {
|
|
725
769
|
seq += 1;
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
770
|
+
const ts = `1950000001.${String(seq).padStart(6, "0")}`;
|
|
771
|
+
await channelMessage({
|
|
772
|
+
event: {
|
|
773
|
+
type: "message",
|
|
774
|
+
subtype: files.length ? "file_share" : undefined,
|
|
775
|
+
channel: channelId,
|
|
776
|
+
thread_ts: threadTs,
|
|
777
|
+
ts,
|
|
731
778
|
user: "U_HUMAN",
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
files: [slackFile()],
|
|
779
|
+
text,
|
|
780
|
+
files,
|
|
735
781
|
},
|
|
736
782
|
body: { event_id: `evt_inbound_files_${seq}` },
|
|
737
783
|
client,
|
|
738
784
|
say: mock(async () => ({})),
|
|
739
|
-
setStatus: mock(async () => {}),
|
|
740
|
-
setTitle: mock(async () => {}),
|
|
741
|
-
getThreadContext: mock(async () => ({})),
|
|
742
785
|
});
|
|
743
|
-
|
|
744
|
-
process.env.ADDITIVE_SLACK = "false";
|
|
786
|
+
return ts;
|
|
745
787
|
}
|
|
746
788
|
|
|
747
|
-
|
|
748
|
-
await
|
|
749
|
-
|
|
750
|
-
restoreSteeringEnv();
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
async function assertQueuedFollowUp() {
|
|
754
|
-
expect(fileRequests).toEqual([]);
|
|
755
|
-
expect(client.chat.postMessage).toHaveBeenCalledTimes(1);
|
|
756
|
-
const [args] = client.chat.postMessage.mock.calls[0] as unknown as [{ text: string }];
|
|
757
|
-
expect(args.text).toContain("`screenshot.png` (follow-ups queued by ADDITIVE_SLACK");
|
|
758
|
-
|
|
759
|
-
// Drain the debounce buffer now, while the DB is still open.
|
|
760
|
-
await instantFlush(`${first!.slackChannelId}:${first!.slackThreadTs}`);
|
|
761
|
-
const queued = await getDbClient().get<{ task: string }>(
|
|
762
|
-
"SELECT task FROM agent_tasks WHERE slackThreadTs = ? AND id != ? ORDER BY createdAt DESC",
|
|
763
|
-
[first!.slackThreadTs, first!.id],
|
|
789
|
+
async function followUp() {
|
|
790
|
+
const tasks = (await getSlackTasksInThread(channelId, threadTs)).filter(
|
|
791
|
+
(task) => task.id !== earlier.id,
|
|
764
792
|
);
|
|
765
|
-
expect(
|
|
793
|
+
expect(tasks).toHaveLength(1);
|
|
794
|
+
return tasks[0]!;
|
|
766
795
|
}
|
|
767
|
-
});
|
|
768
796
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
source: "slack",
|
|
775
|
-
slackChannelId: "C0NOWTEST1",
|
|
776
|
-
slackThreadTs: threadTs,
|
|
777
|
-
});
|
|
778
|
-
await getDbClient().run("UPDATE agent_tasks SET status = 'in_progress' WHERE id = ?", [
|
|
779
|
-
earlier.id,
|
|
780
|
-
]);
|
|
781
|
-
process.env.ADDITIVE_SLACK = "true";
|
|
782
|
-
process.env.STEERING_ENABLED = "false";
|
|
783
|
-
delete process.env.SLACK_THREAD_STEERING;
|
|
784
|
-
const client = slackClient();
|
|
785
|
-
seq += 1;
|
|
797
|
+
function failureNotices() {
|
|
798
|
+
return (client.chat.postMessage.mock.calls as unknown as [{ text: string }][])
|
|
799
|
+
.map(([args]) => args.text)
|
|
800
|
+
.filter((text) => text.includes("Couldn't attach"));
|
|
801
|
+
}
|
|
786
802
|
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
channel:
|
|
803
|
+
test("assistant follow-ups retain files until flush and resolve id-only metadata", async () => {
|
|
804
|
+
client.files.info.mockResolvedValue({ ok: true, file: slackFile() } as never);
|
|
805
|
+
seq += 1;
|
|
806
|
+
await assistantMessage({
|
|
807
|
+
message: {
|
|
808
|
+
channel: channelId,
|
|
793
809
|
thread_ts: threadTs,
|
|
794
810
|
ts: `1950000001.${String(seq).padStart(6, "0")}`,
|
|
795
811
|
user: "U_HUMAN",
|
|
796
|
-
|
|
797
|
-
|
|
812
|
+
subtype: "file_share",
|
|
813
|
+
text: "",
|
|
814
|
+
files: [{ id: "F0SHOT0001" }],
|
|
798
815
|
},
|
|
799
816
|
body: { event_id: `evt_inbound_files_${seq}` },
|
|
800
817
|
client,
|
|
801
818
|
say: mock(async () => ({})),
|
|
819
|
+
setStatus: mock(async () => {}),
|
|
820
|
+
setTitle: mock(async () => {}),
|
|
821
|
+
getThreadContext: mock(async () => ({})),
|
|
822
|
+
});
|
|
823
|
+
expect(fileRequests).toEqual([]);
|
|
824
|
+
expect(client.files.info).not.toHaveBeenCalled();
|
|
825
|
+
expect(failureNotices()).toEqual([]);
|
|
826
|
+
|
|
827
|
+
await instantFlush(key);
|
|
828
|
+
const task = await followUp();
|
|
829
|
+
expect(await readStoredBytes(task.id)).toEqual([PNG_BYTES]);
|
|
830
|
+
expect(task.task).toContain("[File: screenshot.png");
|
|
831
|
+
expect(task.task).not.toContain("not attached");
|
|
832
|
+
expect(await wasDraft(task.id)).toBe(true);
|
|
833
|
+
expect(await getSteeringMessagesForTask(earlier.id)).toEqual([]);
|
|
834
|
+
expect(failureNotices()).toEqual([]);
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
for (const steering of ["off", "lead"]) {
|
|
838
|
+
test(`timed flush attaches every message's files with steering ${steering}`, async () => {
|
|
839
|
+
process.env.SLACK_THREAD_STEERING = steering;
|
|
840
|
+
const nativeSetTimeout = globalThis.setTimeout;
|
|
841
|
+
const bufferMs = Number(process.env.ADDITIVE_SLACK_BUFFER_MS) || 10_000;
|
|
842
|
+
const timer = spyOn(globalThis, "setTimeout").mockImplementation((fn, ms, ...args) =>
|
|
843
|
+
nativeSetTimeout(fn, ms === bufferMs ? 250 : ms, ...args),
|
|
844
|
+
);
|
|
845
|
+
const otherBytes = new Uint8Array([...PNG_BYTES, 4]);
|
|
846
|
+
bytesById.set("F0OTHER0001", otherBytes);
|
|
847
|
+
let lastTs: string;
|
|
848
|
+
try {
|
|
849
|
+
await sendBuffered("", [slackFile()]);
|
|
850
|
+
lastTs = await sendBuffered("compare these", [
|
|
851
|
+
slackFile({ id: "F0OTHER0001", name: "other.png", size: otherBytes.length }),
|
|
852
|
+
]);
|
|
853
|
+
expect(getBufferMessageCount(key)).toBe(2);
|
|
854
|
+
expect(fileRequests).toEqual([]);
|
|
855
|
+
expect(failureNotices()).toEqual([]);
|
|
856
|
+
} finally {
|
|
857
|
+
timer.mockRestore();
|
|
858
|
+
}
|
|
859
|
+
// Wait for the real timer callback and draft promotion, not instantFlush.
|
|
860
|
+
for (let i = 0; i < 200; i++) {
|
|
861
|
+
const tasks = await getSlackTasksInThread(channelId, threadTs);
|
|
862
|
+
if (tasks.some((task) => task.id !== earlier.id && task.status === "pending")) break;
|
|
863
|
+
await Bun.sleep(10);
|
|
864
|
+
}
|
|
865
|
+
const task = await followUp();
|
|
866
|
+
expect(task.status).toBe("pending");
|
|
867
|
+
expect(task.dependsOn).toEqual([earlier.id]);
|
|
868
|
+
expect(task.slackTriggerMessageTs).toBe(lastTs!);
|
|
869
|
+
expect(task.task).toContain("2 message(s) buffered");
|
|
870
|
+
expect(task.task).toContain("compare these");
|
|
871
|
+
expect(task.task).not.toContain("not attached");
|
|
872
|
+
expect(await readStoredBytes(task.id)).toEqual([PNG_BYTES, otherBytes]);
|
|
873
|
+
expect(await wasDraft(task.id)).toBe(true);
|
|
874
|
+
expect(await getSteeringMessagesForTask(earlier.id)).toEqual([]);
|
|
875
|
+
expect(failureNotices()).toEqual([]);
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
test(`!now with files flushes existing files without dependency with steering ${steering}`, async () => {
|
|
879
|
+
process.env.SLACK_THREAD_STEERING = steering;
|
|
880
|
+
await sendBuffered("earlier screenshot", [slackFile()]);
|
|
881
|
+
expect(fileRequests).toEqual([]);
|
|
882
|
+
const otherBytes = new Uint8Array([...PNG_BYTES, 4]);
|
|
883
|
+
bytesById.set("F0OTHER0001", otherBytes);
|
|
884
|
+
await sendBuffered("!now", [
|
|
885
|
+
slackFile({ id: "F0OTHER0001", name: "other.png", size: otherBytes.length }),
|
|
886
|
+
]);
|
|
887
|
+
const task = await followUp();
|
|
888
|
+
expect(task.task).toContain("earlier screenshot");
|
|
889
|
+
expect(task.task).not.toContain("!now");
|
|
890
|
+
expect(task.dependsOn).toEqual([]);
|
|
891
|
+
expect(await getTaskAttachments(task.id)).toHaveLength(2);
|
|
892
|
+
expect(await readStoredBytes(task.id)).toEqual([PNG_BYTES, otherBytes]);
|
|
893
|
+
expect(await getSteeringMessagesForTask(earlier.id)).toEqual([]);
|
|
894
|
+
expect(failureNotices()).toEqual([]);
|
|
802
895
|
});
|
|
803
|
-
} finally {
|
|
804
|
-
process.env.ADDITIVE_SLACK = "false";
|
|
805
|
-
restoreSteeringEnv();
|
|
806
896
|
}
|
|
807
897
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
[
|
|
815
|
-
);
|
|
816
|
-
|
|
817
|
-
|
|
898
|
+
test("text-only follow-ups still steer the running task", async () => {
|
|
899
|
+
await sendBuffered("!now use the safer approach");
|
|
900
|
+
const steering = await getSteeringMessagesForTask(earlier.id);
|
|
901
|
+
expect(steering).toHaveLength(1);
|
|
902
|
+
expect(steering[0]!.body).toContain("use the safer approach");
|
|
903
|
+
expect(await getSlackTasksInThread(channelId, threadTs)).toHaveLength(1);
|
|
904
|
+
expect(fileRequests).toEqual([]);
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
test("download failures and oversize files are reported at flush while good files attach", async () => {
|
|
908
|
+
await sendBuffered("inspect these", [
|
|
909
|
+
slackFile(),
|
|
910
|
+
slackFile({
|
|
911
|
+
id: "F0DENIED001",
|
|
912
|
+
name: "denied.png",
|
|
913
|
+
url_private_download: `${slackFiles.url}forbidden`,
|
|
914
|
+
}),
|
|
915
|
+
slackFile({ id: "F0HUGE0001", name: "huge.png", size: HUGE_BYTES }),
|
|
916
|
+
]);
|
|
917
|
+
expect(failureNotices()).toEqual([]);
|
|
918
|
+
await instantFlush(key);
|
|
919
|
+
const task = await followUp();
|
|
920
|
+
expect(await readStoredBytes(task.id)).toEqual([PNG_BYTES]);
|
|
921
|
+
expect(task.task).toContain("not attached: download failed (HTTP 403)");
|
|
922
|
+
expect(task.task).not.toContain("follow-ups queued by ADDITIVE_SLACK");
|
|
923
|
+
expect(failureNotices()).toHaveLength(1);
|
|
924
|
+
expect(failureNotices()[0]).toContain("`denied.png` (download failed (HTTP 403))");
|
|
925
|
+
expect(failureNotices()[0]).toContain("`huge.png`");
|
|
926
|
+
expect(failureNotices()[0]).not.toContain("`screenshot.png`");
|
|
927
|
+
});
|
|
928
|
+
|
|
929
|
+
test("storage failures are reported after flush", async () => {
|
|
930
|
+
const upload = spyOn(getFileStorageProvider(), "upload").mockRejectedValue(
|
|
931
|
+
new Error("storage unavailable"),
|
|
932
|
+
);
|
|
933
|
+
try {
|
|
934
|
+
await sendBuffered("!now inspect this", [slackFile()]);
|
|
935
|
+
const task = await followUp();
|
|
936
|
+
expect(task.status).toBe("pending");
|
|
937
|
+
expect(await getTaskAttachments(task.id)).toEqual([]);
|
|
938
|
+
expect(failureNotices()).toHaveLength(1);
|
|
939
|
+
expect(failureNotices()[0]).toContain("storage unavailable");
|
|
940
|
+
} finally {
|
|
941
|
+
upload.mockRestore();
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
test("an unavailable Slack app retains the buffered file failure fallback", async () => {
|
|
946
|
+
getApp.mockReturnValue(null);
|
|
947
|
+
await sendBuffered("!now inspect this", [slackFile()]);
|
|
948
|
+
const task = await followUp();
|
|
949
|
+
expect(await getTaskAttachments(task.id)).toEqual([]);
|
|
950
|
+
expect(task.task).toContain("[File: screenshot.png");
|
|
951
|
+
expect(task.task).toContain("not attached: follow-ups queued by ADDITIVE_SLACK");
|
|
952
|
+
expect(await getSteeringMessagesForTask(earlier.id)).toEqual([]);
|
|
953
|
+
});
|
|
818
954
|
});
|
|
819
955
|
|
|
820
956
|
test("a channel message that mentions the bot with a file attaches it", async () => {
|
|
@@ -20,6 +20,7 @@ const ALL_EVENTS: SlackReactionEvent[] = [
|
|
|
20
20
|
|
|
21
21
|
/** The four acceptance-stage names `main` removed as a fixed list at finalize. */
|
|
22
22
|
const DEFAULT_ACCEPTANCE_NAMES = ["eyes", "heavy_plus_sign", "zap", "speech_balloon"];
|
|
23
|
+
const SECRET_SHAPED_GITHUB_PAT = `github_pat_${"a".repeat(32)}`;
|
|
23
24
|
|
|
24
25
|
function clearReactionEnv() {
|
|
25
26
|
for (const key of Object.values(SLACK_REACTION_CONFIG_KEYS)) delete process.env[key];
|
|
@@ -320,7 +321,7 @@ describe("reaction-shortcode.ts", () => {
|
|
|
320
321
|
test("a generic (non-invalid_name) add failure redacts a secret-shaped error message in the emitted log", async () => {
|
|
321
322
|
const logSpy = spyOn(console, "log");
|
|
322
323
|
logSpy.mockClear();
|
|
323
|
-
const leaked =
|
|
324
|
+
const leaked = SECRET_SHAPED_GITHUB_PAT;
|
|
324
325
|
const add = async () => {
|
|
325
326
|
throw new Error(`rate_limited: ${leaked}`);
|
|
326
327
|
};
|
|
@@ -339,7 +340,7 @@ describe("reaction-shortcode.ts", () => {
|
|
|
339
340
|
test("a generic (non-invalid_name/no_reaction) remove failure redacts a secret-shaped error message in the emitted log", async () => {
|
|
340
341
|
const logSpy = spyOn(console, "log");
|
|
341
342
|
logSpy.mockClear();
|
|
342
|
-
const leaked =
|
|
343
|
+
const leaked = SECRET_SHAPED_GITHUB_PAT;
|
|
343
344
|
const remove = async () => {
|
|
344
345
|
const error = new Error(`rate_limited: ${leaked}`) as Error & { data: unknown };
|
|
345
346
|
error.data = { error: "rate_limited" };
|
|
@@ -361,7 +362,7 @@ describe("reaction-shortcode.ts", () => {
|
|
|
361
362
|
test("an invalid_name add failure redacts a secret-shaped reaction name in the emitted log", async () => {
|
|
362
363
|
const errorSpy = spyOn(console, "error");
|
|
363
364
|
errorSpy.mockClear();
|
|
364
|
-
const leaked =
|
|
365
|
+
const leaked = SECRET_SHAPED_GITHUB_PAT;
|
|
365
366
|
const add = async () => {
|
|
366
367
|
throw { data: { error: "invalid_name" } };
|
|
367
368
|
};
|
|
@@ -380,7 +381,7 @@ describe("reaction-shortcode.ts", () => {
|
|
|
380
381
|
test("a fallback add failure after invalid_name redacts a secret-shaped error message in the emitted log", async () => {
|
|
381
382
|
const logSpy = spyOn(console, "log");
|
|
382
383
|
logSpy.mockClear();
|
|
383
|
-
const leaked =
|
|
384
|
+
const leaked = SECRET_SHAPED_GITHUB_PAT;
|
|
384
385
|
const add = async ({ name }: { name: string }) => {
|
|
385
386
|
if (name === "not_a_real_emoji") throw { data: { error: "invalid_name" } };
|
|
386
387
|
throw new Error(`rate_limited: ${leaked}`);
|
|
@@ -9,7 +9,7 @@ import { mcpOverflowNamespace } from "../tools/utils";
|
|
|
9
9
|
import { SKIP_SANDBOX_SPAWN_TESTS } from "./sandbox-spawn-test-helpers";
|
|
10
10
|
|
|
11
11
|
const TEST_DB_PATH = "./test-slack-read-boundaries.sqlite";
|
|
12
|
-
const API_KEY = "test-slack-read-boundaries-key-1234567890";
|
|
12
|
+
const API_KEY = "example-test-slack-read-boundaries-key-1234567890";
|
|
13
13
|
const AGENT_ID = "aaaaaaaa-0000-4000-8000-000000000001";
|
|
14
14
|
const CHANNEL_ID = "C_BUSY_BOUNDARY";
|
|
15
15
|
const FULL_MESSAGE_COUNT = 20;
|