@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
package/src/slack/render-v2.ts
CHANGED
|
@@ -77,7 +77,7 @@ type SlackThreadMessage = {
|
|
|
77
77
|
};
|
|
78
78
|
|
|
79
79
|
export function isSlackRenderV2Enabled(): boolean {
|
|
80
|
-
return isEnvFlagEnabled("SLACK_RENDER_V2",
|
|
80
|
+
return isEnvFlagEnabled("SLACK_RENDER_V2", true);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
export function isSlackDelegationEnabled(): boolean {
|
|
@@ -121,6 +121,88 @@ export async function callSlackWithRetry(
|
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
// Outcome delivery (chat.startStream, with its chat.postMessage fallback) can
|
|
125
|
+
// fail for reasons callSlackWithRetry does not retry (e.g. user_not_found).
|
|
126
|
+
// Without a bound, processSlackRenderV2's 3s tick re-attempts the same
|
|
127
|
+
// failing task forever. This tracks per-task failures in memory and applies
|
|
128
|
+
// capped exponential backoff plus a give-up ceiling so a persistently broken
|
|
129
|
+
// recipient stops hammering Slack instead of retrying at tick cadence.
|
|
130
|
+
const OUTCOME_DELIVERY_MAX_ATTEMPTS = 5;
|
|
131
|
+
const OUTCOME_DELIVERY_BASE_DELAY_MS = 30_000;
|
|
132
|
+
const OUTCOME_DELIVERY_MAX_DELAY_MS = 30 * 60_000;
|
|
133
|
+
const outcomeDeliveryFailures = new Map<string, { count: number; nextAttemptAt: number }>();
|
|
134
|
+
|
|
135
|
+
function outcomeDeliveryGate(taskId: string): boolean {
|
|
136
|
+
const state = outcomeDeliveryFailures.get(taskId);
|
|
137
|
+
if (!state) return true;
|
|
138
|
+
if (state.count >= OUTCOME_DELIVERY_MAX_ATTEMPTS) return false;
|
|
139
|
+
return Date.now() >= state.nextAttemptAt;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function noteOutcomeDeliverySuccess(taskId: string): void {
|
|
143
|
+
outcomeDeliveryFailures.delete(taskId);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function noteOutcomeDeliveryFailure(task: AgentTask, error: unknown): Promise<void> {
|
|
147
|
+
const state = outcomeDeliveryFailures.get(task.id) ?? { count: 0, nextAttemptAt: 0 };
|
|
148
|
+
state.count += 1;
|
|
149
|
+
const delay = Math.min(
|
|
150
|
+
OUTCOME_DELIVERY_BASE_DELAY_MS * 2 ** (state.count - 1),
|
|
151
|
+
OUTCOME_DELIVERY_MAX_DELAY_MS,
|
|
152
|
+
);
|
|
153
|
+
state.nextAttemptAt = Date.now() + delay;
|
|
154
|
+
outcomeDeliveryFailures.set(task.id, state);
|
|
155
|
+
console.error(
|
|
156
|
+
`[Slack] Outcome delivery failed for task ${task.id} (attempt ${state.count}/${OUTCOME_DELIVERY_MAX_ATTEMPTS}):`,
|
|
157
|
+
error,
|
|
158
|
+
);
|
|
159
|
+
if (state.count >= OUTCOME_DELIVERY_MAX_ATTEMPTS) {
|
|
160
|
+
console.error(
|
|
161
|
+
`[Slack] Giving up on outcome delivery for task ${task.id} after ${state.count} attempts; surfacing failure and clearing the working indicator`,
|
|
162
|
+
);
|
|
163
|
+
await surfaceOutcomeDeliveryGiveUp(task, error);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Best-effort: post a visible failure notice and clear the "is working" status so a
|
|
168
|
+
* permanently broken delivery doesn't fail silently or leave the thread spinning. */
|
|
169
|
+
async function surfaceOutcomeDeliveryGiveUp(task: AgentTask, error: unknown): Promise<void> {
|
|
170
|
+
const app = getSlackApp();
|
|
171
|
+
if (!app || !task.slackChannelId || !task.slackThreadTs) return;
|
|
172
|
+
try {
|
|
173
|
+
await app.client.apiCall("chat.postMessage", {
|
|
174
|
+
channel: task.slackChannelId,
|
|
175
|
+
thread_ts: task.slackThreadTs,
|
|
176
|
+
text: `⚠️ Couldn't deliver this task's reply after ${OUTCOME_DELIVERY_MAX_ATTEMPTS} attempts (${
|
|
177
|
+
error instanceof Error ? error.message : String(error)
|
|
178
|
+
}). See task ${getTaskLink(task.id)} for the result.`,
|
|
179
|
+
});
|
|
180
|
+
} catch (postError) {
|
|
181
|
+
console.error(`[Slack] Give-up notice failed to post for task ${task.id}:`, postError);
|
|
182
|
+
}
|
|
183
|
+
await clearAssistantStatus(app.client, task.slackChannelId, task.slackThreadTs);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Clears the assistant "is working" indicator in DM channels. Best-effort: the
|
|
187
|
+
* call throws when the thread isn't an assistant thread, which is expected
|
|
188
|
+
* for non-DM channels and safe to ignore. */
|
|
189
|
+
async function clearAssistantStatus(
|
|
190
|
+
client: WebClient,
|
|
191
|
+
channelId: string,
|
|
192
|
+
threadTs: string,
|
|
193
|
+
): Promise<void> {
|
|
194
|
+
if (!channelId.startsWith("D")) return;
|
|
195
|
+
try {
|
|
196
|
+
await client.apiCall("assistant.threads.setStatus", {
|
|
197
|
+
channel_id: channelId,
|
|
198
|
+
thread_ts: threadTs,
|
|
199
|
+
status: "",
|
|
200
|
+
});
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.warn(`[Slack] Failed to clear assistant status for ${channelId}/${threadTs}:`, error);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
124
206
|
function slackTreeStallMinutes(): number {
|
|
125
207
|
return Number(process.env.SLACK_TREE_STALL_MIN) || 15;
|
|
126
208
|
}
|
|
@@ -665,7 +747,11 @@ async function outcomeContent(task: AgentTask, slackReplySent: boolean): Promise
|
|
|
665
747
|
if (task.status === "cancelled") {
|
|
666
748
|
return `🚫 **Cancelled**\n\n${outcomeText(task.failureReason, "Task was cancelled.")}`;
|
|
667
749
|
}
|
|
668
|
-
|
|
750
|
+
// A deferral's output is engine-authored, never posted by hand via
|
|
751
|
+
// slack-reply, so it must not collapse into the "agent completed" summary
|
|
752
|
+
// even when the agent also sent a slack-reply earlier in the same task.
|
|
753
|
+
const isDeferred = task.tags?.includes("deferred");
|
|
754
|
+
if (slackReplySent && task.status === "completed" && !isDeferred) {
|
|
669
755
|
const agentName = task.agentId
|
|
670
756
|
? ((await getAgentById(task.agentId))?.name ?? "Agent")
|
|
671
757
|
: "Agent";
|
|
@@ -697,12 +783,23 @@ export async function childOutcomeContent(
|
|
|
697
783
|
if (task.status === "failed") {
|
|
698
784
|
return `↳ ❌ ${agentName} — failed\n\n${outcomeText(task.failureReason, "Task failed.")}`;
|
|
699
785
|
}
|
|
700
|
-
if (slackReplySent) {
|
|
786
|
+
if (slackReplySent && !task.tags?.includes("deferred")) {
|
|
701
787
|
return `↳ ✅ ${agentName} completed`;
|
|
702
788
|
}
|
|
703
789
|
return `↳ ✅ ${agentName} — result\n\n${outcomeText(task.output, "Task completed.")}`;
|
|
704
790
|
}
|
|
705
791
|
|
|
792
|
+
// A deferred wake continues a human conversation even though the scheduler
|
|
793
|
+
// assigns source="schedule". Its answer must not depend on delegation cards.
|
|
794
|
+
function isSlackContinuation(task: AgentTask): boolean {
|
|
795
|
+
return (
|
|
796
|
+
task.source === "schedule" &&
|
|
797
|
+
task.taskType === "deferred" &&
|
|
798
|
+
!!task.slackChannelId &&
|
|
799
|
+
!!task.slackThreadTs
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
|
|
706
803
|
/**
|
|
707
804
|
* True when `task` is a candidate for its own child result card (plan
|
|
708
805
|
* section 3.4, rules 1-4, 3, and part of rule 6). Callers still need the
|
|
@@ -712,6 +809,7 @@ export async function childOutcomeContent(
|
|
|
712
809
|
function isChildCardCandidate(task: AgentTask, delegationActivatedAt: string): boolean {
|
|
713
810
|
return (
|
|
714
811
|
task.source !== "slack" &&
|
|
812
|
+
!isSlackContinuation(task) &&
|
|
715
813
|
!!task.slackChannelId &&
|
|
716
814
|
!!task.slackThreadTs &&
|
|
717
815
|
(task.status === "completed" || task.status === "failed") &&
|
|
@@ -721,9 +819,11 @@ function isChildCardCandidate(task: AgentTask, delegationActivatedAt: string): b
|
|
|
721
819
|
);
|
|
722
820
|
}
|
|
723
821
|
|
|
724
|
-
function
|
|
822
|
+
function taskNeedsDirectOutcome(task: AgentTask, activatedAt: string): boolean {
|
|
725
823
|
return (
|
|
726
|
-
task.source === "slack" && isAskOutcomeStatus(task.status)
|
|
824
|
+
((task.source === "slack" && isAskOutcomeStatus(task.status)) ||
|
|
825
|
+
(isSlackContinuation(task) && isOutcomeStatus(task.status))) &&
|
|
826
|
+
task.createdAt >= activatedAt
|
|
727
827
|
);
|
|
728
828
|
}
|
|
729
829
|
|
|
@@ -847,7 +947,17 @@ function attachmentLine(attachments: TaskAttachment[]): string | undefined {
|
|
|
847
947
|
const attachment = attachments.find((item) => item.isPrimary) ?? attachments[0];
|
|
848
948
|
if (!attachment) return undefined;
|
|
849
949
|
const url = taskAttachmentDisplayUrl(attachment);
|
|
850
|
-
|
|
950
|
+
if (!/^https?:\/\//.test(url)) return undefined;
|
|
951
|
+
const label = attachment.name
|
|
952
|
+
.replace(/\s+/g, " ")
|
|
953
|
+
.trim()
|
|
954
|
+
.replace(/[\\[\]()]/g, "\\$&");
|
|
955
|
+
// Keep the Markdown destination intact without changing existing URL escapes.
|
|
956
|
+
const destination = url
|
|
957
|
+
.replace(/[\s\\<>]/g, encodeURIComponent)
|
|
958
|
+
.replace(/\(/g, "%28")
|
|
959
|
+
.replace(/\)/g, "%29");
|
|
960
|
+
return `📎 [${label}](${destination})`;
|
|
851
961
|
}
|
|
852
962
|
|
|
853
963
|
type MarkdownFence = { character: "`" | "~"; length: number };
|
|
@@ -1019,13 +1129,36 @@ export async function streamOutcomeCard(
|
|
|
1019
1129
|
reservationWasCreated = reserved.created;
|
|
1020
1130
|
}
|
|
1021
1131
|
let streamedFreshContent = false;
|
|
1132
|
+
let deliveredViaFallback = false;
|
|
1022
1133
|
if (isPendingSlackMessage(outcome)) {
|
|
1023
1134
|
const reconciled = reservationWasCreated
|
|
1024
1135
|
? undefined
|
|
1025
1136
|
: await findReservedSlackMessage(app.client, outcome, presentation);
|
|
1026
1137
|
streamedFreshContent = !reconciled;
|
|
1027
|
-
|
|
1028
|
-
|
|
1138
|
+
let started = reconciled;
|
|
1139
|
+
if (!started) {
|
|
1140
|
+
try {
|
|
1141
|
+
started = await callSlackWithRetry(app.client, "chat.startStream", startPayload);
|
|
1142
|
+
} catch (error) {
|
|
1143
|
+
// chat.startStream can fail for reasons callSlackWithRetry does not
|
|
1144
|
+
// retry (e.g. user_not_found). Without a fallback the agent's answer
|
|
1145
|
+
// — already computed above — is dropped entirely. Fall back to a
|
|
1146
|
+
// plain chat.postMessage so a streaming-API error never swallows a
|
|
1147
|
+
// reply.
|
|
1148
|
+
console.error(
|
|
1149
|
+
`[Slack] chat.startStream failed for task ${task.id}; falling back to chat.postMessage:`,
|
|
1150
|
+
error,
|
|
1151
|
+
);
|
|
1152
|
+
started = await callSlackWithRetry(app.client, "chat.postMessage", {
|
|
1153
|
+
channel: task.slackChannelId,
|
|
1154
|
+
thread_ts: task.slackThreadTs,
|
|
1155
|
+
text: presentation,
|
|
1156
|
+
...(startPayload.username ? { username: startPayload.username } : {}),
|
|
1157
|
+
...(startPayload.icon_emoji ? { icon_emoji: startPayload.icon_emoji } : {}),
|
|
1158
|
+
});
|
|
1159
|
+
deliveredViaFallback = true;
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1029
1162
|
if (typeof started.ts !== "string" || !started.ts) {
|
|
1030
1163
|
throw new Error("Slack did not return a timestamp for the outcome stream");
|
|
1031
1164
|
}
|
|
@@ -1035,29 +1168,36 @@ export async function streamOutcomeCard(
|
|
|
1035
1168
|
if (!persisted) throw new Error("Failed to persist the outcome stream timestamp");
|
|
1036
1169
|
outcome = persisted;
|
|
1037
1170
|
}
|
|
1038
|
-
if (!
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1171
|
+
if (!deliveredViaFallback) {
|
|
1172
|
+
if (!streamedFreshContent) {
|
|
1173
|
+
// The stream backing this message was started (or reconciled from) an earlier
|
|
1174
|
+
// pass, whose slackReplySent snapshot may have since changed. Overwrite its
|
|
1175
|
+
// content with the freshly computed presentation before finalizing, so a
|
|
1176
|
+
// completed-then-collapsed reply doesn't finalize with stale full output.
|
|
1177
|
+
await callSlackWithRetry(app.client, "chat.update", {
|
|
1178
|
+
channel: task.slackChannelId,
|
|
1179
|
+
ts: outcome.ts,
|
|
1180
|
+
text: presentation,
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
try {
|
|
1184
|
+
await callSlackWithRetry(app.client, "chat.stopStream", {
|
|
1185
|
+
channel: task.slackChannelId,
|
|
1186
|
+
ts: outcome.ts,
|
|
1187
|
+
blocks: await outcomeFooter(task, tasks, duration),
|
|
1188
|
+
});
|
|
1189
|
+
} catch (error) {
|
|
1190
|
+
// A process may have stopped the stream before it persisted the final
|
|
1191
|
+
// permalink. In that one recovery case the message is already immutable,
|
|
1192
|
+
// so continue by resolving and recording its permalink.
|
|
1193
|
+
if (!isStreamAlreadyStopped(error)) throw error;
|
|
1194
|
+
}
|
|
1060
1195
|
}
|
|
1196
|
+
// chat.startStream/chat.postMessage posting a new message in the thread is
|
|
1197
|
+
// what normally clears Slack's own "is working…" assistant indicator.
|
|
1198
|
+
// Clear it explicitly too, so a delivery that only ever fails still
|
|
1199
|
+
// doesn't leave the thread stuck spinning.
|
|
1200
|
+
await clearAssistantStatus(app.client, task.slackChannelId, task.slackThreadTs);
|
|
1061
1201
|
const permalink = await resolvePermalink(app.client, task.slackChannelId, outcome.ts);
|
|
1062
1202
|
return await updateSlackMessageRecord(outcome.id, {
|
|
1063
1203
|
permalink,
|
|
@@ -1123,13 +1263,14 @@ export async function processSlackRenderV2(): Promise<void> {
|
|
|
1123
1263
|
// In-progress asks are eligible only for the timeout backstop. They do
|
|
1124
1264
|
// not represent immediately active outcome work, so avoid waking an old
|
|
1125
1265
|
// tree solely to verify its permalink on every render tick.
|
|
1126
|
-
const
|
|
1266
|
+
const isDirectCandidate =
|
|
1267
|
+
taskNeedsDirectOutcome(task, activatedAt) && isOutcomeStatus(task.status);
|
|
1127
1268
|
const isChildCandidate =
|
|
1128
1269
|
delegationEnabled &&
|
|
1129
1270
|
delegationActivatedAt !== null &&
|
|
1130
1271
|
isChildCardCandidate(task, delegationActivatedAt) &&
|
|
1131
1272
|
!task.slackReplySent;
|
|
1132
|
-
if (
|
|
1273
|
+
if (isDirectCandidate || isChildCandidate) {
|
|
1133
1274
|
needsOutcome = true;
|
|
1134
1275
|
break;
|
|
1135
1276
|
}
|
|
@@ -1199,36 +1340,42 @@ export async function processSlackRenderV2(): Promise<void> {
|
|
|
1199
1340
|
if (!ownerAsk || ownerAsk.createdAt < delegationActivatedAt) continue;
|
|
1200
1341
|
if (childCardsThisTick >= CHILD_CARDS_PER_TICK) continue;
|
|
1201
1342
|
if (askId && (await childCardCountFor(askId)) >= CHILD_CARDS_PER_ASK) continue;
|
|
1343
|
+
if (!outcomeDeliveryGate(task.id)) continue;
|
|
1202
1344
|
try {
|
|
1203
1345
|
const outcome = await streamOutcomeCard(task, tree, { buildContent: childOutcomeContent });
|
|
1204
1346
|
if (outcome) {
|
|
1347
|
+
noteOutcomeDeliverySuccess(task.id);
|
|
1205
1348
|
childCardsThisTick++;
|
|
1206
1349
|
if (askId) childCardCounts.set(askId, (childCardCounts.get(askId) ?? 0) + 1);
|
|
1207
1350
|
await recordSlackDelivery(task, outcome, "child_outcome");
|
|
1208
1351
|
}
|
|
1209
1352
|
outcomeCreated ||= !!outcome;
|
|
1210
1353
|
} catch (error) {
|
|
1211
|
-
|
|
1354
|
+
await noteOutcomeDeliveryFailure(task, error);
|
|
1212
1355
|
}
|
|
1213
1356
|
}
|
|
1214
1357
|
|
|
1215
1358
|
for (const task of tasks) {
|
|
1216
1359
|
if (!isSlackRenderV2Enabled()) return;
|
|
1217
|
-
if (task
|
|
1360
|
+
if (!taskNeedsDirectOutcome(task, activatedAt)) continue;
|
|
1218
1361
|
if ((await getSlackOutcomeMessage(task.id))?.finalizedAt) continue;
|
|
1219
|
-
if (!isAskOutcomeStatus(task.status) || task.createdAt < activatedAt) continue;
|
|
1220
1362
|
const deferByClosure =
|
|
1363
|
+
task.source === "slack" &&
|
|
1221
1364
|
delegationEnabled &&
|
|
1222
1365
|
delegationActivatedAt !== null &&
|
|
1223
1366
|
task.createdAt >= delegationActivatedAt;
|
|
1224
1367
|
|
|
1225
1368
|
if (!deferByClosure) {
|
|
1369
|
+
if (!outcomeDeliveryGate(task.id)) continue;
|
|
1226
1370
|
try {
|
|
1227
1371
|
const outcome = await streamOutcomeCard(task, tree);
|
|
1228
|
-
if (outcome)
|
|
1372
|
+
if (outcome) {
|
|
1373
|
+
noteOutcomeDeliverySuccess(task.id);
|
|
1374
|
+
await finalizeTerminalSlackReactions([task]);
|
|
1375
|
+
}
|
|
1229
1376
|
outcomeCreated ||= !!outcome;
|
|
1230
1377
|
} catch (error) {
|
|
1231
|
-
|
|
1378
|
+
await noteOutcomeDeliveryFailure(task, error);
|
|
1232
1379
|
}
|
|
1233
1380
|
continue;
|
|
1234
1381
|
}
|
|
@@ -1253,6 +1400,7 @@ export async function processSlackRenderV2(): Promise<void> {
|
|
|
1253
1400
|
if (childCardPending) continue;
|
|
1254
1401
|
const state = closureState(task, closure, new Date(), settleSec, timeoutMin);
|
|
1255
1402
|
if (state === "open") continue;
|
|
1403
|
+
if (!outcomeDeliveryGate(task.id)) continue;
|
|
1256
1404
|
try {
|
|
1257
1405
|
const outcome = await streamOutcomeCard(task, tree, {
|
|
1258
1406
|
buildContent: (_t, slackReplySent) =>
|
|
@@ -1260,6 +1408,7 @@ export async function processSlackRenderV2(): Promise<void> {
|
|
|
1260
1408
|
conclusionKind: state === "timedOut" ? "timeout" : "complete",
|
|
1261
1409
|
});
|
|
1262
1410
|
if (outcome) {
|
|
1411
|
+
noteOutcomeDeliverySuccess(task.id);
|
|
1263
1412
|
const app = getSlackApp();
|
|
1264
1413
|
if (app && task.slackChannelId && task.slackTriggerMessageTs) {
|
|
1265
1414
|
const reaction = conclusionReactionChoice(state, task, closure);
|
|
@@ -1280,7 +1429,7 @@ export async function processSlackRenderV2(): Promise<void> {
|
|
|
1280
1429
|
}
|
|
1281
1430
|
outcomeCreated ||= !!outcome;
|
|
1282
1431
|
} catch (error) {
|
|
1283
|
-
|
|
1432
|
+
await noteOutcomeDeliveryFailure(task, error);
|
|
1284
1433
|
}
|
|
1285
1434
|
}
|
|
1286
1435
|
try {
|
|
@@ -1299,4 +1448,5 @@ export function _resetSlackRenderV2ForTests(): void {
|
|
|
1299
1448
|
lastTreeUpdateAt.clear();
|
|
1300
1449
|
treeUpdateTails.clear();
|
|
1301
1450
|
cachedTeamId = undefined;
|
|
1451
|
+
outcomeDeliveryFailures.clear();
|
|
1302
1452
|
}
|
package/src/slack/router.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getAgentById, getAgentWorkingOnThread, getAllAgents } from "../be/db";
|
|
2
|
+
import type { Agent } from "../types";
|
|
2
3
|
import { isEnvFlagEnabled } from "../utils/env-flag";
|
|
3
4
|
import type { AgentMatch } from "./types";
|
|
4
5
|
|
|
@@ -16,6 +17,15 @@ export function hasOtherUserMention(text: string, botUserId: string): boolean {
|
|
|
16
17
|
return mentions.some((m) => m !== `<@${botUserId}>`);
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Build the matches for an explicit swarm#all broadcast.
|
|
22
|
+
*/
|
|
23
|
+
export function broadcastMatches(agents: Agent[]): AgentMatch[] {
|
|
24
|
+
return agents
|
|
25
|
+
.filter((agent) => !agent.isLead)
|
|
26
|
+
.map((agent) => ({ agent, matchedText: "swarm#all" }));
|
|
27
|
+
}
|
|
28
|
+
|
|
19
29
|
/**
|
|
20
30
|
* Routes a Slack message to the appropriate agent(s) based on mentions.
|
|
21
31
|
*
|
|
@@ -50,10 +60,10 @@ export async function routeMessage(
|
|
|
50
60
|
|
|
51
61
|
// Check for swarm#all broadcast
|
|
52
62
|
if (/swarm#all/i.test(text)) {
|
|
53
|
-
const
|
|
54
|
-
|
|
63
|
+
for (const broadcastMatch of broadcastMatches(agents)) {
|
|
64
|
+
const agent = broadcastMatch.agent;
|
|
55
65
|
if (!matches.some((m) => m.agent.id === agent.id)) {
|
|
56
|
-
matches.push(
|
|
66
|
+
matches.push(broadcastMatch);
|
|
57
67
|
}
|
|
58
68
|
}
|
|
59
69
|
}
|
package/src/slack/steering.ts
CHANGED
|
@@ -20,7 +20,7 @@ async function configuredSteeringTarget(
|
|
|
20
20
|
channelId: string,
|
|
21
21
|
threadTs: string,
|
|
22
22
|
): Promise<AgentTask | null> {
|
|
23
|
-
switch (process.env.SLACK_THREAD_STEERING) {
|
|
23
|
+
switch (process.env.SLACK_THREAD_STEERING ?? "lead") {
|
|
24
24
|
case "lead":
|
|
25
25
|
return getLatestLeadTaskInThread(channelId, threadTs);
|
|
26
26
|
case "all":
|
|
@@ -32,8 +32,8 @@ async function configuredSteeringTarget(
|
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* Request steering for the configured Slack thread target, if it is currently
|
|
35
|
-
* in progress.
|
|
36
|
-
* return null so Slack preserves its
|
|
35
|
+
* in progress. Unset configuration targets the lead; explicit off or invalid
|
|
36
|
+
* values return null so Slack preserves its task-creation behavior.
|
|
37
37
|
*/
|
|
38
38
|
export async function requestSlackThreadSteering(
|
|
39
39
|
args: SlackThreadSteeringRequest,
|
|
@@ -6,10 +6,17 @@ import {
|
|
|
6
6
|
} from "../be/db";
|
|
7
7
|
import { createAdditiveBuffer } from "../tasks/additive-buffer";
|
|
8
8
|
import { slackContextKey } from "../tasks/context-key";
|
|
9
|
-
import { createTaskWithSiblingAwareness } from "../tasks/sibling-awareness";
|
|
10
9
|
import { getSlackApp } from "./app";
|
|
11
10
|
import { buildBufferFlushBlocks } from "./blocks";
|
|
12
11
|
import { rewriteSlackMentions } from "./enrich";
|
|
12
|
+
import type { SlackFile } from "./files";
|
|
13
|
+
import {
|
|
14
|
+
bufferedFileFailures,
|
|
15
|
+
buildEffectiveText,
|
|
16
|
+
createSlackTaskWithFiles,
|
|
17
|
+
fetchSlackFiles,
|
|
18
|
+
notifySlackFileFailures,
|
|
19
|
+
} from "./inbound-files";
|
|
13
20
|
import { extractSlackMessageText } from "./message-text";
|
|
14
21
|
import { ensureSlackThreadTree, isSlackRenderV2Enabled } from "./render-v2";
|
|
15
22
|
import { getAgentDisplayName, getAgentEmoji } from "./responses";
|
|
@@ -22,6 +29,7 @@ interface BufferedMessage {
|
|
|
22
29
|
ts: string;
|
|
23
30
|
channelId: string;
|
|
24
31
|
threadTs: string;
|
|
32
|
+
files?: SlackFile[];
|
|
25
33
|
}
|
|
26
34
|
|
|
27
35
|
const BUFFER_TIMEOUT_MS = Number(process.env.ADDITIVE_SLACK_BUFFER_MS) || 10_000;
|
|
@@ -53,6 +61,7 @@ export function bufferThreadMessage(
|
|
|
53
61
|
text: string,
|
|
54
62
|
userId: string,
|
|
55
63
|
ts: string,
|
|
64
|
+
files?: SlackFile[],
|
|
56
65
|
): void {
|
|
57
66
|
slackBuffer.enqueue(makeKey(channelId, threadTs), {
|
|
58
67
|
text,
|
|
@@ -60,6 +69,7 @@ export function bufferThreadMessage(
|
|
|
60
69
|
ts,
|
|
61
70
|
channelId,
|
|
62
71
|
threadTs,
|
|
72
|
+
files,
|
|
63
73
|
});
|
|
64
74
|
}
|
|
65
75
|
|
|
@@ -146,10 +156,33 @@ async function slackFlush(
|
|
|
146
156
|
|
|
147
157
|
console.log(`[Slack] Flushing buffer: ${key} (${items.length} messages, immediate=${immediate})`);
|
|
148
158
|
|
|
159
|
+
// Keep only metadata during the debounce; downloads live for this flush only.
|
|
160
|
+
const app = getSlackApp();
|
|
161
|
+
const files = items.flatMap((item) => item.files ?? []);
|
|
162
|
+
await using inbound = app
|
|
163
|
+
? await fetchSlackFiles(app.client, files)
|
|
164
|
+
: {
|
|
165
|
+
files,
|
|
166
|
+
fetched: [],
|
|
167
|
+
failed: bufferedFileFailures(files),
|
|
168
|
+
async [Symbol.asyncDispose]() {},
|
|
169
|
+
};
|
|
170
|
+
const resolvedFiles = new Map(inbound.files.map((file) => [file.id, file]));
|
|
171
|
+
|
|
149
172
|
// Build combined task description. Any in-body `<@U…>` mentions the
|
|
150
173
|
// requester typed are rewritten via the identity primitive so the agent
|
|
151
174
|
// sees a name or the explicit UNKNOWN sentinel — never a raw Slack ID.
|
|
152
|
-
const combinedText = await rewriteSlackMentions(
|
|
175
|
+
const combinedText = await rewriteSlackMentions(
|
|
176
|
+
items
|
|
177
|
+
.map((item) =>
|
|
178
|
+
buildEffectiveText(
|
|
179
|
+
item.text,
|
|
180
|
+
item.files?.map((file) => resolvedFiles.get(file.id) ?? file),
|
|
181
|
+
inbound.failed,
|
|
182
|
+
),
|
|
183
|
+
)
|
|
184
|
+
.join("\n---\n"),
|
|
185
|
+
);
|
|
153
186
|
const description = `[Thread follow-up — ${items.length} message(s) buffered]\n\n${combinedText}`;
|
|
154
187
|
|
|
155
188
|
// Find the latest active task in this thread for dependency chaining
|
|
@@ -160,18 +193,22 @@ async function slackFlush(
|
|
|
160
193
|
);
|
|
161
194
|
}
|
|
162
195
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
196
|
+
// Steering carries text only. As on the mention path, files need a follow-up
|
|
197
|
+
// task so its attachments exist before a worker can claim it.
|
|
198
|
+
const steering =
|
|
199
|
+
files.length === 0
|
|
200
|
+
? await requestSlackThreadSteering({
|
|
201
|
+
channelId,
|
|
202
|
+
threadTs,
|
|
203
|
+
message: combinedText,
|
|
204
|
+
messageTimestamps: items.map((item) => item.ts),
|
|
205
|
+
})
|
|
206
|
+
: null;
|
|
169
207
|
if (steering) {
|
|
170
208
|
console.log(
|
|
171
209
|
`[Slack] Buffer flushed → steering ${steering.result.outcome} for task ${steering.task.id}`,
|
|
172
210
|
);
|
|
173
211
|
|
|
174
|
-
const app = getSlackApp();
|
|
175
212
|
const agent = steering.task.agentId ? await getAgentById(steering.task.agentId) : undefined;
|
|
176
213
|
if (app) {
|
|
177
214
|
if (!isSlackRenderV2Enabled()) {
|
|
@@ -207,19 +244,26 @@ async function slackFlush(
|
|
|
207
244
|
const dependsOn = !immediate && latestActiveTask ? [latestActiveTask.id] : undefined;
|
|
208
245
|
|
|
209
246
|
const mostRecentTask = await getMostRecentTaskInThread(channelId, threadTs);
|
|
210
|
-
const task = await
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
247
|
+
const { task, unattached } = await createSlackTaskWithFiles(
|
|
248
|
+
fullDescription,
|
|
249
|
+
{
|
|
250
|
+
agentId: lead?.id,
|
|
251
|
+
routingReason:
|
|
252
|
+
lead && mostRecentTask?.agentId === lead.id ? "continuity" : lead ? "skill" : undefined,
|
|
253
|
+
routingSource: lead ? "engine_default" : undefined,
|
|
254
|
+
source: "slack",
|
|
255
|
+
slackChannelId: channelId,
|
|
256
|
+
slackThreadTs: threadTs,
|
|
257
|
+
slackTriggerMessageTs: items.at(-1)!.ts,
|
|
258
|
+
slackUserId: originalRequesterId,
|
|
259
|
+
dependsOn,
|
|
260
|
+
parentTaskId: mostRecentTask?.id,
|
|
261
|
+
contextKey: slackContextKey({ channelId, threadTs }),
|
|
262
|
+
},
|
|
263
|
+
inbound,
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
if (app) await notifySlackFileFailures(app.client, channelId, threadTs, unattached);
|
|
223
267
|
|
|
224
268
|
console.log(
|
|
225
269
|
`[Slack] Buffer flushed → task ${task.id} (dependsOn: ${dependsOn ? dependsOn.join(", ") : "none"})`,
|
|
@@ -231,7 +275,6 @@ async function slackFlush(
|
|
|
231
275
|
}
|
|
232
276
|
|
|
233
277
|
// Slack feedback with Block Kit
|
|
234
|
-
const app = getSlackApp();
|
|
235
278
|
if (app) {
|
|
236
279
|
const hasDependency = !immediate && !!latestActiveTask;
|
|
237
280
|
const blocks = buildBufferFlushBlocks({
|