@desplega.ai/agent-swarm 1.149.1 → 1.151.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -3
- package/dist/{acp-adapter-vhkzfpjg.js → acp-adapter-ex8hzjfc.js} +5 -5
- package/dist/{actions-9d2694yk.js → actions-2bgpyhww.js} +39 -11
- package/dist/{app-fq2ggynt.js → app-1706zhr1.js} +7 -7
- package/dist/{artifact-gj9arwrr.js → artifact-td4q4dch.js} +1 -1
- package/dist/{assistant-edb6n9nn.js → assistant-bavke5p7.js} +44 -20
- package/dist/{azure-openai-responses-cqar64rc.js → azure-openai-responses-f38x2avq.js} +13 -13
- package/dist/{boot-reembed-msweabbp.js → boot-reembed-3cmrrmpa.js} +9 -9
- package/dist/{boot-reembed-q5wd3tha.js → boot-reembed-nafcmmmc.js} +14 -16
- package/dist/{boot-scrub-logs-3g1az933.js → boot-scrub-logs-cd6a2sq0.js} +5 -5
- package/dist/{claude-adapter-v3rnzsdc.js → claude-adapter-wfp2gzvz.js} +15 -16
- package/dist/{claude-managed-adapter-qvsmh6mv.js → claude-managed-adapter-dsmkcrjy.js} +3 -3
- package/dist/{claude-sdk-session-qxrpsna1.js → claude-sdk-session-bht9jjak.js} +15 -16
- package/dist/{cli-5m0qmvdp.js → cli-0qb1z6s2.js} +1 -1
- package/dist/{cli-9d8ntetj.js → cli-2658f6at.js} +1 -1
- package/dist/{cli-7evafqvy.js → cli-2tbvf1z1.js} +85 -69
- package/dist/{cli-k351hv5k.js → cli-3v9xhjcn.js} +24 -12
- package/dist/{cli-1ebpar1q.js → cli-4wppfkcm.js} +350 -20
- package/dist/{cli-4260r1tg.js → cli-5jd2z7av.js} +97 -84
- package/dist/{cli-cv7s5zdq.js → cli-5tpgq60f.js} +9865 -715
- package/dist/{cli-6skz3rk8.js → cli-7tgcstn2.js} +9 -4
- package/dist/{cli-01g3x1ca.js → cli-9004vfdf.js} +108 -56
- package/dist/{cli-1c94wvcw.js → cli-a1z3h107.js} +124 -99
- package/dist/{cli-9br8ypfv.js → cli-czjtgfys.js} +2 -2
- package/dist/{cli-5my3bjsd.js → cli-d3p82erb.js} +7 -7
- package/dist/{cli-e29gmdmw.js → cli-dv6cdjs2.js} +2 -1
- package/dist/{cli-wby1xnnb.js → cli-f1wky6zp.js} +1 -1
- package/dist/{cli-zvz62chv.js → cli-fm4ytsdk.js} +4 -4
- package/dist/{cli-tg8qj8ee.js → cli-frcta2w4.js} +6 -6
- package/dist/{cli-7xx10h0a.js → cli-g69fdc9g.js} +2 -2
- package/dist/{cli-cmp8g3sw.js → cli-gbfwcd3k.js} +44 -9
- package/dist/{handlers-nsg0bcye.js → cli-gjyvhmmh.js} +97 -45
- package/dist/{cli-08b07b4r.js → cli-ht5rs63n.js} +1 -1
- package/dist/{cli-jecc01cb.js → cli-j4nv2wqy.js} +2 -4
- package/dist/{cli-cd60fv6j.js → cli-j9xczhaq.js} +1 -1
- package/dist/cli-pf9q3696.js +172 -0
- package/dist/{cli-xm87hzbw.js → cli-pgj4a5fb.js} +1 -1
- package/dist/{cli-jezx9b9c.js → cli-ppm4975n.js} +9 -3
- package/dist/{cli-3z634pqe.js → cli-q88st9aj.js} +2 -2
- package/dist/{cli-3sysyjve.js → cli-r2q6pevn.js} +142 -49
- package/dist/{cli-07jd68a1.js → cli-re8z5p8p.js} +1 -1
- package/dist/{cli-qgd4sqz5.js → cli-s14agm5s.js} +121 -14
- package/dist/{cli-svbrpf5q.js → cli-sjaae6ff.js} +9872 -424
- package/dist/{cli-73gq0zxn.js → cli-vwm416m6.js} +13 -13
- package/dist/{cli-4wtq5jjv.js → cli-wbyk465z.js} +1 -1
- package/dist/{cli-3apbzgyk.js → cli-z61ykvam.js} +19 -14
- package/dist/{cli-e06a8a7y.js → cli-zkxymas5.js} +4 -3
- package/dist/cli.js +26 -25
- package/dist/{codex-adapter-jyw7a91z.js → codex-adapter-jjjk8w4m.js} +8 -9
- package/dist/{codex-hook-hfrt9shr.js → codex-hook-8jexc34x.js} +3 -3
- package/dist/{codex-session-runner-bmngj0s5.js → codex-session-runner-nd6ayxkx.js} +8 -9
- package/dist/{commands-n755g391.js → commands-9v4psyq1.js} +5 -5
- package/dist/{db-s1cgh5gv.js → db-wnt58tpe.js} +5 -5
- package/dist/dispatcher-05jh1aqk.js +74 -0
- package/dist/{e2b-p04vmtx0.js → e2b-413b8t4v.js} +1 -1
- package/dist/{google-generative-ai-tsskx7te.js → google-generative-ai-ndjmj5fa.js} +7 -7
- package/dist/{google-vertex-ppw8ydpa.js → google-vertex-fr665tqs.js} +10 -10
- package/dist/handlers-tw90pkh5.js +65 -0
- package/dist/{hook-jf0xe8zh.js → hook-123e2scb.js} +11 -12
- package/dist/{hook-h9cqrm8q.js → hook-sxe3r349.js} +11 -12
- package/dist/{http-nc6pydw5.js → http-ecsar8ez.js} +1442 -581
- package/dist/{index-ezxkds2n.js → index-16js521z.js} +335 -107
- package/dist/{index-769nc012.js → index-hw26vj40.js} +25 -19
- package/dist/{index-0wdy6gwc.js → index-nnrbpcjp.js} +25 -17
- package/dist/index-ymn7em0p.js +25 -0
- package/dist/{index-x1zcj09z.js → index-zj518gf9.js} +26 -20
- package/dist/{keepalive-nesm3xb6.js → keepalive-asqtcm4y.js} +7 -7
- package/dist/{lead-bwy6t2za.js → lead-xbfr7fvt.js} +40 -48
- package/dist/{maintenance-8w393zf0.js → maintenance-qma55qvw.js} +9 -9
- package/dist/{oauth-refresh-sweep-sh8xjy08.js → oauth-refresh-sweep-3s04c6qw.js} +6 -6
- package/dist/{onboard-25ps29h1.js → onboard-drskd386.js} +13 -4
- package/dist/{openai-codex-responses-g7w48y30.js → openai-codex-responses-wy6a43wd.js} +13 -13
- package/dist/{openai-responses-nmeyptke.js → openai-responses-6xwf7bzg.js} +19 -19
- package/dist/{opencode-adapter-dce4wskd.js → opencode-adapter-28axqa09.js} +4 -4
- package/dist/{otel-impl-b93gxnz5.js → otel-impl-943c96er.js} +1 -1
- package/dist/{pi-mono-adapter-zvshk5cj.js → pi-mono-adapter-jcqt71cg.js} +17 -19
- package/dist/{pricing-refresh-5v56x7gb.js → pricing-refresh-mx51az05.js} +7 -7
- package/dist/{rbac-roles-x55cnezn.js → rbac-roles-78baecqd.js} +13 -9
- package/dist/{rbac-roles-0p0s6pbj.js → rbac-roles-wcmrg6sv.js} +7 -7
- package/dist/{render-v2-hyd45w8b.js → render-v2-8zjntgf5.js} +7 -7
- package/dist/{seed-pricing-49gbgn96.js → seed-pricing-jqjs6cws.js} +6 -6
- package/dist/{setup-qvq64vzq.js → setup-4pks0vfk.js} +2 -2
- package/dist/work-objects-wangt792.js +165 -0
- package/dist/{worker-scj60cxg.js → worker-p2mr422x.js} +40 -48
- package/openapi.json +1365 -268
- package/package.json +4 -3
- package/src/agentmail/handlers.ts +95 -66
- package/src/be/budget-refusal-notify.ts +1 -0
- package/src/be/db/tasks/read.ts +3 -0
- package/src/be/db/tasks/write.ts +13 -1
- package/src/be/db-client.ts +5 -0
- package/src/be/db.ts +15 -5
- package/src/be/extensions/db.ts +461 -0
- package/src/be/extensions/validate.ts +107 -0
- package/src/be/memory/boot-reembed.ts +17 -12
- package/src/be/memory/index.ts +12 -0
- package/src/be/memory/providers/openai-embedding.ts +4 -0
- package/src/be/memory/types.ts +2 -0
- package/src/be/migrations/153_deferred_task_wait_members.sql +25 -0
- package/src/be/migrations/154_routing_source.sql +3 -0
- package/src/be/migrations/155_extensions.sql +66 -0
- package/src/be/migrations/156_inbox_item_state_read_at.sql +7 -0
- package/src/be/rbac-roles.ts +3 -2
- package/src/be/scripts/typecheck.ts +59 -41
- package/src/be/seed/schedules-seeder.ts +25 -2
- package/src/be/seed/workflows-seeder.ts +17 -0
- package/src/be/seed-scripts/catalog/delegate.ts +3 -1
- package/src/be/seed-skills/bundled-files.generated.json +1 -1
- package/src/be/seed-skills/index.ts +7 -0
- package/src/be/steering.ts +1 -0
- package/src/be/swarm-config-guard.ts +3 -1
- package/src/commands/credential-refresh.ts +70 -0
- package/src/commands/onboard/compose-generator.ts +14 -2
- package/src/commands/onboard/env-generator.ts +7 -0
- package/src/commands/provider-credentials.ts +2 -25
- package/src/commands/runner.ts +24 -57
- package/src/extensions/apply-task-create.ts +135 -0
- package/src/extensions/bundle-path.ts +10 -0
- package/src/extensions/contract-runtime.ts +7 -0
- package/src/extensions/contract-types.generated.ts +4 -0
- package/src/extensions/contract.ts +270 -0
- package/src/extensions/ctx.ts +186 -0
- package/src/extensions/dispatcher.ts +479 -0
- package/src/extensions/identity.ts +44 -0
- package/src/extensions/lifecycle.ts +281 -0
- package/src/extensions/loader.ts +207 -0
- package/src/extensions/post-bridge.ts +161 -0
- package/src/github/handlers.ts +185 -140
- package/src/gitlab/handlers.ts +97 -77
- package/src/heartbeat/heartbeat.ts +155 -83
- package/src/http/agents.ts +1 -1
- package/src/http/all-routes.ts +1 -0
- package/src/http/approval-requests.ts +1 -0
- package/src/http/apps.ts +14 -9
- package/src/http/config.ts +4 -1
- package/src/http/core.ts +38 -2
- package/src/http/extensions.ts +547 -0
- package/src/http/inbox-state.ts +2 -0
- package/src/http/index.ts +41 -3
- package/src/http/mcp-bridge.ts +96 -62
- package/src/http/mcp.ts +1 -1
- package/src/http/script-runs.ts +5 -0
- package/src/http/status.ts +6 -4
- package/src/http/tasks.ts +51 -30
- package/src/integrations/kapso/inbound.ts +15 -10
- package/src/jira/sync.ts +26 -16
- package/src/linear/sync.ts +26 -16
- package/src/prompts/session-templates.ts +18 -13
- package/src/providers/claude-adapter.ts +21 -8
- package/src/rbac/admission.ts +1 -1
- package/src/rbac/elevated-agents.ts +24 -0
- package/src/rbac/legacy-policy.ts +35 -6
- package/src/rbac/permissions.ts +8 -0
- package/src/scheduler/deferred-task-waits.ts +138 -27
- package/src/scheduler/schedule-task.ts +54 -24
- package/src/scheduler/scheduler.ts +108 -125
- package/src/scripts-runtime/executors/native.ts +5 -1
- package/src/scripts-runtime/executors/types.ts +5 -0
- package/src/scripts-runtime/import-allowlist.ts +79 -19
- package/src/scripts-runtime/sdk-allowlist.ts +4 -0
- package/src/scripts-runtime/swarm-config.ts +8 -0
- package/src/scripts-runtime/swarm-sdk.ts +3 -0
- package/src/scripts-runtime/types/stdlib.d.ts +16 -3
- package/src/scripts-runtime/types/swarm-extension.d.ts +562 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +16 -3
- package/src/server.ts +6 -0
- package/src/slack/HEURISTICS.md +3 -1
- package/src/slack/actions.ts +21 -16
- package/src/slack/app.ts +2 -0
- package/src/slack/assistant.ts +4 -12
- package/src/slack/blocks.ts +1 -1
- package/src/slack/handlers.ts +107 -19
- package/src/slack/inbound-files.ts +9 -5
- package/src/slack/render-v2.ts +188 -38
- package/src/slack/router.ts +13 -3
- package/src/slack/steering.ts +3 -3
- package/src/slack/thread-buffer.ts +66 -23
- package/src/slack/work-objects.test.ts +252 -0
- package/src/slack/work-objects.ts +118 -0
- package/src/tasks/errors.ts +9 -0
- package/src/tasks/sibling-awareness.ts +35 -1
- package/src/tasks/worker-follow-up.ts +67 -11
- package/src/tests/acp-adapter.test.ts +10 -10
- package/src/tests/agent-fs-live-url.test.ts +40 -0
- package/src/tests/agent-fs-provision-seeder.test.ts +12 -12
- package/src/tests/api-key-tracking.test.ts +18 -18
- package/src/tests/api-key.test.ts +4 -2
- package/src/tests/apps-rbac.test.ts +1 -1
- package/src/tests/apps-spike5.test.ts +1 -1
- package/src/tests/apps-sync-engine.test.ts +8 -8
- package/src/tests/apps-user-config.test.ts +1 -1
- package/src/tests/artifact-sdk.test.ts +8 -8
- package/src/tests/asset-key-mcp.test.ts +2 -0
- package/src/tests/automation-preflight-alert.test.ts +1 -1
- package/src/tests/base-prompt.test.ts +7 -6
- package/src/tests/budget-user-scope.test.ts +1 -1
- package/src/tests/budgets-routes.test.ts +1 -1
- package/src/tests/claude-adapter-binary.test.ts +22 -40
- package/src/tests/claude-adapter-otel.test.ts +13 -7
- package/src/tests/claude-adapter.test.ts +8 -6
- package/src/tests/claude-hook-credentials.test.ts +3 -3
- package/src/tests/claude-managed-adapter.test.ts +4 -4
- package/src/tests/claude-managed-setup.test.ts +3 -3
- package/src/tests/claude-sdk-lifecycle.test.ts +1 -1
- package/src/tests/claude-sdk-transport.test.ts +6 -6
- package/src/tests/claude-stop-hook.test.ts +8 -8
- package/src/tests/claude-worker-parity.test.ts +3 -3
- package/src/tests/codex-adapter-pool-auth-revalidate.test.ts +5 -5
- package/src/tests/codex-adapter.test.ts +7 -5
- package/src/tests/codex-app-server.test.ts +2 -2
- package/src/tests/codex-login.test.ts +15 -15
- package/src/tests/codex-oauth-credential-info.test.ts +4 -4
- package/src/tests/codex-oauth-keep-warm.test.ts +4 -4
- package/src/tests/codex-oauth-refresh-lock.test.ts +1 -1
- package/src/tests/codex-oauth-storage.test.ts +7 -7
- package/src/tests/codex-pool.test.ts +1 -1
- package/src/tests/codex-steering-hook.test.ts +1 -1
- package/src/tests/codex-swarm-events.test.ts +1 -1
- package/src/tests/config-api-only-keys.test.ts +1 -1
- package/src/tests/config-session-auth.test.ts +2 -2
- package/src/tests/connection-embedded-auth.test.ts +20 -18
- package/src/tests/core-auth.test.ts +1 -1
- package/src/tests/create-page-tool.test.ts +3 -3
- package/src/tests/credential-refresh.test.ts +115 -0
- package/src/tests/credential-wait.test.ts +2 -2
- package/src/tests/credentials.test.ts +6 -4
- package/src/tests/db-queries-oauth.test.ts +40 -40
- package/src/tests/defer-task.test.ts +73 -8
- package/src/tests/deferred-task-wait-migration.test.ts +64 -0
- package/src/tests/deferred-task-wake.test.ts +349 -8
- package/src/tests/devin-adapter.test.ts +1 -1
- package/src/tests/devin-api.test.ts +1 -1
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/e2b-dispatch.test.ts +21 -19
- package/src/tests/e2e-claude-transport.test.ts +3 -3
- package/src/tests/e2e-redact.test.ts +6 -6
- package/src/tests/ensure-token.test.ts +68 -68
- package/src/tests/entrypoint-api-readiness.test.ts +1 -1
- package/src/tests/entrypoint-codex-oauth-seed.test.ts +13 -13
- package/src/tests/env-flag.test.ts +1 -0
- package/src/tests/events-http.test.ts +1 -1
- package/src/tests/extensions-db.test.ts +216 -0
- package/src/tests/extensions-dispatcher.test.ts +275 -0
- package/src/tests/extensions-example-heartbeat-remediate.test.ts +69 -0
- package/src/tests/extensions-example-mcp-install.test.ts +63 -0
- package/src/tests/extensions-example-no-exclamation-marks.test.ts +72 -0
- package/src/tests/extensions-example-rewrite-and-suppress.test.ts +70 -0
- package/src/tests/extensions-example-route-slack-channel.test.ts +217 -0
- package/src/tests/extensions-heartbeat.test.ts +291 -0
- package/src/tests/extensions-http.test.ts +431 -0
- package/src/tests/extensions-lead-equivalence.test.ts +49 -0
- package/src/tests/extensions-lifecycle.test.ts +209 -0
- package/src/tests/extensions-loader.test.ts +95 -0
- package/src/tests/extensions-mcp-tools.test.ts +245 -0
- package/src/tests/extensions-pre-task.test.ts +487 -0
- package/src/tests/extensions-run-context.test.ts +41 -0
- package/src/tests/extensions-tool-call.test.ts +288 -0
- package/src/tests/extensions-validate.test.ts +196 -0
- package/src/tests/fixtures/extensions/bad-import/hooks.ts +8 -0
- package/src/tests/fixtures/extensions/bad-import/manifest.json +9 -0
- package/src/tests/fixtures/extensions/bad-return-shape/hooks.ts +10 -0
- package/src/tests/fixtures/extensions/bad-return-shape/manifest.json +9 -0
- package/src/tests/fixtures/extensions/block-tasks-from-source/hooks.ts +22 -0
- package/src/tests/fixtures/extensions/block-tasks-from-source/manifest.json +9 -0
- package/src/tests/fixtures/extensions/ignore-channel/hooks.ts +22 -0
- package/src/tests/fixtures/extensions/ignore-channel/manifest.json +9 -0
- package/src/tests/fixtures/extensions/load.ts +10 -0
- package/src/tests/fixtures/extensions/minimal/hooks.ts +7 -0
- package/src/tests/fixtures/extensions/minimal/manifest.json +9 -0
- package/src/tests/fixtures/extensions/never-fail-long-tasks/hooks.ts +11 -0
- package/src/tests/fixtures/extensions/never-fail-long-tasks/manifest.json +9 -0
- package/src/tests/fixtures/extensions/no-exclamation-marks/hooks.ts +23 -0
- package/src/tests/fixtures/extensions/no-exclamation-marks/manifest.json +9 -0
- package/src/tests/fixtures/extensions/post-logger/hooks.ts +9 -0
- package/src/tests/fixtures/extensions/post-logger/manifest.json +9 -0
- package/src/tests/fixtures/extensions/priority-a/hooks.ts +7 -0
- package/src/tests/fixtures/extensions/priority-a/manifest.json +9 -0
- package/src/tests/fixtures/extensions/priority-b/hooks.ts +7 -0
- package/src/tests/fixtures/extensions/priority-b/manifest.json +9 -0
- package/src/tests/fixtures/extensions/record-only-on-tag/hooks.ts +16 -0
- package/src/tests/fixtures/extensions/record-only-on-tag/manifest.json +9 -0
- package/src/tests/fixtures/extensions/reserved-assets/hooks.ts +5 -0
- package/src/tests/fixtures/extensions/reserved-assets/manifest.json +10 -0
- package/src/tests/fixtures/extensions/rewrite-progress-prefix/hooks.ts +22 -0
- package/src/tests/fixtures/extensions/rewrite-progress-prefix/manifest.json +9 -0
- package/src/tests/fixtures/extensions/rewrite-task-priority/hooks.ts +7 -0
- package/src/tests/fixtures/extensions/rewrite-task-priority/manifest.json +9 -0
- package/src/tests/fixtures/extensions/route-channel-to-agent/hooks.ts +26 -0
- package/src/tests/fixtures/extensions/route-channel-to-agent/manifest.json +9 -0
- package/src/tests/fixtures/extensions/slow/hooks.ts +10 -0
- package/src/tests/fixtures/extensions/slow/manifest.json +9 -0
- package/src/tests/fixtures/extensions/suppress-lead-follow-up/hooks.ts +12 -0
- package/src/tests/fixtures/extensions/suppress-lead-follow-up/manifest.json +9 -0
- package/src/tests/fixtures/extensions/throws/hooks.ts +12 -0
- package/src/tests/fixtures/extensions/throws/manifest.json +9 -0
- package/src/tests/fixtures/extensions/worker-runtime/hooks.ts +5 -0
- package/src/tests/fixtures/extensions/worker-runtime/manifest.json +9 -0
- package/src/tests/fs-routes.test.ts +1 -1
- package/src/tests/gitlab-auth.test.ts +10 -10
- package/src/tests/harness-provider-resolution.test.ts +8 -5
- package/src/tests/heartbeat-reroute-decision.test.ts +1 -0
- package/src/tests/helper-credentials.test.ts +137 -0
- package/src/tests/http/context-routes.test.ts +2 -2
- package/src/tests/http-api-integration.test.ts +5 -2
- package/src/tests/http-users.test.ts +1 -1
- package/src/tests/identity-refresh.test.ts +2 -2
- package/src/tests/internal-ai/complete-structured.test.ts +2 -2
- package/src/tests/internal-ai/credentials.test.ts +19 -17
- package/src/tests/jira-metadata.test.ts +1 -1
- package/src/tests/jira-oauth.test.ts +5 -5
- package/src/tests/jira-sync.test.ts +1 -1
- package/src/tests/jira-webhook-lifecycle.test.ts +3 -3
- package/src/tests/jira-webhook.test.ts +2 -2
- package/src/tests/kapso-inbound.test.ts +2 -2
- package/src/tests/kv-http.test.ts +1 -1
- package/src/tests/kv-namespace-resolution.test.ts +1 -1
- package/src/tests/kv-page-proxy.test.ts +2 -2
- package/src/tests/launch-password-rejection.test.ts +2 -2
- package/src/tests/linear-webhook.test.ts +1 -1
- package/src/tests/mcp-oauth-dcr-reuse.test.ts +6 -6
- package/src/tests/mcp-oauth-ensure-token.test.ts +28 -22
- package/src/tests/mcp-oauth-manual-client.test.ts +16 -12
- package/src/tests/mcp-oauth-queries.test.ts +35 -32
- package/src/tests/mcp-oauth-resolve-secrets.test.ts +8 -8
- package/src/tests/mcp-oauth-wrapper.test.ts +20 -20
- package/src/tests/mcp-user-route.test.ts +1 -1
- package/src/tests/memory-edges.test.ts +1 -1
- package/src/tests/memory-edit-authorization.test.ts +32 -0
- package/src/tests/memory-rate-endpoint.test.ts +1 -1
- package/src/tests/memory-rate-tool.test.ts +2 -2
- package/src/tests/memory-rater-e2e.test.ts +1 -1
- package/src/tests/memory-rater-llm-summarizer.test.ts +3 -3
- package/src/tests/memory-rater-llm.test.ts +1 -1
- package/src/tests/model-control.test.ts +3 -0
- package/src/tests/multi-runtime-enabled-stats.test.ts +2 -2
- package/src/tests/multi-runtime-registration.test.ts +111 -17
- package/src/tests/oauth-access-token-tool.test.ts +18 -18
- package/src/tests/oauth-callback-flow.test.ts +8 -8
- package/src/tests/oauth-credential-bindings.test.ts +29 -27
- package/src/tests/oauth-keepalive.test.ts +10 -10
- package/src/tests/oauth-presets.test.ts +13 -5
- package/src/tests/oauth-refresh-failure.test.ts +9 -9
- package/src/tests/oauth-refresh-sweep.test.ts +20 -13
- package/src/tests/oauth-wrapper.test.ts +1 -1
- package/src/tests/onboard-compose.test.ts +47 -3
- package/src/tests/onboard-env.test.ts +26 -26
- package/src/tests/onboard-non-interactive.test.ts +4 -4
- package/src/tests/opencode-adapter.test.ts +1 -1
- package/src/tests/opencode-plugin.test.ts +2 -2
- package/src/tests/otel-session-cost-metrics.test.ts +1 -1
- package/src/tests/page-proxy-authed.test.ts +2 -2
- package/src/tests/page-proxy.test.ts +2 -2
- package/src/tests/page-session.test.ts +7 -7
- package/src/tests/page-viewer-auth.test.ts +2 -2
- package/src/tests/pages-authed-mode.test.ts +1 -1
- package/src/tests/pages-http.test.ts +1 -1
- package/src/tests/pages-password-mode.test.ts +1 -1
- package/src/tests/pages-public-authed-401.test.ts +1 -1
- package/src/tests/pages-public-html.test.ts +2 -2
- package/src/tests/pages-view-count.test.ts +1 -1
- package/src/tests/pi-mono-adapter.test.ts +10 -10
- package/src/tests/pi-mono-extension.test.ts +2 -2
- package/src/tests/preload.ts +5 -0
- package/src/tests/pricing-routes.test.ts +1 -1
- package/src/tests/profile-sync-warning.test.ts +8 -8
- package/src/tests/profile-sync.test.ts +8 -8
- package/src/tests/prompt-template-session.test.ts +36 -5
- package/src/tests/provider-process-group.test.ts +95 -4
- package/src/tests/providers/pi-cost.test.ts +1 -1
- package/src/tests/queue-stall-alarm.test.ts +1 -1
- package/src/tests/rbac-admission.test.ts +19 -4
- package/src/tests/rbac-charact-http.test.ts +1 -1
- package/src/tests/rbac-e2e-helpers.ts +1 -1
- package/src/tests/rbac-engine.test.ts +39 -0
- package/src/tests/realtime-room-auth.test.ts +1 -1
- package/src/tests/realtime-room-surfaces.test.ts +1 -1
- package/src/tests/reload-config.test.ts +45 -1
- package/src/tests/routing-affinity-inventory.test.ts +1 -1
- package/src/tests/routing-reason-contract.test.ts +76 -3
- package/src/tests/routing-reason-inventory.test.ts +11 -6
- package/src/tests/routing-source.test.ts +53 -0
- package/src/tests/runner-agent-fs-provision-retry.test.ts +1 -1
- package/src/tests/runner-context-preamble.test.ts +1 -1
- package/src/tests/runner-deployment-resume.test.ts +1 -1
- package/src/tests/runner-fallback-output.test.ts +1 -1
- package/src/tests/runner-identity-refresh.test.ts +1 -1
- package/src/tests/runner-skills-refresh.test.ts +1 -1
- package/src/tests/schedule-target-type.test.ts +3 -3
- package/src/tests/scheduled-tasks.test.ts +14 -5
- package/src/tests/scheduler-occurrence.test.ts +347 -0
- package/src/tests/script-apis-mcp.test.ts +1 -1
- package/src/tests/script-connections-http.test.ts +61 -58
- package/src/tests/script-connections-mcp.test.ts +1 -1
- package/src/tests/script-connections.test.ts +3 -1
- package/src/tests/script-runs-http.test.ts +15 -7
- package/src/tests/script-workflows-runtime-e2e.test.ts +1 -1
- package/src/tests/scripts-boot-reembed.test.ts +4 -0
- package/src/tests/scripts-embeddings.test.ts +4 -0
- package/src/tests/scripts-external-api.test.ts +1 -1
- package/src/tests/scripts-http.test.ts +1 -1
- package/src/tests/scripts-import-allowlist.test.ts +44 -1
- package/src/tests/scripts-mcp-e2e.test.ts +81 -1
- package/src/tests/scripts-only-gating.test.ts +4 -2
- package/src/tests/scripts-retention.test.ts +1 -1
- package/src/tests/scripts-runtime-identity.test.ts +2 -2
- package/src/tests/scripts-runtime-secret-egress.test.ts +1 -1
- package/src/tests/scripts-runtime.test.ts +6 -4
- package/src/tests/scripts-typecheck.test.ts +21 -0
- package/src/tests/sdk-allowlist.test.ts +1 -1
- package/src/tests/secret-scrubber.test.ts +38 -31
- package/src/tests/seed-registry.test.ts +46 -8
- package/src/tests/seed-schedules.test.ts +44 -9
- package/src/tests/seed-workflows.test.ts +21 -9
- package/src/tests/send-task-output-schema.test.ts +9 -1
- package/src/tests/send-task-requested-by.test.ts +1 -0
- package/src/tests/send-task-slack-routing-guard.test.ts +2 -0
- package/src/tests/session-costs-codex-recompute.test.ts +1 -1
- package/src/tests/session-costs-golden.test.ts +2 -2
- package/src/tests/session-costs-model-key-normalize.test.ts +2 -2
- package/src/tests/session-costs-recompute-all-providers.test.ts +1 -1
- package/src/tests/session-tokens.test.ts +1 -1
- package/src/tests/shutdown-error-scrubbing.test.ts +1 -1
- package/src/tests/slack-blocks.test.ts +12 -1
- package/src/tests/slack-file-tools-attachments.test.ts +1 -1
- package/src/tests/slack-inbound-files.test.ts +222 -86
- package/src/tests/slack-reaction-config.test.ts +5 -4
- package/src/tests/slack-read-boundaries.test.ts +1 -1
- package/src/tests/slack-render-v2.test.ts +196 -4
- package/src/tests/slack-router.test.ts +17 -2
- package/src/tests/slack-steering.test.ts +19 -0
- package/src/tests/status.test.ts +19 -7
- package/src/tests/steering-core.test.ts +1 -1
- package/src/tests/steering-enabled.test.ts +6 -8
- package/src/tests/steering-transport.test.ts +4 -4
- package/src/tests/store-progress-output-guidance.test.ts +34 -0
- package/src/tests/swarm-tool-result-gate.test.ts +11 -11
- package/src/tests/swarm-x-tool.test.ts +7 -5
- package/src/tests/sync-ai-toolbox-skills.test.ts +19 -0
- package/src/tests/task-supersede-resume.test.ts +1 -1
- package/src/tests/task-tool-preload-mcp.test.ts +4 -1
- package/src/tests/task-tools-ctx.test.ts +2 -0
- package/src/tests/tasks-requested-by-filter.test.ts +2 -2
- package/src/tests/telemetry-init.test.ts +11 -3
- package/src/tests/tool-annotations.test.ts +1 -1
- package/src/tests/tool-output-agent-id.test.ts +1 -0
- package/src/tests/tool-registrar-no-input.test.ts +42 -1
- package/src/tests/tracker-fold.test.ts +7 -7
- package/src/tests/tracker-tools.test.ts +3 -3
- package/src/tests/ui-followup-lead-delegation.test.ts +1 -0
- package/src/tests/unified-oauth-migration.test.ts +5 -5
- package/src/tests/user-token-rest-auth.test.ts +1 -1
- package/src/tests/user-token-routes.test.ts +1 -1
- package/src/tests/whoami-route.test.ts +1 -1
- package/src/tests/workflow-agent-task.test.ts +15 -13
- package/src/tests/workflow-ctx.test.ts +1 -1
- package/src/tests/workflow-e2e.test.ts +1 -1
- package/src/tests/workflow-engine-v2.test.ts +3 -1
- package/src/tests/workflow-executors.test.ts +2 -2
- package/src/tests/workflow-http-v2.test.ts +8 -6
- package/src/tests/workflow-input-redaction.test.ts +7 -5
- package/src/tests/workflow-integration-io.test.ts +2 -2
- package/src/tests/workflow-llm.test.ts +5 -5
- package/src/tests/workflow-registry.test.ts +1 -1
- package/src/tests/workflow-swarm-script.test.ts +1 -1
- package/src/tests/workflow-triggers-v2.test.ts +23 -21
- package/src/tests/x-composio.test.ts +9 -7
- package/src/tests/x402-client.test.ts +1 -1
- package/src/tests/x402-config.test.ts +28 -41
- package/src/tools/defer-task.ts +156 -24
- package/src/tools/extension-common.ts +84 -0
- package/src/tools/extension-install.ts +65 -0
- package/src/tools/extension-list.ts +66 -0
- package/src/tools/script-common.ts +43 -18
- package/src/tools/send-task.ts +87 -105
- package/src/tools/skills/skill-publish.ts +1 -0
- package/src/tools/store-progress.ts +1 -1
- package/src/tools/task-action.ts +43 -22
- package/src/tools/task-tool-ctx.ts +2 -0
- package/src/tools/templates.ts +2 -1
- package/src/tools/tool-config.ts +3 -1
- package/src/tools/utils.ts +140 -16
- package/src/types.ts +144 -0
- package/src/utils/constants.ts +7 -1
- package/src/utils/credentials.test.ts +16 -16
- package/src/utils/multi-runtime.ts +2 -2
- package/src/utils/steering-enabled.ts +4 -4
- package/src/workflows/executors/agent-task.ts +18 -3
- package/templates/ai-toolbox.manifest.json +7 -1
- package/templates/extensions/README.md +17 -0
- package/templates/extensions/notify-on-complete/README.md +11 -0
- package/templates/extensions/notify-on-complete/hooks.ts +61 -0
- package/templates/extensions/notify-on-complete/manifest.json +7 -0
- package/templates/extensions/require-ticket-ref/README.md +11 -0
- package/templates/extensions/require-ticket-ref/hooks.ts +30 -0
- package/templates/extensions/require-ticket-ref/manifest.json +7 -0
- package/templates/extensions/require-verification-note/README.md +11 -0
- package/templates/extensions/require-verification-note/hooks.ts +32 -0
- package/templates/extensions/require-verification-note/manifest.json +7 -0
- package/templates/schedules/daily-hn-briefing/config.json +1 -1
- package/templates/schedules/gtm-weekly-review/config.json +1 -1
- package/templates/schedules/weekly-code-health-reports/config.json +1 -1
- package/templates/schedules/weekly-dependabot-triage/config.json +1 -1
- package/templates/schedules/weekly-dora-metrics/config.json +1 -1
- package/templates/schedules/weekly-harness-upgrade-check/config.json +1 -1
- package/templates/skills/delegate-work/files/scripts/codex-exec.sh +1 -1
- package/templates/skills/scheduling/content.md +2 -0
- package/templates/skills/serply-search/SKILL.md +95 -0
- package/templates/skills/serply-search/config.json +13 -0
- package/templates/skills/serply-search/content.md +90 -0
- package/templates/skills/swarm-extensions/config.json +14 -0
- package/templates/skills/swarm-extensions/content.md +120 -0
- package/templates/skills/swarm-scripts/SKILL.md +2 -2
- package/templates/skills/swarm-scripts/content.md +2 -2
- package/templates/workflows/claude-code-changelog-watch/config.json +1 -1
- package/templates/workflows/gsc-topic-miner/config.json +1 -1
- package/templates/workflows/linear-drain-loop/config.json +1 -1
- package/templates/workflows/llm-safe-release-context/config.json +1 -1
- package/templates/workflows/ralph-loop/config.json +1 -1
- package/dist/cli-7fehefdf.js +0 -562
- package/dist/cli-ayvy5kde.js +0 -164
- package/dist/cli-ddfw1mc4.js +0 -4322
- package/dist/cli-hzqmgcm2.js +0 -3066
- package/dist/cli-j4d4f6hv.js +0 -338
- package/dist/cli-qgm2pfpq.js +0 -6377
- package/dist/cli-sdzq01t9.js +0 -823
- package/dist/cli-xvkmedgn.js +0 -332
- package/dist/cli-y3pq4v9g.js +0 -664
- package/dist/{anthropic-messages-vs2rabzp.js → anthropic-messages-2y8nktar.js} +19 -19
- package/dist/{claude-managed-setup-f5pmsyrp.js → claude-managed-setup-48dwg9bv.js} +3 -3
- package/dist/{cli-81r683bv.js → cli-ch38q5z4.js} +0 -0
- package/dist/{cli-6664w7y4.js → cli-kj76p1ez.js} +8 -8
- package/dist/{cli-c71mg51n.js → cli-r2fpe7cx.js} +5 -5
- package/dist/{cli-4xz3pmrq.js → cli-y6x0gqtg.js} +16 -16
- package/dist/{index-aa3sa6s8.js → index-2wsdsnmm.js} +1 -1
- package/dist/{mistral-conversations-4ryk7h83.js → mistral-conversations-qhr84ph3.js} +9 -9
- package/dist/{openai-completions-0bqmsajw.js → openai-completions-32rdwmze.js} +19 -19
- package/dist/{openrouter-images-qx18fyp8.js → openrouter-images-9f9ek4rw.js} +3 -3
- package/dist/{pi-messages-79jvzz1d.js → pi-messages-b1mbr6gp.js} +4 -4
package/src/tools/defer-task.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
getAgentById,
|
|
10
10
|
getDbClient,
|
|
11
11
|
getTaskById,
|
|
12
|
+
getUserById,
|
|
12
13
|
updateAgentStatusFromCapacity,
|
|
13
14
|
} from "@/be/db";
|
|
14
15
|
import { reconcileDeferredTaskWaits } from "@/scheduler/deferred-task-waits";
|
|
@@ -17,6 +18,7 @@ import { getTaskOutputValidationError } from "@/tasks/terminal-result-guard";
|
|
|
17
18
|
import { assertOwnsTask, ownerCtx } from "@/tools/task-tool-ctx";
|
|
18
19
|
import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
|
|
19
20
|
import { isTerminalTaskStatus } from "@/types";
|
|
21
|
+
import { getAppUrl } from "@/utils/constants";
|
|
20
22
|
|
|
21
23
|
/** Thrown inside the transaction to abort and roll back the schedule INSERT. */
|
|
22
24
|
class DeferAbortedError extends Error {}
|
|
@@ -27,6 +29,109 @@ function renderChecks(checks?: string[]): string {
|
|
|
27
29
|
return `\n\nChecks:\n${checks.map((c) => `- ${c}`).join("\n")}`;
|
|
28
30
|
}
|
|
29
31
|
|
|
32
|
+
/** Date/time parts of `date` as rendered in IANA zone `tz`. */
|
|
33
|
+
function partsInTz(date: Date, tz: string) {
|
|
34
|
+
const fmt = new Intl.DateTimeFormat("en-US", {
|
|
35
|
+
timeZone: tz,
|
|
36
|
+
year: "numeric",
|
|
37
|
+
month: "2-digit",
|
|
38
|
+
day: "2-digit",
|
|
39
|
+
hour: "2-digit",
|
|
40
|
+
minute: "2-digit",
|
|
41
|
+
second: "2-digit",
|
|
42
|
+
hour12: false,
|
|
43
|
+
});
|
|
44
|
+
const parts = Object.fromEntries(fmt.formatToParts(date).map((p) => [p.type, p.value]));
|
|
45
|
+
// Some locales render midnight as "24" under hour12: false.
|
|
46
|
+
const hour = parts.hour === "24" ? "00" : parts.hour;
|
|
47
|
+
return {
|
|
48
|
+
year: parts.year!,
|
|
49
|
+
month: parts.month!,
|
|
50
|
+
day: parts.day!,
|
|
51
|
+
hour: hour!,
|
|
52
|
+
minute: parts.minute!,
|
|
53
|
+
second: parts.second!,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* "today"/"tomorrow" or "MM-dd" plus "HH:MM:ss", both computed in `tz`.
|
|
59
|
+
* Falls back to UTC (parts still computed correctly) if `tz` is not a valid
|
|
60
|
+
* IANA zone — the caller decides whether to label the fallback.
|
|
61
|
+
*/
|
|
62
|
+
function formatDeferralDateTime(
|
|
63
|
+
target: Date,
|
|
64
|
+
now: Date,
|
|
65
|
+
tz: string,
|
|
66
|
+
): { date: string; time: string } {
|
|
67
|
+
let zone = tz;
|
|
68
|
+
try {
|
|
69
|
+
Intl.DateTimeFormat("en-US", { timeZone: zone });
|
|
70
|
+
} catch {
|
|
71
|
+
zone = "UTC";
|
|
72
|
+
}
|
|
73
|
+
const t = partsInTz(target, zone);
|
|
74
|
+
const time = `${t.hour}:${t.minute}:${t.second}`;
|
|
75
|
+
const n = partsInTz(now, zone);
|
|
76
|
+
if (t.year === n.year && t.month === n.month && t.day === n.day) return { date: "today", time };
|
|
77
|
+
const tm = partsInTz(new Date(now.getTime() + 86_400_000), zone);
|
|
78
|
+
if (t.year === tm.year && t.month === tm.month && t.day === tm.day) {
|
|
79
|
+
return { date: "tomorrow", time };
|
|
80
|
+
}
|
|
81
|
+
return { date: `${t.month}-${t.day}`, time };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Resolve the requesting human's IANA timezone from `users.timezone` via
|
|
86
|
+
* `task.requestedByUserId`. We do not currently capture a Slack profile `tz`
|
|
87
|
+
* anywhere in the ingest path (checked `src/slack/enrich.ts`, the only place
|
|
88
|
+
* that reads `users.info`) — a user only has a timezone here if someone set
|
|
89
|
+
* it explicitly via `manage-user`. Falls back to UTC, flagged as such.
|
|
90
|
+
*/
|
|
91
|
+
async function resolveRequesterTimezone(
|
|
92
|
+
requestedByUserId: string | undefined,
|
|
93
|
+
): Promise<{ tz: string; isFallback: boolean }> {
|
|
94
|
+
if (requestedByUserId) {
|
|
95
|
+
const user = await getUserById(requestedByUserId);
|
|
96
|
+
if (user?.timezone) return { tz: user.timezone, isFallback: false };
|
|
97
|
+
}
|
|
98
|
+
return { tz: "UTC", isFallback: true };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** First line of `note`, `Pending:` prefix stripped, capped to ~100 chars. */
|
|
102
|
+
function renderShortDesc(note: string, max = 100): string {
|
|
103
|
+
const firstLine = (note.split("\n")[0] ?? "").trim().replace(/^pending:\s*/i, "");
|
|
104
|
+
if (firstLine.length <= max) return firstLine;
|
|
105
|
+
return `${firstLine.slice(0, max - 1).trimEnd()}…`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Human-facing deferral text for tasks without an outputSchema — this is what
|
|
110
|
+
* lands verbatim in a human's Slack thread as the task's terminal output.
|
|
111
|
+
* One line: `Deferred until {date} {time} ([shortId](url)) -> {short desc}`.
|
|
112
|
+
* No ISO timestamp, no bare UUID, no checks list, no `Pending:` prefix —
|
|
113
|
+
* those stay in the task log. The `[text](url)` link is plain GFM markdown:
|
|
114
|
+
* `markdownToSlack` (src/slack/blocks.ts) already down-converts it to a
|
|
115
|
+
* readable `text (url)` fallback for Slack, and it reads fine verbatim in
|
|
116
|
+
* the non-Slack task-output row / UI too.
|
|
117
|
+
*/
|
|
118
|
+
function renderHumanFacingDeferral(
|
|
119
|
+
note: string,
|
|
120
|
+
nextRunAt: string,
|
|
121
|
+
scheduleId: string,
|
|
122
|
+
tz: string,
|
|
123
|
+
isFallbackTz: boolean,
|
|
124
|
+
): string {
|
|
125
|
+
const now = new Date();
|
|
126
|
+
const target = new Date(nextRunAt);
|
|
127
|
+
const { date, time } = formatDeferralDateTime(target, now, tz);
|
|
128
|
+
const timeLabel = isFallbackTz ? `${time} UTC` : time;
|
|
129
|
+
const shortId = scheduleId.slice(0, 8);
|
|
130
|
+
const link = `[${shortId}](${getAppUrl()}/schedules/${scheduleId})`;
|
|
131
|
+
const shortDesc = renderShortDesc(note);
|
|
132
|
+
return `Deferred until ${date} ${timeLabel} (${link}) -> ${shortDesc}`;
|
|
133
|
+
}
|
|
134
|
+
|
|
30
135
|
export const registerDeferTaskTool = (server: McpServer) => {
|
|
31
136
|
createToolRegistrar(server)(
|
|
32
137
|
"defer-task",
|
|
@@ -34,7 +139,7 @@ export const registerDeferTaskTool = (server: McpServer) => {
|
|
|
34
139
|
title: "Defer Task",
|
|
35
140
|
annotations: { destructiveHint: false, idempotentHint: false },
|
|
36
141
|
description:
|
|
37
|
-
"Completes this task now with status `completed` and books a wake-up for you. Use when the result needs time: a build, a deploy, a reply. The task reaches its final state on this call; the lead sees your summary as its output unless the task has an outputSchema. For a task with an outputSchema, provide output as a JSON string matching that schema; it is stored verbatim as terminal output, while deferral details remain visible in the task log. A one-off schedule wakes you up later with a child task that carries this task as its parent. Optionally provide wakeOn to wake early
|
|
142
|
+
"Completes this task now with status `completed` and books a wake-up for you. Use when the result needs time: a build, a deploy, a reply. The task reaches its final state on this call; the lead sees your summary as its output unless the task has an outputSchema. For a task with an outputSchema, provide output as a JSON string matching that schema; it is stored verbatim as terminal output, while deferral details remain visible in the task log. A one-off schedule wakes you up later with a child task that carries this task as its parent. Optionally provide wakeOn with taskId or taskIds to wake early on task outcomes; mode defaults to all, or use any for the first match. delayMs or runAt remains required as the ceiling for the whole set. Provide delayMs or runAt, a summary of what you did, and a note that says what is pending and what to check.",
|
|
38
143
|
inputSchema: z.object({
|
|
39
144
|
taskId: z.string().describe("The ID of the task you are working on."),
|
|
40
145
|
delayMs: z
|
|
@@ -51,12 +156,20 @@ export const registerDeferTaskTool = (server: McpServer) => {
|
|
|
51
156
|
wakeOn: z
|
|
52
157
|
.object({
|
|
53
158
|
event: z.enum(["task.completed", "task.failed", "settled"]),
|
|
54
|
-
taskId: z.string().min(1),
|
|
159
|
+
taskId: z.string().min(1).optional(),
|
|
160
|
+
taskIds: z.array(z.string().min(1)).min(1).optional(),
|
|
161
|
+
mode: z.enum(["all", "any"]).optional(),
|
|
55
162
|
})
|
|
56
163
|
.strict()
|
|
164
|
+
.refine((wake) => (wake.taskId !== undefined) !== (wake.taskIds !== undefined), {
|
|
165
|
+
message: "Provide exactly one of wakeOn.taskId or wakeOn.taskIds.",
|
|
166
|
+
})
|
|
167
|
+
.refine((wake) => !wake.taskIds || new Set(wake.taskIds).size === wake.taskIds.length, {
|
|
168
|
+
message: "wakeOn.taskIds must not contain duplicates.",
|
|
169
|
+
})
|
|
57
170
|
.optional()
|
|
58
171
|
.describe(
|
|
59
|
-
"Wake early on
|
|
172
|
+
"Wake early on a task event. Provide taskId or nonempty, unique taskIds. mode defaults to all (every member must match); any wakes on the first match. settled covers completed, failed, or cancelled. Deferred and superseded members follow their continuations; a superseded member without a resume child holds to the ceiling. Any already-terminal member rejects the request; one delayMs/runAt ceiling is still required for the whole set.",
|
|
60
173
|
),
|
|
61
174
|
summary: z
|
|
62
175
|
.string()
|
|
@@ -144,8 +257,10 @@ export const registerDeferTaskTool = (server: McpServer) => {
|
|
|
144
257
|
}
|
|
145
258
|
const nextRunAt = delayMs ? new Date(Date.now() + delayMs).toISOString() : runAt!;
|
|
146
259
|
|
|
260
|
+
const watchedTaskIds = wakeOn?.taskIds ?? (wakeOn?.taskId ? [wakeOn.taskId] : []);
|
|
261
|
+
const wakeMode = wakeOn?.mode ?? "all";
|
|
147
262
|
const wakeDescription = wakeOn
|
|
148
|
-
? `on ${wakeOn.event} for
|
|
263
|
+
? `on ${wakeOn.event} for ${wakeMode} of tasks ${watchedTaskIds.join(", ")}, or by ${nextRunAt}`
|
|
149
264
|
: `at ${nextRunAt}`;
|
|
150
265
|
const checksBlock = renderChecks(checks);
|
|
151
266
|
const taskTemplate = `Resume task ${taskId}: ${note}${checksBlock}`;
|
|
@@ -163,16 +278,20 @@ export const registerDeferTaskTool = (server: McpServer) => {
|
|
|
163
278
|
if (outputValidationError) return toolErr(outputValidationError);
|
|
164
279
|
}
|
|
165
280
|
|
|
281
|
+
const { tz: requesterTz, isFallback: isFallbackTz } = await resolveRequesterTimezone(
|
|
282
|
+
task.requestedByUserId,
|
|
283
|
+
);
|
|
284
|
+
|
|
166
285
|
try {
|
|
167
286
|
const committed = await getDbClient().transaction(async () => {
|
|
168
|
-
|
|
169
|
-
if (
|
|
287
|
+
for (const watchedId of watchedTaskIds) {
|
|
288
|
+
if (watchedId === taskId)
|
|
170
289
|
throw new DeferAbortedError("Cannot wake on the task being deferred.");
|
|
171
|
-
const watched = await getTaskById(
|
|
172
|
-
if (!watched) throw new DeferAbortedError(`Watched task ${
|
|
290
|
+
const watched = await getTaskById(watchedId);
|
|
291
|
+
if (!watched) throw new DeferAbortedError(`Watched task ${watchedId} not found.`);
|
|
173
292
|
if (isTerminalTaskStatus(watched.status))
|
|
174
293
|
throw new DeferAbortedError(
|
|
175
|
-
`Watched task ${
|
|
294
|
+
`Watched task ${watchedId} is already ${watched.status}; read its result instead of deferring.`,
|
|
176
295
|
);
|
|
177
296
|
}
|
|
178
297
|
const schedule = await createScheduledTask({
|
|
@@ -200,20 +319,35 @@ export const registerDeferTaskTool = (server: McpServer) => {
|
|
|
200
319
|
|
|
201
320
|
if (wakeOn) {
|
|
202
321
|
await getDbClient().run(
|
|
203
|
-
"INSERT INTO deferred_task_waits (scheduleId,
|
|
204
|
-
[schedule.id, wakeOn.
|
|
322
|
+
"INSERT INTO deferred_task_waits (scheduleId, eventName, mode, created_by, updated_by) VALUES (?, ?, ?, ?, ?)",
|
|
323
|
+
[schedule.id, wakeOn.event, wakeMode, createdBy ?? null, createdBy ?? null],
|
|
205
324
|
);
|
|
325
|
+
for (const watchedId of watchedTaskIds) {
|
|
326
|
+
await getDbClient().run(
|
|
327
|
+
"INSERT INTO deferred_task_wait_members (scheduleId, taskId, created_by, updated_by) VALUES (?, ?, ?, ?)",
|
|
328
|
+
[schedule.id, watchedId, createdBy ?? null, createdBy ?? null],
|
|
329
|
+
);
|
|
330
|
+
}
|
|
206
331
|
getDbClient().afterCommit(() => {
|
|
207
|
-
void
|
|
208
|
-
|
|
209
|
-
|
|
332
|
+
void Promise.all(watchedTaskIds.map((id) => reconcileDeferredTaskWaits(id))).catch(
|
|
333
|
+
(err) => {
|
|
334
|
+
console.error("[defer-task] Event wake reconciliation failed:", err);
|
|
335
|
+
},
|
|
336
|
+
);
|
|
210
337
|
});
|
|
211
338
|
}
|
|
212
339
|
|
|
340
|
+
// Full detail for the task log — ISO timestamp, schedule id, checks.
|
|
341
|
+
// Never shown to a human directly; the wake-up task gets note+checks
|
|
342
|
+
// through taskTemplate instead.
|
|
213
343
|
const deferralDetails = `${summary}\n\nDeferred until ${nextRunAt} (schedule ${schedule.id}). Pending: ${note}${checksBlock}`;
|
|
214
344
|
|
|
215
|
-
const terminalOutput = task.outputSchema
|
|
216
|
-
|
|
345
|
+
const terminalOutput = task.outputSchema
|
|
346
|
+
? output!
|
|
347
|
+
: renderHumanFacingDeferral(note, nextRunAt, schedule.id, requesterTz, isFallbackTz);
|
|
348
|
+
const completed = await completeTask(taskId, terminalOutput, {
|
|
349
|
+
addTags: ["deferred"],
|
|
350
|
+
});
|
|
217
351
|
if (!completed) {
|
|
218
352
|
// Another writer terminally completed/failed/cancelled this task
|
|
219
353
|
// between our early check and this transaction's write. Abort:
|
|
@@ -225,14 +359,12 @@ export const registerDeferTaskTool = (server: McpServer) => {
|
|
|
225
359
|
);
|
|
226
360
|
}
|
|
227
361
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
});
|
|
235
|
-
}
|
|
362
|
+
await createLogEntry({
|
|
363
|
+
eventType: "task_progress",
|
|
364
|
+
taskId,
|
|
365
|
+
agentId: requestInfo.agentId,
|
|
366
|
+
newValue: deferralDetails,
|
|
367
|
+
});
|
|
236
368
|
|
|
237
369
|
// afterCommit: the transaction can still roll back; business-use must
|
|
238
370
|
// not be told the task completed for a write that never landed.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { proxySwarmApi } from "./script-common";
|
|
3
|
+
import { type RequestInfo, type SwarmToolResult, swarmToolOutputSchema, toolErr } from "./utils";
|
|
4
|
+
|
|
5
|
+
export const EXTENSION_TRANSPORT_ERROR =
|
|
6
|
+
"extension_* tools require HTTP MCP transport because agent identity is unavailable over stdio. Use MCP_BASE_URL=http://... or invoke the HTTP API directly.";
|
|
7
|
+
|
|
8
|
+
const extensionListItemSchema = z.looseObject({
|
|
9
|
+
id: z.string().optional(),
|
|
10
|
+
name: z.string().optional(),
|
|
11
|
+
version: z.number().optional(),
|
|
12
|
+
activeVersion: z.number().optional(),
|
|
13
|
+
enabled: z.boolean().optional(),
|
|
14
|
+
status: z.string().optional(),
|
|
15
|
+
priority: z.number().optional(),
|
|
16
|
+
consecutiveFailures: z.number().optional(),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export const extensionToolOutputSchema = swarmToolOutputSchema({
|
|
20
|
+
id: z.string().optional(),
|
|
21
|
+
name: z.string().optional(),
|
|
22
|
+
version: z.number().optional(),
|
|
23
|
+
activeVersion: z.number().optional(),
|
|
24
|
+
enabled: z.boolean().optional(),
|
|
25
|
+
status: z.string().optional(),
|
|
26
|
+
priority: z.number().optional(),
|
|
27
|
+
consecutiveFailures: z.number().optional(),
|
|
28
|
+
contentDeduped: z.boolean().optional(),
|
|
29
|
+
extensions: z.array(extensionListItemSchema).optional(),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
33
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function coerceExtensionSummary(entry: unknown) {
|
|
37
|
+
const extension = isRecord(entry) ? entry : {};
|
|
38
|
+
return {
|
|
39
|
+
id: typeof extension.id === "string" ? extension.id : undefined,
|
|
40
|
+
name: typeof extension.name === "string" ? extension.name : undefined,
|
|
41
|
+
version: typeof extension.version === "number" ? extension.version : undefined,
|
|
42
|
+
activeVersion:
|
|
43
|
+
typeof extension.activeVersion === "number" ? extension.activeVersion : undefined,
|
|
44
|
+
enabled: typeof extension.enabled === "boolean" ? extension.enabled : undefined,
|
|
45
|
+
status: typeof extension.status === "string" ? extension.status : undefined,
|
|
46
|
+
priority: typeof extension.priority === "number" ? extension.priority : undefined,
|
|
47
|
+
consecutiveFailures:
|
|
48
|
+
typeof extension.consecutiveFailures === "number" ? extension.consecutiveFailures : undefined,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function errorMessage(data: unknown, status: number): string {
|
|
53
|
+
if (isRecord(data) && typeof data.error === "string" && data.error) return data.error;
|
|
54
|
+
return `Extensions API request failed with ${status}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function renderDiagnostics(data: unknown): string | undefined {
|
|
58
|
+
if (!isRecord(data) || !Array.isArray(data.diagnostics)) return undefined;
|
|
59
|
+
const diagnostics = data.diagnostics.map(String).filter(Boolean);
|
|
60
|
+
return diagnostics.length > 0 ? diagnostics.join("\n") : undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function proxyExtensionsApi<T>(args: {
|
|
64
|
+
method: "GET" | "POST";
|
|
65
|
+
path: string;
|
|
66
|
+
body?: unknown;
|
|
67
|
+
requestInfo: RequestInfo;
|
|
68
|
+
success: (data: unknown) => SwarmToolResult<T & Record<string, unknown>>;
|
|
69
|
+
}): Promise<SwarmToolResult<T & Record<string, unknown>>> {
|
|
70
|
+
return proxySwarmApi({
|
|
71
|
+
...args,
|
|
72
|
+
transportError: EXTENSION_TRANSPORT_ERROR,
|
|
73
|
+
respond: (data, response) => {
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
const diagnostics = renderDiagnostics(data);
|
|
76
|
+
if (response.status === 400 && diagnostics) {
|
|
77
|
+
return toolErr("Extension rejected by typecheck.", { details: diagnostics });
|
|
78
|
+
}
|
|
79
|
+
return toolErr(errorMessage(data, response.status), { details: diagnostics });
|
|
80
|
+
}
|
|
81
|
+
return args.success(data);
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
import { createToolRegistrar, toolOk } from "@/tools/utils";
|
|
4
|
+
import { ExtensionManifestSchema } from "@/types";
|
|
5
|
+
import {
|
|
6
|
+
coerceExtensionSummary,
|
|
7
|
+
extensionToolOutputSchema,
|
|
8
|
+
proxyExtensionsApi,
|
|
9
|
+
} from "./extension-common";
|
|
10
|
+
|
|
11
|
+
export const EXTENSION_INSTALL_DESCRIPTION =
|
|
12
|
+
"Validate and store a TypeScript extension bundle (manifest plus a files map with one hooks.ts). Fetch the hook contract first: GET /api/extensions/type-defs returns swarm-extension.d.ts with every event and modify shape. The extension remains disabled until an operator enables it.";
|
|
13
|
+
|
|
14
|
+
function extensionInstallResult(data: unknown, fallbackName: string) {
|
|
15
|
+
const body = data as {
|
|
16
|
+
extension?: unknown;
|
|
17
|
+
contentDeduped?: unknown;
|
|
18
|
+
};
|
|
19
|
+
const extension = coerceExtensionSummary(body.extension);
|
|
20
|
+
const name = extension.name ?? fallbackName;
|
|
21
|
+
const version = typeof extension.version === "number" ? ` v${extension.version}` : "";
|
|
22
|
+
const deduped = body.contentDeduped === true ? " Content was unchanged." : "";
|
|
23
|
+
const enableInstruction =
|
|
24
|
+
"An operator must enable it from the dashboard or `POST /api/extensions/{id}/enable`.";
|
|
25
|
+
|
|
26
|
+
return toolOk(`Extension \`${name}\`${version} installed.${deduped} ${enableInstruction}`, {
|
|
27
|
+
data: {
|
|
28
|
+
...extension,
|
|
29
|
+
name,
|
|
30
|
+
contentDeduped: body.contentDeduped === true,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const registerExtensionInstallTool = (server: McpServer) => {
|
|
36
|
+
createToolRegistrar(server)(
|
|
37
|
+
"extension-install",
|
|
38
|
+
{
|
|
39
|
+
title: "Extension Install",
|
|
40
|
+
description: EXTENSION_INSTALL_DESCRIPTION,
|
|
41
|
+
annotations: { openWorldHint: false },
|
|
42
|
+
inputSchema: z
|
|
43
|
+
.object({
|
|
44
|
+
manifest: ExtensionManifestSchema.describe("Extension bundle manifest."),
|
|
45
|
+
files: z.record(z.string(), z.string()).describe("Bundle files keyed by relative path."),
|
|
46
|
+
priority: z
|
|
47
|
+
.number()
|
|
48
|
+
.int()
|
|
49
|
+
.optional()
|
|
50
|
+
.describe("Handler priority. Lower values run first."),
|
|
51
|
+
config: z.record(z.string(), z.unknown()).optional().describe("Extension configuration."),
|
|
52
|
+
})
|
|
53
|
+
.strict(),
|
|
54
|
+
outputSchema: extensionToolOutputSchema,
|
|
55
|
+
},
|
|
56
|
+
async (args, requestInfo) =>
|
|
57
|
+
proxyExtensionsApi({
|
|
58
|
+
method: "POST",
|
|
59
|
+
path: "/api/extensions/install",
|
|
60
|
+
body: args,
|
|
61
|
+
requestInfo,
|
|
62
|
+
success: (data) => extensionInstallResult(data, args.manifest.name),
|
|
63
|
+
}),
|
|
64
|
+
);
|
|
65
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
import { createToolRegistrar, toolOk } from "@/tools/utils";
|
|
4
|
+
import {
|
|
5
|
+
coerceExtensionSummary,
|
|
6
|
+
extensionToolOutputSchema,
|
|
7
|
+
proxyExtensionsApi,
|
|
8
|
+
} from "./extension-common";
|
|
9
|
+
|
|
10
|
+
export const EXTENSION_LIST_DESCRIPTION =
|
|
11
|
+
"List installed extensions and their activation state. To write a new one, fetch GET /api/extensions/type-defs for the hook contract and install it with extension-install.";
|
|
12
|
+
|
|
13
|
+
type ExtensionListItem = ReturnType<typeof coerceExtensionSummary>;
|
|
14
|
+
|
|
15
|
+
function extensionListItems(data: unknown): ExtensionListItem[] {
|
|
16
|
+
if (
|
|
17
|
+
typeof data !== "object" ||
|
|
18
|
+
data === null ||
|
|
19
|
+
!Array.isArray((data as { extensions?: unknown }).extensions)
|
|
20
|
+
) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
return (data as { extensions: unknown[] }).extensions.map(coerceExtensionSummary);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function renderExtensionsTable(extensions: ExtensionListItem[]): string {
|
|
27
|
+
const header = "| Name | Version | Active | Enabled | Status | Priority | Failures |";
|
|
28
|
+
const divider = "| --- | ---: | ---: | --- | --- | ---: | ---: |";
|
|
29
|
+
const rows = extensions.map(
|
|
30
|
+
(extension) =>
|
|
31
|
+
`| ${extension.name ?? "?"} | ${extension.version ?? "?"} | ${extension.activeVersion ?? "?"} | ${extension.enabled ?? false} | ${extension.status ?? "unknown"} | ${extension.priority ?? "?"} | ${extension.consecutiveFailures ?? 0} |`,
|
|
32
|
+
);
|
|
33
|
+
return [header, divider, ...rows].join("\n");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const registerExtensionListTool = (server: McpServer) => {
|
|
37
|
+
createToolRegistrar(server)(
|
|
38
|
+
"extension-list",
|
|
39
|
+
{
|
|
40
|
+
title: "Extension List",
|
|
41
|
+
description: EXTENSION_LIST_DESCRIPTION,
|
|
42
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
43
|
+
inputSchema: z
|
|
44
|
+
.object({
|
|
45
|
+
enabledOnly: z.boolean().optional().describe("Return only enabled extensions."),
|
|
46
|
+
})
|
|
47
|
+
.strict(),
|
|
48
|
+
outputSchema: extensionToolOutputSchema,
|
|
49
|
+
},
|
|
50
|
+
async ({ enabledOnly }, requestInfo) =>
|
|
51
|
+
proxyExtensionsApi({
|
|
52
|
+
method: "GET",
|
|
53
|
+
path: "/api/extensions",
|
|
54
|
+
requestInfo,
|
|
55
|
+
success: (data) => {
|
|
56
|
+
const extensions = extensionListItems(data).filter(
|
|
57
|
+
(extension) => !enabledOnly || extension.enabled,
|
|
58
|
+
);
|
|
59
|
+
return toolOk(`Found ${extensions.length} extension(s).`, {
|
|
60
|
+
details: renderExtensionsTable(extensions),
|
|
61
|
+
data: { extensions },
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
}),
|
|
65
|
+
);
|
|
66
|
+
};
|
|
@@ -3,6 +3,7 @@ import { getApiKey } from "@/utils/api-key";
|
|
|
3
3
|
import { getMcpBaseUrl } from "@/utils/constants";
|
|
4
4
|
import {
|
|
5
5
|
type RequestInfo,
|
|
6
|
+
type SwarmToolData,
|
|
6
7
|
type SwarmToolResult,
|
|
7
8
|
swarmToolOutputSchema,
|
|
8
9
|
toolErr,
|
|
@@ -161,7 +162,37 @@ export async function proxyScriptsApi(args: {
|
|
|
161
162
|
*/
|
|
162
163
|
uncappedDetails?: boolean;
|
|
163
164
|
}): Promise<SwarmToolResult> {
|
|
164
|
-
|
|
165
|
+
return proxySwarmApi({
|
|
166
|
+
...args,
|
|
167
|
+
transportError: SCRIPT_TRANSPORT_ERROR,
|
|
168
|
+
respond: (data, res) => {
|
|
169
|
+
const toolData = { status: res.status, data };
|
|
170
|
+
const failure = describeScriptFailure(res.ok, res.status, data);
|
|
171
|
+
if (failure) {
|
|
172
|
+
return toolErr(failure.message, {
|
|
173
|
+
details: failure.details ?? capDetails(args.failureDetails?.(data)),
|
|
174
|
+
data: toolData,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const successDetails = args.successDetails?.(data);
|
|
179
|
+
return toolOk(args.successMessage(data), {
|
|
180
|
+
details: args.uncappedDetails ? successDetails : capDetails(successDetails),
|
|
181
|
+
data: toolData,
|
|
182
|
+
});
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export async function proxySwarmApi<T extends Record<string, unknown>>(args: {
|
|
188
|
+
method: "GET" | "POST" | "PATCH" | "DELETE";
|
|
189
|
+
path: string;
|
|
190
|
+
body?: unknown;
|
|
191
|
+
requestInfo: RequestInfo;
|
|
192
|
+
transportError: string;
|
|
193
|
+
respond: (data: unknown, response: { ok: boolean; status: number }) => SwarmToolResult<T>;
|
|
194
|
+
}): Promise<SwarmToolResult<T>> {
|
|
195
|
+
if (!args.requestInfo.agentId) return toolErr(args.transportError);
|
|
165
196
|
|
|
166
197
|
const apiKey = getApiKey();
|
|
167
198
|
const headers: Record<string, string> = {
|
|
@@ -172,8 +203,7 @@ export async function proxyScriptsApi(args: {
|
|
|
172
203
|
if (args.requestInfo.sourceTaskId) {
|
|
173
204
|
headers["X-Source-Task-Id"] = args.requestInfo.sourceTaskId;
|
|
174
205
|
}
|
|
175
|
-
//
|
|
176
|
-
// rides the proxy like the agent identity, never as tool or script input.
|
|
206
|
+
// Forward runtime identity from the worker's MCP session, like agent identity.
|
|
177
207
|
if (args.requestInfo.runtimeInstanceId) {
|
|
178
208
|
headers["X-Runtime-Instance-ID"] = args.requestInfo.runtimeInstanceId;
|
|
179
209
|
}
|
|
@@ -189,22 +219,17 @@ export async function proxyScriptsApi(args: {
|
|
|
189
219
|
try {
|
|
190
220
|
data = JSON.parse(text);
|
|
191
221
|
} catch {
|
|
192
|
-
|
|
222
|
+
const contentType = res.headers.get("content-type") ?? "unknown";
|
|
223
|
+
// Transport-level failure: only `{ status }` is known here, and that shape
|
|
224
|
+
// is not statically related to the caller's `T`. Tool output schemas are
|
|
225
|
+
// loose with every data field optional, so the wire result stays valid.
|
|
226
|
+
const transportData: SwarmToolData = { status: res.status };
|
|
227
|
+
return toolErr<T>(
|
|
228
|
+
`Swarm API returned non-JSON response (HTTP ${res.status}, Content-Type: ${contentType}). Check API/proxy routing.`,
|
|
229
|
+
{ details: capDetails(text), data: transportData as T },
|
|
230
|
+
);
|
|
193
231
|
}
|
|
194
232
|
}
|
|
195
233
|
|
|
196
|
-
|
|
197
|
-
const failure = describeScriptFailure(res.ok, res.status, data);
|
|
198
|
-
if (failure) {
|
|
199
|
-
return toolErr(failure.message, {
|
|
200
|
-
details: failure.details ?? capDetails(args.failureDetails?.(data)),
|
|
201
|
-
data: toolData,
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const successDetails = args.successDetails?.(data);
|
|
206
|
-
return toolOk(args.successMessage(data), {
|
|
207
|
-
details: args.uncappedDetails ? successDetails : capDetails(successDetails),
|
|
208
|
-
data: toolData,
|
|
209
|
-
});
|
|
234
|
+
return args.respond(data, res);
|
|
210
235
|
}
|