@desplega.ai/agent-swarm 1.150.0 → 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 +11 -0
- package/dist/{acp-adapter-d5mzggyk.js → acp-adapter-ex8hzjfc.js} +5 -5
- package/dist/{actions-wn6ew0p7.js → actions-2bgpyhww.js} +38 -11
- package/dist/{anthropic-messages-ec35ktvx.js → anthropic-messages-2y8nktar.js} +16 -16
- package/dist/{app-dty7p9hg.js → app-1706zhr1.js} +7 -7
- package/dist/{assistant-q4vhgjtx.js → assistant-bavke5p7.js} +41 -16
- package/dist/{azure-openai-responses-g85jv7m8.js → azure-openai-responses-f38x2avq.js} +13 -13
- package/dist/{boot-reembed-92292pgf.js → boot-reembed-3cmrrmpa.js} +8 -8
- package/dist/{boot-reembed-n15q6s9x.js → boot-reembed-nafcmmmc.js} +13 -15
- package/dist/{boot-scrub-logs-44pc955m.js → boot-scrub-logs-cd6a2sq0.js} +5 -5
- package/dist/{claude-adapter-cjegjbje.js → claude-adapter-wfp2gzvz.js} +12 -13
- package/dist/{claude-managed-adapter-p3ts5m9m.js → claude-managed-adapter-dsmkcrjy.js} +3 -3
- package/dist/{claude-sdk-session-7ywbn12v.js → claude-sdk-session-bht9jjak.js} +12 -13
- package/dist/{cli-ceb5y160.js → cli-0qb1z6s2.js} +1 -1
- package/dist/{cli-ryz8q8p1.js → cli-2tbvf1z1.js} +40 -40
- package/dist/{cli-zthatyrx.js → cli-3v9xhjcn.js} +5 -5
- package/dist/{cli-mzb0n642.js → cli-4wppfkcm.js} +350 -20
- package/dist/{cli-3ckzxdt1.js → cli-5jd2z7av.js} +90 -81
- package/dist/{cli-3mh119qv.js → cli-5tpgq60f.js} +9726 -693
- package/dist/{cli-ck1vqzdj.js → cli-7tgcstn2.js} +9 -4
- package/dist/{cli-ttje5a4z.js → cli-9004vfdf.js} +105 -56
- package/dist/{cli-asvrh7kj.js → cli-9xvdm3bx.js} +1 -1
- package/dist/{cli-9gqddmxs.js → cli-a1z3h107.js} +8 -8
- package/dist/{cli-72xdszyx.js → cli-czjtgfys.js} +2 -2
- package/dist/{cli-5my3bjsd.js → cli-d3p82erb.js} +7 -7
- package/dist/{cli-kyysm7m3.js → cli-f1wky6zp.js} +1 -1
- package/dist/{cli-a45d5v9b.js → cli-fm4ytsdk.js} +2 -2
- package/dist/{cli-111ey3ra.js → cli-frcta2w4.js} +5 -5
- package/dist/{cli-h505yhex.js → cli-g69fdc9g.js} +2 -2
- package/dist/{cli-cmp8g3sw.js → cli-gbfwcd3k.js} +44 -9
- package/dist/{cli-hbagregr.js → cli-gjyvhmmh.js} +88 -13
- package/dist/{cli-hmyynq9b.js → cli-ht5rs63n.js} +1 -1
- package/dist/{cli-dyx0237s.js → cli-j4nv2wqy.js} +2 -4
- package/dist/{cli-vr6qj104.js → cli-j9xczhaq.js} +1 -1
- package/dist/cli-pf9q3696.js +172 -0
- package/dist/{cli-7fesa1ky.js → cli-pgj4a5fb.js} +1 -1
- package/dist/{cli-7kwmt3r5.js → cli-ppm4975n.js} +9 -3
- package/dist/{cli-8chp8xab.js → cli-q88st9aj.js} +2 -2
- package/dist/{cli-kt7wevqh.js → cli-r2q6pevn.js} +132 -46
- package/dist/{cli-07jd68a1.js → cli-re8z5p8p.js} +1 -1
- package/dist/{cli-qp69dd5d.js → cli-s14agm5s.js} +105 -12
- package/dist/{cli-zd21jrrc.js → cli-sjaae6ff.js} +9872 -424
- package/dist/{cli-taym3dmg.js → cli-vwm416m6.js} +13 -13
- package/dist/{cli-1mx65de9.js → cli-wbyk465z.js} +1 -1
- package/dist/{cli-n75jt793.js → cli-z61ykvam.js} +6 -5
- package/dist/{cli-gm0fhz13.js → cli-zkxymas5.js} +2 -1
- package/dist/cli.js +22 -21
- package/dist/{codex-adapter-rn3aq7jm.js → codex-adapter-jjjk8w4m.js} +8 -9
- package/dist/{codex-hook-gthcmm0g.js → codex-hook-8jexc34x.js} +2 -2
- package/dist/{codex-session-runner-bge0cbsw.js → codex-session-runner-nd6ayxkx.js} +8 -9
- package/dist/{commands-26phw0pc.js → commands-9v4psyq1.js} +5 -5
- package/dist/{db-jxjx6s1b.js → db-wnt58tpe.js} +5 -5
- package/dist/dispatcher-05jh1aqk.js +74 -0
- package/dist/{google-generative-ai-tsskx7te.js → google-generative-ai-ndjmj5fa.js} +7 -7
- package/dist/{google-vertex-pkhpkbq9.js → google-vertex-fr665tqs.js} +10 -10
- package/dist/handlers-tw90pkh5.js +65 -0
- package/dist/{hook-t6fqsggw.js → hook-123e2scb.js} +9 -10
- package/dist/{hook-jw0ztn96.js → hook-sxe3r349.js} +9 -10
- package/dist/{http-hyc7s57s.js → http-ecsar8ez.js} +1431 -579
- package/dist/{index-sfhaq59d.js → index-16js521z.js} +119 -101
- package/dist/{index-wtwpthe1.js → index-hw26vj40.js} +25 -19
- package/dist/{index-bcyx5djw.js → index-nnrbpcjp.js} +25 -17
- package/dist/index-ymn7em0p.js +25 -0
- package/dist/{index-1d9r6a74.js → index-zj518gf9.js} +26 -20
- package/dist/{keepalive-fbsak4ya.js → keepalive-asqtcm4y.js} +7 -7
- package/dist/{lead-rdzdm627.js → lead-xbfr7fvt.js} +41 -49
- package/dist/{maintenance-25z1c1vf.js → maintenance-qma55qvw.js} +8 -8
- package/dist/{node-209sxmg3.js → node-x1g4v0pq.js} +9 -9
- package/dist/{oauth-refresh-sweep-j3d7gtse.js → oauth-refresh-sweep-3s04c6qw.js} +6 -6
- package/dist/{onboard-dkcwrc75.js → onboard-drskd386.js} +13 -4
- package/dist/{openai-codex-responses-pm5gnyfa.js → openai-codex-responses-wy6a43wd.js} +11 -11
- package/dist/{openai-responses-vwvk1taj.js → openai-responses-6xwf7bzg.js} +13 -13
- package/dist/{opencode-adapter-dce4wskd.js → opencode-adapter-28axqa09.js} +4 -4
- package/dist/{otel-impl-8rgyy3j8.js → otel-impl-943c96er.js} +1 -1
- package/dist/{pi-mono-adapter-4s2bbkgq.js → pi-mono-adapter-jcqt71cg.js} +14 -16
- package/dist/{pricing-refresh-9cek71nq.js → pricing-refresh-mx51az05.js} +7 -7
- package/dist/{rbac-roles-z98s2ypy.js → rbac-roles-78baecqd.js} +13 -9
- package/dist/{rbac-roles-vk8mqp9v.js → rbac-roles-wcmrg6sv.js} +7 -7
- package/dist/{render-v2-3j26g0qh.js → render-v2-8zjntgf5.js} +7 -7
- package/dist/{seed-pricing-7rxhcnzm.js → seed-pricing-jqjs6cws.js} +6 -6
- package/dist/{setup-e2t7vz0c.js → setup-4pks0vfk.js} +2 -2
- package/dist/{work-objects-8d3nd3x2.js → work-objects-wangt792.js} +38 -14
- package/dist/{worker-0x9fn7nq.js → worker-p2mr422x.js} +41 -49
- package/openapi.json +1351 -265
- package/package.json +2 -1
- package/src/agentmail/handlers.ts +95 -71
- package/src/be/db-client.ts +5 -0
- package/src/be/db.ts +11 -3
- 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/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 +52 -38
- package/src/be/seed-skills/index.ts +7 -0
- package/src/be/swarm-config-guard.ts +3 -1
- package/src/commands/onboard/compose-generator.ts +14 -2
- package/src/commands/onboard/env-generator.ts +7 -0
- package/src/commands/runner.ts +1 -1
- 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 -149
- package/src/gitlab/handlers.ts +97 -81
- package/src/heartbeat/heartbeat.ts +152 -83
- package/src/http/agents.ts +1 -1
- package/src/http/all-routes.ts +1 -0
- package/src/http/apps.ts +14 -10
- package/src/http/core.ts +34 -0
- package/src/http/extensions.ts +547 -0
- package/src/http/inbox-state.ts +2 -0
- package/src/http/index.ts +25 -0
- package/src/http/mcp-bridge.ts +96 -62
- package/src/http/mcp.ts +1 -1
- package/src/http/status.ts +6 -4
- package/src/http/tasks.ts +51 -35
- package/src/integrations/kapso/inbound.ts +15 -11
- package/src/jira/sync.ts +26 -18
- package/src/linear/sync.ts +26 -18
- package/src/prompts/session-templates.ts +5 -4
- 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 +133 -51
- package/src/scheduler/schedule-task.ts +50 -24
- package/src/scheduler/scheduler.ts +4 -2
- 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 +9 -0
- package/src/scripts-runtime/types/swarm-extension.d.ts +562 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +9 -0
- package/src/server.ts +6 -0
- package/src/slack/HEURISTICS.md +1 -1
- package/src/slack/actions.ts +21 -17
- package/src/slack/blocks.ts +1 -1
- package/src/slack/handlers.ts +98 -5
- package/src/slack/inbound-files.ts +6 -2
- package/src/slack/render-v2.ts +171 -35
- package/src/slack/router.ts +13 -3
- package/src/slack/steering.ts +3 -3
- package/src/tasks/errors.ts +9 -0
- package/src/tasks/sibling-awareness.ts +35 -1
- package/src/tasks/worker-follow-up.ts +64 -12
- package/src/tests/base-prompt.test.ts +1 -0
- package/src/tests/deferred-task-wake.test.ts +161 -9
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/env-flag.test.ts +1 -0
- 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/memory-edit-authorization.test.ts +32 -0
- package/src/tests/multi-runtime-enabled-stats.test.ts +2 -2
- package/src/tests/multi-runtime-registration.test.ts +14 -14
- package/src/tests/onboard-compose.test.ts +44 -0
- package/src/tests/preload.ts +5 -0
- package/src/tests/rbac-admission.test.ts +18 -3
- package/src/tests/rbac-engine.test.ts +39 -0
- package/src/tests/realtime-room-auth.test.ts +1 -1
- package/src/tests/reload-config.test.ts +44 -0
- package/src/tests/routing-affinity-inventory.test.ts +1 -1
- package/src/tests/script-connections.test.ts +2 -0
- package/src/tests/scripts-boot-reembed.test.ts +4 -0
- package/src/tests/scripts-embeddings.test.ts +4 -0
- package/src/tests/scripts-import-allowlist.test.ts +44 -1
- package/src/tests/scripts-mcp-e2e.test.ts +80 -0
- package/src/tests/scripts-only-gating.test.ts +2 -0
- package/src/tests/scripts-runtime-identity.test.ts +1 -1
- package/src/tests/slack-blocks.test.ts +12 -1
- package/src/tests/slack-render-v2.test.ts +76 -3
- package/src/tests/slack-router.test.ts +17 -2
- package/src/tests/slack-steering.test.ts +19 -0
- package/src/tests/status.test.ts +12 -0
- package/src/tests/steering-enabled.test.ts +6 -8
- package/src/tests/task-tool-preload-mcp.test.ts +4 -1
- package/src/tests/tool-annotations.test.ts +1 -1
- package/src/tests/tool-registrar-no-input.test.ts +42 -1
- package/src/tools/defer-task.ts +1 -1
- 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 +69 -107
- package/src/tools/task-action.ts +43 -22
- package/src/tools/task-tool-ctx.ts +2 -0
- package/src/tools/tool-config.ts +3 -1
- package/src/tools/utils.ts +140 -16
- package/src/types.ts +133 -0
- package/src/utils/multi-runtime.ts +2 -2
- package/src/utils/steering-enabled.ts +4 -4
- package/src/workflows/executors/agent-task.ts +13 -3
- 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/skills/scheduling/content.md +2 -0
- package/templates/skills/swarm-extensions/config.json +14 -0
- package/templates/skills/swarm-extensions/content.md +120 -0
- package/dist/cli-b8x19vbh.js +0 -823
- package/dist/cli-n1yj5fd5.js +0 -164
- package/dist/cli-q04q68be.js +0 -3066
- package/dist/cli-qgm2pfpq.js +0 -6377
- package/dist/cli-rnsr8asm.js +0 -4323
- package/dist/cli-rwp3ksm7.js +0 -545
- package/dist/cli-xjb9wghj.js +0 -668
- package/dist/cli-xvkmedgn.js +0 -332
- package/dist/cli-ydk0wjj8.js +0 -338
- package/dist/handlers-vr1djg4n.js +0 -41
- 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-wk0gjrn3.js → cli-kj76p1ez.js} +6 -6
- package/dist/{cli-c71mg51n.js → cli-r2fpe7cx.js} +5 -5
- package/dist/{cli-8qx7jnrk.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-qdcmkfnr.js → openai-completions-32rdwmze.js} +17 -17
- package/dist/{openrouter-images-qx18fyp8.js → openrouter-images-9f9ek4rw.js} +3 -3
- package/dist/{pi-messages-79jvzz1d.js → pi-messages-b1mbr6gp.js} +4 -4
|
@@ -0,0 +1,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
|
}
|
package/src/tools/send-task.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
hasCapacity,
|
|
16
16
|
} from "@/be/db";
|
|
17
17
|
import { repointTrackerSyncBySwarmId } from "@/be/db-queries/tracker";
|
|
18
|
+
import { applyPreTaskCreate } from "@/extensions/apply-task-create";
|
|
18
19
|
import { checkSlackRoutingCoherence } from "@/tasks/slack-routing";
|
|
19
20
|
import { findDuplicateTask } from "@/tools/task-dedup";
|
|
20
21
|
import { ownerCtx, type ToolCtx } from "@/tools/task-tool-ctx";
|
|
@@ -28,6 +29,7 @@ import {
|
|
|
28
29
|
import {
|
|
29
30
|
type AgentTask,
|
|
30
31
|
AssetKeySchema,
|
|
32
|
+
type CreateTaskOptions,
|
|
31
33
|
FollowUpConfigSchema,
|
|
32
34
|
ModelTierSchema,
|
|
33
35
|
ReasoningEffortSchema,
|
|
@@ -382,6 +384,53 @@ export async function sendTaskHandler(
|
|
|
382
384
|
const effectiveRoutingSource =
|
|
383
385
|
agentId !== undefined ? "declared" : effectiveAgentId ? "engine_default" : undefined;
|
|
384
386
|
|
|
387
|
+
const requestedTaskOptions: CreateTaskOptions = {
|
|
388
|
+
key: assetKey,
|
|
389
|
+
agentId: offerMode ? undefined : effectiveAgentId,
|
|
390
|
+
offeredTo: offerMode ? effectiveAgentId : undefined,
|
|
391
|
+
creatorAgentId,
|
|
392
|
+
requestedByUserId,
|
|
393
|
+
source: "mcp",
|
|
394
|
+
sourceTaskId,
|
|
395
|
+
taskType,
|
|
396
|
+
tags,
|
|
397
|
+
priority,
|
|
398
|
+
dependsOn,
|
|
399
|
+
dir,
|
|
400
|
+
parentTaskId: effectiveParentTaskId,
|
|
401
|
+
vcsRepo: effectiveVcsRepo,
|
|
402
|
+
model: normalizedModel.model,
|
|
403
|
+
modelTier: normalizedModel.modelTier,
|
|
404
|
+
effort,
|
|
405
|
+
slackChannelId,
|
|
406
|
+
slackThreadTs,
|
|
407
|
+
slackUserId,
|
|
408
|
+
overrideSlackContext,
|
|
409
|
+
followUpConfig,
|
|
410
|
+
outputSchema,
|
|
411
|
+
routingReason: effectiveRoutingReason,
|
|
412
|
+
routingSource: effectiveRoutingSource,
|
|
413
|
+
routingNote: effectiveRoutingNote,
|
|
414
|
+
routingAffinity:
|
|
415
|
+
effectiveLeadOnly || requiredCapabilities?.length
|
|
416
|
+
? { leadOnly: effectiveLeadOnly, capabilities: requiredCapabilities ?? [] }
|
|
417
|
+
: undefined,
|
|
418
|
+
};
|
|
419
|
+
const preCreate = await applyPreTaskCreate({
|
|
420
|
+
description: task,
|
|
421
|
+
options: requestedTaskOptions,
|
|
422
|
+
origin: "mcp",
|
|
423
|
+
requestInfo: ctx.kind === "owner" ? ctx.requestInfo : undefined,
|
|
424
|
+
});
|
|
425
|
+
if (preCreate.kind === "blocked") {
|
|
426
|
+
return toolErr(preCreate.reason, {
|
|
427
|
+
data: { yourAgentId: creatorAgentId },
|
|
428
|
+
details: JSON.stringify({ extension: preCreate.extension }),
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
const taskDescription = preCreate.description;
|
|
432
|
+
const taskOptions = preCreate.options;
|
|
433
|
+
|
|
385
434
|
// The three dedup guards are pure reads, so they run twice: once here as a
|
|
386
435
|
// fast path (keeping this tool's existing early-exit responses), and once
|
|
387
436
|
// inside the write transaction below, where the check is authoritative.
|
|
@@ -405,9 +454,9 @@ export async function sendTaskHandler(
|
|
|
405
454
|
// Dedup guard: check for similar recent tasks
|
|
406
455
|
if (!allowDuplicate && creatorAgentId) {
|
|
407
456
|
const duplicate = await findDuplicateTask({
|
|
408
|
-
taskDescription
|
|
409
|
-
creatorAgentId,
|
|
410
|
-
targetAgentId:
|
|
457
|
+
taskDescription,
|
|
458
|
+
creatorAgentId: taskOptions.creatorAgentId ?? creatorAgentId,
|
|
459
|
+
targetAgentId: taskOptions.agentId ?? taskOptions.offeredTo,
|
|
411
460
|
});
|
|
412
461
|
if (duplicate) {
|
|
413
462
|
return {
|
|
@@ -487,42 +536,13 @@ export async function sendTaskHandler(
|
|
|
487
536
|
const raced = await evaluateDedupGuards();
|
|
488
537
|
if (raced) return { success: raced.ok, message: raced.message, task: raced.task };
|
|
489
538
|
|
|
490
|
-
const finalTags = tags;
|
|
491
|
-
|
|
492
539
|
// If no agentId (and no auto-routed agentId), create an unassigned task for the pool
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
creatorAgentId,
|
|
497
|
-
requestedByUserId,
|
|
498
|
-
sourceTaskId,
|
|
499
|
-
taskType,
|
|
500
|
-
tags: finalTags,
|
|
501
|
-
priority,
|
|
502
|
-
dependsOn,
|
|
503
|
-
dir,
|
|
504
|
-
parentTaskId: effectiveParentTaskId,
|
|
505
|
-
vcsRepo: effectiveVcsRepo,
|
|
506
|
-
model: normalizedModel.model,
|
|
507
|
-
modelTier: normalizedModel.modelTier,
|
|
508
|
-
effort,
|
|
509
|
-
slackChannelId,
|
|
510
|
-
slackThreadTs,
|
|
511
|
-
slackUserId,
|
|
512
|
-
overrideSlackContext,
|
|
513
|
-
followUpConfig,
|
|
514
|
-
outputSchema,
|
|
515
|
-
routingReason: effectiveRoutingReason,
|
|
516
|
-
routingSource: effectiveRoutingSource,
|
|
517
|
-
routingNote: effectiveRoutingNote,
|
|
518
|
-
routingAffinity:
|
|
519
|
-
effectiveLeadOnly || requiredCapabilities?.length
|
|
520
|
-
? { leadOnly: effectiveLeadOnly, capabilities: requiredCapabilities ?? [] }
|
|
521
|
-
: undefined,
|
|
522
|
-
});
|
|
540
|
+
const targetAgentId = taskOptions.offeredTo ?? taskOptions.agentId ?? undefined;
|
|
541
|
+
if (!targetAgentId) {
|
|
542
|
+
const newTask = await createTaskExtended(taskDescription, taskOptions);
|
|
523
543
|
await transferTrackerSyncToResumeChild({
|
|
524
|
-
parentTaskId:
|
|
525
|
-
taskType,
|
|
544
|
+
parentTaskId: taskOptions.parentTaskId,
|
|
545
|
+
taskType: taskOptions.taskType,
|
|
526
546
|
child: newTask,
|
|
527
547
|
});
|
|
528
548
|
|
|
@@ -533,16 +553,16 @@ export async function sendTaskHandler(
|
|
|
533
553
|
};
|
|
534
554
|
}
|
|
535
555
|
|
|
536
|
-
const agent = await getAgentById(
|
|
556
|
+
const agent = await getAgentById(targetAgentId);
|
|
537
557
|
|
|
538
558
|
if (!agent) {
|
|
539
559
|
return {
|
|
540
560
|
success: false,
|
|
541
|
-
message: `Agent with ID "${
|
|
561
|
+
message: `Agent with ID "${targetAgentId}" not found.`,
|
|
542
562
|
};
|
|
543
563
|
}
|
|
544
564
|
|
|
545
|
-
if (
|
|
565
|
+
if (taskOptions.routingAffinity?.leadOnly && !agent.isLead) {
|
|
546
566
|
return {
|
|
547
567
|
success: false,
|
|
548
568
|
message: `Lead-only task requires a Lead agent; "${agent.name}" is not a Lead.`,
|
|
@@ -550,49 +570,20 @@ export async function sendTaskHandler(
|
|
|
550
570
|
}
|
|
551
571
|
|
|
552
572
|
// For direct assignment (not offer), check if agent has capacity
|
|
553
|
-
if (!
|
|
554
|
-
const activeCount = await getActiveTaskCount(
|
|
573
|
+
if (!taskOptions.offeredTo && !(await hasCapacity(targetAgentId))) {
|
|
574
|
+
const activeCount = await getActiveTaskCount(targetAgentId);
|
|
555
575
|
return {
|
|
556
576
|
success: false,
|
|
557
577
|
message: `Agent "${agent.name}" is at capacity (${activeCount}/${agent.maxTasks ?? 1} tasks). Use offerMode: true to offer the task instead, or wait for a task to complete.`,
|
|
558
578
|
};
|
|
559
579
|
}
|
|
560
580
|
|
|
561
|
-
if (
|
|
581
|
+
if (taskOptions.offeredTo) {
|
|
562
582
|
// Offer the task to the agent (they must accept/reject)
|
|
563
|
-
const newTask = await createTaskExtended(
|
|
564
|
-
key: assetKey,
|
|
565
|
-
offeredTo: effectiveAgentId,
|
|
566
|
-
creatorAgentId,
|
|
567
|
-
requestedByUserId,
|
|
568
|
-
sourceTaskId,
|
|
569
|
-
taskType,
|
|
570
|
-
tags: finalTags,
|
|
571
|
-
priority,
|
|
572
|
-
dependsOn,
|
|
573
|
-
dir,
|
|
574
|
-
parentTaskId: effectiveParentTaskId,
|
|
575
|
-
vcsRepo: effectiveVcsRepo,
|
|
576
|
-
model: normalizedModel.model,
|
|
577
|
-
modelTier: normalizedModel.modelTier,
|
|
578
|
-
effort,
|
|
579
|
-
slackChannelId,
|
|
580
|
-
slackThreadTs,
|
|
581
|
-
slackUserId,
|
|
582
|
-
overrideSlackContext,
|
|
583
|
-
followUpConfig,
|
|
584
|
-
outputSchema,
|
|
585
|
-
routingReason: effectiveRoutingReason,
|
|
586
|
-
routingSource: effectiveRoutingSource,
|
|
587
|
-
routingNote: effectiveRoutingNote,
|
|
588
|
-
routingAffinity:
|
|
589
|
-
effectiveLeadOnly || requiredCapabilities?.length
|
|
590
|
-
? { leadOnly: effectiveLeadOnly, capabilities: requiredCapabilities ?? [] }
|
|
591
|
-
: undefined,
|
|
592
|
-
});
|
|
583
|
+
const newTask = await createTaskExtended(taskDescription, taskOptions);
|
|
593
584
|
await transferTrackerSyncToResumeChild({
|
|
594
|
-
parentTaskId:
|
|
595
|
-
taskType,
|
|
585
|
+
parentTaskId: taskOptions.parentTaskId,
|
|
586
|
+
taskType: taskOptions.taskType,
|
|
596
587
|
child: newTask,
|
|
597
588
|
});
|
|
598
589
|
|
|
@@ -604,39 +595,10 @@ export async function sendTaskHandler(
|
|
|
604
595
|
}
|
|
605
596
|
|
|
606
597
|
// Direct assignment
|
|
607
|
-
const newTask = await createTaskExtended(
|
|
608
|
-
key: assetKey,
|
|
609
|
-
agentId: effectiveAgentId,
|
|
610
|
-
creatorAgentId,
|
|
611
|
-
requestedByUserId,
|
|
612
|
-
sourceTaskId,
|
|
613
|
-
taskType,
|
|
614
|
-
tags: finalTags,
|
|
615
|
-
priority,
|
|
616
|
-
dependsOn,
|
|
617
|
-
dir,
|
|
618
|
-
parentTaskId: effectiveParentTaskId,
|
|
619
|
-
vcsRepo: effectiveVcsRepo,
|
|
620
|
-
model: normalizedModel.model,
|
|
621
|
-
modelTier: normalizedModel.modelTier,
|
|
622
|
-
effort,
|
|
623
|
-
slackChannelId,
|
|
624
|
-
slackThreadTs,
|
|
625
|
-
slackUserId,
|
|
626
|
-
overrideSlackContext,
|
|
627
|
-
followUpConfig,
|
|
628
|
-
outputSchema,
|
|
629
|
-
routingReason: effectiveRoutingReason,
|
|
630
|
-
routingSource: effectiveRoutingSource,
|
|
631
|
-
routingNote: effectiveRoutingNote,
|
|
632
|
-
routingAffinity:
|
|
633
|
-
effectiveLeadOnly || requiredCapabilities?.length
|
|
634
|
-
? { leadOnly: effectiveLeadOnly, capabilities: requiredCapabilities ?? [] }
|
|
635
|
-
: undefined,
|
|
636
|
-
});
|
|
598
|
+
const newTask = await createTaskExtended(taskDescription, taskOptions);
|
|
637
599
|
await transferTrackerSyncToResumeChild({
|
|
638
|
-
parentTaskId:
|
|
639
|
-
taskType,
|
|
600
|
+
parentTaskId: taskOptions.parentTaskId,
|
|
601
|
+
taskType: taskOptions.taskType,
|
|
640
602
|
child: newTask,
|
|
641
603
|
});
|
|
642
604
|
|
package/src/tools/task-action.ts
CHANGED
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
updateTaskClaudeSessionId,
|
|
29
29
|
} from "@/be/db";
|
|
30
30
|
import { touchRuntimeInstance } from "@/be/multi-runtime";
|
|
31
|
+
import { applyPreTaskCreate } from "@/extensions/apply-task-create";
|
|
31
32
|
import { assertOwnsTask, ownerCtx, type ToolCtx } from "@/tools/task-tool-ctx";
|
|
32
33
|
import {
|
|
33
34
|
createToolRegistrar,
|
|
@@ -39,6 +40,7 @@ import {
|
|
|
39
40
|
import {
|
|
40
41
|
AssetKeySchema,
|
|
41
42
|
BudgetRefusalCauseSchema,
|
|
43
|
+
type CreateTaskOptions,
|
|
42
44
|
ModelTierSchema,
|
|
43
45
|
ReasoningEffortSchema,
|
|
44
46
|
splitLegacyModelAlias,
|
|
@@ -265,7 +267,14 @@ export async function taskActionHandler(
|
|
|
265
267
|
|
|
266
268
|
const agentId = ctx.agentId;
|
|
267
269
|
let assetKey: string | undefined;
|
|
270
|
+
let preparedCreate: { description: string; options: CreateTaskOptions } | undefined;
|
|
268
271
|
if (action === "create") {
|
|
272
|
+
if (!task) {
|
|
273
|
+
return taskActionResult(
|
|
274
|
+
{ success: false, message: "Task description is required for 'create' action." },
|
|
275
|
+
agentId,
|
|
276
|
+
);
|
|
277
|
+
}
|
|
269
278
|
try {
|
|
270
279
|
assetKey = key
|
|
271
280
|
? await authorizeAssetKeyWrite(key, await resolveTaskAuditUserId(ctx.sourceTaskId, agentId))
|
|
@@ -279,33 +288,45 @@ export async function taskActionHandler(
|
|
|
279
288
|
: String(error);
|
|
280
289
|
return taskActionResult({ success: false, message }, agentId);
|
|
281
290
|
}
|
|
291
|
+
|
|
292
|
+
const preCreate = await applyPreTaskCreate({
|
|
293
|
+
description: task,
|
|
294
|
+
options: {
|
|
295
|
+
key: assetKey,
|
|
296
|
+
creatorAgentId: agentId,
|
|
297
|
+
source: "mcp",
|
|
298
|
+
taskType,
|
|
299
|
+
tags,
|
|
300
|
+
priority,
|
|
301
|
+
dependsOn,
|
|
302
|
+
dir,
|
|
303
|
+
model: normalizedModel.model,
|
|
304
|
+
modelTier: normalizedModel.modelTier,
|
|
305
|
+
effort: input.effort,
|
|
306
|
+
routingAffinity:
|
|
307
|
+
leadOnly || requiredCapabilities?.length
|
|
308
|
+
? { leadOnly, capabilities: requiredCapabilities ?? [] }
|
|
309
|
+
: undefined,
|
|
310
|
+
},
|
|
311
|
+
origin: "mcp",
|
|
312
|
+
requestInfo: ctx.requestInfo,
|
|
313
|
+
});
|
|
314
|
+
if (preCreate.kind === "blocked") {
|
|
315
|
+
return taskActionResult({ success: false, message: preCreate.reason }, agentId);
|
|
316
|
+
}
|
|
317
|
+
preparedCreate = {
|
|
318
|
+
description: preCreate.description,
|
|
319
|
+
options: preCreate.options,
|
|
320
|
+
};
|
|
282
321
|
}
|
|
283
322
|
|
|
284
323
|
const result = await getDbClient().transaction(async (): Promise<TaskActionResult> => {
|
|
285
324
|
switch (action) {
|
|
286
325
|
case "create": {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
const newTask = await createTaskExtended(task, {
|
|
294
|
-
key: assetKey,
|
|
295
|
-
creatorAgentId: agentId,
|
|
296
|
-
taskType,
|
|
297
|
-
tags,
|
|
298
|
-
priority,
|
|
299
|
-
dependsOn,
|
|
300
|
-
dir,
|
|
301
|
-
model: normalizedModel.model,
|
|
302
|
-
modelTier: normalizedModel.modelTier,
|
|
303
|
-
effort: input.effort,
|
|
304
|
-
routingAffinity:
|
|
305
|
-
leadOnly || requiredCapabilities?.length
|
|
306
|
-
? { leadOnly, capabilities: requiredCapabilities ?? [] }
|
|
307
|
-
: undefined,
|
|
308
|
-
});
|
|
326
|
+
const newTask = await createTaskExtended(
|
|
327
|
+
preparedCreate!.description,
|
|
328
|
+
preparedCreate!.options,
|
|
329
|
+
);
|
|
309
330
|
return {
|
|
310
331
|
success: true,
|
|
311
332
|
message: `Created unassigned task "${newTask.id}".`,
|
|
@@ -9,6 +9,7 @@ export type ToolCtx =
|
|
|
9
9
|
runtimeInstanceId?: string;
|
|
10
10
|
sourceTaskId?: string;
|
|
11
11
|
sessionId?: string;
|
|
12
|
+
requestInfo?: RequestInfo;
|
|
12
13
|
}
|
|
13
14
|
| { kind: "user"; userId: string; user: User; sessionId?: string };
|
|
14
15
|
|
|
@@ -19,6 +20,7 @@ export function ownerCtx(info: RequestInfo): ToolCtx {
|
|
|
19
20
|
runtimeInstanceId: info.runtimeInstanceId,
|
|
20
21
|
sourceTaskId: info.sourceTaskId,
|
|
21
22
|
sessionId: info.sessionId,
|
|
23
|
+
requestInfo: info,
|
|
22
24
|
};
|
|
23
25
|
}
|
|
24
26
|
|
package/src/tools/tool-config.ts
CHANGED
|
@@ -197,13 +197,15 @@ export const DEFERRED_TOOLS = new Set([
|
|
|
197
197
|
"room-reset",
|
|
198
198
|
"room-decode",
|
|
199
199
|
|
|
200
|
-
// Reusable scripts (
|
|
200
|
+
// Reusable scripts and extensions (8)
|
|
201
201
|
"script-upsert",
|
|
202
202
|
"script-delete",
|
|
203
203
|
"script-apis",
|
|
204
204
|
"launch-script-run",
|
|
205
205
|
"get-script-run",
|
|
206
206
|
"list-script-runs",
|
|
207
|
+
"extension-install",
|
|
208
|
+
"extension-list",
|
|
207
209
|
|
|
208
210
|
// External command routes (1)
|
|
209
211
|
"swarm_x",
|