@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
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
getUserById,
|
|
17
17
|
getWorkflow,
|
|
18
18
|
getWorkflowRun,
|
|
19
|
+
type UpdateScheduledTaskData,
|
|
19
20
|
updateScheduledTask,
|
|
20
21
|
} from "@/be/db";
|
|
21
22
|
import {
|
|
@@ -38,11 +39,12 @@ import {
|
|
|
38
39
|
reconcileDeferredTaskWaits,
|
|
39
40
|
stopDeferredTaskWaits,
|
|
40
41
|
} from "./deferred-task-waits";
|
|
41
|
-
import { createStandaloneScheduleTask } from "./schedule-task";
|
|
42
|
+
import { createStandaloneScheduleTask, prepareStandaloneScheduleTask } from "./schedule-task";
|
|
42
43
|
|
|
43
44
|
export { createStandaloneScheduleTask } from "./schedule-task";
|
|
44
45
|
|
|
45
46
|
let schedulerInterval: ReturnType<typeof setInterval> | null = null;
|
|
47
|
+
let schedulerRun: object | null = null;
|
|
46
48
|
let isProcessing = false;
|
|
47
49
|
let executorRegistry: ExecutorRegistry | null = null;
|
|
48
50
|
|
|
@@ -201,8 +203,10 @@ export async function dispatchScheduleTarget(
|
|
|
201
203
|
}
|
|
202
204
|
}
|
|
203
205
|
if (!triggeredWorkflows) {
|
|
206
|
+
// Extension hooks must run before the transaction opens (see schedule-task.ts).
|
|
207
|
+
const prepared = await prepareStandaloneScheduleTask(schedule, extraTags);
|
|
204
208
|
const task = await getDbClient().transaction(async () =>
|
|
205
|
-
createStandaloneScheduleTask(schedule, extraTags),
|
|
209
|
+
createStandaloneScheduleTask(schedule, extraTags, prepared),
|
|
206
210
|
);
|
|
207
211
|
return { triggeredWorkflows, task };
|
|
208
212
|
}
|
|
@@ -220,96 +224,75 @@ async function throwIfWorkflowNeedsSetup(scheduleId: string, runIds: string[]):
|
|
|
220
224
|
}
|
|
221
225
|
}
|
|
222
226
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
227
|
+
/**
|
|
228
|
+
* Claim the snapshot's occurrence under BEGIN IMMEDIATE. A competing poller,
|
|
229
|
+
* recovery pass, or schedule edit invalidates it before any target is dispatched.
|
|
230
|
+
*/
|
|
231
|
+
async function claimScheduleOccurrence(snapshot: ScheduledTask, extraTags: string[]) {
|
|
232
|
+
return getDbClient().transaction(async () => {
|
|
233
|
+
const current = await getScheduledTaskById(snapshot.id);
|
|
234
|
+
if (!current?.enabled || !current.nextRunAt || current.nextRunAt !== snapshot.nextRunAt) {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
const schedule = renderScheduledTaskParams(current);
|
|
238
|
+
|
|
239
|
+
// Event-driven waits already atomically create their child and consume the
|
|
240
|
+
// ceiling. Keep that transaction (and its afterCommit emits) intact: disabling
|
|
241
|
+
// a one-time schedule first would prevent its wake-up task from being created.
|
|
242
|
+
const deferred = await dispatchDeferredTaskWait(schedule.id, "ceiling", extraTags);
|
|
243
|
+
if (deferred) {
|
|
244
|
+
return {
|
|
245
|
+
schedule,
|
|
246
|
+
claimed: (await getScheduledTaskById(schedule.id))!,
|
|
247
|
+
dispatched: { triggeredWorkflows: false, ...deferred },
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const now = new Date();
|
|
252
|
+
const nextRunAt =
|
|
253
|
+
schedule.scheduleType === "one_time"
|
|
254
|
+
? null
|
|
255
|
+
: calculateNextRun(
|
|
256
|
+
schedule.timezone?.includes("{{") ? { ...schedule, timezone: "UTC" } : schedule,
|
|
257
|
+
new Date(Math.max(now.getTime(), new Date(current.nextRunAt).getTime())),
|
|
258
|
+
);
|
|
259
|
+
const claimed = (await updateScheduledTask(schedule.id, {
|
|
260
|
+
lastRunAt: now.toISOString(),
|
|
261
|
+
nextRunAt,
|
|
262
|
+
...(schedule.scheduleType === "one_time" ? { enabled: false } : {}),
|
|
263
|
+
}))!;
|
|
264
|
+
return { schedule, claimed, dispatched: undefined };
|
|
242
265
|
});
|
|
243
266
|
}
|
|
244
267
|
|
|
245
|
-
/**
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
268
|
+
/** A slow target must not overwrite a newer occurrence or a schedule edit. */
|
|
269
|
+
async function updateClaimedSchedule(claimed: ScheduledTask, updates: UpdateScheduledTaskData) {
|
|
270
|
+
await getDbClient().transaction(async () => {
|
|
271
|
+
const current = await getScheduledTaskById(claimed.id);
|
|
272
|
+
if (
|
|
273
|
+
current?.nextRunAt !== claimed.nextRunAt ||
|
|
274
|
+
current?.lastRunAt !== claimed.lastRunAt ||
|
|
275
|
+
current?.enabled !== claimed.enabled ||
|
|
276
|
+
current?.lastUpdatedAt !== claimed.lastUpdatedAt
|
|
277
|
+
)
|
|
278
|
+
return;
|
|
279
|
+
await updateScheduledTask(claimed.id, updates);
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** Recover ONE catch-up occurrence per schedule, using the poller's same claim. */
|
|
250
284
|
async function recoverMissedSchedules(): Promise<void> {
|
|
251
|
-
const now =
|
|
285
|
+
const now = Date.now();
|
|
252
286
|
const dueSchedules = await getDueScheduledTasks();
|
|
253
|
-
|
|
254
|
-
for (const storedSchedule of dueSchedules) {
|
|
255
|
-
const schedule = renderScheduledTaskParams(storedSchedule);
|
|
287
|
+
for (const schedule of dueSchedules) {
|
|
256
288
|
if (!schedule.nextRunAt) continue;
|
|
257
|
-
const missedBy = now
|
|
289
|
+
const missedBy = now - new Date(schedule.nextRunAt).getTime();
|
|
258
290
|
if (missedBy < 15000) continue; // Less than 15s — normal timing jitter
|
|
259
|
-
|
|
260
291
|
console.log(
|
|
261
292
|
`[Scheduler] Recovering missed schedule "${schedule.name}" ` +
|
|
262
293
|
`(was due ${Math.round(missedBy / 1000)}s ago)`,
|
|
263
294
|
);
|
|
264
|
-
|
|
265
|
-
let triggeredWorkflows = false;
|
|
266
|
-
try {
|
|
267
|
-
({ triggeredWorkflows } = await dispatchScheduleTarget(schedule, ["recovered"]));
|
|
268
|
-
|
|
269
|
-
// Update schedule state regardless of workflow/task path
|
|
270
|
-
if (schedule.scheduleType === "one_time") {
|
|
271
|
-
await updateScheduledTask(schedule.id, {
|
|
272
|
-
lastRunAt: now.toISOString(),
|
|
273
|
-
nextRunAt: null,
|
|
274
|
-
enabled: false,
|
|
275
|
-
lastUpdatedAt: now.toISOString(),
|
|
276
|
-
});
|
|
277
|
-
} else {
|
|
278
|
-
const nextRun = calculateNextRun(schedule, now);
|
|
279
|
-
await updateScheduledTask(schedule.id, {
|
|
280
|
-
lastRunAt: now.toISOString(),
|
|
281
|
-
nextRunAt: nextRun,
|
|
282
|
-
lastUpdatedAt: now.toISOString(),
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
if (schedule.scheduleType === "one_time") {
|
|
287
|
-
console.log(`[Scheduler] One-time schedule "${schedule.name}" recovered and auto-disabled`);
|
|
288
|
-
}
|
|
289
|
-
telemetry.schedule("executed", {
|
|
290
|
-
scheduleType: schedule.scheduleType,
|
|
291
|
-
triggeredWorkflows,
|
|
292
|
-
wasRecovered: true,
|
|
293
|
-
});
|
|
294
|
-
} catch (err) {
|
|
295
|
-
if (err instanceof AutomationNeedsSetupError) {
|
|
296
|
-
await advanceScheduleAfterNeedsSetup(schedule, now);
|
|
297
|
-
telemetry.schedule("error", {
|
|
298
|
-
scheduleType: schedule.scheduleType,
|
|
299
|
-
triggeredWorkflows,
|
|
300
|
-
wasRecovered: true,
|
|
301
|
-
consecutiveErrors: schedule.consecutiveErrors ?? 0,
|
|
302
|
-
});
|
|
303
|
-
continue;
|
|
304
|
-
}
|
|
305
|
-
telemetry.schedule("error", {
|
|
306
|
-
scheduleType: schedule.scheduleType,
|
|
307
|
-
triggeredWorkflows,
|
|
308
|
-
wasRecovered: true,
|
|
309
|
-
consecutiveErrors: schedule.consecutiveErrors ?? 0,
|
|
310
|
-
});
|
|
311
|
-
console.error(`[Scheduler] Error recovering "${schedule.name}":`, err);
|
|
312
|
-
}
|
|
295
|
+
await executeSchedule(schedule, ["recovered"]);
|
|
313
296
|
}
|
|
314
297
|
}
|
|
315
298
|
|
|
@@ -360,49 +343,43 @@ function getBackoffMs(consecutiveErrors: number): number {
|
|
|
360
343
|
* Execute a single scheduled task by creating an agent task.
|
|
361
344
|
* Tracks consecutive errors and applies exponential backoff on failure.
|
|
362
345
|
*/
|
|
363
|
-
export async function executeSchedule(
|
|
364
|
-
schedule
|
|
346
|
+
export async function executeSchedule(
|
|
347
|
+
schedule: ScheduledTask,
|
|
348
|
+
extraTags: string[] = [],
|
|
349
|
+
): Promise<void> {
|
|
350
|
+
let claimed = schedule;
|
|
365
351
|
let triggeredWorkflows = false;
|
|
352
|
+
const wasRecovered = extraTags.includes("recovered");
|
|
366
353
|
try {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
const nextRun = calculateNextRun(schedule, new Date());
|
|
384
|
-
await updateScheduledTask(schedule.id, {
|
|
385
|
-
lastRunAt: now,
|
|
386
|
-
nextRunAt: nextRun,
|
|
387
|
-
lastUpdatedAt: now,
|
|
388
|
-
consecutiveErrors: 0,
|
|
389
|
-
lastErrorAt: null,
|
|
390
|
-
lastErrorMessage: null,
|
|
391
|
-
});
|
|
392
|
-
console.log(`[Scheduler] Executed schedule "${schedule.name}", next run: ${nextRun}`);
|
|
393
|
-
}
|
|
354
|
+
const occurrence = await claimScheduleOccurrence(schedule, extraTags);
|
|
355
|
+
if (!occurrence) return;
|
|
356
|
+
({ schedule, claimed } = occurrence);
|
|
357
|
+
// The claim has committed before external workflow/script dispatch begins.
|
|
358
|
+
// Never re-arm it: a target may have produced effects before reporting failure.
|
|
359
|
+
({ triggeredWorkflows } =
|
|
360
|
+
occurrence.dispatched ?? (await dispatchScheduleTarget(schedule, extraTags)));
|
|
361
|
+
|
|
362
|
+
await updateClaimedSchedule(claimed, {
|
|
363
|
+
consecutiveErrors: 0,
|
|
364
|
+
lastErrorAt: null,
|
|
365
|
+
lastErrorMessage: null,
|
|
366
|
+
});
|
|
367
|
+
console.log(
|
|
368
|
+
`[Scheduler] Executed schedule "${schedule.name}", next run: ${claimed.nextRunAt ?? "disabled"}`,
|
|
369
|
+
);
|
|
394
370
|
telemetry.schedule("executed", {
|
|
395
371
|
scheduleType: schedule.scheduleType,
|
|
396
372
|
triggeredWorkflows,
|
|
397
|
-
wasRecovered
|
|
373
|
+
wasRecovered,
|
|
398
374
|
});
|
|
399
375
|
} catch (err) {
|
|
400
376
|
if (err instanceof AutomationNeedsSetupError) {
|
|
401
|
-
|
|
377
|
+
// Preflight records its own diagnostic. The claim already advanced the
|
|
378
|
+
// cadence, so no retry/backoff or second schedule advancement is needed.
|
|
402
379
|
telemetry.schedule("error", {
|
|
403
380
|
scheduleType: schedule.scheduleType,
|
|
404
381
|
triggeredWorkflows,
|
|
405
|
-
wasRecovered
|
|
382
|
+
wasRecovered,
|
|
406
383
|
consecutiveErrors: schedule.consecutiveErrors ?? 0,
|
|
407
384
|
});
|
|
408
385
|
return;
|
|
@@ -446,11 +423,11 @@ export async function executeSchedule(schedule: ScheduledTask): Promise<void> {
|
|
|
446
423
|
console.log(`[Scheduler] Backing off "${schedule.name}" for ${backoff / 1000}s`);
|
|
447
424
|
}
|
|
448
425
|
|
|
449
|
-
await
|
|
426
|
+
await updateClaimedSchedule(claimed, updates);
|
|
450
427
|
telemetry.schedule("error", {
|
|
451
428
|
scheduleType: schedule.scheduleType,
|
|
452
429
|
triggeredWorkflows,
|
|
453
|
-
wasRecovered
|
|
430
|
+
wasRecovered,
|
|
454
431
|
consecutiveErrors: errorCount,
|
|
455
432
|
});
|
|
456
433
|
}
|
|
@@ -466,23 +443,29 @@ export function startScheduler(
|
|
|
466
443
|
intervalMs = 10000,
|
|
467
444
|
opts?: { runId?: string },
|
|
468
445
|
): void {
|
|
469
|
-
if (
|
|
446
|
+
if (schedulerRun) {
|
|
470
447
|
console.log("[Scheduler] Already running");
|
|
471
448
|
return;
|
|
472
449
|
}
|
|
473
450
|
|
|
451
|
+
const run = {};
|
|
452
|
+
schedulerRun = run;
|
|
474
453
|
executorRegistry = registry;
|
|
475
454
|
console.log(`[Scheduler] Starting with ${intervalMs}ms polling interval`);
|
|
476
455
|
|
|
477
|
-
//
|
|
456
|
+
// Both success and failure resume polling, but only after recovery settles.
|
|
457
|
+
// The identity prevents stop/restart during startup from installing an old timer.
|
|
478
458
|
void initDeferredTaskWaits()
|
|
479
|
-
.then(() => recoverMissedSchedules())
|
|
480
|
-
.
|
|
481
|
-
.
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
459
|
+
.then(() => (schedulerRun === run ? recoverMissedSchedules() : undefined))
|
|
460
|
+
.catch((err) => console.error("[Scheduler] Recovery failed:", err))
|
|
461
|
+
.then(async () => {
|
|
462
|
+
if (schedulerRun !== run) return;
|
|
463
|
+
schedulerInterval = setInterval(() => {
|
|
464
|
+
void processSchedules().catch((err) => console.error("[Scheduler] Poll failed:", err));
|
|
465
|
+
}, intervalMs);
|
|
466
|
+
await processSchedules();
|
|
467
|
+
})
|
|
468
|
+
.catch((err) => console.error("[Scheduler] Poll failed:", err));
|
|
486
469
|
|
|
487
470
|
ensure({
|
|
488
471
|
id: "scheduler_started",
|
|
@@ -531,11 +514,11 @@ async function processSchedules(): Promise<void> {
|
|
|
531
514
|
* Stop the scheduler polling loop.
|
|
532
515
|
*/
|
|
533
516
|
export function stopScheduler(): void {
|
|
517
|
+
schedulerRun = null;
|
|
534
518
|
stopDeferredTaskWaits();
|
|
535
519
|
if (schedulerInterval) {
|
|
536
520
|
clearInterval(schedulerInterval);
|
|
537
521
|
schedulerInterval = null;
|
|
538
|
-
isProcessing = false;
|
|
539
522
|
console.log("[Scheduler] Stopped");
|
|
540
523
|
}
|
|
541
524
|
}
|
|
@@ -92,7 +92,11 @@ async function readRuntimeError(
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
async function writeBareImportShim(
|
|
95
|
+
export async function writeBareImportShim(
|
|
96
|
+
tmpdir: string,
|
|
97
|
+
name: string,
|
|
98
|
+
targetUrl: URL,
|
|
99
|
+
): Promise<void> {
|
|
96
100
|
const dir = `${tmpdir}/node_modules/${name}`;
|
|
97
101
|
await Bun.$`mkdir -p ${dir}`;
|
|
98
102
|
await Bun.write(`${dir}/package.json`, JSON.stringify({ type: "module", main: "index.ts" }));
|
|
@@ -15,6 +15,11 @@ export type SwarmConfigPayload = {
|
|
|
15
15
|
* context like the agent identity — scripts never supply or override it.
|
|
16
16
|
*/
|
|
17
17
|
runtimeInstanceId?: { value: string; isSecret: false };
|
|
18
|
+
/**
|
|
19
|
+
* Per-process secret set only by the in-process extension `ctx.swarm` SDK. The
|
|
20
|
+
* bridge requires it to grant the `extension` call origin. Never sent to scripts.
|
|
21
|
+
*/
|
|
22
|
+
extensionToken?: { value: string; isSecret: true };
|
|
18
23
|
};
|
|
19
24
|
user: Record<string, { value: string; isSecret: boolean }>;
|
|
20
25
|
egressSecrets?: EgressSecretEntry[];
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const SCRIPT_ALLOWED_BARE_IMPORTS = ["swarm-sdk", "stdlib", "zod"] as const;
|
|
4
4
|
const FORBIDDEN_HINTS = new Set(["node:", "bun:", "fs", "child_process", "crypto", "bun:sqlite"]);
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
|
|
6
|
+
export type ImportAllowlistOptions = {
|
|
7
|
+
allowedBare: readonly string[];
|
|
8
|
+
allowRelative: boolean;
|
|
9
|
+
// Extensions need strict dynamic imports because they run in-process without a sandbox.
|
|
10
|
+
strictDynamic?: boolean;
|
|
11
|
+
};
|
|
7
12
|
|
|
8
13
|
export type ImportAllowlistResult =
|
|
9
14
|
| { ok: true }
|
|
@@ -13,11 +18,18 @@ function isRelative(specifier: string): boolean {
|
|
|
13
18
|
return specifier.startsWith("./") || specifier.startsWith("../");
|
|
14
19
|
}
|
|
15
20
|
|
|
16
|
-
function isAllowed(specifier: string): boolean {
|
|
17
|
-
return
|
|
21
|
+
function isAllowed(specifier: string, options: ImportAllowlistOptions): boolean {
|
|
22
|
+
return (
|
|
23
|
+
(options.allowRelative && isRelative(specifier)) || options.allowedBare.includes(specifier)
|
|
24
|
+
);
|
|
18
25
|
}
|
|
19
26
|
|
|
20
|
-
|
|
27
|
+
type CollectedImports = {
|
|
28
|
+
imports: string[];
|
|
29
|
+
unsupportedSyntax: "import()" | "require()" | "import-equals" | null;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function collectImportSpecifiers(source: string, strictDynamic = false): CollectedImports {
|
|
21
33
|
const sourceFile = ts.createSourceFile(
|
|
22
34
|
"user-script.ts",
|
|
23
35
|
source,
|
|
@@ -26,6 +38,7 @@ function collectImportSpecifiers(source: string): string[] {
|
|
|
26
38
|
ts.ScriptKind.TS,
|
|
27
39
|
);
|
|
28
40
|
const imports: string[] = [];
|
|
41
|
+
let unsupportedSyntax: CollectedImports["unsupportedSyntax"] = null;
|
|
29
42
|
|
|
30
43
|
const visit = (node: ts.Node) => {
|
|
31
44
|
if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) {
|
|
@@ -34,19 +47,42 @@ function collectImportSpecifiers(source: string): string[] {
|
|
|
34
47
|
imports.push(moduleSpecifier.text);
|
|
35
48
|
}
|
|
36
49
|
|
|
50
|
+
if (
|
|
51
|
+
strictDynamic &&
|
|
52
|
+
ts.isImportEqualsDeclaration(node) &&
|
|
53
|
+
ts.isExternalModuleReference(node.moduleReference)
|
|
54
|
+
) {
|
|
55
|
+
const specifier = node.moduleReference.expression;
|
|
56
|
+
if (specifier && ts.isStringLiteralLike(specifier)) imports.push(specifier.text);
|
|
57
|
+
else unsupportedSyntax ??= "import-equals";
|
|
58
|
+
}
|
|
59
|
+
|
|
37
60
|
if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
|
38
61
|
const [arg] = node.arguments;
|
|
39
|
-
if (arg && ts.isStringLiteral(arg)
|
|
62
|
+
if (arg && (ts.isStringLiteral(arg) || (strictDynamic && ts.isStringLiteralLike(arg))))
|
|
63
|
+
imports.push(arg.text);
|
|
64
|
+
else if (strictDynamic) unsupportedSyntax ??= "import()";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (
|
|
68
|
+
strictDynamic &&
|
|
69
|
+
ts.isCallExpression(node) &&
|
|
70
|
+
ts.isIdentifier(node.expression) &&
|
|
71
|
+
node.expression.text === "require"
|
|
72
|
+
) {
|
|
73
|
+
const [arg] = node.arguments;
|
|
74
|
+
if (arg && ts.isStringLiteralLike(arg)) imports.push(arg.text);
|
|
75
|
+
else unsupportedSyntax ??= "require()";
|
|
40
76
|
}
|
|
41
77
|
|
|
42
78
|
ts.forEachChild(node, visit);
|
|
43
79
|
};
|
|
44
80
|
|
|
45
81
|
visit(sourceFile);
|
|
46
|
-
return imports;
|
|
82
|
+
return { imports, unsupportedSyntax };
|
|
47
83
|
}
|
|
48
84
|
|
|
49
|
-
function findForbiddenDynamicCode(source: string): string | null {
|
|
85
|
+
function findForbiddenDynamicCode(source: string, subject: string): string | null {
|
|
50
86
|
const sourceFile = ts.createSourceFile(
|
|
51
87
|
"user-script.ts",
|
|
52
88
|
source,
|
|
@@ -64,7 +100,7 @@ function findForbiddenDynamicCode(source: string): string | null {
|
|
|
64
100
|
node.expression.kind === ts.SyntaxKind.Identifier &&
|
|
65
101
|
node.expression.getText(sourceFile) === "eval"
|
|
66
102
|
) {
|
|
67
|
-
diagnostic =
|
|
103
|
+
diagnostic = `eval is not allowed in ${subject}`;
|
|
68
104
|
return;
|
|
69
105
|
}
|
|
70
106
|
|
|
@@ -73,7 +109,7 @@ function findForbiddenDynamicCode(source: string): string | null {
|
|
|
73
109
|
node.expression.kind === ts.SyntaxKind.Identifier &&
|
|
74
110
|
node.expression.getText(sourceFile) === "Function"
|
|
75
111
|
) {
|
|
76
|
-
diagnostic =
|
|
112
|
+
diagnostic = `Function constructor is not allowed in ${subject}`;
|
|
77
113
|
return;
|
|
78
114
|
}
|
|
79
115
|
|
|
@@ -82,7 +118,7 @@ function findForbiddenDynamicCode(source: string): string | null {
|
|
|
82
118
|
node.expression.kind === ts.SyntaxKind.Identifier &&
|
|
83
119
|
node.expression.getText(sourceFile) === "Function"
|
|
84
120
|
) {
|
|
85
|
-
diagnostic =
|
|
121
|
+
diagnostic = `Function constructor is not allowed in ${subject}`;
|
|
86
122
|
return;
|
|
87
123
|
}
|
|
88
124
|
|
|
@@ -93,23 +129,47 @@ function findForbiddenDynamicCode(source: string): string | null {
|
|
|
93
129
|
return diagnostic;
|
|
94
130
|
}
|
|
95
131
|
|
|
96
|
-
export function
|
|
97
|
-
|
|
132
|
+
export function checkImportAllowlist(
|
|
133
|
+
source: string,
|
|
134
|
+
options: ImportAllowlistOptions,
|
|
135
|
+
): ImportAllowlistResult {
|
|
136
|
+
const isExtension = options.allowedBare.includes("swarm-extension");
|
|
137
|
+
const subject = isExtension ? "swarm extensions" : "swarm scripts";
|
|
138
|
+
const dynamicDiagnostic = findForbiddenDynamicCode(source, subject);
|
|
98
139
|
if (dynamicDiagnostic) return { ok: false, diagnostic: dynamicDiagnostic, imports: [] };
|
|
99
140
|
|
|
100
|
-
const
|
|
101
|
-
|
|
141
|
+
const collected = collectImportSpecifiers(source, options.strictDynamic);
|
|
142
|
+
if (collected.unsupportedSyntax) {
|
|
143
|
+
return {
|
|
144
|
+
ok: false,
|
|
145
|
+
diagnostic: `Computed ${collected.unsupportedSyntax} is not allowed in ${subject}. Use a string literal from the import allowlist.`,
|
|
146
|
+
imports: collected.imports,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const imports = collected.imports;
|
|
150
|
+
const rejected = imports.filter((specifier) => !isAllowed(specifier, options));
|
|
102
151
|
if (rejected.length === 0) return { ok: true };
|
|
103
152
|
|
|
104
153
|
const hint = rejected.find(
|
|
105
154
|
(specifier) => FORBIDDEN_HINTS.has(specifier) || specifier.startsWith("node:"),
|
|
106
155
|
);
|
|
107
156
|
const reason = hint
|
|
108
|
-
? `Import '${hint}' is not allowed in
|
|
109
|
-
: `Import '${rejected[0]}' is not on the
|
|
157
|
+
? `Import '${hint}' is not allowed in ${subject}`
|
|
158
|
+
: `Import '${rejected[0]}' is not on the ${subject} allowlist`;
|
|
159
|
+
const listed = options.allowedBare.map((specifier) => `"${specifier}"`).join(", ");
|
|
160
|
+
const allowlistRemedy = options.allowRelative
|
|
161
|
+
? `Allowed imports are ${listed}, and relative paths ("./" or "../").`
|
|
162
|
+
: `Allowed imports are ${listed}. Relative imports are not allowed.`;
|
|
110
163
|
const remedy =
|
|
111
164
|
hint === "crypto" || hint === "node:crypto"
|
|
112
165
|
? "The global crypto object already provides randomUUID, getRandomValues, and subtle.digest; delete the import and use crypto directly."
|
|
113
|
-
:
|
|
166
|
+
: allowlistRemedy;
|
|
114
167
|
return { ok: false, diagnostic: `${reason}. ${remedy}`, imports: rejected };
|
|
115
168
|
}
|
|
169
|
+
|
|
170
|
+
export function validateScriptImports(source: string): ImportAllowlistResult {
|
|
171
|
+
return checkImportAllowlist(source, {
|
|
172
|
+
allowedBare: SCRIPT_ALLOWED_BARE_IMPORTS,
|
|
173
|
+
allowRelative: true,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
@@ -56,6 +56,10 @@ export const SDK_TOOL_NAME_MAP = {
|
|
|
56
56
|
script_getRun: "get-script-run",
|
|
57
57
|
script_listRuns: "list-script-runs",
|
|
58
58
|
|
|
59
|
+
// ── extensions ──
|
|
60
|
+
extension_install: "extension-install",
|
|
61
|
+
extension_list: "extension-list",
|
|
62
|
+
|
|
59
63
|
// ── swarm / agent ──
|
|
60
64
|
swarm_get: "get-swarm",
|
|
61
65
|
agent_info: "my-agent-info",
|
|
@@ -7,6 +7,8 @@ export class SwarmConfig {
|
|
|
7
7
|
readonly mcpBaseUrl: RedactedValue<string>;
|
|
8
8
|
/** Per-boot runtime identity of the invoking worker; system context, never script input. */
|
|
9
9
|
readonly runtimeInstanceId: RedactedValue<string> | undefined;
|
|
10
|
+
/** Per-process extension bridge secret; present only for in-process extension SDKs. */
|
|
11
|
+
readonly extensionToken: RedactedValue<string> | undefined;
|
|
10
12
|
|
|
11
13
|
private readonly userValues: Map<string, RedactedValue<string>>;
|
|
12
14
|
|
|
@@ -29,6 +31,12 @@ export class SwarmConfig {
|
|
|
29
31
|
isSecret: payload.system.runtimeInstanceId.isSecret,
|
|
30
32
|
})
|
|
31
33
|
: undefined;
|
|
34
|
+
this.extensionToken = payload.system.extensionToken
|
|
35
|
+
? Redacted.make(payload.system.extensionToken.value, {
|
|
36
|
+
type: "system",
|
|
37
|
+
isSecret: payload.system.extensionToken.isSecret,
|
|
38
|
+
})
|
|
39
|
+
: undefined;
|
|
32
40
|
this.userValues = new Map(
|
|
33
41
|
Object.entries(payload.user ?? {}).map(([key, value]) => [
|
|
34
42
|
key,
|
|
@@ -20,6 +20,9 @@ function headers(config: SwarmConfig): Record<string, string> {
|
|
|
20
20
|
...(config.runtimeInstanceId
|
|
21
21
|
? { "X-Runtime-Instance-ID": Redacted.value(config.runtimeInstanceId) }
|
|
22
22
|
: {}),
|
|
23
|
+
...(config.extensionToken
|
|
24
|
+
? { "X-Extension-Token": Redacted.value(config.extensionToken) }
|
|
25
|
+
: {}),
|
|
23
26
|
"Content-Type": "application/json",
|
|
24
27
|
};
|
|
25
28
|
}
|
|
@@ -36,10 +36,14 @@ declare module "swarm-sdk" {
|
|
|
36
36
|
| "reroute_fault";
|
|
37
37
|
export type TaskSendArgs = Record<string, unknown> & {
|
|
38
38
|
task: string;
|
|
39
|
-
routingNote?: string;
|
|
40
39
|
} & (
|
|
41
|
-
| {
|
|
42
|
-
|
|
40
|
+
| {
|
|
41
|
+
agentId: string;
|
|
42
|
+
routingReason: RoutingReason;
|
|
43
|
+
/** At least 10 characters after trim; maximum 200. */
|
|
44
|
+
routingNote: string;
|
|
45
|
+
}
|
|
46
|
+
| { agentId?: never; routingReason?: never; routingNote?: string }
|
|
43
47
|
);
|
|
44
48
|
export type AgentTaskStepConfig = {
|
|
45
49
|
template?: string;
|
|
@@ -513,6 +517,15 @@ declare module "swarm-sdk" {
|
|
|
513
517
|
offset?: number;
|
|
514
518
|
}): Promise<unknown>;
|
|
515
519
|
|
|
520
|
+
// --- write: extensions ---
|
|
521
|
+
extension_install(args: {
|
|
522
|
+
manifest: Record<string, unknown>;
|
|
523
|
+
files: Record<string, string>;
|
|
524
|
+
priority?: number;
|
|
525
|
+
config?: Record<string, unknown>;
|
|
526
|
+
}): Promise<unknown>;
|
|
527
|
+
extension_list(args?: { enabledOnly?: boolean }): Promise<unknown>;
|
|
528
|
+
|
|
516
529
|
// --- write: repos ---
|
|
517
530
|
repo_update(args: Record<string, unknown>): Promise<unknown>;
|
|
518
531
|
|