@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
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agents that act with lead privileges without carrying the `isLead` flag.
|
|
3
|
+
*
|
|
4
|
+
* Extension system agents (`ext:<name>`) run operator-enabled code inside the
|
|
5
|
+
* API process. Their `ctx.swarm` calls must pass lead-only checks such as
|
|
6
|
+
* posting to Slack, but the agent row keeps `isLead: false` so lead selection
|
|
7
|
+
* (Slack routing, follow-ups, heartbeat triage) never picks them.
|
|
8
|
+
*
|
|
9
|
+
* The registry is process-local and mirrors the loaded-extension set: the
|
|
10
|
+
* extension dispatcher grants on load and revokes on dispose.
|
|
11
|
+
*/
|
|
12
|
+
const leadEquivalentAgentIds = new Set<string>();
|
|
13
|
+
|
|
14
|
+
export function grantLeadEquivalence(agentId: string): void {
|
|
15
|
+
leadEquivalentAgentIds.add(agentId);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function revokeLeadEquivalence(agentId: string): void {
|
|
19
|
+
leadEquivalentAgentIds.delete(agentId);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function hasLeadEquivalence(agentId: string | null | undefined): boolean {
|
|
23
|
+
return !!agentId && leadEquivalentAgentIds.has(agentId);
|
|
24
|
+
}
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
* Compile-time exhaustiveness: `LEGACY_POLICY` must cover every
|
|
11
11
|
* `PermissionVerb` (enforced via `satisfies`).
|
|
12
12
|
*/
|
|
13
|
+
|
|
14
|
+
import { hasLeadEquivalence } from "./elevated-agents";
|
|
13
15
|
import type { PermissionVerb } from "./permissions";
|
|
14
16
|
import type { RbacPrincipal, RbacResource } from "./types";
|
|
15
17
|
|
|
@@ -24,10 +26,33 @@ export type LegacyRule = {
|
|
|
24
26
|
|
|
25
27
|
// ── Named rules (research §3 Rule column) ────────────────────────────────────
|
|
26
28
|
|
|
29
|
+
/** Lead agents, plus extension system agents registered as lead-equivalent. */
|
|
30
|
+
function actsAsLead(principal: RbacPrincipal): boolean {
|
|
31
|
+
return principal.kind === "agent" && (principal.isLead || hasLeadEquivalence(principal.agentId));
|
|
32
|
+
}
|
|
33
|
+
|
|
27
34
|
const leadOnly: LegacyRule = {
|
|
28
35
|
name: "lead-only",
|
|
29
36
|
denyReason: "requires lead agent",
|
|
30
|
-
evaluate: (principal) => principal
|
|
37
|
+
evaluate: (principal) => actsAsLead(principal),
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Human principals: the shared API key (operator) and dashboard session users.
|
|
42
|
+
* Dashboard users carry no admin flag today, so they are treated like the
|
|
43
|
+
* operator, matching the config routes (`src/http/config.ts`).
|
|
44
|
+
*/
|
|
45
|
+
const operatorOrUser: LegacyRule = {
|
|
46
|
+
name: "operator-or-user",
|
|
47
|
+
denyReason: "requires operator or user authentication",
|
|
48
|
+
evaluate: (principal) => principal.kind === "operator" || principal.kind === "user",
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const leadOrOperatorOrUser: LegacyRule = {
|
|
52
|
+
name: "lead-or-operator-or-user",
|
|
53
|
+
denyReason: "requires lead agent, operator, or user authentication",
|
|
54
|
+
evaluate: (principal) =>
|
|
55
|
+
principal.kind === "operator" || principal.kind === "user" || actsAsLead(principal),
|
|
31
56
|
};
|
|
32
57
|
|
|
33
58
|
const leadOrTaskCreator: LegacyRule = {
|
|
@@ -35,7 +60,7 @@ const leadOrTaskCreator: LegacyRule = {
|
|
|
35
60
|
denyReason: "requires lead agent or task creator",
|
|
36
61
|
evaluate: (principal, resource) => {
|
|
37
62
|
if (principal.kind !== "agent") return false;
|
|
38
|
-
if (principal
|
|
63
|
+
if (actsAsLead(principal)) return true;
|
|
39
64
|
return (
|
|
40
65
|
resource?.kind === "task" &&
|
|
41
66
|
resource.creatorAgentId != null &&
|
|
@@ -49,7 +74,7 @@ const leadOrResourceOwner: LegacyRule = {
|
|
|
49
74
|
denyReason: "requires lead agent or resource owner",
|
|
50
75
|
evaluate: (principal, resource) => {
|
|
51
76
|
if (principal.kind !== "agent") return false;
|
|
52
|
-
if (principal
|
|
77
|
+
if (actsAsLead(principal)) return true;
|
|
53
78
|
return (
|
|
54
79
|
resource?.kind === "owned" &&
|
|
55
80
|
resource.ownerAgentId != null &&
|
|
@@ -63,7 +88,7 @@ const leadOrOwnNamespace: LegacyRule = {
|
|
|
63
88
|
denyReason: "requires lead agent or your own task:agent: namespace",
|
|
64
89
|
evaluate: (principal, resource) => {
|
|
65
90
|
if (principal.kind !== "agent") return false;
|
|
66
|
-
if (principal
|
|
91
|
+
if (actsAsLead(principal)) return true;
|
|
67
92
|
// A blank agent id can never own a namespace — the pre-migration guards
|
|
68
93
|
// used truthiness (`if (info.agentId && ...)`), so `X-Agent-ID: ""` plus
|
|
69
94
|
// the literal namespace `task:agent:` must stay denied.
|
|
@@ -105,7 +130,7 @@ const memoryOwnerOrLeadSwarm: LegacyRule = {
|
|
|
105
130
|
evaluate: (principal, resource) => {
|
|
106
131
|
if (principal.kind !== "agent" || resource?.kind !== "owned") return false;
|
|
107
132
|
if (resource.ownerAgentId != null && resource.ownerAgentId === principal.agentId) return true;
|
|
108
|
-
return principal
|
|
133
|
+
return actsAsLead(principal) && resource.scope === "swarm";
|
|
109
134
|
},
|
|
110
135
|
};
|
|
111
136
|
|
|
@@ -121,7 +146,7 @@ const taskFsMutate: LegacyRule = {
|
|
|
121
146
|
denyReason: "requires operator, user, lead agent, task assignee, or task creator",
|
|
122
147
|
evaluate: (principal, resource) => {
|
|
123
148
|
if (principal.kind === "operator" || principal.kind === "user") return true;
|
|
124
|
-
if (principal
|
|
149
|
+
if (actsAsLead(principal)) return true;
|
|
125
150
|
if (resource?.kind !== "task") return false;
|
|
126
151
|
return (
|
|
127
152
|
(resource.agentId != null && resource.agentId === principal.agentId) ||
|
|
@@ -132,6 +157,8 @@ const taskFsMutate: LegacyRule = {
|
|
|
132
157
|
|
|
133
158
|
/** All named (non-composite) rule kinds, keyed by identifier. */
|
|
134
159
|
export const LEGACY_RULES = {
|
|
160
|
+
"operator-or-user": operatorOrUser,
|
|
161
|
+
"lead-or-operator-or-user": leadOrOperatorOrUser,
|
|
135
162
|
"lead-only": leadOnly,
|
|
136
163
|
"lead-or-task-creator": leadOrTaskCreator,
|
|
137
164
|
"lead-or-resource-owner": leadOrResourceOwner,
|
|
@@ -203,4 +230,6 @@ export const LEGACY_POLICY = {
|
|
|
203
230
|
"script.api.update": leadOnly,
|
|
204
231
|
"script.api.rotate": leadOnly,
|
|
205
232
|
"script.api.delete": leadOnly,
|
|
233
|
+
"extension.write": leadOrOperatorOrUser,
|
|
234
|
+
"extension.activate": operatorOrUser,
|
|
206
235
|
} as const satisfies Record<PermissionVerb, LegacyRule>;
|
package/src/rbac/permissions.ts
CHANGED
|
@@ -257,6 +257,14 @@ export const PERMISSIONS = {
|
|
|
257
257
|
description: "Delete an external script API endpoint.",
|
|
258
258
|
namespace: "script",
|
|
259
259
|
},
|
|
260
|
+
"extension.write": {
|
|
261
|
+
description: "Install, update, or uninstall a swarm extension.",
|
|
262
|
+
namespace: "extension",
|
|
263
|
+
},
|
|
264
|
+
"extension.activate": {
|
|
265
|
+
description: "Enable, disable, or activate a version of a swarm extension.",
|
|
266
|
+
namespace: "extension",
|
|
267
|
+
},
|
|
260
268
|
} as const satisfies Record<string, { description: string; namespace: string }>;
|
|
261
269
|
|
|
262
270
|
export type PermissionVerb = keyof typeof PERMISSIONS;
|
|
@@ -1,21 +1,94 @@
|
|
|
1
1
|
import { getDbClient, getScheduledTaskById, getUserById, updateScheduledTask } from "@/be/db";
|
|
2
|
-
import type {
|
|
2
|
+
import type { PreparedTaskCreate } from "@/tasks/sibling-awareness";
|
|
3
|
+
import type { AgentTask, ScheduledTask } from "@/types";
|
|
3
4
|
import { workflowEventBus } from "@/workflows/event-bus";
|
|
4
|
-
import { createStandaloneScheduleTask } from "./schedule-task";
|
|
5
|
+
import { createStandaloneScheduleTask, prepareStandaloneScheduleTask } from "./schedule-task";
|
|
5
6
|
|
|
6
7
|
export type TaskWakeEvent = "task.completed" | "task.failed" | "settled";
|
|
7
8
|
|
|
8
9
|
type TaskWait = {
|
|
9
10
|
scheduleId: string;
|
|
10
|
-
taskId: string;
|
|
11
11
|
eventName: TaskWakeEvent;
|
|
12
|
+
mode: "all" | "any";
|
|
12
13
|
status: "pending" | "fired";
|
|
13
14
|
};
|
|
14
15
|
|
|
16
|
+
type WaitMember = { taskId: string; taskStatus: string | null; deferred: number };
|
|
17
|
+
|
|
18
|
+
type WakeReason = "ceiling" | "task.completed" | "task.failed" | "task.cancelled";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Read the wait row and its member set and decide whether the wait is ready.
|
|
22
|
+
* Returns the wake-up cause text, or undefined when the wait is absent, already
|
|
23
|
+
* fired, not addressed by `reason`, or its member set has not matched yet.
|
|
24
|
+
* Pure reads: callers run it once outside the transaction (to build the task
|
|
25
|
+
* through the extension pre-hooks) and once more under the write lock, where
|
|
26
|
+
* the answer is authoritative. An event only identifies a candidate wait; it
|
|
27
|
+
* never proves the set is ready.
|
|
28
|
+
*/
|
|
29
|
+
async function resolveWaitCause(
|
|
30
|
+
scheduleId: string,
|
|
31
|
+
reason: WakeReason,
|
|
32
|
+
): Promise<{ wait: TaskWait; cause: string } | undefined> {
|
|
33
|
+
const client = getDbClient();
|
|
34
|
+
const wait = await client.get<TaskWait>(
|
|
35
|
+
"SELECT * FROM deferred_task_waits WHERE scheduleId = ?",
|
|
36
|
+
[scheduleId],
|
|
37
|
+
);
|
|
38
|
+
if (!wait || wait.status !== "pending") return undefined;
|
|
39
|
+
if (reason !== "ceiling" && wait.eventName !== "settled" && wait.eventName !== reason)
|
|
40
|
+
return undefined;
|
|
41
|
+
if (reason === "ceiling") return { wait, cause: "ceiling expired" };
|
|
42
|
+
const members = await client.query<WaitMember>(
|
|
43
|
+
`SELECT m.taskId, t.status AS taskStatus, EXISTS (
|
|
44
|
+
SELECT 1 FROM scheduled_tasks d
|
|
45
|
+
WHERE d.parentTaskId = t.id AND d.taskType = 'deferred' AND d.enabled = 1
|
|
46
|
+
) AS deferred
|
|
47
|
+
FROM deferred_task_wait_members m LEFT JOIN agent_tasks t ON t.id = m.taskId
|
|
48
|
+
WHERE m.scheduleId = ?`,
|
|
49
|
+
[scheduleId],
|
|
50
|
+
);
|
|
51
|
+
const matched = members.filter(
|
|
52
|
+
(member) =>
|
|
53
|
+
!member.deferred &&
|
|
54
|
+
(member.taskStatus === "completed" ||
|
|
55
|
+
member.taskStatus === "failed" ||
|
|
56
|
+
member.taskStatus === "cancelled") &&
|
|
57
|
+
(wait.eventName === "settled" || wait.eventName === `task.${member.taskStatus}`),
|
|
58
|
+
);
|
|
59
|
+
if (!matched.length || (wait.mode === "all" && matched.length !== members.length))
|
|
60
|
+
return undefined;
|
|
61
|
+
const cause =
|
|
62
|
+
members.length === 1
|
|
63
|
+
? `task.${matched[0]!.taskStatus} for task ${matched[0]!.taskId}`
|
|
64
|
+
: `${wait.mode} tasks matched ${wait.eventName}: ${matched.map((member) => `${member.taskId} (${member.taskStatus})`).join(", ")}`;
|
|
65
|
+
return { wait, cause };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function wakeSchedule(schedule: ScheduledTask, cause: string): ScheduledTask {
|
|
69
|
+
return { ...schedule, taskTemplate: `${schedule.taskTemplate}\n\nWake-up cause: ${cause}.` };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function prepareDeferredWakeTask(
|
|
73
|
+
scheduleId: string,
|
|
74
|
+
reason: WakeReason,
|
|
75
|
+
extraTags: string[],
|
|
76
|
+
): Promise<PreparedTaskCreate | undefined> {
|
|
77
|
+
const resolved = await resolveWaitCause(scheduleId, reason);
|
|
78
|
+
if (!resolved) return undefined;
|
|
79
|
+
let schedule = await getScheduledTaskById(scheduleId);
|
|
80
|
+
if (!schedule?.enabled) return undefined;
|
|
81
|
+
if (schedule.createdBy && !(await getUserById(schedule.createdBy))) {
|
|
82
|
+
schedule = { ...schedule, createdBy: undefined };
|
|
83
|
+
}
|
|
84
|
+
if (!schedule.taskTemplate) throw new Error(`Schedule "${schedule.name}" has no taskTemplate`);
|
|
85
|
+
return await prepareStandaloneScheduleTask(wakeSchedule(schedule, resolved.cause), extraTags);
|
|
86
|
+
}
|
|
87
|
+
|
|
15
88
|
/** Undefined means a normal schedule; an empty result means another caller won. */
|
|
16
89
|
export async function dispatchDeferredTaskWait(
|
|
17
90
|
scheduleId: string,
|
|
18
|
-
reason:
|
|
91
|
+
reason: WakeReason,
|
|
19
92
|
extraTags: string[] = [],
|
|
20
93
|
): Promise<{ task?: AgentTask } | undefined> {
|
|
21
94
|
const client = getDbClient();
|
|
@@ -26,14 +99,15 @@ export async function dispatchDeferredTaskWait(
|
|
|
26
99
|
]))
|
|
27
100
|
)
|
|
28
101
|
return undefined;
|
|
102
|
+
// Extension pre-hooks must run outside the transaction, so build the wake-up task
|
|
103
|
+
// first; the transaction below re-reads the wait and its member set before claiming.
|
|
104
|
+
// The prepared description carries the cause seen here; for an `any` wait a member
|
|
105
|
+
// that settles between this read and the claim is missing from that text only.
|
|
106
|
+
const preparedForWait = await prepareDeferredWakeTask(scheduleId, reason, extraTags);
|
|
107
|
+
if (!preparedForWait) return {};
|
|
29
108
|
return client.transaction(async () => {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
[scheduleId],
|
|
33
|
-
);
|
|
34
|
-
if (!wait || wait.status !== "pending") return {};
|
|
35
|
-
if (reason !== "ceiling" && wait.eventName !== "settled" && wait.eventName !== reason)
|
|
36
|
-
return {};
|
|
109
|
+
const resolved = await resolveWaitCause(scheduleId, reason);
|
|
110
|
+
if (!resolved) return {};
|
|
37
111
|
let schedule = await getScheduledTaskById(scheduleId);
|
|
38
112
|
if (!schedule?.enabled) return {};
|
|
39
113
|
const now = new Date().toISOString();
|
|
@@ -46,13 +120,10 @@ export async function dispatchDeferredTaskWait(
|
|
|
46
120
|
schedule = { ...schedule, createdBy: undefined };
|
|
47
121
|
}
|
|
48
122
|
if (!schedule.taskTemplate) throw new Error(`Schedule "${schedule.name}" has no taskTemplate`);
|
|
49
|
-
const cause = reason === "ceiling" ? "ceiling expired" : `${reason} for task ${wait.taskId}`;
|
|
50
123
|
const task = await createStandaloneScheduleTask(
|
|
51
|
-
|
|
52
|
-
...schedule,
|
|
53
|
-
taskTemplate: `${schedule.taskTemplate}\n\nWake-up cause: ${cause}.`,
|
|
54
|
-
},
|
|
124
|
+
wakeSchedule(schedule, resolved.cause),
|
|
55
125
|
extraTags,
|
|
126
|
+
preparedForWait,
|
|
56
127
|
);
|
|
57
128
|
await client.run("UPDATE deferred_task_waits SET childTaskId = ? WHERE scheduleId = ?", [
|
|
58
129
|
task.id,
|
|
@@ -73,17 +144,50 @@ export async function dispatchDeferredTaskWait(
|
|
|
73
144
|
|
|
74
145
|
/** Reconcile durable producer state, including terminal transitions missed by this process. */
|
|
75
146
|
export async function reconcileDeferredTaskWaits(taskId?: string): Promise<void> {
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
147
|
+
const client = getDbClient();
|
|
148
|
+
const replacements = await client.query<{ taskId: string; resumeTaskId: string }>(
|
|
149
|
+
`SELECT DISTINCT m.taskId, (
|
|
150
|
+
SELECT r.id FROM agent_tasks r
|
|
151
|
+
WHERE r.parentTaskId = t.id AND r.taskType = 'resume'
|
|
152
|
+
ORDER BY r.createdAt, r.id LIMIT 1
|
|
153
|
+
) AS resumeTaskId
|
|
154
|
+
FROM deferred_task_wait_members m
|
|
155
|
+
JOIN deferred_task_waits w ON w.scheduleId = m.scheduleId
|
|
156
|
+
JOIN agent_tasks t ON t.id = m.taskId
|
|
157
|
+
WHERE w.status = 'pending' AND t.status = 'superseded'
|
|
158
|
+
AND resumeTaskId IS NOT NULL`,
|
|
159
|
+
);
|
|
160
|
+
if (replacements.length) {
|
|
161
|
+
await client.transaction(async () => {
|
|
162
|
+
for (const replacement of replacements) {
|
|
163
|
+
// Collapse members that now watch the same child. Fired waits and their
|
|
164
|
+
// original ceilings stay untouched. No resume child (including skipped
|
|
165
|
+
// recovery) means hold to the ceiling, never wake on supersession.
|
|
166
|
+
await client.run(
|
|
167
|
+
`UPDATE OR REPLACE deferred_task_wait_members SET taskId = ?, updated_at = ?
|
|
168
|
+
WHERE taskId = ? AND scheduleId IN (
|
|
169
|
+
SELECT scheduleId FROM deferred_task_waits WHERE status = 'pending'
|
|
170
|
+
)`,
|
|
171
|
+
[replacement.resumeTaskId, new Date().toISOString(), replacement.taskId],
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
// Catch up through a chain missed while stopped, and check the replacements
|
|
176
|
+
// even when the triggering event named their superseded parent.
|
|
177
|
+
return reconcileDeferredTaskWaits();
|
|
178
|
+
}
|
|
179
|
+
const rows = await client.query<TaskWait & { taskStatus: "completed" | "failed" | "cancelled" }>(
|
|
180
|
+
`SELECT DISTINCT w.*, t.status AS taskStatus FROM deferred_task_waits w
|
|
181
|
+
JOIN deferred_task_wait_members m ON m.scheduleId = w.scheduleId
|
|
182
|
+
JOIN agent_tasks t ON t.id = m.taskId
|
|
79
183
|
JOIN scheduled_tasks s ON s.id = w.scheduleId
|
|
80
|
-
WHERE w.status = 'pending' AND s.enabled = 1 AND t.status IN ('completed', 'failed')
|
|
184
|
+
WHERE w.status = 'pending' AND s.enabled = 1 AND t.status IN ('completed', 'failed', 'cancelled')
|
|
81
185
|
AND NOT EXISTS (
|
|
82
186
|
SELECT 1 FROM scheduled_tasks d
|
|
83
187
|
WHERE d.parentTaskId = t.id AND d.taskType = 'deferred' AND d.enabled = 1
|
|
84
188
|
)
|
|
85
189
|
AND (w.eventName = 'settled' OR w.eventName = 'task.' || t.status)
|
|
86
|
-
${taskId ? "AND
|
|
190
|
+
${taskId ? "AND m.taskId = ?" : ""}`,
|
|
87
191
|
taskId ? [taskId] : [],
|
|
88
192
|
);
|
|
89
193
|
for (const row of rows) {
|
|
@@ -96,7 +200,7 @@ export async function reconcileDeferredTaskWaits(taskId?: string): Promise<void>
|
|
|
96
200
|
}
|
|
97
201
|
}
|
|
98
202
|
|
|
99
|
-
function
|
|
203
|
+
function onTaskEvent(payload: unknown): void {
|
|
100
204
|
if (
|
|
101
205
|
!payload ||
|
|
102
206
|
typeof payload !== "object" ||
|
|
@@ -109,15 +213,22 @@ function onTaskTerminal(payload: unknown): void {
|
|
|
109
213
|
});
|
|
110
214
|
}
|
|
111
215
|
|
|
112
|
-
/**
|
|
216
|
+
/** Fixed listeners read durable waits, so boot needs no per-wait in-memory registry. */
|
|
113
217
|
export async function initDeferredTaskWaits(): Promise<void> {
|
|
114
218
|
stopDeferredTaskWaits();
|
|
115
|
-
workflowEventBus.on("task.completed",
|
|
116
|
-
workflowEventBus.on("task.failed",
|
|
219
|
+
workflowEventBus.on("task.completed", onTaskEvent);
|
|
220
|
+
workflowEventBus.on("task.failed", onTaskEvent);
|
|
221
|
+
workflowEventBus.on("task.cancelled", onTaskEvent);
|
|
222
|
+
workflowEventBus.on("task.superseded", onTaskEvent);
|
|
223
|
+
// The resume may be committed after its parent's supersede event.
|
|
224
|
+
workflowEventBus.on("task.created", onTaskEvent);
|
|
117
225
|
await reconcileDeferredTaskWaits();
|
|
118
226
|
}
|
|
119
227
|
|
|
120
228
|
export function stopDeferredTaskWaits(): void {
|
|
121
|
-
workflowEventBus.off("task.completed",
|
|
122
|
-
workflowEventBus.off("task.failed",
|
|
229
|
+
workflowEventBus.off("task.completed", onTaskEvent);
|
|
230
|
+
workflowEventBus.off("task.failed", onTaskEvent);
|
|
231
|
+
workflowEventBus.off("task.cancelled", onTaskEvent);
|
|
232
|
+
workflowEventBus.off("task.superseded", onTaskEvent);
|
|
233
|
+
workflowEventBus.off("task.created", onTaskEvent);
|
|
123
234
|
}
|
|
@@ -1,40 +1,70 @@
|
|
|
1
|
-
import { getDbClient } from "@/be/db";
|
|
1
|
+
import { createTaskExtended, getDbClient } from "@/be/db";
|
|
2
2
|
import { scheduleContextKey } from "@/tasks/context-key";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
type PreparedTaskCreate,
|
|
5
|
+
prepareTaskWithSiblingAwareness,
|
|
6
|
+
} from "@/tasks/sibling-awareness";
|
|
4
7
|
import type { AgentTask, ScheduledTask } from "@/types";
|
|
5
8
|
|
|
6
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Run the `pre.task.create` extension hooks for a schedule firing. Call this OUTSIDE
|
|
11
|
+
* the transaction that creates the task: the dispatcher skips hooks while a
|
|
12
|
+
* transaction is open. Throws `TaskCreationBlockedError` when a hook blocks.
|
|
13
|
+
*/
|
|
14
|
+
export async function prepareStandaloneScheduleTask(
|
|
7
15
|
schedule: ScheduledTask,
|
|
8
16
|
extraTags: string[] = [],
|
|
9
|
-
): Promise<
|
|
17
|
+
): Promise<PreparedTaskCreate> {
|
|
10
18
|
if (!schedule.taskTemplate) {
|
|
11
19
|
throw new Error(`Schedule "${schedule.name}" has no taskTemplate (targetType=agent-task)`);
|
|
12
20
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
21
|
+
return await prepareTaskWithSiblingAwareness(
|
|
22
|
+
schedule.taskTemplate,
|
|
23
|
+
{
|
|
24
|
+
key: schedule.key,
|
|
25
|
+
creatorAgentId: schedule.createdByAgentId,
|
|
26
|
+
taskType: schedule.taskType,
|
|
27
|
+
tags: [...schedule.tags, "scheduled", `schedule:${schedule.name}`, ...extraTags],
|
|
28
|
+
priority: schedule.priority,
|
|
29
|
+
agentId: schedule.targetAgentId,
|
|
30
|
+
routingReason: schedule.targetAgentId ? "human_pinned" : undefined,
|
|
31
|
+
routingSource: schedule.targetAgentId ? "engine_default" : undefined,
|
|
32
|
+
model: schedule.model,
|
|
33
|
+
modelTier: schedule.modelTier,
|
|
34
|
+
scheduleId: schedule.id,
|
|
35
|
+
source: "schedule",
|
|
36
|
+
requestedByUserId: schedule.createdBy,
|
|
37
|
+
contextKey: scheduleContextKey({ scheduleId: schedule.id }),
|
|
38
|
+
// Set only by `defer-task`. An explicit parent wins over the sibling-awareness
|
|
39
|
+
// auto-wiring (see withSiblingAwareness in src/tasks/sibling-awareness.ts), so
|
|
40
|
+
// the wake-up run continues the deferred task rather than a random sibling.
|
|
41
|
+
parentTaskId: schedule.parentTaskId,
|
|
42
|
+
},
|
|
43
|
+
{ origin: "schedule" },
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Insert the schedule's task. Pass the result of `prepareStandaloneScheduleTask`
|
|
49
|
+
* when calling from inside a transaction; without it the hooks run here.
|
|
50
|
+
*/
|
|
51
|
+
export async function createStandaloneScheduleTask(
|
|
52
|
+
schedule: ScheduledTask,
|
|
53
|
+
extraTags: string[] = [],
|
|
54
|
+
prepared?: PreparedTaskCreate,
|
|
55
|
+
): Promise<AgentTask> {
|
|
56
|
+
const { description, options } =
|
|
57
|
+
prepared ?? (await prepareStandaloneScheduleTask(schedule, extraTags));
|
|
58
|
+
const task = await createTaskExtended(description, options);
|
|
32
59
|
// Both timer and event dispatch wrap this helper in a transaction. Transfer
|
|
33
60
|
// pending watchers before the resume task becomes visible or its schedule is
|
|
34
61
|
// disabled; their own schedules retain the original ceilings.
|
|
35
62
|
if (schedule.taskType === "deferred" && schedule.parentTaskId) {
|
|
36
63
|
await getDbClient().run(
|
|
37
|
-
|
|
64
|
+
`UPDATE deferred_task_wait_members SET taskId = ?, updated_at = ?
|
|
65
|
+
WHERE taskId = ? AND scheduleId IN (
|
|
66
|
+
SELECT scheduleId FROM deferred_task_waits WHERE status = 'pending'
|
|
67
|
+
)`,
|
|
38
68
|
[task.id, new Date().toISOString(), schedule.parentTaskId],
|
|
39
69
|
);
|
|
40
70
|
}
|