@desplega.ai/agent-swarm 1.111.0 → 1.113.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/dist/{actions-a4crgvtd.js → actions-q4n7cz6e.js} +6 -6
- package/dist/{app-va5mr9yw.js → app-es4nzc71.js} +3 -3
- package/dist/{assistant-0jm4vy8r.js → assistant-0x8ey0vs.js} +9 -9
- package/dist/{boot-reembed-50zzk97q.js → boot-reembed-69agkhv6.js} +4 -4
- package/dist/{boot-reembed-0t815mmm.js → boot-reembed-p0ny05t8.js} +3 -3
- package/dist/{boot-scrub-logs-r99nbmxh.js → boot-scrub-logs-yybrkmvr.js} +2 -2
- package/dist/{cli-h3vhr1gj.js → cli-30bbaveh.js} +2 -2
- package/dist/{cli-ee3fkebs.js → cli-36ymmpmp.js} +2 -2
- package/dist/{cli-dza5039b.js → cli-4xyj9jtq.js} +1495 -254
- package/dist/{cli-3cm8ar1c.js → cli-bgef578c.js} +1 -1
- package/dist/{cli-xqzvygxp.js → cli-bgvskydy.js} +11 -2
- package/dist/{cli-t8m00q3b.js → cli-bnzbn8j8.js} +3 -3
- package/dist/{cli-kavm2cv5.js → cli-c3frqf65.js} +4 -3
- package/dist/{cli-2b0ew5v8.js → cli-c9mtm09b.js} +1 -1
- package/dist/{cli-0d5xddf1.js → cli-f3qa069v.js} +2 -2
- package/dist/{cli-r3y5chyg.js → cli-gtkqc144.js} +4 -4
- package/dist/{cli-0d404x64.js → cli-h3k622d0.js} +1 -1
- package/dist/{cli-y7v6e7yr.js → cli-hjhvekf4.js} +4 -4
- package/dist/{cli-fdnpymsv.js → cli-hsetkkd3.js} +4 -2
- package/dist/{cli-8j1h9627.js → cli-mq580e06.js} +61 -14
- package/dist/{cli-4ke1amr9.js → cli-ncfgsqbd.js} +63 -7
- package/dist/{cli-jrmjxsnk.js → cli-pwc5e83c.js} +3 -3
- package/dist/{cli-4f6vw5vy.js → cli-qwgtacd6.js} +2 -2
- package/dist/{cli-gchg43wt.js → cli-r5g6ngtn.js} +1 -1
- package/dist/{cli-6tfm5fr1.js → cli-tbw13sx8.js} +1 -1
- package/dist/{cli-f1f82qgv.js → cli-trmapn9k.js} +5 -5
- package/dist/{cli-aqr96mny.js → cli-y4ycrnjc.js} +1 -1
- package/dist/{cli-z626gjqj.js → cli-y6mrptcn.js} +1 -1
- package/dist/{cli-1aqc46he.js → cli-z46pnksz.js} +5 -5
- package/dist/cli.js +11 -10
- package/dist/{commands-t3rdvb55.js → commands-z5dbwta3.js} +2 -2
- package/dist/{db-mjqc8hj6.js → db-bbgahh4z.js} +2 -2
- package/dist/{handlers-mcmg5qgc.js → handlers-6b44317z.js} +9 -9
- package/dist/{hook-0p4q1tym.js → hook-sbp5fmps.js} +1 -1
- package/dist/{http-gs6fk9sg.js → http-2xz43gz3.js} +356 -94
- package/dist/{index-20mf9wvc.js → index-0gzmqj4k.js} +8 -8
- package/dist/{index-w15fk28m.js → index-5ghxn8s6.js} +7 -7
- package/dist/{index-744yvc8x.js → index-nhat35em.js} +9 -9
- package/dist/{index-6zpajd9e.js → index-vfedgdw6.js} +26 -15
- package/dist/{keepalive-3gvnbxaj.js → keepalive-62gfj91d.js} +4 -4
- package/dist/{lead-yjbmhj3g.js → lead-ag0akn0v.js} +17 -17
- package/dist/{maintenance-jbvy2bfg.js → maintenance-xxrrzk5x.js} +4 -4
- package/dist/{onboard-sexatejh.js → onboard-64zyfcbs.js} +2 -2
- package/dist/{otel-impl-sgqvcw0f.js → otel-impl-b86qseaa.js} +1 -1
- package/dist/{pricing-refresh-rat240yg.js → pricing-refresh-cgtkffpa.js} +4 -4
- package/dist/{seed-pricing-7yfaf123.js → seed-pricing-1r1y1xkq.js} +3 -3
- package/dist/{setup-vm5vcc6c.js → setup-mkcgkjf7.js} +2 -2
- package/dist/{worker-02y2r0r2.js → worker-351sze63.js} +17 -17
- package/openapi.json +314 -3
- package/package.json +4 -3
- package/src/be/db.ts +43 -3
- package/src/be/memory/providers/sqlite-store.ts +5 -1
- package/src/be/migrations/108_rbac_permission_audit.sql +26 -0
- package/src/be/rbac-audit.ts +218 -0
- package/src/be/scripts/typecheck.ts +2 -0
- package/src/commands/runner.ts +154 -8
- package/src/github/handlers.ts +128 -25
- package/src/http/all-routes.ts +58 -0
- package/src/http/config.ts +55 -0
- package/src/http/favorites.ts +5 -0
- package/src/http/fs.ts +27 -7
- package/src/http/index.ts +26 -0
- package/src/http/kv.ts +21 -4
- package/src/http/route-def.ts +9 -0
- package/src/http/schedules.ts +56 -22
- package/src/http/scripts.ts +33 -0
- package/src/http/workflows.ts +13 -2
- package/src/prompts/base-prompt.ts +7 -6
- package/src/prompts/session-templates.ts +38 -11
- package/src/rbac/can.ts +44 -0
- package/src/rbac/index.ts +13 -0
- package/src/rbac/legacy-policy.ts +185 -0
- package/src/rbac/permissions.ts +182 -0
- package/src/rbac/types.ts +45 -0
- package/src/scripts-runtime/sdk-allowlist.ts +3 -0
- package/src/scripts-runtime/swarm-sdk.ts +81 -0
- package/src/scripts-runtime/types/stdlib.d.ts +18 -2
- package/src/scripts-runtime/types/swarm-sdk.d.ts +20 -2
- package/src/server.ts +6 -0
- package/src/slack/message-text.ts +25 -0
- package/src/stdio.ts +43 -0
- package/src/tests/base-prompt.test.ts +7 -4
- package/src/tests/github-event-filter.test.ts +3 -2
- package/src/tests/github-handlers-inline-comments.test.ts +114 -24
- package/src/tests/harness-provider-resolution.test.ts +5 -0
- package/src/tests/http-api-integration.test.ts +8 -1
- package/src/tests/prompt-template-session.test.ts +21 -8
- package/src/tests/rbac-audit.test.ts +345 -0
- package/src/tests/rbac-charact-http.test.ts +272 -0
- package/src/tests/rbac-charact-misc-tools.test.ts +428 -0
- package/src/tests/rbac-charact-skills.test.ts +492 -0
- package/src/tests/rbac-charact-slack.test.ts +283 -0
- package/src/tests/rbac-e2e-helpers.ts +305 -0
- package/src/tests/rbac-engine.test.ts +433 -0
- package/src/tests/rbac-lifecycle-e2e.test.ts +262 -0
- package/src/tests/rbac-wire-e2e.test.ts +574 -0
- package/src/tests/runner-repo-autostash.test.ts +168 -1
- package/src/tests/schedule-http-triage-tools.test.ts +121 -0
- package/src/tests/scheduled-tasks.test.ts +34 -0
- package/src/tests/scripts-http.test.ts +56 -7
- package/src/tests/slack-delete.test.ts +157 -0
- package/src/tests/slack-message-text.test.ts +37 -1
- package/src/tests/slack-update.test.ts +167 -0
- package/src/tests/swarm-config-reserved-keys.test.ts +17 -0
- package/src/tests/tool-annotations.test.ts +4 -0
- package/src/tests/update-schedule-mcp-tool.test.ts +57 -0
- package/src/tests/workflow-http-v2.test.ts +80 -0
- package/src/tools/cancel-task.ts +13 -3
- package/src/tools/context-diff.ts +12 -1
- package/src/tools/context-history.ts +12 -1
- package/src/tools/credential-bindings/tool.ts +12 -1
- package/src/tools/delete-channel.ts +12 -1
- package/src/tools/get-task-details.ts +1 -1
- package/src/tools/inject-learning.ts +12 -1
- package/src/tools/kv/kv-delete.ts +3 -18
- package/src/tools/kv/kv-incr.ts +3 -18
- package/src/tools/kv/kv-set.ts +3 -20
- package/src/tools/kv/kv-write-auth.ts +40 -0
- package/src/tools/manage-user.ts +12 -1
- package/src/tools/mcp-servers/mcp-server-create.ts +12 -1
- package/src/tools/mcp-servers/mcp-server-delete.ts +12 -1
- package/src/tools/mcp-servers/mcp-server-install.ts +12 -1
- package/src/tools/mcp-servers/mcp-server-uninstall.ts +12 -1
- package/src/tools/mcp-servers/mcp-server-update.ts +12 -1
- package/src/tools/memory-delete.ts +12 -4
- package/src/tools/register-kapso-number.ts +23 -2
- package/src/tools/schedules/create-schedule.ts +4 -3
- package/src/tools/schedules/index.ts +1 -0
- package/src/tools/schedules/list-schedules.ts +36 -2
- package/src/tools/schedules/patch-schedule.ts +405 -0
- package/src/tools/schedules/update-schedule.ts +2 -2
- package/src/tools/script-connections/tool.ts +12 -1
- package/src/tools/skills/skill-create.ts +12 -1
- package/src/tools/skills/skill-delete.ts +12 -1
- package/src/tools/skills/skill-install-remote.ts +12 -1
- package/src/tools/skills/skill-install.ts +12 -1
- package/src/tools/skills/skill-uninstall.ts +12 -1
- package/src/tools/skills/skill-update.ts +25 -2
- package/src/tools/slack-delete.ts +109 -0
- package/src/tools/slack-post.ts +8 -1
- package/src/tools/slack-read.ts +8 -1
- package/src/tools/slack-reply.ts +11 -3
- package/src/tools/slack-start-thread.ts +10 -1
- package/src/tools/slack-update.ts +134 -0
- package/src/tools/slack-upload-file.ts +8 -1
- package/src/tools/swarm-config/delete-config.ts +28 -1
- package/src/tools/swarm-config/get-config.ts +32 -5
- package/src/tools/swarm-config/list-config.ts +31 -5
- package/src/tools/swarm-config/set-config.ts +38 -1
- package/src/tools/task-action.ts +1 -1
- package/src/tools/task-tool-ctx.ts +19 -3
- package/src/tools/tool-config.ts +5 -2
- package/src/tools/update-profile.ts +8 -1
- package/src/tools/workflows/list-workflows.ts +14 -2
- package/templates/skills/swarm-scripts/SKILL.md +4 -9
- package/templates/skills/swarm-scripts/content.md +20 -9
|
@@ -14,13 +14,13 @@ import {
|
|
|
14
14
|
getOAuthApp,
|
|
15
15
|
getOAuthTokens,
|
|
16
16
|
upsertOAuthApp
|
|
17
|
-
} from "./cli-
|
|
17
|
+
} from "./cli-bgef578c.js";
|
|
18
18
|
import {
|
|
19
19
|
startPricingRefreshLoop
|
|
20
|
-
} from "./cli-
|
|
20
|
+
} from "./cli-36ymmpmp.js";
|
|
21
21
|
import {
|
|
22
22
|
seedPricingFromModelsDev
|
|
23
|
-
} from "./cli-
|
|
23
|
+
} from "./cli-tbw13sx8.js";
|
|
24
24
|
import {
|
|
25
25
|
createTrackerAgentMapping,
|
|
26
26
|
createTrackerSync,
|
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
repointTrackerSyncBySwarmId,
|
|
36
36
|
updateTrackerSync,
|
|
37
37
|
updateTrackerSyncSwarmId
|
|
38
|
-
} from "./cli-
|
|
38
|
+
} from "./cli-r5g6ngtn.js";
|
|
39
39
|
import {
|
|
40
40
|
ZodFirstPartyTypeKind,
|
|
41
41
|
objectType
|
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
ensure,
|
|
46
46
|
require_dist,
|
|
47
47
|
runScheduleNow
|
|
48
|
-
} from "./cli-
|
|
48
|
+
} from "./cli-gtkqc144.js";
|
|
49
49
|
import {
|
|
50
50
|
TriggerSchemaError,
|
|
51
51
|
applyDefinitionPatch,
|
|
@@ -57,7 +57,7 @@ import {
|
|
|
57
57
|
startWorkflowExecution,
|
|
58
58
|
validateDefinition,
|
|
59
59
|
validateJsonSchema
|
|
60
|
-
} from "./cli-
|
|
60
|
+
} from "./cli-z46pnksz.js";
|
|
61
61
|
import {
|
|
62
62
|
getApiKey
|
|
63
63
|
} from "./cli-f14fvzag.js";
|
|
@@ -73,7 +73,7 @@ import {
|
|
|
73
73
|
setScriptConnectionEnabled,
|
|
74
74
|
upsertCredentialBinding,
|
|
75
75
|
upsertScriptConnection
|
|
76
|
-
} from "./cli-
|
|
76
|
+
} from "./cli-qwgtacd6.js";
|
|
77
77
|
import {
|
|
78
78
|
CANDIDATE_SET_MULTIPLIER,
|
|
79
79
|
computeScore,
|
|
@@ -85,16 +85,17 @@ import {
|
|
|
85
85
|
isGraphExpansionEnabled,
|
|
86
86
|
rerank,
|
|
87
87
|
rowToCandidate
|
|
88
|
-
} from "./cli-
|
|
88
|
+
} from "./cli-f3qa069v.js";
|
|
89
89
|
import {
|
|
90
90
|
createAdditiveBuffer,
|
|
91
|
-
extractSlackMessageText
|
|
92
|
-
|
|
91
|
+
extractSlackMessageText,
|
|
92
|
+
parseSlackTs
|
|
93
|
+
} from "./cli-bgvskydy.js";
|
|
93
94
|
import {
|
|
94
95
|
getSlackApp,
|
|
95
96
|
startSlackApp,
|
|
96
97
|
stopSlackApp
|
|
97
|
-
} from "./cli-
|
|
98
|
+
} from "./cli-trmapn9k.js";
|
|
98
99
|
import {
|
|
99
100
|
findOrCreateUserByEmail,
|
|
100
101
|
findUserByEmail,
|
|
@@ -106,7 +107,7 @@ import {
|
|
|
106
107
|
recordIdentityEvent,
|
|
107
108
|
resolveUserByToken,
|
|
108
109
|
unlinkIdentity
|
|
109
|
-
} from "./cli-
|
|
110
|
+
} from "./cli-h3k622d0.js";
|
|
110
111
|
import {
|
|
111
112
|
markdownToSlack
|
|
112
113
|
} from "./cli-6xd0bvf8.js";
|
|
@@ -117,7 +118,7 @@ import {
|
|
|
117
118
|
createTaskWithSiblingAwareness,
|
|
118
119
|
githubContextKey,
|
|
119
120
|
linearContextKey
|
|
120
|
-
} from "./cli-
|
|
121
|
+
} from "./cli-y4ycrnjc.js";
|
|
121
122
|
import {
|
|
122
123
|
getAppUrl,
|
|
123
124
|
getMcpBaseUrl,
|
|
@@ -346,7 +347,7 @@ import {
|
|
|
346
347
|
upsertService,
|
|
347
348
|
upsertSwarmConfig,
|
|
348
349
|
validateConfigValue
|
|
349
|
-
} from "./cli-
|
|
350
|
+
} from "./cli-ncfgsqbd.js";
|
|
350
351
|
import {
|
|
351
352
|
init_zod
|
|
352
353
|
} from "./cli-q21d49ac.js";
|
|
@@ -390,7 +391,7 @@ import {
|
|
|
390
391
|
import {
|
|
391
392
|
init_package,
|
|
392
393
|
package_default
|
|
393
|
-
} from "./cli-
|
|
394
|
+
} from "./cli-c3frqf65.js";
|
|
394
395
|
import {
|
|
395
396
|
init_secret_scrubber,
|
|
396
397
|
refreshSecretScrubberCache,
|
|
@@ -19612,6 +19613,300 @@ function registerGithubTaskReactions() {
|
|
|
19612
19613
|
init_zod();
|
|
19613
19614
|
init_db();
|
|
19614
19615
|
|
|
19616
|
+
// src/rbac/legacy-policy.ts
|
|
19617
|
+
var leadOnly = {
|
|
19618
|
+
name: "lead-only",
|
|
19619
|
+
denyReason: "requires lead agent",
|
|
19620
|
+
evaluate: (principal) => principal.kind === "agent" && principal.isLead
|
|
19621
|
+
};
|
|
19622
|
+
var leadOrTaskCreator = {
|
|
19623
|
+
name: "lead-or-task-creator",
|
|
19624
|
+
denyReason: "requires lead agent or task creator",
|
|
19625
|
+
evaluate: (principal, resource) => {
|
|
19626
|
+
if (principal.kind !== "agent")
|
|
19627
|
+
return false;
|
|
19628
|
+
if (principal.isLead)
|
|
19629
|
+
return true;
|
|
19630
|
+
return resource?.kind === "task" && resource.creatorAgentId != null && resource.creatorAgentId === principal.agentId;
|
|
19631
|
+
}
|
|
19632
|
+
};
|
|
19633
|
+
var leadOrResourceOwner = {
|
|
19634
|
+
name: "lead-or-resource-owner",
|
|
19635
|
+
denyReason: "requires lead agent or resource owner",
|
|
19636
|
+
evaluate: (principal, resource) => {
|
|
19637
|
+
if (principal.kind !== "agent")
|
|
19638
|
+
return false;
|
|
19639
|
+
if (principal.isLead)
|
|
19640
|
+
return true;
|
|
19641
|
+
return resource?.kind === "owned" && resource.ownerAgentId != null && resource.ownerAgentId === principal.agentId;
|
|
19642
|
+
}
|
|
19643
|
+
};
|
|
19644
|
+
var leadOrOwnNamespace = {
|
|
19645
|
+
name: "lead-or-own-namespace",
|
|
19646
|
+
denyReason: "requires lead agent or your own task:agent: namespace",
|
|
19647
|
+
evaluate: (principal, resource) => {
|
|
19648
|
+
if (principal.kind !== "agent")
|
|
19649
|
+
return false;
|
|
19650
|
+
if (principal.isLead)
|
|
19651
|
+
return true;
|
|
19652
|
+
return principal.agentId !== "" && resource?.kind === "kv-namespace" && resource.namespace === `task:agent:${principal.agentId}`;
|
|
19653
|
+
}
|
|
19654
|
+
};
|
|
19655
|
+
var requesterOwnsTask = {
|
|
19656
|
+
name: "requester-owns-task",
|
|
19657
|
+
denyReason: "not the task requester",
|
|
19658
|
+
evaluate: (principal, resource) => {
|
|
19659
|
+
if (principal.kind !== "user")
|
|
19660
|
+
return true;
|
|
19661
|
+
return resource?.kind === "task" && resource.requestedByUserId === principal.userId;
|
|
19662
|
+
}
|
|
19663
|
+
};
|
|
19664
|
+
var memoryOwnerOrLeadSwarm = {
|
|
19665
|
+
name: "memory-owner-or-lead-swarm",
|
|
19666
|
+
denyReason: "requires memory owner, or lead agent for swarm-scoped memories",
|
|
19667
|
+
evaluate: (principal, resource) => {
|
|
19668
|
+
if (principal.kind !== "agent" || resource?.kind !== "owned")
|
|
19669
|
+
return false;
|
|
19670
|
+
if (resource.ownerAgentId != null && resource.ownerAgentId === principal.agentId)
|
|
19671
|
+
return true;
|
|
19672
|
+
return principal.isLead && resource.scope === "swarm";
|
|
19673
|
+
}
|
|
19674
|
+
};
|
|
19675
|
+
var taskFsMutate = {
|
|
19676
|
+
name: "operator-or-user-or-lead-or-task-owner",
|
|
19677
|
+
denyReason: "requires operator, user, lead agent, task assignee, or task creator",
|
|
19678
|
+
evaluate: (principal, resource) => {
|
|
19679
|
+
if (principal.kind === "operator" || principal.kind === "user")
|
|
19680
|
+
return true;
|
|
19681
|
+
if (principal.isLead)
|
|
19682
|
+
return true;
|
|
19683
|
+
if (resource?.kind !== "task")
|
|
19684
|
+
return false;
|
|
19685
|
+
return resource.agentId != null && resource.agentId === principal.agentId || resource.creatorAgentId != null && resource.creatorAgentId === principal.agentId;
|
|
19686
|
+
}
|
|
19687
|
+
};
|
|
19688
|
+
var LEGACY_POLICY = {
|
|
19689
|
+
"user.manage": leadOnly,
|
|
19690
|
+
"agent.profile.update.any": leadOnly,
|
|
19691
|
+
"agent.context.read.any": leadOnly,
|
|
19692
|
+
"task.cancel.any": leadOrTaskCreator,
|
|
19693
|
+
"task.read.own": requesterOwnsTask,
|
|
19694
|
+
"task.cancel.own": requesterOwnsTask,
|
|
19695
|
+
"task.action.own": requesterOwnsTask,
|
|
19696
|
+
"task.fs.mutate": taskFsMutate,
|
|
19697
|
+
"memory.learning.inject": leadOnly,
|
|
19698
|
+
"memory.delete.any": memoryOwnerOrLeadSwarm,
|
|
19699
|
+
"channel.delete": leadOnly,
|
|
19700
|
+
"integration.kapso.manage": leadOnly,
|
|
19701
|
+
"integration.slack.post": leadOnly,
|
|
19702
|
+
"integration.slack.read": leadOnly,
|
|
19703
|
+
"integration.slack.thread.start": leadOnly,
|
|
19704
|
+
"integration.slack.upload": leadOnly,
|
|
19705
|
+
"integration.slack.delete": leadOnly,
|
|
19706
|
+
"integration.slack.update": leadOnly,
|
|
19707
|
+
"credential-binding.manage": leadOnly,
|
|
19708
|
+
"script-connection.manage": leadOnly,
|
|
19709
|
+
"config.credential-bindings.write": leadOnly,
|
|
19710
|
+
"config.write.any": leadOnly,
|
|
19711
|
+
"config.delete.any": leadOnly,
|
|
19712
|
+
"config.read.secrets": leadOnly,
|
|
19713
|
+
"skill.create.swarm": leadOnly,
|
|
19714
|
+
"skill.install.any": leadOnly,
|
|
19715
|
+
"skill.install.global": leadOnly,
|
|
19716
|
+
"skill.uninstall.any": leadOnly,
|
|
19717
|
+
"skill.update.any": leadOrResourceOwner,
|
|
19718
|
+
"skill.promote.swarm": leadOnly,
|
|
19719
|
+
"skill.delete.any": leadOrResourceOwner,
|
|
19720
|
+
"mcp-server.create.swarm": leadOnly,
|
|
19721
|
+
"mcp-server.install.any": leadOnly,
|
|
19722
|
+
"mcp-server.uninstall.any": leadOnly,
|
|
19723
|
+
"mcp-server.delete.any": leadOrResourceOwner,
|
|
19724
|
+
"mcp-server.update.any": leadOrResourceOwner,
|
|
19725
|
+
"kv.write.any": leadOrOwnNamespace,
|
|
19726
|
+
"script.global.write": leadOnly,
|
|
19727
|
+
"script.global.delete": leadOnly
|
|
19728
|
+
};
|
|
19729
|
+
|
|
19730
|
+
// src/rbac/can.ts
|
|
19731
|
+
var auditSink = null;
|
|
19732
|
+
function setAuditSink(fn) {
|
|
19733
|
+
auditSink = fn;
|
|
19734
|
+
}
|
|
19735
|
+
function clearAuditSink() {
|
|
19736
|
+
auditSink = null;
|
|
19737
|
+
}
|
|
19738
|
+
function can(check) {
|
|
19739
|
+
const rule = LEGACY_POLICY[check.verb];
|
|
19740
|
+
const decision = rule.evaluate(check.principal, check.resource) ? { allow: true } : { allow: false, reason: rule.denyReason, missing: check.verb };
|
|
19741
|
+
if (auditSink) {
|
|
19742
|
+
try {
|
|
19743
|
+
auditSink(check, decision);
|
|
19744
|
+
} catch {}
|
|
19745
|
+
}
|
|
19746
|
+
return decision;
|
|
19747
|
+
}
|
|
19748
|
+
// src/rbac/permissions.ts
|
|
19749
|
+
init_zod();
|
|
19750
|
+
var PERMISSIONS = {
|
|
19751
|
+
"user.manage": {
|
|
19752
|
+
description: "Create, update, or deactivate user profiles.",
|
|
19753
|
+
namespace: "user"
|
|
19754
|
+
},
|
|
19755
|
+
"agent.profile.update.any": {
|
|
19756
|
+
description: "Update another agent's profile.",
|
|
19757
|
+
namespace: "agent"
|
|
19758
|
+
},
|
|
19759
|
+
"agent.context.read.any": {
|
|
19760
|
+
description: "View or diff another agent's context history.",
|
|
19761
|
+
namespace: "agent"
|
|
19762
|
+
},
|
|
19763
|
+
"task.cancel.any": {
|
|
19764
|
+
description: "Cancel any task (beyond tasks the caller created).",
|
|
19765
|
+
namespace: "task"
|
|
19766
|
+
},
|
|
19767
|
+
"task.read.own": {
|
|
19768
|
+
description: "Read details of a task the principal requested.",
|
|
19769
|
+
namespace: "task"
|
|
19770
|
+
},
|
|
19771
|
+
"task.cancel.own": {
|
|
19772
|
+
description: "Cancel a task the principal requested.",
|
|
19773
|
+
namespace: "task"
|
|
19774
|
+
},
|
|
19775
|
+
"task.action.own": {
|
|
19776
|
+
description: "Run actions (follow-up, retry, ...) on a task the principal requested.",
|
|
19777
|
+
namespace: "task"
|
|
19778
|
+
},
|
|
19779
|
+
"task.fs.mutate": {
|
|
19780
|
+
description: "Mutate a task's filesystem artifacts and attachments.",
|
|
19781
|
+
namespace: "task"
|
|
19782
|
+
},
|
|
19783
|
+
"memory.learning.inject": {
|
|
19784
|
+
description: "Inject a learning into another agent's memory.",
|
|
19785
|
+
namespace: "memory"
|
|
19786
|
+
},
|
|
19787
|
+
"memory.delete.any": {
|
|
19788
|
+
description: "Delete a memory entry (own entries, or swarm-scoped entries as lead).",
|
|
19789
|
+
namespace: "memory"
|
|
19790
|
+
},
|
|
19791
|
+
"channel.delete": {
|
|
19792
|
+
description: "Delete a Slack channel.",
|
|
19793
|
+
namespace: "channel"
|
|
19794
|
+
},
|
|
19795
|
+
"integration.kapso.manage": {
|
|
19796
|
+
description: "Register or unregister a Kapso inbound number.",
|
|
19797
|
+
namespace: "integration"
|
|
19798
|
+
},
|
|
19799
|
+
"integration.slack.post": {
|
|
19800
|
+
description: "Post to a direct Slack channel.",
|
|
19801
|
+
namespace: "integration"
|
|
19802
|
+
},
|
|
19803
|
+
"integration.slack.read": {
|
|
19804
|
+
description: "Read a direct Slack channel.",
|
|
19805
|
+
namespace: "integration"
|
|
19806
|
+
},
|
|
19807
|
+
"integration.slack.thread.start": {
|
|
19808
|
+
description: "Start a thread in a direct Slack channel.",
|
|
19809
|
+
namespace: "integration"
|
|
19810
|
+
},
|
|
19811
|
+
"integration.slack.upload": {
|
|
19812
|
+
description: "Upload a file to a direct Slack channel.",
|
|
19813
|
+
namespace: "integration"
|
|
19814
|
+
},
|
|
19815
|
+
"integration.slack.delete": {
|
|
19816
|
+
description: "Delete a Slack message.",
|
|
19817
|
+
namespace: "integration"
|
|
19818
|
+
},
|
|
19819
|
+
"integration.slack.update": {
|
|
19820
|
+
description: "Update (edit) a Slack message.",
|
|
19821
|
+
namespace: "integration"
|
|
19822
|
+
},
|
|
19823
|
+
"credential-binding.manage": {
|
|
19824
|
+
description: "Manage script credential bindings.",
|
|
19825
|
+
namespace: "credential-binding"
|
|
19826
|
+
},
|
|
19827
|
+
"script-connection.manage": {
|
|
19828
|
+
description: "Manage script connections.",
|
|
19829
|
+
namespace: "script-connection"
|
|
19830
|
+
},
|
|
19831
|
+
"config.credential-bindings.write": {
|
|
19832
|
+
description: "Write the SCRIPT_CREDENTIAL_BINDINGS swarm-config key.",
|
|
19833
|
+
namespace: "config"
|
|
19834
|
+
},
|
|
19835
|
+
"config.write.any": {
|
|
19836
|
+
description: "Write any swarm-config key.",
|
|
19837
|
+
namespace: "config"
|
|
19838
|
+
},
|
|
19839
|
+
"config.delete.any": {
|
|
19840
|
+
description: "Delete any swarm-config entry.",
|
|
19841
|
+
namespace: "config"
|
|
19842
|
+
},
|
|
19843
|
+
"config.read.secrets": {
|
|
19844
|
+
description: "Read unmasked secret config values.",
|
|
19845
|
+
namespace: "config"
|
|
19846
|
+
},
|
|
19847
|
+
"skill.create.swarm": {
|
|
19848
|
+
description: "Create a swarm-scoped skill.",
|
|
19849
|
+
namespace: "skill"
|
|
19850
|
+
},
|
|
19851
|
+
"skill.install.any": {
|
|
19852
|
+
description: "Install a skill for another agent.",
|
|
19853
|
+
namespace: "skill"
|
|
19854
|
+
},
|
|
19855
|
+
"skill.install.global": {
|
|
19856
|
+
description: "Install a remote/global skill.",
|
|
19857
|
+
namespace: "skill"
|
|
19858
|
+
},
|
|
19859
|
+
"skill.uninstall.any": {
|
|
19860
|
+
description: "Uninstall a skill for another agent.",
|
|
19861
|
+
namespace: "skill"
|
|
19862
|
+
},
|
|
19863
|
+
"skill.update.any": {
|
|
19864
|
+
description: "Update a skill the caller does not own.",
|
|
19865
|
+
namespace: "skill"
|
|
19866
|
+
},
|
|
19867
|
+
"skill.promote.swarm": {
|
|
19868
|
+
description: "Promote a skill to swarm scope (skill-approval path).",
|
|
19869
|
+
namespace: "skill"
|
|
19870
|
+
},
|
|
19871
|
+
"skill.delete.any": {
|
|
19872
|
+
description: "Delete a skill the caller does not own.",
|
|
19873
|
+
namespace: "skill"
|
|
19874
|
+
},
|
|
19875
|
+
"mcp-server.create.swarm": {
|
|
19876
|
+
description: "Create a swarm- or global-scoped MCP server.",
|
|
19877
|
+
namespace: "mcp-server"
|
|
19878
|
+
},
|
|
19879
|
+
"mcp-server.install.any": {
|
|
19880
|
+
description: "Install an MCP server for another agent.",
|
|
19881
|
+
namespace: "mcp-server"
|
|
19882
|
+
},
|
|
19883
|
+
"mcp-server.uninstall.any": {
|
|
19884
|
+
description: "Uninstall an MCP server for another agent.",
|
|
19885
|
+
namespace: "mcp-server"
|
|
19886
|
+
},
|
|
19887
|
+
"mcp-server.delete.any": {
|
|
19888
|
+
description: "Delete an MCP server the caller does not own.",
|
|
19889
|
+
namespace: "mcp-server"
|
|
19890
|
+
},
|
|
19891
|
+
"mcp-server.update.any": {
|
|
19892
|
+
description: "Update an MCP server the caller does not own.",
|
|
19893
|
+
namespace: "mcp-server"
|
|
19894
|
+
},
|
|
19895
|
+
"kv.write.any": {
|
|
19896
|
+
description: "Write another agent's task:agent: KV namespace.",
|
|
19897
|
+
namespace: "kv"
|
|
19898
|
+
},
|
|
19899
|
+
"script.global.write": {
|
|
19900
|
+
description: "Create or update a global-scope script.",
|
|
19901
|
+
namespace: "script"
|
|
19902
|
+
},
|
|
19903
|
+
"script.global.delete": {
|
|
19904
|
+
description: "Delete a global-scope script.",
|
|
19905
|
+
namespace: "script"
|
|
19906
|
+
}
|
|
19907
|
+
};
|
|
19908
|
+
var PERMISSION_VERBS = Object.keys(PERMISSIONS);
|
|
19909
|
+
var PermissionVerbSchema = _enum(PERMISSION_VERBS);
|
|
19615
19910
|
// src/tools/task-tool-ctx.ts
|
|
19616
19911
|
function ownerCtx(info) {
|
|
19617
19912
|
return {
|
|
@@ -19629,8 +19924,14 @@ function userCtx(user, sessionId) {
|
|
|
19629
19924
|
sessionId
|
|
19630
19925
|
};
|
|
19631
19926
|
}
|
|
19632
|
-
function assertOwnsTask(ctx, task) {
|
|
19633
|
-
|
|
19927
|
+
function assertOwnsTask(ctx, task, verb = "task.read.own") {
|
|
19928
|
+
const decision = can({
|
|
19929
|
+
principal: ctx.kind === "owner" ? ctx.agentId ? { kind: "agent", agentId: ctx.agentId, isLead: false } : { kind: "operator" } : { kind: "user", userId: ctx.userId },
|
|
19930
|
+
verb,
|
|
19931
|
+
resource: { kind: "task", taskId: task.id, requestedByUserId: task.requestedByUserId },
|
|
19932
|
+
source: "mcp"
|
|
19933
|
+
});
|
|
19934
|
+
if (decision.allow) {
|
|
19634
19935
|
return null;
|
|
19635
19936
|
}
|
|
19636
19937
|
const message = `Forbidden: this task is not yours (task ${task.id}).`;
|
|
@@ -19672,7 +19973,7 @@ async function initOtel(serviceRole = process.env.AGENT_ROLE || "api") {
|
|
|
19672
19973
|
return;
|
|
19673
19974
|
initialized2 = true;
|
|
19674
19975
|
try {
|
|
19675
|
-
const impl = await import("./otel-impl-
|
|
19976
|
+
const impl = await import("./otel-impl-b86qseaa.js");
|
|
19676
19977
|
await impl.boot(serviceRole);
|
|
19677
19978
|
realWithSpan = impl.withSpan;
|
|
19678
19979
|
realStartSpan = impl.startSpan;
|
|
@@ -19852,8 +20153,17 @@ async function cancelTaskHandler(ctx, { taskId, reason }) {
|
|
|
19852
20153
|
message: `Task "${taskId}" not found.`
|
|
19853
20154
|
};
|
|
19854
20155
|
}
|
|
19855
|
-
const
|
|
19856
|
-
|
|
20156
|
+
const decision = can({
|
|
20157
|
+
principal: { kind: "agent", agentId: callerAgent.id, isLead: callerAgent.isLead },
|
|
20158
|
+
verb: "task.cancel.any",
|
|
20159
|
+
resource: {
|
|
20160
|
+
kind: "task",
|
|
20161
|
+
taskId: existingTask2.id,
|
|
20162
|
+
creatorAgentId: existingTask2.creatorAgentId
|
|
20163
|
+
},
|
|
20164
|
+
source: "mcp"
|
|
20165
|
+
});
|
|
20166
|
+
if (!decision.allow) {
|
|
19857
20167
|
return {
|
|
19858
20168
|
success: false,
|
|
19859
20169
|
message: "Only the lead or task creator can cancel tasks."
|
|
@@ -19882,7 +20192,7 @@ async function cancelTaskHandler(ctx, { taskId, reason }) {
|
|
|
19882
20192
|
message: `Task "${taskId}" not found.`
|
|
19883
20193
|
};
|
|
19884
20194
|
}
|
|
19885
|
-
const ownershipError = assertOwnsTask(ctx, existingTask);
|
|
20195
|
+
const ownershipError = assertOwnsTask(ctx, existingTask, "task.cancel.own");
|
|
19886
20196
|
if (ownershipError)
|
|
19887
20197
|
return ownershipError;
|
|
19888
20198
|
const cancelled = cancelTask(taskId, reason);
|
|
@@ -19999,7 +20309,17 @@ var registerContextDiffTool = (server) => {
|
|
|
19999
20309
|
}
|
|
20000
20310
|
if (version.agentId !== requestInfo.agentId) {
|
|
20001
20311
|
const callerAgent = getAgentById(requestInfo.agentId);
|
|
20002
|
-
|
|
20312
|
+
const decision = can({
|
|
20313
|
+
principal: {
|
|
20314
|
+
kind: "agent",
|
|
20315
|
+
agentId: requestInfo.agentId,
|
|
20316
|
+
isLead: callerAgent?.isLead ?? false
|
|
20317
|
+
},
|
|
20318
|
+
verb: "agent.context.read.any",
|
|
20319
|
+
resource: { kind: "agent", agentId: version.agentId },
|
|
20320
|
+
source: "mcp"
|
|
20321
|
+
});
|
|
20322
|
+
if (!decision.allow) {
|
|
20003
20323
|
return {
|
|
20004
20324
|
content: [
|
|
20005
20325
|
{
|
|
@@ -20125,7 +20445,17 @@ var registerContextHistoryTool = (server) => {
|
|
|
20125
20445
|
}
|
|
20126
20446
|
if (targetAgentId !== requestInfo.agentId) {
|
|
20127
20447
|
const callerAgent = getAgentById(requestInfo.agentId);
|
|
20128
|
-
|
|
20448
|
+
const decision = can({
|
|
20449
|
+
principal: {
|
|
20450
|
+
kind: "agent",
|
|
20451
|
+
agentId: requestInfo.agentId,
|
|
20452
|
+
isLead: callerAgent?.isLead ?? false
|
|
20453
|
+
},
|
|
20454
|
+
verb: "agent.context.read.any",
|
|
20455
|
+
resource: { kind: "agent", agentId: targetAgentId },
|
|
20456
|
+
source: "mcp"
|
|
20457
|
+
});
|
|
20458
|
+
if (!decision.allow) {
|
|
20129
20459
|
return {
|
|
20130
20460
|
content: [
|
|
20131
20461
|
{
|
|
@@ -20953,7 +21283,17 @@ var registerCredentialBindingsTool = (server) => {
|
|
|
20953
21283
|
};
|
|
20954
21284
|
}
|
|
20955
21285
|
const agent = getAgentById(requestInfo.agentId);
|
|
20956
|
-
|
|
21286
|
+
const decision = can({
|
|
21287
|
+
principal: {
|
|
21288
|
+
kind: "agent",
|
|
21289
|
+
agentId: requestInfo.agentId,
|
|
21290
|
+
isLead: agent?.isLead ?? false
|
|
21291
|
+
},
|
|
21292
|
+
verb: "credential-binding.manage",
|
|
21293
|
+
resource: { kind: "none" },
|
|
21294
|
+
source: "mcp"
|
|
21295
|
+
});
|
|
21296
|
+
if (!decision.allow) {
|
|
20957
21297
|
return {
|
|
20958
21298
|
content: [{ type: "text", text: "Only the lead can manage credential bindings." }],
|
|
20959
21299
|
structuredContent: {
|
|
@@ -21210,7 +21550,17 @@ var registerDeleteChannelTool = (server) => {
|
|
|
21210
21550
|
};
|
|
21211
21551
|
}
|
|
21212
21552
|
const callingAgent = getAgentById(requestInfo.agentId);
|
|
21213
|
-
|
|
21553
|
+
const decision = can({
|
|
21554
|
+
principal: {
|
|
21555
|
+
kind: "agent",
|
|
21556
|
+
agentId: requestInfo.agentId,
|
|
21557
|
+
isLead: callingAgent?.isLead ?? false
|
|
21558
|
+
},
|
|
21559
|
+
verb: "channel.delete",
|
|
21560
|
+
resource: { kind: "none" },
|
|
21561
|
+
source: "mcp"
|
|
21562
|
+
});
|
|
21563
|
+
if (!decision.allow) {
|
|
21214
21564
|
return {
|
|
21215
21565
|
content: [
|
|
21216
21566
|
{ type: "text", text: "Not authorized. Only the lead agent can delete channels." }
|
|
@@ -21389,7 +21739,7 @@ async function getTaskDetailsHandler(ctx, { taskId }) {
|
|
|
21389
21739
|
}
|
|
21390
21740
|
};
|
|
21391
21741
|
}
|
|
21392
|
-
const ownershipError = assertOwnsTask(ctx, task);
|
|
21742
|
+
const ownershipError = assertOwnsTask(ctx, task, "task.read.own");
|
|
21393
21743
|
if (ownershipError)
|
|
21394
21744
|
return ownershipError;
|
|
21395
21745
|
const logs = getLogsByTaskIdChronological(taskId);
|
|
@@ -21592,7 +21942,17 @@ var registerInjectLearningTool = (server) => {
|
|
|
21592
21942
|
};
|
|
21593
21943
|
}
|
|
21594
21944
|
const callerAgent = getAgentById(requestInfo.agentId);
|
|
21595
|
-
|
|
21945
|
+
const decision = can({
|
|
21946
|
+
principal: {
|
|
21947
|
+
kind: "agent",
|
|
21948
|
+
agentId: requestInfo.agentId,
|
|
21949
|
+
isLead: callerAgent?.isLead ?? false
|
|
21950
|
+
},
|
|
21951
|
+
verb: "memory.learning.inject",
|
|
21952
|
+
resource: { kind: "agent", agentId: targetAgentId },
|
|
21953
|
+
source: "mcp"
|
|
21954
|
+
});
|
|
21955
|
+
if (!decision.allow) {
|
|
21596
21956
|
return {
|
|
21597
21957
|
content: [{ type: "text", text: "Only the lead agent can inject learnings." }],
|
|
21598
21958
|
structuredContent: {
|
|
@@ -21771,6 +22131,28 @@ init_zod();
|
|
|
21771
22131
|
init_db();
|
|
21772
22132
|
init_types();
|
|
21773
22133
|
|
|
22134
|
+
// src/tools/kv/kv-write-auth.ts
|
|
22135
|
+
init_db();
|
|
22136
|
+
function kvWriteAuthError(namespace, info) {
|
|
22137
|
+
if (namespace.startsWith("task:page:")) {
|
|
22138
|
+
return "task:page:* writes require a page-proxy request, not an MCP call";
|
|
22139
|
+
}
|
|
22140
|
+
if (namespace.startsWith("task:agent:")) {
|
|
22141
|
+
const ownNamespace = info.agentId != null && namespace === `task:agent:${info.agentId}`;
|
|
22142
|
+
const agent = !ownNamespace && info.agentId ? getAgentById(info.agentId) : null;
|
|
22143
|
+
const allowed = info.agentId != null && can({
|
|
22144
|
+
principal: { kind: "agent", agentId: info.agentId, isLead: agent?.isLead ?? false },
|
|
22145
|
+
verb: "kv.write.any",
|
|
22146
|
+
resource: { kind: "kv-namespace", namespace },
|
|
22147
|
+
source: "mcp"
|
|
22148
|
+
}).allow;
|
|
22149
|
+
if (!allowed) {
|
|
22150
|
+
return "writes to another agent's namespace require lead";
|
|
22151
|
+
}
|
|
22152
|
+
}
|
|
22153
|
+
return null;
|
|
22154
|
+
}
|
|
22155
|
+
|
|
21774
22156
|
// src/tools/kv/resolve-namespace.ts
|
|
21775
22157
|
init_db();
|
|
21776
22158
|
function resolveNamespace(explicit, info) {
|
|
@@ -21799,23 +22181,6 @@ function resolveNamespace(explicit, info) {
|
|
|
21799
22181
|
}
|
|
21800
22182
|
|
|
21801
22183
|
// src/tools/kv/kv-delete.ts
|
|
21802
|
-
function authError(namespace, info) {
|
|
21803
|
-
if (namespace.startsWith("task:page:")) {
|
|
21804
|
-
return "task:page:* writes require a page-proxy request, not an MCP call";
|
|
21805
|
-
}
|
|
21806
|
-
if (namespace.startsWith("task:agent:")) {
|
|
21807
|
-
const target = namespace.slice("task:agent:".length);
|
|
21808
|
-
if (info.agentId && target === info.agentId)
|
|
21809
|
-
return null;
|
|
21810
|
-
if (info.agentId) {
|
|
21811
|
-
const agent = getAgentById(info.agentId);
|
|
21812
|
-
if (agent?.isLead)
|
|
21813
|
-
return null;
|
|
21814
|
-
}
|
|
21815
|
-
return "writes to another agent's namespace require lead";
|
|
21816
|
-
}
|
|
21817
|
-
return null;
|
|
21818
|
-
}
|
|
21819
22184
|
var registerKvDeleteTool = (server) => {
|
|
21820
22185
|
createToolRegistrar(server)("kv-delete", {
|
|
21821
22186
|
title: "KV Delete",
|
|
@@ -21844,7 +22209,7 @@ var registerKvDeleteTool = (server) => {
|
|
|
21844
22209
|
}
|
|
21845
22210
|
};
|
|
21846
22211
|
}
|
|
21847
|
-
const authErr =
|
|
22212
|
+
const authErr = kvWriteAuthError(resolved.namespace, { agentId: requestInfo.agentId });
|
|
21848
22213
|
if (authErr) {
|
|
21849
22214
|
return {
|
|
21850
22215
|
content: [{ type: "text", text: authErr }],
|
|
@@ -21928,23 +22293,6 @@ var registerKvGetTool = (server) => {
|
|
|
21928
22293
|
init_zod();
|
|
21929
22294
|
init_db();
|
|
21930
22295
|
init_types();
|
|
21931
|
-
function authError2(namespace, info) {
|
|
21932
|
-
if (namespace.startsWith("task:page:")) {
|
|
21933
|
-
return "task:page:* writes require a page-proxy request, not an MCP call";
|
|
21934
|
-
}
|
|
21935
|
-
if (namespace.startsWith("task:agent:")) {
|
|
21936
|
-
const target = namespace.slice("task:agent:".length);
|
|
21937
|
-
if (info.agentId && target === info.agentId)
|
|
21938
|
-
return null;
|
|
21939
|
-
if (info.agentId) {
|
|
21940
|
-
const agent = getAgentById(info.agentId);
|
|
21941
|
-
if (agent?.isLead)
|
|
21942
|
-
return null;
|
|
21943
|
-
}
|
|
21944
|
-
return "writes to another agent's namespace require lead";
|
|
21945
|
-
}
|
|
21946
|
-
return null;
|
|
21947
|
-
}
|
|
21948
22296
|
var registerKvIncrTool = (server) => {
|
|
21949
22297
|
createToolRegistrar(server)("kv-incr", {
|
|
21950
22298
|
title: "KV Incr",
|
|
@@ -21974,7 +22322,7 @@ var registerKvIncrTool = (server) => {
|
|
|
21974
22322
|
}
|
|
21975
22323
|
};
|
|
21976
22324
|
}
|
|
21977
|
-
const authErr =
|
|
22325
|
+
const authErr = kvWriteAuthError(resolved.namespace, { agentId: requestInfo.agentId });
|
|
21978
22326
|
if (authErr) {
|
|
21979
22327
|
return {
|
|
21980
22328
|
content: [{ type: "text", text: authErr }],
|
|
@@ -22095,23 +22443,6 @@ init_zod();
|
|
|
22095
22443
|
init_db();
|
|
22096
22444
|
init_types();
|
|
22097
22445
|
var MAX_KV_BODY_BYTES = 2 * 1024 * 1024;
|
|
22098
|
-
function authError3(namespace, info) {
|
|
22099
|
-
if (namespace.startsWith("task:page:")) {
|
|
22100
|
-
return "task:page:* writes require a page-proxy request, not an MCP call";
|
|
22101
|
-
}
|
|
22102
|
-
if (namespace.startsWith("task:agent:")) {
|
|
22103
|
-
const target = namespace.slice("task:agent:".length);
|
|
22104
|
-
if (info.agentId && target === info.agentId)
|
|
22105
|
-
return null;
|
|
22106
|
-
if (info.agentId) {
|
|
22107
|
-
const agent = getAgentById(info.agentId);
|
|
22108
|
-
if (agent?.isLead)
|
|
22109
|
-
return null;
|
|
22110
|
-
}
|
|
22111
|
-
return "writes to another agent's namespace require lead";
|
|
22112
|
-
}
|
|
22113
|
-
return null;
|
|
22114
|
-
}
|
|
22115
22446
|
var registerKvSetTool = (server) => {
|
|
22116
22447
|
createToolRegistrar(server)("kv-set", {
|
|
22117
22448
|
title: "KV Set",
|
|
@@ -22143,7 +22474,7 @@ var registerKvSetTool = (server) => {
|
|
|
22143
22474
|
}
|
|
22144
22475
|
};
|
|
22145
22476
|
}
|
|
22146
|
-
const authErr =
|
|
22477
|
+
const authErr = kvWriteAuthError(resolved.namespace, { agentId: requestInfo.agentId });
|
|
22147
22478
|
if (authErr) {
|
|
22148
22479
|
return {
|
|
22149
22480
|
content: [{ type: "text", text: authErr }],
|
|
@@ -22412,7 +22743,17 @@ var registerManageUserTool = (server) => {
|
|
|
22412
22743
|
inputSchema: InputSchema
|
|
22413
22744
|
}, async (input, requestInfo) => {
|
|
22414
22745
|
const callerAgent = requestInfo.agentId ? getAgentById(requestInfo.agentId) : null;
|
|
22415
|
-
|
|
22746
|
+
const decision = can({
|
|
22747
|
+
principal: {
|
|
22748
|
+
kind: "agent",
|
|
22749
|
+
agentId: requestInfo.agentId ?? "",
|
|
22750
|
+
isLead: callerAgent?.isLead ?? false
|
|
22751
|
+
},
|
|
22752
|
+
verb: "user.manage",
|
|
22753
|
+
resource: { kind: "none" },
|
|
22754
|
+
source: "mcp"
|
|
22755
|
+
});
|
|
22756
|
+
if (!decision.allow || !callerAgent) {
|
|
22416
22757
|
return {
|
|
22417
22758
|
content: [
|
|
22418
22759
|
{ type: "text", text: "Only the lead agent can manage user profiles." }
|
|
@@ -22634,7 +22975,17 @@ var registerMcpServerCreateTool = (server) => {
|
|
|
22634
22975
|
const scope = args.scope ?? "agent";
|
|
22635
22976
|
if (scope === "swarm" || scope === "global") {
|
|
22636
22977
|
const agent = getAgentById(requestInfo.agentId);
|
|
22637
|
-
|
|
22978
|
+
const decision = can({
|
|
22979
|
+
principal: {
|
|
22980
|
+
kind: "agent",
|
|
22981
|
+
agentId: requestInfo.agentId,
|
|
22982
|
+
isLead: agent?.isLead ?? false
|
|
22983
|
+
},
|
|
22984
|
+
verb: "mcp-server.create.swarm",
|
|
22985
|
+
resource: { kind: "none" },
|
|
22986
|
+
source: "mcp"
|
|
22987
|
+
});
|
|
22988
|
+
if (!decision.allow) {
|
|
22638
22989
|
return {
|
|
22639
22990
|
content: [
|
|
22640
22991
|
{
|
|
@@ -22722,7 +23073,17 @@ var registerMcpServerDeleteTool = (server) => {
|
|
|
22722
23073
|
};
|
|
22723
23074
|
}
|
|
22724
23075
|
const agent = getAgentById(requestInfo.agentId);
|
|
22725
|
-
|
|
23076
|
+
const decision = can({
|
|
23077
|
+
principal: {
|
|
23078
|
+
kind: "agent",
|
|
23079
|
+
agentId: requestInfo.agentId,
|
|
23080
|
+
isLead: agent?.isLead ?? false
|
|
23081
|
+
},
|
|
23082
|
+
verb: "mcp-server.delete.any",
|
|
23083
|
+
resource: { kind: "owned", ownerAgentId: existing.ownerAgentId },
|
|
23084
|
+
source: "mcp"
|
|
23085
|
+
});
|
|
23086
|
+
if (!decision.allow) {
|
|
22726
23087
|
return {
|
|
22727
23088
|
content: [
|
|
22728
23089
|
{ type: "text", text: "Only the owning agent or lead can delete this MCP server." }
|
|
@@ -22841,7 +23202,17 @@ var registerMcpServerInstallTool = (server) => {
|
|
|
22841
23202
|
const targetAgentId = args.agentId ?? requestInfo.agentId;
|
|
22842
23203
|
if (targetAgentId !== requestInfo.agentId) {
|
|
22843
23204
|
const agent = getAgentById(requestInfo.agentId);
|
|
22844
|
-
|
|
23205
|
+
const decision = can({
|
|
23206
|
+
principal: {
|
|
23207
|
+
kind: "agent",
|
|
23208
|
+
agentId: requestInfo.agentId,
|
|
23209
|
+
isLead: agent?.isLead ?? false
|
|
23210
|
+
},
|
|
23211
|
+
verb: "mcp-server.install.any",
|
|
23212
|
+
resource: { kind: "agent", agentId: targetAgentId },
|
|
23213
|
+
source: "mcp"
|
|
23214
|
+
});
|
|
23215
|
+
if (!decision.allow) {
|
|
22845
23216
|
return {
|
|
22846
23217
|
content: [
|
|
22847
23218
|
{
|
|
@@ -22987,7 +23358,17 @@ var registerMcpServerUninstallTool = (server) => {
|
|
|
22987
23358
|
const targetAgentId = args.agentId ?? requestInfo.agentId;
|
|
22988
23359
|
if (targetAgentId !== requestInfo.agentId) {
|
|
22989
23360
|
const agent = getAgentById(requestInfo.agentId);
|
|
22990
|
-
|
|
23361
|
+
const decision = can({
|
|
23362
|
+
principal: {
|
|
23363
|
+
kind: "agent",
|
|
23364
|
+
agentId: requestInfo.agentId,
|
|
23365
|
+
isLead: agent?.isLead ?? false
|
|
23366
|
+
},
|
|
23367
|
+
verb: "mcp-server.uninstall.any",
|
|
23368
|
+
resource: { kind: "agent", agentId: targetAgentId },
|
|
23369
|
+
source: "mcp"
|
|
23370
|
+
});
|
|
23371
|
+
if (!decision.allow) {
|
|
22991
23372
|
return {
|
|
22992
23373
|
content: [
|
|
22993
23374
|
{
|
|
@@ -23067,7 +23448,17 @@ var registerMcpServerUpdateTool = (server) => {
|
|
|
23067
23448
|
};
|
|
23068
23449
|
}
|
|
23069
23450
|
const agent = getAgentById(requestInfo.agentId);
|
|
23070
|
-
|
|
23451
|
+
const decision = can({
|
|
23452
|
+
principal: {
|
|
23453
|
+
kind: "agent",
|
|
23454
|
+
agentId: requestInfo.agentId,
|
|
23455
|
+
isLead: agent?.isLead ?? false
|
|
23456
|
+
},
|
|
23457
|
+
verb: "mcp-server.update.any",
|
|
23458
|
+
resource: { kind: "owned", ownerAgentId: existing.ownerAgentId },
|
|
23459
|
+
source: "mcp"
|
|
23460
|
+
});
|
|
23461
|
+
if (!decision.allow) {
|
|
23071
23462
|
return {
|
|
23072
23463
|
content: [
|
|
23073
23464
|
{ type: "text", text: "Only the owning agent or lead can update this MCP server." }
|
|
@@ -23179,10 +23570,18 @@ var registerMemoryDeleteTool = (server) => {
|
|
|
23179
23570
|
}
|
|
23180
23571
|
};
|
|
23181
23572
|
}
|
|
23182
|
-
const isOwner = memory.agentId === requestInfo.agentId;
|
|
23183
23573
|
const agent = getAgentById(requestInfo.agentId);
|
|
23184
|
-
const
|
|
23185
|
-
|
|
23574
|
+
const decision = can({
|
|
23575
|
+
principal: {
|
|
23576
|
+
kind: "agent",
|
|
23577
|
+
agentId: requestInfo.agentId,
|
|
23578
|
+
isLead: agent?.isLead ?? false
|
|
23579
|
+
},
|
|
23580
|
+
verb: "memory.delete.any",
|
|
23581
|
+
resource: { kind: "owned", ownerAgentId: memory.agentId, scope: memory.scope },
|
|
23582
|
+
source: "mcp"
|
|
23583
|
+
});
|
|
23584
|
+
if (!decision.allow) {
|
|
23186
23585
|
return {
|
|
23187
23586
|
content: [{ type: "text", text: "You don't have permission to delete this memory." }],
|
|
23188
23587
|
structuredContent: {
|
|
@@ -25411,7 +25810,17 @@ var registerRegisterKapsoNumberTool = (server) => {
|
|
|
25411
25810
|
}, async ({ phoneNumberId, agentId, workflowId, name }, requestInfo) => {
|
|
25412
25811
|
try {
|
|
25413
25812
|
const callerAgent = requestInfo.agentId ? getAgentById(requestInfo.agentId) : null;
|
|
25414
|
-
|
|
25813
|
+
const decision = can({
|
|
25814
|
+
principal: {
|
|
25815
|
+
kind: "agent",
|
|
25816
|
+
agentId: requestInfo.agentId ?? "",
|
|
25817
|
+
isLead: callerAgent?.isLead ?? false
|
|
25818
|
+
},
|
|
25819
|
+
verb: "integration.kapso.manage",
|
|
25820
|
+
resource: { kind: "none" },
|
|
25821
|
+
source: "mcp"
|
|
25822
|
+
});
|
|
25823
|
+
if (!decision.allow) {
|
|
25415
25824
|
const msg = "Permission denied. Only the lead can register a Kapso number.";
|
|
25416
25825
|
return {
|
|
25417
25826
|
content: [{ type: "text", text: msg }],
|
|
@@ -25489,7 +25898,17 @@ var registerUnregisterKapsoNumberTool = (server) => {
|
|
|
25489
25898
|
}, async ({ phoneNumberId }, requestInfo) => {
|
|
25490
25899
|
try {
|
|
25491
25900
|
const callerAgent = requestInfo.agentId ? getAgentById(requestInfo.agentId) : null;
|
|
25492
|
-
|
|
25901
|
+
const decision = can({
|
|
25902
|
+
principal: {
|
|
25903
|
+
kind: "agent",
|
|
25904
|
+
agentId: requestInfo.agentId ?? "",
|
|
25905
|
+
isLead: callerAgent?.isLead ?? false
|
|
25906
|
+
},
|
|
25907
|
+
verb: "integration.kapso.manage",
|
|
25908
|
+
resource: { kind: "none" },
|
|
25909
|
+
source: "mcp"
|
|
25910
|
+
});
|
|
25911
|
+
if (!decision.allow) {
|
|
25493
25912
|
const msg = "Permission denied. Only the lead can unregister a Kapso number.";
|
|
25494
25913
|
return {
|
|
25495
25914
|
content: [{ type: "text", text: msg }],
|
|
@@ -25917,7 +26336,7 @@ init_types();
|
|
|
25917
26336
|
var createScheduleInputSchema = object({
|
|
25918
26337
|
name: string().min(1).max(100).describe("Unique name for the schedule (e.g., 'daily-cleanup')"),
|
|
25919
26338
|
taskTemplate: string().min(1).optional().describe("The task description that will be created each time. Required when targetType is 'agent-task' (the default)."),
|
|
25920
|
-
targetType: ScheduledTaskTargetTypeSchema.default("agent-task").optional().describe("Execution target
|
|
26339
|
+
targetType: ScheduledTaskTargetTypeSchema.default("agent-task").optional().describe("Execution target. Use 'workflow' + workflowId when the schedule only starts a workflow; " + "use 'script' + scriptName/scriptArgs when it only runs a catalog script; " + "use 'agent-task' only when a reasoning agent genuinely needs to be in the loop. " + "Do not create an agent-task whose taskTemplate just tells an agent to trigger a workflow or script."),
|
|
25921
26340
|
workflowId: string().uuid().optional().describe("Workflow ID to trigger. Required when targetType is 'workflow'."),
|
|
25922
26341
|
scriptName: string().optional().describe("Catalog script name (global scope). Required when targetType is 'script'."),
|
|
25923
26342
|
scriptArgs: record(string(), unknown()).optional().describe("JSON args passed to the script. Used when targetType is 'script'."),
|
|
@@ -26493,6 +26912,8 @@ var registerListSchedulesTool = (server) => {
|
|
|
26493
26912
|
name: string().optional().describe("Filter by name (partial match)"),
|
|
26494
26913
|
scheduleType: _enum(["recurring", "one_time"]).optional().describe("Filter by schedule type"),
|
|
26495
26914
|
hideCompleted: boolean().default(true).optional().describe("Hide completed one-time schedules (default: true)"),
|
|
26915
|
+
consecutiveErrorsMin: number().int().min(0).optional().describe("Only return schedules with at least this many consecutive errors."),
|
|
26916
|
+
lastRunStatus: _enum(["failed", "succeeded"]).optional().describe("Filter by derived last run status. `failed` means consecutiveErrors > 0; `succeeded` means lastRunAt is set and consecutiveErrors is 0."),
|
|
26496
26917
|
includeFull: boolean().optional().describe("Return the full `taskTemplate` instead of a short `taskTemplatePreview`. Default false.")
|
|
26497
26918
|
}),
|
|
26498
26919
|
outputSchema: object({
|
|
@@ -26516,13 +26937,24 @@ var registerListSchedulesTool = (server) => {
|
|
|
26516
26937
|
nextRunAt: string().optional(),
|
|
26517
26938
|
createdByAgentId: string().optional(),
|
|
26518
26939
|
timezone: string(),
|
|
26940
|
+
consecutiveErrors: number().optional(),
|
|
26941
|
+
lastErrorAt: string().optional(),
|
|
26942
|
+
lastErrorMessage: string().optional(),
|
|
26519
26943
|
scheduleType: string(),
|
|
26520
26944
|
createdAt: string(),
|
|
26521
26945
|
lastUpdatedAt: string()
|
|
26522
26946
|
})),
|
|
26523
26947
|
count: number()
|
|
26524
26948
|
})
|
|
26525
|
-
}, async ({
|
|
26949
|
+
}, async ({
|
|
26950
|
+
enabled: enabled2,
|
|
26951
|
+
name,
|
|
26952
|
+
scheduleType,
|
|
26953
|
+
hideCompleted,
|
|
26954
|
+
consecutiveErrorsMin,
|
|
26955
|
+
lastRunStatus,
|
|
26956
|
+
includeFull
|
|
26957
|
+
}, requestInfo, _meta) => {
|
|
26526
26958
|
if (!requestInfo.agentId) {
|
|
26527
26959
|
return {
|
|
26528
26960
|
content: [{ type: "text", text: 'Agent ID not found. Set the "X-Agent-ID" header.' }],
|
|
@@ -26535,7 +26967,14 @@ var registerListSchedulesTool = (server) => {
|
|
|
26535
26967
|
};
|
|
26536
26968
|
}
|
|
26537
26969
|
try {
|
|
26538
|
-
const filters = {
|
|
26970
|
+
const filters = {
|
|
26971
|
+
enabled: enabled2,
|
|
26972
|
+
name,
|
|
26973
|
+
scheduleType,
|
|
26974
|
+
hideCompleted,
|
|
26975
|
+
consecutiveErrorsMin,
|
|
26976
|
+
lastRunStatus
|
|
26977
|
+
};
|
|
26539
26978
|
const schedules = includeFull ? getScheduledTasks(filters) : getScheduledTasks(filters, { slim: true });
|
|
26540
26979
|
const count = schedules.length;
|
|
26541
26980
|
const statusSummary = count === 0 ? "No schedules found." : `Found ${count} schedule${count === 1 ? "" : "s"}.`;
|
|
@@ -26579,106 +27018,7 @@ ${scheduleList}`
|
|
|
26579
27018
|
}
|
|
26580
27019
|
});
|
|
26581
27020
|
};
|
|
26582
|
-
// src/tools/schedules/
|
|
26583
|
-
init_zod();
|
|
26584
|
-
init_db();
|
|
26585
|
-
var registerRunScheduleNowTool = (server) => {
|
|
26586
|
-
createToolRegistrar(server)("run-schedule-now", {
|
|
26587
|
-
title: "Run Schedule Now",
|
|
26588
|
-
annotations: { destructiveHint: false },
|
|
26589
|
-
description: "Immediately execute a scheduled task, creating a task right away. Does not affect the regular schedule timing.",
|
|
26590
|
-
inputSchema: object({
|
|
26591
|
-
scheduleId: string().uuid().optional().describe("Schedule ID to run"),
|
|
26592
|
-
name: string().optional().describe("Schedule name to run (alternative to ID)")
|
|
26593
|
-
}),
|
|
26594
|
-
outputSchema: object({
|
|
26595
|
-
yourAgentId: string().uuid().optional(),
|
|
26596
|
-
success: boolean(),
|
|
26597
|
-
message: string(),
|
|
26598
|
-
schedule: object({
|
|
26599
|
-
id: string(),
|
|
26600
|
-
name: string(),
|
|
26601
|
-
nextRunAt: string().optional()
|
|
26602
|
-
}).optional()
|
|
26603
|
-
})
|
|
26604
|
-
}, async ({ scheduleId, name }, requestInfo, _meta) => {
|
|
26605
|
-
if (!requestInfo.agentId) {
|
|
26606
|
-
return {
|
|
26607
|
-
content: [{ type: "text", text: 'Agent ID not found. Set the "X-Agent-ID" header.' }],
|
|
26608
|
-
structuredContent: {
|
|
26609
|
-
success: false,
|
|
26610
|
-
message: 'Agent ID not found. Set the "X-Agent-ID" header.'
|
|
26611
|
-
}
|
|
26612
|
-
};
|
|
26613
|
-
}
|
|
26614
|
-
if (!scheduleId && !name) {
|
|
26615
|
-
return {
|
|
26616
|
-
content: [{ type: "text", text: "Either scheduleId or name must be provided." }],
|
|
26617
|
-
structuredContent: {
|
|
26618
|
-
success: false,
|
|
26619
|
-
message: "Either scheduleId or name must be provided."
|
|
26620
|
-
}
|
|
26621
|
-
};
|
|
26622
|
-
}
|
|
26623
|
-
const schedule = scheduleId ? getScheduledTaskById(scheduleId) : name ? getScheduledTaskByName(name) : null;
|
|
26624
|
-
if (!schedule) {
|
|
26625
|
-
return {
|
|
26626
|
-
content: [{ type: "text", text: "Schedule not found." }],
|
|
26627
|
-
structuredContent: {
|
|
26628
|
-
success: false,
|
|
26629
|
-
message: "Schedule not found."
|
|
26630
|
-
}
|
|
26631
|
-
};
|
|
26632
|
-
}
|
|
26633
|
-
if (!schedule.enabled) {
|
|
26634
|
-
return {
|
|
26635
|
-
content: [
|
|
26636
|
-
{
|
|
26637
|
-
type: "text",
|
|
26638
|
-
text: `Schedule "${schedule.name}" is disabled. Enable it first or use it as a template.`
|
|
26639
|
-
}
|
|
26640
|
-
],
|
|
26641
|
-
structuredContent: {
|
|
26642
|
-
success: false,
|
|
26643
|
-
message: `Schedule "${schedule.name}" is disabled.`
|
|
26644
|
-
}
|
|
26645
|
-
};
|
|
26646
|
-
}
|
|
26647
|
-
try {
|
|
26648
|
-
await runScheduleNow(schedule.id);
|
|
26649
|
-
const updated = getScheduledTaskById(schedule.id);
|
|
26650
|
-
return {
|
|
26651
|
-
content: [
|
|
26652
|
-
{
|
|
26653
|
-
type: "text",
|
|
26654
|
-
text: `Executed schedule "${schedule.name}". Task created. Next regular run: ${updated?.nextRunAt || "not scheduled"}`
|
|
26655
|
-
}
|
|
26656
|
-
],
|
|
26657
|
-
structuredContent: {
|
|
26658
|
-
yourAgentId: requestInfo.agentId,
|
|
26659
|
-
success: true,
|
|
26660
|
-
message: `Executed schedule "${schedule.name}".`,
|
|
26661
|
-
schedule: updated ? {
|
|
26662
|
-
id: updated.id,
|
|
26663
|
-
name: updated.name,
|
|
26664
|
-
nextRunAt: updated.nextRunAt
|
|
26665
|
-
} : undefined
|
|
26666
|
-
}
|
|
26667
|
-
};
|
|
26668
|
-
} catch (error) {
|
|
26669
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
26670
|
-
return {
|
|
26671
|
-
content: [{ type: "text", text: `Failed to run schedule: ${message}` }],
|
|
26672
|
-
structuredContent: {
|
|
26673
|
-
yourAgentId: requestInfo.agentId,
|
|
26674
|
-
success: false,
|
|
26675
|
-
message: `Failed to run schedule: ${message}`
|
|
26676
|
-
}
|
|
26677
|
-
};
|
|
26678
|
-
}
|
|
26679
|
-
});
|
|
26680
|
-
};
|
|
26681
|
-
// src/tools/schedules/update-schedule.ts
|
|
27021
|
+
// src/tools/schedules/patch-schedule.ts
|
|
26682
27022
|
init_zod();
|
|
26683
27023
|
var import_cron_parser2 = __toESM(require_dist(), 1);
|
|
26684
27024
|
init_db();
|
|
@@ -26697,11 +27037,11 @@ function validateRecurringTiming(merged) {
|
|
|
26697
27037
|
return null;
|
|
26698
27038
|
}
|
|
26699
27039
|
|
|
26700
|
-
// src/tools/schedules/
|
|
27040
|
+
// src/tools/schedules/patch-schedule.ts
|
|
26701
27041
|
init_types();
|
|
26702
|
-
var
|
|
26703
|
-
scheduleId: string().uuid().optional().describe("Schedule ID to
|
|
26704
|
-
name: string().optional().describe("Schedule name to
|
|
27042
|
+
var patchScheduleInputSchema = object({
|
|
27043
|
+
scheduleId: string().uuid().optional().describe("Schedule ID to patch"),
|
|
27044
|
+
name: string().optional().describe("Schedule name to patch (alternative to ID)"),
|
|
26705
27045
|
newName: string().min(1).max(100).optional().describe("New name for the schedule"),
|
|
26706
27046
|
taskTemplate: string().min(1).optional().describe("New task template"),
|
|
26707
27047
|
targetType: ScheduledTaskTargetTypeSchema.optional().describe("Change the execution target: 'agent-task', 'workflow', or 'script'."),
|
|
@@ -26720,12 +27060,12 @@ var updateScheduleInputSchema = object({
|
|
|
26720
27060
|
model: string().trim().min(1).nullable().optional().describe("Concrete model override for tasks created by this schedule. Set to null to clear."),
|
|
26721
27061
|
modelTier: ModelTierSchema.nullable().optional().describe("Portable model tier for tasks created by this schedule. Set to null to clear.")
|
|
26722
27062
|
});
|
|
26723
|
-
var
|
|
26724
|
-
createToolRegistrar(server)("
|
|
26725
|
-
title: "
|
|
27063
|
+
var registerPatchScheduleTool = (server) => {
|
|
27064
|
+
createToolRegistrar(server)("patch-schedule", {
|
|
27065
|
+
title: "Patch Scheduled Task",
|
|
26726
27066
|
annotations: { idempotentHint: true },
|
|
26727
|
-
description: "
|
|
26728
|
-
inputSchema:
|
|
27067
|
+
description: "Patch an existing scheduled task by shallow-merging provided fields over the current row. Any registered agent can patch schedules.",
|
|
27068
|
+
inputSchema: patchScheduleInputSchema,
|
|
26729
27069
|
outputSchema: object({
|
|
26730
27070
|
yourAgentId: string().uuid().optional(),
|
|
26731
27071
|
success: boolean(),
|
|
@@ -26837,6 +27177,429 @@ var registerUpdateScheduleTool = (server) => {
|
|
|
26837
27177
|
};
|
|
26838
27178
|
}
|
|
26839
27179
|
}
|
|
27180
|
+
if (targetAgentId) {
|
|
27181
|
+
const agent = getAgentById(targetAgentId);
|
|
27182
|
+
if (!agent) {
|
|
27183
|
+
return {
|
|
27184
|
+
content: [{ type: "text", text: `Target agent not found: ${targetAgentId}` }],
|
|
27185
|
+
structuredContent: {
|
|
27186
|
+
success: false,
|
|
27187
|
+
message: `Target agent not found: ${targetAgentId}`
|
|
27188
|
+
}
|
|
27189
|
+
};
|
|
27190
|
+
}
|
|
27191
|
+
}
|
|
27192
|
+
if (newName && newName !== schedule.name) {
|
|
27193
|
+
const existing = getScheduledTaskByName(newName);
|
|
27194
|
+
if (existing) {
|
|
27195
|
+
return {
|
|
27196
|
+
content: [{ type: "text", text: `Schedule with name "${newName}" already exists.` }],
|
|
27197
|
+
structuredContent: {
|
|
27198
|
+
success: false,
|
|
27199
|
+
message: `Schedule with name "${newName}" already exists.`
|
|
27200
|
+
}
|
|
27201
|
+
};
|
|
27202
|
+
}
|
|
27203
|
+
}
|
|
27204
|
+
const mergedTargetType = targetType ?? schedule.targetType;
|
|
27205
|
+
const mergedTaskTemplate = taskTemplate !== undefined ? taskTemplate : schedule.taskTemplate;
|
|
27206
|
+
const mergedWorkflowId = workflowId !== undefined ? workflowId : schedule.workflowId;
|
|
27207
|
+
const mergedScriptName = scriptName !== undefined ? scriptName : schedule.scriptName;
|
|
27208
|
+
if (mergedTargetType === "agent-task" && !mergedTaskTemplate) {
|
|
27209
|
+
const message = "taskTemplate is required when targetType is 'agent-task'.";
|
|
27210
|
+
return {
|
|
27211
|
+
content: [{ type: "text", text: message }],
|
|
27212
|
+
structuredContent: { success: false, message }
|
|
27213
|
+
};
|
|
27214
|
+
}
|
|
27215
|
+
if (mergedTargetType === "workflow") {
|
|
27216
|
+
if (!mergedWorkflowId) {
|
|
27217
|
+
const message = "workflowId is required when targetType is 'workflow'.";
|
|
27218
|
+
return {
|
|
27219
|
+
content: [{ type: "text", text: message }],
|
|
27220
|
+
structuredContent: { success: false, message }
|
|
27221
|
+
};
|
|
27222
|
+
}
|
|
27223
|
+
if (!getWorkflow(mergedWorkflowId)) {
|
|
27224
|
+
const message = `Workflow not found: ${mergedWorkflowId}`;
|
|
27225
|
+
return {
|
|
27226
|
+
content: [{ type: "text", text: message }],
|
|
27227
|
+
structuredContent: { success: false, message }
|
|
27228
|
+
};
|
|
27229
|
+
}
|
|
27230
|
+
}
|
|
27231
|
+
if (mergedTargetType === "script") {
|
|
27232
|
+
if (!mergedScriptName) {
|
|
27233
|
+
const message = "scriptName is required when targetType is 'script'.";
|
|
27234
|
+
return {
|
|
27235
|
+
content: [{ type: "text", text: message }],
|
|
27236
|
+
structuredContent: { success: false, message }
|
|
27237
|
+
};
|
|
27238
|
+
}
|
|
27239
|
+
if (!getScript({ name: mergedScriptName, scope: "global" })) {
|
|
27240
|
+
const message = `Script not found: ${mergedScriptName}`;
|
|
27241
|
+
return {
|
|
27242
|
+
content: [{ type: "text", text: message }],
|
|
27243
|
+
structuredContent: { success: false, message }
|
|
27244
|
+
};
|
|
27245
|
+
}
|
|
27246
|
+
}
|
|
27247
|
+
try {
|
|
27248
|
+
const updateData = {};
|
|
27249
|
+
if (newName !== undefined)
|
|
27250
|
+
updateData.name = newName;
|
|
27251
|
+
if (taskTemplate !== undefined)
|
|
27252
|
+
updateData.taskTemplate = taskTemplate;
|
|
27253
|
+
if (targetType !== undefined)
|
|
27254
|
+
updateData.targetType = targetType;
|
|
27255
|
+
if (workflowId !== undefined)
|
|
27256
|
+
updateData.workflowId = workflowId;
|
|
27257
|
+
if (scriptName !== undefined)
|
|
27258
|
+
updateData.scriptName = scriptName;
|
|
27259
|
+
if (scriptArgs !== undefined)
|
|
27260
|
+
updateData.scriptArgs = scriptArgs;
|
|
27261
|
+
if (cronExpression !== undefined)
|
|
27262
|
+
updateData.cronExpression = cronExpression;
|
|
27263
|
+
if (intervalMs !== undefined)
|
|
27264
|
+
updateData.intervalMs = intervalMs;
|
|
27265
|
+
if (description !== undefined)
|
|
27266
|
+
updateData.description = description;
|
|
27267
|
+
if (taskType !== undefined)
|
|
27268
|
+
updateData.taskType = taskType;
|
|
27269
|
+
if (tags !== undefined)
|
|
27270
|
+
updateData.tags = tags;
|
|
27271
|
+
if (priority !== undefined)
|
|
27272
|
+
updateData.priority = priority;
|
|
27273
|
+
if (targetAgentId !== undefined)
|
|
27274
|
+
updateData.targetAgentId = targetAgentId;
|
|
27275
|
+
if (timezone !== undefined)
|
|
27276
|
+
updateData.timezone = timezone;
|
|
27277
|
+
if (enabled2 !== undefined)
|
|
27278
|
+
updateData.enabled = enabled2;
|
|
27279
|
+
if (model !== undefined || modelTier !== undefined) {
|
|
27280
|
+
const normalizedModel = splitLegacyModelAlias({ model, modelTier });
|
|
27281
|
+
if (model !== undefined)
|
|
27282
|
+
updateData.model = normalizedModel.model ?? null;
|
|
27283
|
+
if (modelTier !== undefined || normalizedModel.modelTier) {
|
|
27284
|
+
updateData.modelTier = normalizedModel.modelTier ?? null;
|
|
27285
|
+
}
|
|
27286
|
+
}
|
|
27287
|
+
if (schedule.scheduleType === "one_time") {
|
|
27288
|
+
if (enabled2 === false) {
|
|
27289
|
+
updateData.nextRunAt = null;
|
|
27290
|
+
}
|
|
27291
|
+
} else {
|
|
27292
|
+
const timing = mergeScheduleTiming({
|
|
27293
|
+
cronExpression: schedule.cronExpression ?? null,
|
|
27294
|
+
intervalMs: schedule.intervalMs ?? null
|
|
27295
|
+
}, { cronExpression, intervalMs });
|
|
27296
|
+
const timingError = validateRecurringTiming(timing);
|
|
27297
|
+
if (timingError) {
|
|
27298
|
+
return {
|
|
27299
|
+
content: [
|
|
27300
|
+
{
|
|
27301
|
+
type: "text",
|
|
27302
|
+
text: "At least one of intervalMs or cronExpression must be set for recurring schedules."
|
|
27303
|
+
}
|
|
27304
|
+
],
|
|
27305
|
+
structuredContent: {
|
|
27306
|
+
success: false,
|
|
27307
|
+
message: "At least one of intervalMs or cronExpression must be set for recurring schedules."
|
|
27308
|
+
}
|
|
27309
|
+
};
|
|
27310
|
+
}
|
|
27311
|
+
const needsNextRunRecalc = cronExpression !== undefined || intervalMs !== undefined || timezone !== undefined || enabled2 === true && !schedule.enabled;
|
|
27312
|
+
if (needsNextRunRecalc && enabled2 !== false) {
|
|
27313
|
+
const mergedTimezone = timezone !== undefined ? timezone : schedule.timezone;
|
|
27314
|
+
updateData.nextRunAt = calculateNextRun({
|
|
27315
|
+
cronExpression: timing.mergedCron,
|
|
27316
|
+
intervalMs: timing.mergedInterval,
|
|
27317
|
+
timezone: mergedTimezone
|
|
27318
|
+
}, new Date);
|
|
27319
|
+
} else if (enabled2 === false) {
|
|
27320
|
+
updateData.nextRunAt = null;
|
|
27321
|
+
}
|
|
27322
|
+
}
|
|
27323
|
+
const updatedBy = resolveTaskAuditUserId(requestInfo.sourceTaskId, requestInfo.agentId) ?? undefined;
|
|
27324
|
+
const updated = updateScheduledTask(schedule.id, { ...updateData, updatedBy });
|
|
27325
|
+
if (!updated) {
|
|
27326
|
+
return {
|
|
27327
|
+
content: [{ type: "text", text: "Failed to update schedule." }],
|
|
27328
|
+
structuredContent: {
|
|
27329
|
+
success: false,
|
|
27330
|
+
message: "Failed to update schedule."
|
|
27331
|
+
}
|
|
27332
|
+
};
|
|
27333
|
+
}
|
|
27334
|
+
return {
|
|
27335
|
+
content: [
|
|
27336
|
+
{
|
|
27337
|
+
type: "text",
|
|
27338
|
+
text: `Patched schedule "${updated.name}". Next run: ${updated.nextRunAt || "disabled"}`
|
|
27339
|
+
}
|
|
27340
|
+
],
|
|
27341
|
+
structuredContent: {
|
|
27342
|
+
yourAgentId: requestInfo.agentId,
|
|
27343
|
+
success: true,
|
|
27344
|
+
message: `Patched schedule "${updated.name}".`,
|
|
27345
|
+
schedule: updated
|
|
27346
|
+
}
|
|
27347
|
+
};
|
|
27348
|
+
} catch (error) {
|
|
27349
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
27350
|
+
return {
|
|
27351
|
+
content: [{ type: "text", text: `Failed to update schedule: ${message}` }],
|
|
27352
|
+
structuredContent: {
|
|
27353
|
+
yourAgentId: requestInfo.agentId,
|
|
27354
|
+
success: false,
|
|
27355
|
+
message: `Failed to update schedule: ${message}`
|
|
27356
|
+
}
|
|
27357
|
+
};
|
|
27358
|
+
}
|
|
27359
|
+
});
|
|
27360
|
+
};
|
|
27361
|
+
// src/tools/schedules/run-schedule-now.ts
|
|
27362
|
+
init_zod();
|
|
27363
|
+
init_db();
|
|
27364
|
+
var registerRunScheduleNowTool = (server) => {
|
|
27365
|
+
createToolRegistrar(server)("run-schedule-now", {
|
|
27366
|
+
title: "Run Schedule Now",
|
|
27367
|
+
annotations: { destructiveHint: false },
|
|
27368
|
+
description: "Immediately execute a scheduled task, creating a task right away. Does not affect the regular schedule timing.",
|
|
27369
|
+
inputSchema: object({
|
|
27370
|
+
scheduleId: string().uuid().optional().describe("Schedule ID to run"),
|
|
27371
|
+
name: string().optional().describe("Schedule name to run (alternative to ID)")
|
|
27372
|
+
}),
|
|
27373
|
+
outputSchema: object({
|
|
27374
|
+
yourAgentId: string().uuid().optional(),
|
|
27375
|
+
success: boolean(),
|
|
27376
|
+
message: string(),
|
|
27377
|
+
schedule: object({
|
|
27378
|
+
id: string(),
|
|
27379
|
+
name: string(),
|
|
27380
|
+
nextRunAt: string().optional()
|
|
27381
|
+
}).optional()
|
|
27382
|
+
})
|
|
27383
|
+
}, async ({ scheduleId, name }, requestInfo, _meta) => {
|
|
27384
|
+
if (!requestInfo.agentId) {
|
|
27385
|
+
return {
|
|
27386
|
+
content: [{ type: "text", text: 'Agent ID not found. Set the "X-Agent-ID" header.' }],
|
|
27387
|
+
structuredContent: {
|
|
27388
|
+
success: false,
|
|
27389
|
+
message: 'Agent ID not found. Set the "X-Agent-ID" header.'
|
|
27390
|
+
}
|
|
27391
|
+
};
|
|
27392
|
+
}
|
|
27393
|
+
if (!scheduleId && !name) {
|
|
27394
|
+
return {
|
|
27395
|
+
content: [{ type: "text", text: "Either scheduleId or name must be provided." }],
|
|
27396
|
+
structuredContent: {
|
|
27397
|
+
success: false,
|
|
27398
|
+
message: "Either scheduleId or name must be provided."
|
|
27399
|
+
}
|
|
27400
|
+
};
|
|
27401
|
+
}
|
|
27402
|
+
const schedule = scheduleId ? getScheduledTaskById(scheduleId) : name ? getScheduledTaskByName(name) : null;
|
|
27403
|
+
if (!schedule) {
|
|
27404
|
+
return {
|
|
27405
|
+
content: [{ type: "text", text: "Schedule not found." }],
|
|
27406
|
+
structuredContent: {
|
|
27407
|
+
success: false,
|
|
27408
|
+
message: "Schedule not found."
|
|
27409
|
+
}
|
|
27410
|
+
};
|
|
27411
|
+
}
|
|
27412
|
+
if (!schedule.enabled) {
|
|
27413
|
+
return {
|
|
27414
|
+
content: [
|
|
27415
|
+
{
|
|
27416
|
+
type: "text",
|
|
27417
|
+
text: `Schedule "${schedule.name}" is disabled. Enable it first or use it as a template.`
|
|
27418
|
+
}
|
|
27419
|
+
],
|
|
27420
|
+
structuredContent: {
|
|
27421
|
+
success: false,
|
|
27422
|
+
message: `Schedule "${schedule.name}" is disabled.`
|
|
27423
|
+
}
|
|
27424
|
+
};
|
|
27425
|
+
}
|
|
27426
|
+
try {
|
|
27427
|
+
await runScheduleNow(schedule.id);
|
|
27428
|
+
const updated = getScheduledTaskById(schedule.id);
|
|
27429
|
+
return {
|
|
27430
|
+
content: [
|
|
27431
|
+
{
|
|
27432
|
+
type: "text",
|
|
27433
|
+
text: `Executed schedule "${schedule.name}". Task created. Next regular run: ${updated?.nextRunAt || "not scheduled"}`
|
|
27434
|
+
}
|
|
27435
|
+
],
|
|
27436
|
+
structuredContent: {
|
|
27437
|
+
yourAgentId: requestInfo.agentId,
|
|
27438
|
+
success: true,
|
|
27439
|
+
message: `Executed schedule "${schedule.name}".`,
|
|
27440
|
+
schedule: updated ? {
|
|
27441
|
+
id: updated.id,
|
|
27442
|
+
name: updated.name,
|
|
27443
|
+
nextRunAt: updated.nextRunAt
|
|
27444
|
+
} : undefined
|
|
27445
|
+
}
|
|
27446
|
+
};
|
|
27447
|
+
} catch (error) {
|
|
27448
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
27449
|
+
return {
|
|
27450
|
+
content: [{ type: "text", text: `Failed to run schedule: ${message}` }],
|
|
27451
|
+
structuredContent: {
|
|
27452
|
+
yourAgentId: requestInfo.agentId,
|
|
27453
|
+
success: false,
|
|
27454
|
+
message: `Failed to run schedule: ${message}`
|
|
27455
|
+
}
|
|
27456
|
+
};
|
|
27457
|
+
}
|
|
27458
|
+
});
|
|
27459
|
+
};
|
|
27460
|
+
// src/tools/schedules/update-schedule.ts
|
|
27461
|
+
init_zod();
|
|
27462
|
+
var import_cron_parser3 = __toESM(require_dist(), 1);
|
|
27463
|
+
init_db();
|
|
27464
|
+
init_types();
|
|
27465
|
+
var updateScheduleInputSchema = object({
|
|
27466
|
+
scheduleId: string().uuid().optional().describe("Schedule ID to update"),
|
|
27467
|
+
name: string().optional().describe("Schedule name to update (alternative to ID)"),
|
|
27468
|
+
newName: string().min(1).max(100).optional().describe("New name for the schedule"),
|
|
27469
|
+
taskTemplate: string().min(1).optional().describe("New task template"),
|
|
27470
|
+
targetType: ScheduledTaskTargetTypeSchema.optional().describe("Change the execution target: 'agent-task', 'workflow', or 'script'."),
|
|
27471
|
+
workflowId: string().uuid().nullable().optional().describe("New workflow ID (required when targetType is 'workflow'; null to clear)"),
|
|
27472
|
+
scriptName: string().nullable().optional().describe("New catalog script name (required when targetType is 'script'; null to clear)"),
|
|
27473
|
+
scriptArgs: record(string(), unknown()).nullable().optional().describe("New JSON args for the script target (null to clear)"),
|
|
27474
|
+
cronExpression: string().nullable().optional().describe("New cron expression (null to clear)"),
|
|
27475
|
+
intervalMs: number().int().positive().nullable().optional().describe("New interval in milliseconds (null to clear)"),
|
|
27476
|
+
description: string().optional().describe("New description"),
|
|
27477
|
+
taskType: string().max(50).optional().describe("New task type"),
|
|
27478
|
+
tags: array(string()).optional().describe("New tags"),
|
|
27479
|
+
priority: number().int().min(0).max(100).optional().describe("New priority"),
|
|
27480
|
+
targetAgentId: string().uuid().nullable().optional().describe("New target agent ID"),
|
|
27481
|
+
timezone: string().optional().describe("New timezone"),
|
|
27482
|
+
enabled: boolean().optional().describe("Enable or disable the schedule"),
|
|
27483
|
+
model: string().trim().min(1).nullable().optional().describe("Concrete model override for tasks created by this schedule. Set to null to clear."),
|
|
27484
|
+
modelTier: ModelTierSchema.nullable().optional().describe("Portable model tier for tasks created by this schedule. Set to null to clear.")
|
|
27485
|
+
});
|
|
27486
|
+
var registerUpdateScheduleTool = (server) => {
|
|
27487
|
+
createToolRegistrar(server)("update-schedule", {
|
|
27488
|
+
title: "Update Scheduled Task",
|
|
27489
|
+
annotations: { idempotentHint: true },
|
|
27490
|
+
description: "Update an existing scheduled task. Any registered agent can update schedules.",
|
|
27491
|
+
inputSchema: updateScheduleInputSchema,
|
|
27492
|
+
outputSchema: object({
|
|
27493
|
+
yourAgentId: string().uuid().optional(),
|
|
27494
|
+
success: boolean(),
|
|
27495
|
+
message: string(),
|
|
27496
|
+
schedule: object({
|
|
27497
|
+
id: string(),
|
|
27498
|
+
name: string(),
|
|
27499
|
+
description: string().optional(),
|
|
27500
|
+
cronExpression: string().optional(),
|
|
27501
|
+
intervalMs: number().optional(),
|
|
27502
|
+
taskTemplate: string().optional(),
|
|
27503
|
+
taskType: string().optional(),
|
|
27504
|
+
tags: array(string()),
|
|
27505
|
+
priority: number(),
|
|
27506
|
+
targetAgentId: string().optional(),
|
|
27507
|
+
enabled: boolean(),
|
|
27508
|
+
lastRunAt: string().optional(),
|
|
27509
|
+
nextRunAt: string().optional(),
|
|
27510
|
+
createdByAgentId: string().optional(),
|
|
27511
|
+
timezone: string(),
|
|
27512
|
+
model: string().optional(),
|
|
27513
|
+
modelTier: ModelTierSchema.optional(),
|
|
27514
|
+
scheduleType: string(),
|
|
27515
|
+
targetType: ScheduledTaskTargetTypeSchema.optional(),
|
|
27516
|
+
workflowId: string().optional(),
|
|
27517
|
+
scriptName: string().optional(),
|
|
27518
|
+
scriptArgs: record(string(), unknown()).optional(),
|
|
27519
|
+
createdAt: string(),
|
|
27520
|
+
lastUpdatedAt: string()
|
|
27521
|
+
}).optional()
|
|
27522
|
+
})
|
|
27523
|
+
}, async ({
|
|
27524
|
+
scheduleId,
|
|
27525
|
+
name,
|
|
27526
|
+
newName,
|
|
27527
|
+
taskTemplate,
|
|
27528
|
+
targetType,
|
|
27529
|
+
workflowId,
|
|
27530
|
+
scriptName,
|
|
27531
|
+
scriptArgs,
|
|
27532
|
+
cronExpression,
|
|
27533
|
+
intervalMs,
|
|
27534
|
+
description,
|
|
27535
|
+
taskType,
|
|
27536
|
+
tags,
|
|
27537
|
+
priority,
|
|
27538
|
+
targetAgentId,
|
|
27539
|
+
timezone,
|
|
27540
|
+
enabled: enabled2,
|
|
27541
|
+
model,
|
|
27542
|
+
modelTier
|
|
27543
|
+
}, requestInfo, _meta) => {
|
|
27544
|
+
if (!requestInfo.agentId) {
|
|
27545
|
+
return {
|
|
27546
|
+
content: [{ type: "text", text: 'Agent ID not found. Set the "X-Agent-ID" header.' }],
|
|
27547
|
+
structuredContent: {
|
|
27548
|
+
success: false,
|
|
27549
|
+
message: 'Agent ID not found. Set the "X-Agent-ID" header.'
|
|
27550
|
+
}
|
|
27551
|
+
};
|
|
27552
|
+
}
|
|
27553
|
+
const schedule = scheduleId ? getScheduledTaskById(scheduleId) : name ? getScheduledTaskByName(name) : null;
|
|
27554
|
+
if (!schedule) {
|
|
27555
|
+
return {
|
|
27556
|
+
content: [{ type: "text", text: "Schedule not found." }],
|
|
27557
|
+
structuredContent: {
|
|
27558
|
+
success: false,
|
|
27559
|
+
message: "Schedule not found."
|
|
27560
|
+
}
|
|
27561
|
+
};
|
|
27562
|
+
}
|
|
27563
|
+
const caller = getAgentById(requestInfo.agentId);
|
|
27564
|
+
if (!caller) {
|
|
27565
|
+
return {
|
|
27566
|
+
content: [{ type: "text", text: "Agent not found." }],
|
|
27567
|
+
structuredContent: {
|
|
27568
|
+
success: false,
|
|
27569
|
+
message: "Agent not found."
|
|
27570
|
+
}
|
|
27571
|
+
};
|
|
27572
|
+
}
|
|
27573
|
+
if (schedule.scheduleType === "one_time" && !schedule.enabled && schedule.lastRunAt) {
|
|
27574
|
+
return {
|
|
27575
|
+
content: [
|
|
27576
|
+
{
|
|
27577
|
+
type: "text",
|
|
27578
|
+
text: `One-time schedule "${schedule.name}" has already executed. Create a new one instead.`
|
|
27579
|
+
}
|
|
27580
|
+
],
|
|
27581
|
+
structuredContent: {
|
|
27582
|
+
success: false,
|
|
27583
|
+
message: `One-time schedule "${schedule.name}" has already executed. Create a new one instead.`
|
|
27584
|
+
}
|
|
27585
|
+
};
|
|
27586
|
+
}
|
|
27587
|
+
if (cronExpression) {
|
|
27588
|
+
try {
|
|
27589
|
+
import_cron_parser3.CronExpressionParser.parse(cronExpression, {
|
|
27590
|
+
tz: timezone || schedule.timezone || "UTC"
|
|
27591
|
+
});
|
|
27592
|
+
} catch (err) {
|
|
27593
|
+
const message = err instanceof Error ? err.message : "Invalid cron expression";
|
|
27594
|
+
return {
|
|
27595
|
+
content: [{ type: "text", text: `Invalid cron expression: ${message}` }],
|
|
27596
|
+
structuredContent: {
|
|
27597
|
+
success: false,
|
|
27598
|
+
message: `Invalid cron expression: ${message}`
|
|
27599
|
+
}
|
|
27600
|
+
};
|
|
27601
|
+
}
|
|
27602
|
+
}
|
|
26840
27603
|
if (targetAgentId && targetAgentId !== null) {
|
|
26841
27604
|
const agent = getAgentById(targetAgentId);
|
|
26842
27605
|
if (!agent) {
|
|
@@ -26946,7 +27709,7 @@ var registerUpdateScheduleTool = (server) => {
|
|
|
26946
27709
|
}
|
|
26947
27710
|
if (schedule.scheduleType === "one_time") {
|
|
26948
27711
|
if (enabled2 === false) {
|
|
26949
|
-
updateData.nextRunAt =
|
|
27712
|
+
updateData.nextRunAt = null;
|
|
26950
27713
|
}
|
|
26951
27714
|
} else {
|
|
26952
27715
|
const timing = mergeScheduleTiming({
|
|
@@ -26977,7 +27740,7 @@ var registerUpdateScheduleTool = (server) => {
|
|
|
26977
27740
|
timezone: mergedTimezone
|
|
26978
27741
|
}, new Date);
|
|
26979
27742
|
} else if (enabled2 === false) {
|
|
26980
|
-
updateData.nextRunAt =
|
|
27743
|
+
updateData.nextRunAt = null;
|
|
26981
27744
|
}
|
|
26982
27745
|
}
|
|
26983
27746
|
const updatedBy = resolveTaskAuditUserId(requestInfo.sourceTaskId, requestInfo.agentId) ?? undefined;
|
|
@@ -27256,7 +28019,17 @@ var registerScriptConnectionsTool = (server) => {
|
|
|
27256
28019
|
};
|
|
27257
28020
|
}
|
|
27258
28021
|
const agent = getAgentById(requestInfo.agentId);
|
|
27259
|
-
|
|
28022
|
+
const decision = can({
|
|
28023
|
+
principal: {
|
|
28024
|
+
kind: "agent",
|
|
28025
|
+
agentId: requestInfo.agentId,
|
|
28026
|
+
isLead: agent?.isLead ?? false
|
|
28027
|
+
},
|
|
28028
|
+
verb: "script-connection.manage",
|
|
28029
|
+
resource: { kind: "none" },
|
|
28030
|
+
source: "mcp"
|
|
28031
|
+
});
|
|
28032
|
+
if (!decision.allow) {
|
|
27260
28033
|
return {
|
|
27261
28034
|
content: [{ type: "text", text: "Only the lead can manage script connections." }],
|
|
27262
28035
|
structuredContent: {
|
|
@@ -27982,7 +28755,17 @@ var registerSkillCreateTool = (server) => {
|
|
|
27982
28755
|
const parsed = parseSkillContent(args.content);
|
|
27983
28756
|
if (args.scope === "swarm") {
|
|
27984
28757
|
const agent = getAgentById(requestInfo.agentId);
|
|
27985
|
-
|
|
28758
|
+
const decision = can({
|
|
28759
|
+
principal: {
|
|
28760
|
+
kind: "agent",
|
|
28761
|
+
agentId: requestInfo.agentId,
|
|
28762
|
+
isLead: agent?.isLead ?? false
|
|
28763
|
+
},
|
|
28764
|
+
verb: "skill.create.swarm",
|
|
28765
|
+
resource: { kind: "none" },
|
|
28766
|
+
source: "mcp"
|
|
28767
|
+
});
|
|
28768
|
+
if (!decision.allow) {
|
|
27986
28769
|
return {
|
|
27987
28770
|
content: [
|
|
27988
28771
|
{
|
|
@@ -28072,7 +28855,17 @@ var registerSkillDeleteTool = (server) => {
|
|
|
28072
28855
|
};
|
|
28073
28856
|
}
|
|
28074
28857
|
const agent = getAgentById(requestInfo.agentId);
|
|
28075
|
-
|
|
28858
|
+
const decision = can({
|
|
28859
|
+
principal: {
|
|
28860
|
+
kind: "agent",
|
|
28861
|
+
agentId: requestInfo.agentId,
|
|
28862
|
+
isLead: agent?.isLead ?? false
|
|
28863
|
+
},
|
|
28864
|
+
verb: "skill.delete.any",
|
|
28865
|
+
resource: { kind: "owned", ownerAgentId: existing.ownerAgentId },
|
|
28866
|
+
source: "mcp"
|
|
28867
|
+
});
|
|
28868
|
+
if (!decision.allow) {
|
|
28076
28869
|
return {
|
|
28077
28870
|
content: [{ type: "text", text: "Only the owning agent or lead can delete this skill." }],
|
|
28078
28871
|
structuredContent: {
|
|
@@ -28267,7 +29060,17 @@ var registerSkillInstallTool = (server) => {
|
|
|
28267
29060
|
const targetAgentId = args.agentId ?? requestInfo.agentId;
|
|
28268
29061
|
if (targetAgentId !== requestInfo.agentId) {
|
|
28269
29062
|
const agent = getAgentById(requestInfo.agentId);
|
|
28270
|
-
|
|
29063
|
+
const decision = can({
|
|
29064
|
+
principal: {
|
|
29065
|
+
kind: "agent",
|
|
29066
|
+
agentId: requestInfo.agentId,
|
|
29067
|
+
isLead: agent?.isLead ?? false
|
|
29068
|
+
},
|
|
29069
|
+
verb: "skill.install.any",
|
|
29070
|
+
resource: { kind: "agent", agentId: targetAgentId },
|
|
29071
|
+
source: "mcp"
|
|
29072
|
+
});
|
|
29073
|
+
if (!decision.allow) {
|
|
28271
29074
|
return {
|
|
28272
29075
|
content: [{ type: "text", text: "Only leads can install skills for other agents." }],
|
|
28273
29076
|
structuredContent: {
|
|
@@ -28353,7 +29156,17 @@ var registerSkillInstallRemoteTool = (server) => {
|
|
|
28353
29156
|
};
|
|
28354
29157
|
}
|
|
28355
29158
|
const agent = getAgentById(requestInfo.agentId);
|
|
28356
|
-
|
|
29159
|
+
const decision = can({
|
|
29160
|
+
principal: {
|
|
29161
|
+
kind: "agent",
|
|
29162
|
+
agentId: requestInfo.agentId,
|
|
29163
|
+
isLead: agent?.isLead ?? false
|
|
29164
|
+
},
|
|
29165
|
+
verb: "skill.install.global",
|
|
29166
|
+
resource: { kind: "none" },
|
|
29167
|
+
source: "mcp"
|
|
29168
|
+
});
|
|
29169
|
+
if (!decision.allow) {
|
|
28357
29170
|
return {
|
|
28358
29171
|
content: [{ type: "text", text: "Only lead agents can install remote skills." }],
|
|
28359
29172
|
structuredContent: {
|
|
@@ -28777,7 +29590,17 @@ var registerSkillUninstallTool = (server) => {
|
|
|
28777
29590
|
const targetAgentId = args.agentId ?? requestInfo.agentId;
|
|
28778
29591
|
if (targetAgentId !== requestInfo.agentId) {
|
|
28779
29592
|
const agent = getAgentById(requestInfo.agentId);
|
|
28780
|
-
|
|
29593
|
+
const decision = can({
|
|
29594
|
+
principal: {
|
|
29595
|
+
kind: "agent",
|
|
29596
|
+
agentId: requestInfo.agentId,
|
|
29597
|
+
isLead: agent?.isLead ?? false
|
|
29598
|
+
},
|
|
29599
|
+
verb: "skill.uninstall.any",
|
|
29600
|
+
resource: { kind: "agent", agentId: targetAgentId },
|
|
29601
|
+
source: "mcp"
|
|
29602
|
+
});
|
|
29603
|
+
if (!decision.allow) {
|
|
28781
29604
|
return {
|
|
28782
29605
|
content: [{ type: "text", text: "Only leads can uninstall skills for other agents." }],
|
|
28783
29606
|
structuredContent: {
|
|
@@ -28852,7 +29675,17 @@ var registerSkillUpdateTool = (server) => {
|
|
|
28852
29675
|
};
|
|
28853
29676
|
}
|
|
28854
29677
|
const agent = getAgentById(requestInfo.agentId);
|
|
28855
|
-
|
|
29678
|
+
const updateDecision = can({
|
|
29679
|
+
principal: {
|
|
29680
|
+
kind: "agent",
|
|
29681
|
+
agentId: requestInfo.agentId,
|
|
29682
|
+
isLead: agent?.isLead ?? false
|
|
29683
|
+
},
|
|
29684
|
+
verb: "skill.update.any",
|
|
29685
|
+
resource: { kind: "owned", ownerAgentId: existing.ownerAgentId },
|
|
29686
|
+
source: "mcp"
|
|
29687
|
+
});
|
|
29688
|
+
if (!updateDecision.allow) {
|
|
28856
29689
|
return {
|
|
28857
29690
|
content: [
|
|
28858
29691
|
{ type: "text", text: "Only the owning agent or lead can update this skill." }
|
|
@@ -28892,7 +29725,16 @@ var registerSkillUpdateTool = (server) => {
|
|
|
28892
29725
|
updates.isEnabled = args.isEnabled;
|
|
28893
29726
|
}
|
|
28894
29727
|
if (args.scope !== undefined && args.scope !== existing.scope) {
|
|
28895
|
-
if (args.scope === "swarm" && !
|
|
29728
|
+
if (args.scope === "swarm" && !can({
|
|
29729
|
+
principal: {
|
|
29730
|
+
kind: "agent",
|
|
29731
|
+
agentId: requestInfo.agentId,
|
|
29732
|
+
isLead: agent?.isLead ?? false
|
|
29733
|
+
},
|
|
29734
|
+
verb: "skill.promote.swarm",
|
|
29735
|
+
resource: { kind: "owned", ownerAgentId: existing.ownerAgentId },
|
|
29736
|
+
source: "mcp"
|
|
29737
|
+
}).allow) {
|
|
28896
29738
|
return {
|
|
28897
29739
|
content: [
|
|
28898
29740
|
{
|
|
@@ -28942,6 +29784,93 @@ var registerSkillUpdateTool = (server) => {
|
|
|
28942
29784
|
}
|
|
28943
29785
|
});
|
|
28944
29786
|
};
|
|
29787
|
+
// src/tools/slack-delete.ts
|
|
29788
|
+
init_zod();
|
|
29789
|
+
init_db();
|
|
29790
|
+
var registerSlackDeleteTool = (server) => {
|
|
29791
|
+
createToolRegistrar(server)("slack-delete", {
|
|
29792
|
+
title: "Delete a Slack message",
|
|
29793
|
+
description: "Deletes a Slack message that THIS bot authored (e.g. a message previously posted via `slack-post`/`slack-reply`). Cannot delete messages authored by humans or other apps. Requires lead privileges.",
|
|
29794
|
+
annotations: { openWorldHint: true, destructiveHint: true },
|
|
29795
|
+
inputSchema: object({
|
|
29796
|
+
channelId: string().min(1).describe("The Slack channel ID the message is in."),
|
|
29797
|
+
messageTs: string().min(1).describe("Timestamp of the message to delete. Accepts the dotted form (1783411554.596189), the 'p' deep-link form (p1783411554596189), or a full Slack permalink URL.")
|
|
29798
|
+
}),
|
|
29799
|
+
outputSchema: object({
|
|
29800
|
+
success: boolean(),
|
|
29801
|
+
message: string()
|
|
29802
|
+
})
|
|
29803
|
+
}, async ({ channelId, messageTs }, requestInfo, _meta) => {
|
|
29804
|
+
if (!requestInfo.agentId) {
|
|
29805
|
+
return {
|
|
29806
|
+
content: [{ type: "text", text: "Agent ID not found." }],
|
|
29807
|
+
structuredContent: { success: false, message: "Agent ID not found." }
|
|
29808
|
+
};
|
|
29809
|
+
}
|
|
29810
|
+
const agent = getAgentById(requestInfo.agentId);
|
|
29811
|
+
if (!agent) {
|
|
29812
|
+
return {
|
|
29813
|
+
content: [{ type: "text", text: "Agent not found." }],
|
|
29814
|
+
structuredContent: { success: false, message: "Agent not found." }
|
|
29815
|
+
};
|
|
29816
|
+
}
|
|
29817
|
+
const decision = can({
|
|
29818
|
+
principal: { kind: "agent", agentId: agent.id, isLead: agent.isLead },
|
|
29819
|
+
verb: "integration.slack.delete",
|
|
29820
|
+
resource: { kind: "none" },
|
|
29821
|
+
source: "mcp"
|
|
29822
|
+
});
|
|
29823
|
+
if (!decision.allow) {
|
|
29824
|
+
return {
|
|
29825
|
+
content: [{ type: "text", text: "Deleting Slack messages requires lead privileges." }],
|
|
29826
|
+
structuredContent: {
|
|
29827
|
+
success: false,
|
|
29828
|
+
message: "Deleting Slack messages requires lead privileges."
|
|
29829
|
+
}
|
|
29830
|
+
};
|
|
29831
|
+
}
|
|
29832
|
+
const app = getSlackApp();
|
|
29833
|
+
if (!app) {
|
|
29834
|
+
return {
|
|
29835
|
+
content: [{ type: "text", text: "Slack not configured." }],
|
|
29836
|
+
structuredContent: { success: false, message: "Slack not configured." }
|
|
29837
|
+
};
|
|
29838
|
+
}
|
|
29839
|
+
try {
|
|
29840
|
+
const ts = parseSlackTs(messageTs);
|
|
29841
|
+
await app.client.chat.delete({ channel: channelId, ts });
|
|
29842
|
+
return {
|
|
29843
|
+
content: [{ type: "text", text: "Message deleted successfully." }],
|
|
29844
|
+
structuredContent: { success: true, message: "Message deleted successfully." }
|
|
29845
|
+
};
|
|
29846
|
+
} catch (error) {
|
|
29847
|
+
const errorCode = error?.data?.error;
|
|
29848
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
29849
|
+
let message;
|
|
29850
|
+
switch (errorCode) {
|
|
29851
|
+
case "message_not_found":
|
|
29852
|
+
message = "No message found at that timestamp in this channel.";
|
|
29853
|
+
break;
|
|
29854
|
+
case "cant_delete_message":
|
|
29855
|
+
message = "Cannot delete this message — the bot can only delete messages it authored.";
|
|
29856
|
+
break;
|
|
29857
|
+
case "channel_not_found":
|
|
29858
|
+
message = "Channel not found or the bot has no access.";
|
|
29859
|
+
break;
|
|
29860
|
+
case "not_in_channel":
|
|
29861
|
+
message = "The bot is not in that channel.";
|
|
29862
|
+
break;
|
|
29863
|
+
default:
|
|
29864
|
+
message = `Failed to delete message: ${errorMsg}`;
|
|
29865
|
+
}
|
|
29866
|
+
return {
|
|
29867
|
+
content: [{ type: "text", text: message }],
|
|
29868
|
+
structuredContent: { success: false, message }
|
|
29869
|
+
};
|
|
29870
|
+
}
|
|
29871
|
+
});
|
|
29872
|
+
};
|
|
29873
|
+
|
|
28945
29874
|
// src/tools/slack-download-file.ts
|
|
28946
29875
|
init_zod();
|
|
28947
29876
|
init_db();
|
|
@@ -29415,7 +30344,13 @@ var registerSlackPostTool = (server) => {
|
|
|
29415
30344
|
structuredContent: { success: false, message: "Agent not found." }
|
|
29416
30345
|
};
|
|
29417
30346
|
}
|
|
29418
|
-
|
|
30347
|
+
const decision = can({
|
|
30348
|
+
principal: { kind: "agent", agentId: agent.id, isLead: agent.isLead },
|
|
30349
|
+
verb: "integration.slack.post",
|
|
30350
|
+
resource: { kind: "none" },
|
|
30351
|
+
source: "mcp"
|
|
30352
|
+
});
|
|
30353
|
+
if (!decision.allow) {
|
|
29419
30354
|
return {
|
|
29420
30355
|
content: [{ type: "text", text: "Posting to Slack channels requires lead privileges." }],
|
|
29421
30356
|
structuredContent: {
|
|
@@ -29575,7 +30510,13 @@ var registerSlackReadTool = (server) => {
|
|
|
29575
30510
|
slackChannelId = task.slackChannelId;
|
|
29576
30511
|
slackThreadTs = task.slackThreadTs;
|
|
29577
30512
|
} else if (channelId) {
|
|
29578
|
-
|
|
30513
|
+
const decision = can({
|
|
30514
|
+
principal: { kind: "agent", agentId: agent.id, isLead: agent.isLead },
|
|
30515
|
+
verb: "integration.slack.read",
|
|
30516
|
+
resource: { kind: "none" },
|
|
30517
|
+
source: "mcp"
|
|
30518
|
+
});
|
|
30519
|
+
if (!decision.allow) {
|
|
29579
30520
|
return {
|
|
29580
30521
|
content: [{ type: "text", text: "Direct channel access requires lead privileges." }],
|
|
29581
30522
|
structuredContent: {
|
|
@@ -29755,7 +30696,8 @@ var registerSlackReplyTool = (server) => {
|
|
|
29755
30696
|
}),
|
|
29756
30697
|
outputSchema: object({
|
|
29757
30698
|
success: boolean(),
|
|
29758
|
-
message: string()
|
|
30699
|
+
message: string(),
|
|
30700
|
+
messageTs: string().optional()
|
|
29759
30701
|
})
|
|
29760
30702
|
}, async ({ inboxMessageId, taskId, message }, requestInfo, _meta) => {
|
|
29761
30703
|
if (!requestInfo.agentId) {
|
|
@@ -29827,7 +30769,7 @@ var registerSlackReplyTool = (server) => {
|
|
|
29827
30769
|
}
|
|
29828
30770
|
try {
|
|
29829
30771
|
const slackMessage = markdownToSlack(message);
|
|
29830
|
-
await withAutoJoin(app.client, slackChannelId, () => app.client.chat.postMessage({
|
|
30772
|
+
const result = await withAutoJoin(app.client, slackChannelId, () => app.client.chat.postMessage({
|
|
29831
30773
|
channel: slackChannelId,
|
|
29832
30774
|
thread_ts: slackThreadTs,
|
|
29833
30775
|
text: slackMessage,
|
|
@@ -29843,13 +30785,19 @@ var registerSlackReplyTool = (server) => {
|
|
|
29843
30785
|
}
|
|
29844
30786
|
]
|
|
29845
30787
|
}));
|
|
30788
|
+
const messageTs = result.ts;
|
|
29846
30789
|
if (taskId) {
|
|
29847
30790
|
markTaskSlackReplySent(taskId);
|
|
29848
30791
|
console.log(`[Slack] Marked slackReplySent=1 for task ${taskId}`);
|
|
29849
30792
|
}
|
|
29850
30793
|
return {
|
|
29851
|
-
content: [
|
|
29852
|
-
|
|
30794
|
+
content: [
|
|
30795
|
+
{
|
|
30796
|
+
type: "text",
|
|
30797
|
+
text: `Reply sent successfully.${messageTs ? ` Message timestamp: ${messageTs}` : ""}`
|
|
30798
|
+
}
|
|
30799
|
+
],
|
|
30800
|
+
structuredContent: { success: true, message: "Reply sent successfully.", messageTs }
|
|
29853
30801
|
};
|
|
29854
30802
|
} catch (error) {
|
|
29855
30803
|
return {
|
|
@@ -29876,7 +30824,8 @@ var registerSlackStartThreadTool = (server) => {
|
|
|
29876
30824
|
success: boolean(),
|
|
29877
30825
|
message: string(),
|
|
29878
30826
|
channelId: string().optional(),
|
|
29879
|
-
ts: string().optional()
|
|
30827
|
+
ts: string().optional(),
|
|
30828
|
+
messageTs: string().optional()
|
|
29880
30829
|
})
|
|
29881
30830
|
}, async ({ channelId, message }, requestInfo, _meta) => {
|
|
29882
30831
|
if (!requestInfo.agentId) {
|
|
@@ -29892,7 +30841,13 @@ var registerSlackStartThreadTool = (server) => {
|
|
|
29892
30841
|
structuredContent: { success: false, message: "Agent not found." }
|
|
29893
30842
|
};
|
|
29894
30843
|
}
|
|
29895
|
-
|
|
30844
|
+
const decision = can({
|
|
30845
|
+
principal: { kind: "agent", agentId: agent.id, isLead: agent.isLead },
|
|
30846
|
+
verb: "integration.slack.thread.start",
|
|
30847
|
+
resource: { kind: "none" },
|
|
30848
|
+
source: "mcp"
|
|
30849
|
+
});
|
|
30850
|
+
if (!decision.allow) {
|
|
29896
30851
|
return {
|
|
29897
30852
|
content: [{ type: "text", text: "Posting to Slack channels requires lead privileges." }],
|
|
29898
30853
|
structuredContent: {
|
|
@@ -29953,7 +30908,8 @@ var registerSlackStartThreadTool = (server) => {
|
|
|
29953
30908
|
success: true,
|
|
29954
30909
|
message: "Thread started successfully.",
|
|
29955
30910
|
channelId: resolvedChannelId,
|
|
29956
|
-
ts
|
|
30911
|
+
ts,
|
|
30912
|
+
messageTs: ts
|
|
29957
30913
|
}
|
|
29958
30914
|
};
|
|
29959
30915
|
} catch (error) {
|
|
@@ -29966,6 +30922,116 @@ var registerSlackStartThreadTool = (server) => {
|
|
|
29966
30922
|
});
|
|
29967
30923
|
};
|
|
29968
30924
|
|
|
30925
|
+
// src/tools/slack-update.ts
|
|
30926
|
+
init_zod();
|
|
30927
|
+
init_db();
|
|
30928
|
+
var registerSlackUpdateTool = (server) => {
|
|
30929
|
+
createToolRegistrar(server)("slack-update", {
|
|
30930
|
+
title: "Edit a Slack message",
|
|
30931
|
+
description: "Edits (in place) the text of a Slack message that THIS bot authored — use it to post corrections to your own messages. Cannot edit messages authored by humans or other apps. Note: editing may reset the message's display name/icon to the app default (Slack's chat.update cannot set the crown persona). Requires lead privileges.",
|
|
30932
|
+
annotations: { openWorldHint: true },
|
|
30933
|
+
inputSchema: object({
|
|
30934
|
+
channelId: string().min(1).describe("The Slack channel ID the message is in."),
|
|
30935
|
+
messageTs: string().min(1).describe("Timestamp of the message to edit (dotted, 'p' deep-link, or full permalink URL)."),
|
|
30936
|
+
message: string().min(1).max(4000).describe("The new message content.")
|
|
30937
|
+
}),
|
|
30938
|
+
outputSchema: object({
|
|
30939
|
+
success: boolean(),
|
|
30940
|
+
message: string(),
|
|
30941
|
+
messageTs: string().optional()
|
|
30942
|
+
})
|
|
30943
|
+
}, async ({ channelId, messageTs, message }, requestInfo, _meta) => {
|
|
30944
|
+
if (!requestInfo.agentId) {
|
|
30945
|
+
return {
|
|
30946
|
+
content: [{ type: "text", text: "Agent ID not found." }],
|
|
30947
|
+
structuredContent: { success: false, message: "Agent ID not found." }
|
|
30948
|
+
};
|
|
30949
|
+
}
|
|
30950
|
+
const agent = getAgentById(requestInfo.agentId);
|
|
30951
|
+
if (!agent) {
|
|
30952
|
+
return {
|
|
30953
|
+
content: [{ type: "text", text: "Agent not found." }],
|
|
30954
|
+
structuredContent: { success: false, message: "Agent not found." }
|
|
30955
|
+
};
|
|
30956
|
+
}
|
|
30957
|
+
const decision = can({
|
|
30958
|
+
principal: { kind: "agent", agentId: agent.id, isLead: agent.isLead },
|
|
30959
|
+
verb: "integration.slack.update",
|
|
30960
|
+
resource: { kind: "none" },
|
|
30961
|
+
source: "mcp"
|
|
30962
|
+
});
|
|
30963
|
+
if (!decision.allow) {
|
|
30964
|
+
return {
|
|
30965
|
+
content: [{ type: "text", text: "Editing Slack messages requires lead privileges." }],
|
|
30966
|
+
structuredContent: {
|
|
30967
|
+
success: false,
|
|
30968
|
+
message: "Editing Slack messages requires lead privileges."
|
|
30969
|
+
}
|
|
30970
|
+
};
|
|
30971
|
+
}
|
|
30972
|
+
const app = getSlackApp();
|
|
30973
|
+
if (!app) {
|
|
30974
|
+
return {
|
|
30975
|
+
content: [{ type: "text", text: "Slack not configured." }],
|
|
30976
|
+
structuredContent: { success: false, message: "Slack not configured." }
|
|
30977
|
+
};
|
|
30978
|
+
}
|
|
30979
|
+
try {
|
|
30980
|
+
const ts = parseSlackTs(messageTs);
|
|
30981
|
+
const slackMessage = markdownToSlack(message);
|
|
30982
|
+
const result = await app.client.chat.update({
|
|
30983
|
+
channel: channelId,
|
|
30984
|
+
ts,
|
|
30985
|
+
text: slackMessage,
|
|
30986
|
+
blocks: [
|
|
30987
|
+
{
|
|
30988
|
+
type: "section",
|
|
30989
|
+
text: {
|
|
30990
|
+
type: "mrkdwn",
|
|
30991
|
+
text: slackMessage
|
|
30992
|
+
}
|
|
30993
|
+
}
|
|
30994
|
+
]
|
|
30995
|
+
});
|
|
30996
|
+
return {
|
|
30997
|
+
content: [{ type: "text", text: "Message updated successfully." }],
|
|
30998
|
+
structuredContent: {
|
|
30999
|
+
success: true,
|
|
31000
|
+
message: "Message updated successfully.",
|
|
31001
|
+
messageTs: result.ts
|
|
31002
|
+
}
|
|
31003
|
+
};
|
|
31004
|
+
} catch (error) {
|
|
31005
|
+
const errorCode = error?.data?.error;
|
|
31006
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
31007
|
+
let message2;
|
|
31008
|
+
switch (errorCode) {
|
|
31009
|
+
case "message_not_found":
|
|
31010
|
+
message2 = "No message found at that timestamp in this channel.";
|
|
31011
|
+
break;
|
|
31012
|
+
case "cant_update_message":
|
|
31013
|
+
message2 = "Cannot edit this message — the bot can only edit messages it authored.";
|
|
31014
|
+
break;
|
|
31015
|
+
case "edit_window_closed":
|
|
31016
|
+
message2 = "The edit window for this message has closed.";
|
|
31017
|
+
break;
|
|
31018
|
+
case "channel_not_found":
|
|
31019
|
+
message2 = "Channel not found or the bot has no access.";
|
|
31020
|
+
break;
|
|
31021
|
+
case "not_in_channel":
|
|
31022
|
+
message2 = "The bot is not in that channel.";
|
|
31023
|
+
break;
|
|
31024
|
+
default:
|
|
31025
|
+
message2 = `Failed to update message: ${errorMsg}`;
|
|
31026
|
+
}
|
|
31027
|
+
return {
|
|
31028
|
+
content: [{ type: "text", text: message2 }],
|
|
31029
|
+
structuredContent: { success: false, message: message2 }
|
|
31030
|
+
};
|
|
31031
|
+
}
|
|
31032
|
+
});
|
|
31033
|
+
};
|
|
31034
|
+
|
|
29969
31035
|
// src/tools/slack-upload-file.ts
|
|
29970
31036
|
init_zod();
|
|
29971
31037
|
init_db();
|
|
@@ -30087,7 +31153,13 @@ var registerSlackUploadFileTool = (server) => {
|
|
|
30087
31153
|
slackChannelId = task.slackChannelId;
|
|
30088
31154
|
slackThreadTs = resolveTaskUploadThreadTs(task);
|
|
30089
31155
|
} else if (channelId) {
|
|
30090
|
-
|
|
31156
|
+
const decision = can({
|
|
31157
|
+
principal: { kind: "agent", agentId: agent.id, isLead: agent.isLead },
|
|
31158
|
+
verb: "integration.slack.upload",
|
|
31159
|
+
resource: { kind: "none" },
|
|
31160
|
+
source: "mcp"
|
|
31161
|
+
});
|
|
31162
|
+
if (!decision.allow) {
|
|
30091
31163
|
return {
|
|
30092
31164
|
content: [{ type: "text", text: "Direct channel access requires lead privileges." }],
|
|
30093
31165
|
structuredContent: {
|
|
@@ -32308,24 +33380,25 @@ function parseNextPageLink(linkHeader) {
|
|
|
32308
33380
|
const match = linkHeader.match(/<([^>]+)>;\s*rel="next"/);
|
|
32309
33381
|
return match ? match[1] ?? null : null;
|
|
32310
33382
|
}
|
|
32311
|
-
|
|
32312
|
-
|
|
32313
|
-
|
|
32314
|
-
|
|
32315
|
-
|
|
32316
|
-
const headers = {
|
|
33383
|
+
var REVIEW_COMMENTS_EMPTY_RETRY_DELAYS_MS = [1500, 3000, 3000];
|
|
33384
|
+
var defaultReviewCommentsRetryDelay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
33385
|
+
var reviewCommentsRetryDelay = defaultReviewCommentsRetryDelay;
|
|
33386
|
+
function buildReviewCommentsHeaders(token) {
|
|
33387
|
+
return {
|
|
32317
33388
|
Accept: "application/vnd.github+json",
|
|
32318
33389
|
Authorization: `Bearer ${token}`,
|
|
32319
33390
|
"X-GitHub-Api-Version": "2022-11-28"
|
|
32320
33391
|
};
|
|
33392
|
+
}
|
|
33393
|
+
async function fetchPaginatedReviewComments(initialUrl, headers) {
|
|
32321
33394
|
const allComments = [];
|
|
32322
|
-
let url =
|
|
33395
|
+
let url = initialUrl;
|
|
32323
33396
|
try {
|
|
32324
33397
|
while (url) {
|
|
32325
33398
|
const response = await fetch(url, { headers });
|
|
32326
33399
|
if (!response.ok) {
|
|
32327
33400
|
console.error(`[GitHub] Failed to fetch review inline comments: ${response.status}`);
|
|
32328
|
-
return allComments;
|
|
33401
|
+
return { comments: allComments, degraded: true };
|
|
32329
33402
|
}
|
|
32330
33403
|
const page = await response.json();
|
|
32331
33404
|
if (Array.isArray(page)) {
|
|
@@ -32333,11 +33406,66 @@ async function fetchReviewComments(repo, prNumber, reviewId, installationId) {
|
|
|
32333
33406
|
}
|
|
32334
33407
|
url = parseNextPageLink(response.headers.get("link"));
|
|
32335
33408
|
}
|
|
32336
|
-
return allComments;
|
|
33409
|
+
return { comments: allComments, degraded: false };
|
|
32337
33410
|
} catch (error) {
|
|
32338
33411
|
console.error("[GitHub] Error fetching review inline comments:", error);
|
|
32339
|
-
return allComments;
|
|
33412
|
+
return { comments: allComments, degraded: true };
|
|
33413
|
+
}
|
|
33414
|
+
}
|
|
33415
|
+
async function fetchReviewScopedComments(repo, prNumber, reviewId, headers) {
|
|
33416
|
+
const url = `https://api.github.com/repos/${repo}/pulls/${prNumber}/reviews/${reviewId}/comments?per_page=100`;
|
|
33417
|
+
let result = await fetchPaginatedReviewComments(url, headers);
|
|
33418
|
+
if (result.degraded || result.comments.length > 0) {
|
|
33419
|
+
return result;
|
|
32340
33420
|
}
|
|
33421
|
+
for (const delayMs of REVIEW_COMMENTS_EMPTY_RETRY_DELAYS_MS) {
|
|
33422
|
+
await reviewCommentsRetryDelay(delayMs);
|
|
33423
|
+
result = await fetchPaginatedReviewComments(url, headers);
|
|
33424
|
+
if (result.degraded || result.comments.length > 0) {
|
|
33425
|
+
return result;
|
|
33426
|
+
}
|
|
33427
|
+
}
|
|
33428
|
+
return result;
|
|
33429
|
+
}
|
|
33430
|
+
async function fetchPrLevelReviewComments(repo, prNumber, reviewId, headers) {
|
|
33431
|
+
const url = `https://api.github.com/repos/${repo}/pulls/${prNumber}/comments?per_page=100`;
|
|
33432
|
+
const result = await fetchPaginatedReviewComments(url, headers);
|
|
33433
|
+
return {
|
|
33434
|
+
comments: result.comments.filter((comment) => comment.pull_request_review_id === reviewId),
|
|
33435
|
+
degraded: result.degraded
|
|
33436
|
+
};
|
|
33437
|
+
}
|
|
33438
|
+
function dedupeReviewComments(comments) {
|
|
33439
|
+
const byId = new Map;
|
|
33440
|
+
for (const comment of comments) {
|
|
33441
|
+
if (!byId.has(comment.id)) {
|
|
33442
|
+
byId.set(comment.id, comment);
|
|
33443
|
+
}
|
|
33444
|
+
}
|
|
33445
|
+
return [...byId.values()];
|
|
33446
|
+
}
|
|
33447
|
+
async function fetchReviewComments(repo, prNumber, reviewId, installationId) {
|
|
33448
|
+
const token = await getInstallationToken(installationId);
|
|
33449
|
+
if (!token) {
|
|
33450
|
+
return { comments: [], degraded: true };
|
|
33451
|
+
}
|
|
33452
|
+
const headers = buildReviewCommentsHeaders(token);
|
|
33453
|
+
const scopedResult = await fetchReviewScopedComments(repo, prNumber, reviewId, headers);
|
|
33454
|
+
if (!scopedResult.degraded && scopedResult.comments.length > 0) {
|
|
33455
|
+
return scopedResult;
|
|
33456
|
+
}
|
|
33457
|
+
const fallbackResult = await fetchPrLevelReviewComments(repo, prNumber, reviewId, headers);
|
|
33458
|
+
const mergedComments = dedupeReviewComments([
|
|
33459
|
+
...scopedResult.comments,
|
|
33460
|
+
...fallbackResult.comments
|
|
33461
|
+
]);
|
|
33462
|
+
if (fallbackResult.comments.length > 0) {
|
|
33463
|
+
return { comments: mergedComments, degraded: false };
|
|
33464
|
+
}
|
|
33465
|
+
return {
|
|
33466
|
+
comments: mergedComments,
|
|
33467
|
+
degraded: scopedResult.degraded || fallbackResult.degraded
|
|
33468
|
+
};
|
|
32341
33469
|
}
|
|
32342
33470
|
function buildInlineCommentsSection(comments) {
|
|
32343
33471
|
if (comments.length === 0)
|
|
@@ -32359,6 +33487,14 @@ ${items.join(`
|
|
|
32359
33487
|
|
|
32360
33488
|
`)}`;
|
|
32361
33489
|
}
|
|
33490
|
+
function buildInlineCommentsDegradedSection(repo, prNumber) {
|
|
33491
|
+
return `
|
|
33492
|
+
|
|
33493
|
+
## ⚠️ Inline comments could NOT be auto-fetched
|
|
33494
|
+
The automatic inline-comment fetch failed or was unverifiable while the reviewer submitted this review. Inline comments ARE the change requests. BEFORE scoping or dispatching this task you MUST fetch them yourself:
|
|
33495
|
+
\`gh api "repos/${repo}/pulls/${prNumber}/comments?per_page=100" --jq '.[] | {id,path,line,body}'\`
|
|
33496
|
+
Reply to and resolve EVERY unresolved inline thread. Do NOT dispatch off the review body alone.`;
|
|
33497
|
+
}
|
|
32362
33498
|
async function handlePullRequestReview(event) {
|
|
32363
33499
|
const { action, review, pull_request: pr, repository, sender, installation } = event;
|
|
32364
33500
|
const requestedByUserId = resolveGitHubSender(sender.login, "pull_request_review", `Review on PR #${pr.number}: ${review.state}`);
|
|
@@ -32369,8 +33505,8 @@ async function handlePullRequestReview(event) {
|
|
|
32369
33505
|
if (isDuplicate2(eventKey)) {
|
|
32370
33506
|
return { created: false };
|
|
32371
33507
|
}
|
|
32372
|
-
const inlineComments = installation?.id ? await fetchReviewComments(repository.full_name, pr.number, review.id, installation.id) : [];
|
|
32373
|
-
if (review.state === "commented" && !review.body && inlineComments.length === 0) {
|
|
33508
|
+
const { comments: inlineComments, degraded } = installation?.id ? await fetchReviewComments(repository.full_name, pr.number, review.id, installation.id) : { comments: [], degraded: true };
|
|
33509
|
+
if (review.state === "commented" && !review.body && inlineComments.length === 0 && !degraded) {
|
|
32374
33510
|
return { created: false };
|
|
32375
33511
|
}
|
|
32376
33512
|
const existingTask = findTaskByVcs(repository.full_name, pr.number);
|
|
@@ -32384,13 +33520,13 @@ async function handlePullRequestReview(event) {
|
|
|
32384
33520
|
|
|
32385
33521
|
Review Comment:
|
|
32386
33522
|
${review.body}` : "";
|
|
32387
|
-
const inlineCommentsSection = buildInlineCommentsSection(inlineComments);
|
|
33523
|
+
const inlineCommentsSection = buildInlineCommentsSection(inlineComments) + (degraded ? buildInlineCommentsDegradedSection(repository.full_name, pr.number) : "");
|
|
32388
33524
|
const relatedTaskSection = existingTask ? `Related task: ${existingTask.id}
|
|
32389
33525
|
\uD83D\uDD00 Consider routing to the same agent working on the related task.
|
|
32390
33526
|
` : "";
|
|
32391
33527
|
const hasInlineComments = inlineComments.length > 0;
|
|
32392
33528
|
const baseReviewSuggestion = review.state === "approved" ? "\uD83D\uDCA1 Suggested: Merge the PR or wait for additional reviews" : review.state === "changes_requested" ? "\uD83D\uDCA1 Suggested: Address the requested changes and update the PR" : "\uD83D\uDCA1 Suggested: Review the feedback and respond if needed";
|
|
32393
|
-
const reviewSuggestions = hasInlineComments ? `${baseReviewSuggestion}
|
|
33529
|
+
const reviewSuggestions = hasInlineComments || degraded ? `${baseReviewSuggestion}
|
|
32394
33530
|
\uD83D\uDCAC Address EVERY inline comment. After pushing fixes, reply to and resolve each inline review thread on GitHub so the reviewer sees visible confirmation.` : baseReviewSuggestion;
|
|
32395
33531
|
const result = resolveTemplate("github.pull_request.review_submitted", {
|
|
32396
33532
|
review_emoji: emoji,
|
|
@@ -33171,7 +34307,7 @@ async function notifySlack(text) {
|
|
|
33171
34307
|
return;
|
|
33172
34308
|
}
|
|
33173
34309
|
try {
|
|
33174
|
-
const { getSlackApp: getSlackApp2 } = await import("./app-
|
|
34310
|
+
const { getSlackApp: getSlackApp2 } = await import("./app-es4nzc71.js");
|
|
33175
34311
|
const app = getSlackApp2();
|
|
33176
34312
|
if (!app) {
|
|
33177
34313
|
console.warn("[Jira webhook keepalive] Slack not available, cannot send notification");
|
|
@@ -33240,7 +34376,7 @@ async function deleteJiraWebhook(webhookId) {
|
|
|
33240
34376
|
console.log(`[Jira webhook keepalive] Deleted webhook id=${webhookId}`);
|
|
33241
34377
|
}
|
|
33242
34378
|
async function overwriteWebhookIds(next) {
|
|
33243
|
-
const { getDb: getDb2 } = await import("./db-
|
|
34379
|
+
const { getDb: getDb2 } = await import("./db-bbgahh4z.js");
|
|
33244
34380
|
const db = getDb2();
|
|
33245
34381
|
const txn = db.transaction(() => {
|
|
33246
34382
|
const row = db.query("SELECT metadata FROM oauth_apps WHERE provider = 'jira'").get();
|
|
@@ -107689,6 +108825,28 @@ var registerDeleteConfigTool = (server) => {
|
|
|
107689
108825
|
}
|
|
107690
108826
|
};
|
|
107691
108827
|
}
|
|
108828
|
+
const agent = getAgentById(requestInfo.agentId);
|
|
108829
|
+
const decision = can({
|
|
108830
|
+
principal: {
|
|
108831
|
+
kind: "agent",
|
|
108832
|
+
agentId: requestInfo.agentId,
|
|
108833
|
+
isLead: agent?.isLead ?? false
|
|
108834
|
+
},
|
|
108835
|
+
verb: "config.delete.any",
|
|
108836
|
+
resource: { kind: "none" },
|
|
108837
|
+
source: "mcp"
|
|
108838
|
+
});
|
|
108839
|
+
if (!decision.allow) {
|
|
108840
|
+
const message = "Deleting swarm config requires the lead agent.";
|
|
108841
|
+
return {
|
|
108842
|
+
content: [{ type: "text", text: message }],
|
|
108843
|
+
structuredContent: {
|
|
108844
|
+
yourAgentId: requestInfo.agentId,
|
|
108845
|
+
success: false,
|
|
108846
|
+
message
|
|
108847
|
+
}
|
|
108848
|
+
};
|
|
108849
|
+
}
|
|
107692
108850
|
try {
|
|
107693
108851
|
const existing = getSwarmConfigLookupById(id);
|
|
107694
108852
|
if (!existing) {
|
|
@@ -107781,8 +108939,27 @@ var registerGetConfigTool = (server) => {
|
|
|
107781
108939
|
if (key) {
|
|
107782
108940
|
configs = configs.filter((c) => c.key === key);
|
|
107783
108941
|
}
|
|
107784
|
-
|
|
108942
|
+
let effectiveIncludeSecrets = includeSecrets ?? false;
|
|
108943
|
+
let secretsNote = "";
|
|
107785
108944
|
if (includeSecrets) {
|
|
108945
|
+
const agent = getAgentById(requestInfo.agentId);
|
|
108946
|
+
const decision = can({
|
|
108947
|
+
principal: {
|
|
108948
|
+
kind: "agent",
|
|
108949
|
+
agentId: requestInfo.agentId,
|
|
108950
|
+
isLead: agent?.isLead ?? false
|
|
108951
|
+
},
|
|
108952
|
+
verb: "config.read.secrets",
|
|
108953
|
+
resource: { kind: "none" },
|
|
108954
|
+
source: "mcp"
|
|
108955
|
+
});
|
|
108956
|
+
if (!decision.allow) {
|
|
108957
|
+
effectiveIncludeSecrets = false;
|
|
108958
|
+
secretsNote = " (secret values masked: reading unmasked secrets requires the lead agent)";
|
|
108959
|
+
}
|
|
108960
|
+
}
|
|
108961
|
+
const result = effectiveIncludeSecrets ? configs : maskSecrets(configs);
|
|
108962
|
+
if (effectiveIncludeSecrets) {
|
|
107786
108963
|
for (const c of result) {
|
|
107787
108964
|
if (c.isSecret && c.value) {
|
|
107788
108965
|
registerVolatileSecret(c.value, `config:${c.key}`);
|
|
@@ -107790,7 +108967,7 @@ var registerGetConfigTool = (server) => {
|
|
|
107790
108967
|
}
|
|
107791
108968
|
}
|
|
107792
108969
|
const count = result.length;
|
|
107793
|
-
const configList = count === 0 ? "No configs found." : result.map((c) => `- ${c.key}=${c.isSecret && !
|
|
108970
|
+
const configList = count === 0 ? "No configs found." : result.map((c) => `- ${c.key}=${c.isSecret && !effectiveIncludeSecrets ? "********" : c.value} (scope: ${c.scope}${c.scopeId ? `, scopeId: ${c.scopeId}` : ""})`).join(`
|
|
107794
108971
|
`);
|
|
107795
108972
|
return {
|
|
107796
108973
|
content: [
|
|
@@ -107798,13 +108975,13 @@ var registerGetConfigTool = (server) => {
|
|
|
107798
108975
|
type: "text",
|
|
107799
108976
|
text: count === 0 ? "No configs found." : `Found ${count} config(s):
|
|
107800
108977
|
|
|
107801
|
-
${configList}`
|
|
108978
|
+
${configList}${secretsNote}`
|
|
107802
108979
|
}
|
|
107803
108980
|
],
|
|
107804
108981
|
structuredContent: {
|
|
107805
108982
|
yourAgentId: requestInfo.agentId,
|
|
107806
108983
|
success: true,
|
|
107807
|
-
message: count === 0 ? "No configs found." : `Found ${count} config(s)
|
|
108984
|
+
message: count === 0 ? "No configs found." : `Found ${count} config(s).${secretsNote}`,
|
|
107808
108985
|
configs: result,
|
|
107809
108986
|
count
|
|
107810
108987
|
}
|
|
@@ -107865,8 +109042,27 @@ var registerListConfigTool = (server) => {
|
|
|
107865
109042
|
scopeId,
|
|
107866
109043
|
key
|
|
107867
109044
|
});
|
|
107868
|
-
|
|
109045
|
+
let effectiveIncludeSecrets = includeSecrets ?? false;
|
|
109046
|
+
let secretsNote = "";
|
|
107869
109047
|
if (includeSecrets) {
|
|
109048
|
+
const agent = getAgentById(requestInfo.agentId);
|
|
109049
|
+
const decision = can({
|
|
109050
|
+
principal: {
|
|
109051
|
+
kind: "agent",
|
|
109052
|
+
agentId: requestInfo.agentId,
|
|
109053
|
+
isLead: agent?.isLead ?? false
|
|
109054
|
+
},
|
|
109055
|
+
verb: "config.read.secrets",
|
|
109056
|
+
resource: { kind: "none" },
|
|
109057
|
+
source: "mcp"
|
|
109058
|
+
});
|
|
109059
|
+
if (!decision.allow) {
|
|
109060
|
+
effectiveIncludeSecrets = false;
|
|
109061
|
+
secretsNote = " (secret values masked: reading unmasked secrets requires the lead agent)";
|
|
109062
|
+
}
|
|
109063
|
+
}
|
|
109064
|
+
const result = effectiveIncludeSecrets ? configs : maskSecrets(configs);
|
|
109065
|
+
if (effectiveIncludeSecrets) {
|
|
107870
109066
|
for (const c of result) {
|
|
107871
109067
|
if (c.isSecret && c.value) {
|
|
107872
109068
|
registerVolatileSecret(c.value, `config:${c.key}`);
|
|
@@ -107874,7 +109070,7 @@ var registerListConfigTool = (server) => {
|
|
|
107874
109070
|
}
|
|
107875
109071
|
}
|
|
107876
109072
|
const count = result.length;
|
|
107877
|
-
const configList = count === 0 ? "No configs found." : result.map((c) => `- [${c.scope}${c.scopeId ? `:${c.scopeId}` : ""}] ${c.key}=${c.isSecret && !
|
|
109073
|
+
const configList = count === 0 ? "No configs found." : result.map((c) => `- [${c.scope}${c.scopeId ? `:${c.scopeId}` : ""}] ${c.key}=${c.isSecret && !effectiveIncludeSecrets ? "********" : c.value}${c.description ? ` — ${c.description}` : ""}`).join(`
|
|
107878
109074
|
`);
|
|
107879
109075
|
return {
|
|
107880
109076
|
content: [
|
|
@@ -107882,13 +109078,13 @@ var registerListConfigTool = (server) => {
|
|
|
107882
109078
|
type: "text",
|
|
107883
109079
|
text: count === 0 ? "No configs found." : `Found ${count} config(s):
|
|
107884
109080
|
|
|
107885
|
-
${configList}`
|
|
109081
|
+
${configList}${secretsNote}`
|
|
107886
109082
|
}
|
|
107887
109083
|
],
|
|
107888
109084
|
structuredContent: {
|
|
107889
109085
|
yourAgentId: requestInfo.agentId,
|
|
107890
109086
|
success: true,
|
|
107891
|
-
message: count === 0 ? "No configs found." : `Found ${count} config(s)
|
|
109087
|
+
message: count === 0 ? "No configs found." : `Found ${count} config(s).${secretsNote}`,
|
|
107892
109088
|
configs: result,
|
|
107893
109089
|
count
|
|
107894
109090
|
}
|
|
@@ -107972,7 +109168,17 @@ var registerSetConfigTool = (server) => {
|
|
|
107972
109168
|
}
|
|
107973
109169
|
if (key.toUpperCase() === CREDENTIAL_BINDINGS_CONFIG_KEY) {
|
|
107974
109170
|
const agent = getAgentById(requestInfo.agentId);
|
|
107975
|
-
|
|
109171
|
+
const decision = can({
|
|
109172
|
+
principal: {
|
|
109173
|
+
kind: "agent",
|
|
109174
|
+
agentId: requestInfo.agentId,
|
|
109175
|
+
isLead: agent?.isLead ?? false
|
|
109176
|
+
},
|
|
109177
|
+
verb: "config.credential-bindings.write",
|
|
109178
|
+
resource: { kind: "none" },
|
|
109179
|
+
source: "mcp"
|
|
109180
|
+
});
|
|
109181
|
+
if (!decision.allow) {
|
|
107976
109182
|
const message = "Only the lead can manage SCRIPT_CREDENTIAL_BINDINGS. Use the credential-bindings tool.";
|
|
107977
109183
|
return {
|
|
107978
109184
|
content: [{ type: "text", text: message }],
|
|
@@ -107983,6 +109189,29 @@ var registerSetConfigTool = (server) => {
|
|
|
107983
109189
|
}
|
|
107984
109190
|
};
|
|
107985
109191
|
}
|
|
109192
|
+
} else {
|
|
109193
|
+
const agent = getAgentById(requestInfo.agentId);
|
|
109194
|
+
const decision = can({
|
|
109195
|
+
principal: {
|
|
109196
|
+
kind: "agent",
|
|
109197
|
+
agentId: requestInfo.agentId,
|
|
109198
|
+
isLead: agent?.isLead ?? false
|
|
109199
|
+
},
|
|
109200
|
+
verb: "config.write.any",
|
|
109201
|
+
resource: { kind: "none" },
|
|
109202
|
+
source: "mcp"
|
|
109203
|
+
});
|
|
109204
|
+
if (!decision.allow) {
|
|
109205
|
+
const message = "Writing swarm config requires the lead agent.";
|
|
109206
|
+
return {
|
|
109207
|
+
content: [{ type: "text", text: message }],
|
|
109208
|
+
structuredContent: {
|
|
109209
|
+
yourAgentId: requestInfo.agentId,
|
|
109210
|
+
success: false,
|
|
109211
|
+
message
|
|
109212
|
+
}
|
|
109213
|
+
};
|
|
109214
|
+
}
|
|
107986
109215
|
}
|
|
107987
109216
|
const validationError = validateConfigValue(key, value);
|
|
107988
109217
|
if (validationError) {
|
|
@@ -108336,7 +109565,7 @@ async function taskActionHandler(ctx, input) {
|
|
|
108336
109565
|
if (!existingTask) {
|
|
108337
109566
|
return { success: false, message: `Task "${taskId}" not found.` };
|
|
108338
109567
|
}
|
|
108339
|
-
const ownershipError = assertOwnsTask(ctx, existingTask);
|
|
109568
|
+
const ownershipError = assertOwnsTask(ctx, existingTask, "task.action.own");
|
|
108340
109569
|
if (ownershipError)
|
|
108341
109570
|
return ownershipError;
|
|
108342
109571
|
if (action === "to_backlog") {
|
|
@@ -109072,7 +110301,13 @@ var registerUpdateProfileTool = (server) => {
|
|
|
109072
110301
|
}
|
|
109073
110302
|
};
|
|
109074
110303
|
}
|
|
109075
|
-
|
|
110304
|
+
const decision = can({
|
|
110305
|
+
principal: { kind: "agent", agentId: callingAgent.id, isLead: callingAgent.isLead },
|
|
110306
|
+
verb: "agent.profile.update.any",
|
|
110307
|
+
resource: { kind: "agent", agentId: targetAgentId },
|
|
110308
|
+
source: "mcp"
|
|
110309
|
+
});
|
|
110310
|
+
if (!decision.allow) {
|
|
109076
110311
|
return {
|
|
109077
110312
|
content: [
|
|
109078
110313
|
{
|
|
@@ -109758,6 +110993,7 @@ var registerListWorkflowRunsTool = (server) => {
|
|
|
109758
110993
|
// src/tools/workflows/list-workflows.ts
|
|
109759
110994
|
init_zod();
|
|
109760
110995
|
init_db();
|
|
110996
|
+
init_types();
|
|
109761
110997
|
var registerListWorkflowsTool = (server) => {
|
|
109762
110998
|
createToolRegistrar(server)("list-workflows", {
|
|
109763
110999
|
title: "List Workflows",
|
|
@@ -109765,6 +111001,8 @@ var registerListWorkflowsTool = (server) => {
|
|
|
109765
111001
|
description: "List all automation workflows, optionally filtered by enabled status. Returns SLIM rows WITHOUT the full `definition` (DAG) — each row carries a `nodeCount` instead. To inspect or patch a workflow's nodes/triggers, call `get-workflow` by id, or pass `includeFull: true` here.",
|
|
109766
111002
|
inputSchema: exports_external.object({
|
|
109767
111003
|
enabled: exports_external.boolean().optional().describe("Filter by enabled status (omit to return all)"),
|
|
111004
|
+
consecutiveErrorsMin: exports_external.number().int().min(0).optional().describe("Only return workflows with at least this many latest consecutive failed runs."),
|
|
111005
|
+
lastRunStatus: WorkflowRunStatusSchema.optional().describe("Only return workflows whose latest run has this status."),
|
|
109768
111006
|
includeFull: exports_external.boolean().optional().describe("Return the full workflow `definition` + trigger config instead of slim rows. Default false — prefer `get-workflow` to fetch a single workflow in full.")
|
|
109769
111007
|
}),
|
|
109770
111008
|
outputSchema: exports_external.object({
|
|
@@ -109772,9 +111010,9 @@ var registerListWorkflowsTool = (server) => {
|
|
|
109772
111010
|
message: exports_external.string(),
|
|
109773
111011
|
workflows: exports_external.array(exports_external.unknown())
|
|
109774
111012
|
})
|
|
109775
|
-
}, async ({ enabled: enabled2, includeFull }) => {
|
|
111013
|
+
}, async ({ enabled: enabled2, consecutiveErrorsMin, lastRunStatus, includeFull }) => {
|
|
109776
111014
|
try {
|
|
109777
|
-
const filters =
|
|
111015
|
+
const filters = { enabled: enabled2, consecutiveErrorsMin, lastRunStatus };
|
|
109778
111016
|
const workflows = includeFull ? listWorkflows(filters) : listWorkflows(filters, { slim: true });
|
|
109779
111017
|
return {
|
|
109780
111018
|
content: [{ type: "text", text: `Found ${workflows.length} workflow(s).` }],
|
|
@@ -110292,6 +111530,8 @@ function createServer() {
|
|
|
110292
111530
|
registerSlackListChannelsTool(server);
|
|
110293
111531
|
registerSlackUploadFileTool(server);
|
|
110294
111532
|
registerSlackDownloadFileTool(server);
|
|
111533
|
+
registerSlackDeleteTool(server);
|
|
111534
|
+
registerSlackUpdateTool(server);
|
|
110295
111535
|
registerRegisterAgentmailInboxTool(server);
|
|
110296
111536
|
registerRegisterKapsoNumberTool(server);
|
|
110297
111537
|
registerUnregisterKapsoNumberTool(server);
|
|
@@ -110322,6 +111562,7 @@ function createServer() {
|
|
|
110322
111562
|
registerListSchedulesTool(server);
|
|
110323
111563
|
registerCreateScheduleTool(server);
|
|
110324
111564
|
registerUpdateScheduleTool(server);
|
|
111565
|
+
registerPatchScheduleTool(server);
|
|
110325
111566
|
registerDeleteScheduleTool(server);
|
|
110326
111567
|
registerRunScheduleNowTool(server);
|
|
110327
111568
|
}
|
|
@@ -110527,4 +111768,4 @@ function writeSkillsToFilesystem(entries, harnessType = "all", home) {
|
|
|
110527
111768
|
return { synced, removed, errors };
|
|
110528
111769
|
}
|
|
110529
111770
|
|
|
110530
|
-
export { DEFAULT_NEGOTIATED_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, isJSONRPCRequest, isJSONRPCResultResponse, isJSONRPCErrorResponse, JSONRPCMessageSchema, isInitializeRequest, McpServer, isGitHubEnabled, initGitHub, verifyWebhookSignature, userCtx, isPollTracingEnabled, initOtel, withSpan, startSpan, withSpanContext, withRemoteContext, injectTraceContext, recordSessionCost, createToolRegistrar, cancelTaskInputSchema, cancelTaskOutputSchema, cancelTaskHandler, setCorsHeaders, parseQueryParams, getPathSegments, safeRequestUrlForLog, agentWithCapacity, parseBody, BODY_TOO_LARGE, enforceContentLengthCap, json, jsonError, triggerSchemaErrorResponse, deriveApiBaseUrl, httpServerSemconvAttributes, describeRequestRoute, route, assertSelectOnlyQuery, executeReadOnlyQuery, handleDbQuery, snapshotMetric, snapshotPage, getTaskDetailsInputSchema, getTaskDetailsOutputSchema, getTaskDetailsHandler, getTasksInputSchema, getTasksOutputSchema, getTasksHandler, storeLinks, refreshLinks, canReadMemory, getLinksForMemory, recordRetrievals, expandCandidatesWithGraph, markKapsoMessageRead, sendKapsoReaction, getKapsoConfig, getKapsoNumberMapping, markKapsoMessageSeen, resolveHttpAuditUserId, createEvent, createEventsBatch, getEventCountsFiltered, getEventsFiltered, mergeScheduleTiming, validateRecurringTiming, sendTaskOutputSchema, sendTaskHandler, parseSkillContent, ExplicitSelfDuplicateError, applyRating, shouldPersistAutomaticTaskMemory, isAgentMailEnabled, initAgentMail, verifyAgentMailWebhook, isInboxAllowed, isSenderAllowed, handleMessageReceived, handlePullRequest, handleIssue, handleComment, handlePullRequestReview, handleCheckRun, handleCheckSuite, handleWorkflowRun, getJiraMetadata, updateJiraMetadata, clearJiraMetadata, registerJiraWebhook, deleteJiraWebhook, isJiraEnabled, initJira, handleJiraWebhook, handleAgentSessionEvent, handleIssueUpdate, handleIssueDelete, handleAgentSessionPrompted, isLinearEnabled, initLinear, loadGlobalConfigsIntoEnv, reloadGlobalConfigsAndIntegrations, scheduleIntegrationsReload, handleCore, canClaim, emitBudgetRefusalSideEffects, taskActionInputSchema, taskActionOutputSchema, taskActionHandler, hasCapability, getEnabledCapabilities, createServer, writeSkillsToFilesystem };
|
|
111771
|
+
export { DEFAULT_NEGOTIATED_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, isJSONRPCRequest, isJSONRPCResultResponse, isJSONRPCErrorResponse, JSONRPCMessageSchema, isInitializeRequest, McpServer, isGitHubEnabled, initGitHub, verifyWebhookSignature, setAuditSink, clearAuditSink, can, userCtx, isPollTracingEnabled, initOtel, withSpan, startSpan, withSpanContext, withRemoteContext, injectTraceContext, recordSessionCost, createToolRegistrar, cancelTaskInputSchema, cancelTaskOutputSchema, cancelTaskHandler, setCorsHeaders, parseQueryParams, getPathSegments, safeRequestUrlForLog, agentWithCapacity, parseBody, BODY_TOO_LARGE, enforceContentLengthCap, json, jsonError, triggerSchemaErrorResponse, deriveApiBaseUrl, httpServerSemconvAttributes, describeRequestRoute, route, assertSelectOnlyQuery, executeReadOnlyQuery, handleDbQuery, snapshotMetric, snapshotPage, getTaskDetailsInputSchema, getTaskDetailsOutputSchema, getTaskDetailsHandler, getTasksInputSchema, getTasksOutputSchema, getTasksHandler, storeLinks, refreshLinks, canReadMemory, getLinksForMemory, recordRetrievals, expandCandidatesWithGraph, markKapsoMessageRead, sendKapsoReaction, getKapsoConfig, getKapsoNumberMapping, markKapsoMessageSeen, resolveHttpAuditUserId, createEvent, createEventsBatch, getEventCountsFiltered, getEventsFiltered, mergeScheduleTiming, validateRecurringTiming, sendTaskOutputSchema, sendTaskHandler, parseSkillContent, ExplicitSelfDuplicateError, applyRating, shouldPersistAutomaticTaskMemory, isAgentMailEnabled, initAgentMail, verifyAgentMailWebhook, isInboxAllowed, isSenderAllowed, handleMessageReceived, handlePullRequest, handleIssue, handleComment, handlePullRequestReview, handleCheckRun, handleCheckSuite, handleWorkflowRun, getJiraMetadata, updateJiraMetadata, clearJiraMetadata, registerJiraWebhook, deleteJiraWebhook, isJiraEnabled, initJira, handleJiraWebhook, handleAgentSessionEvent, handleIssueUpdate, handleIssueDelete, handleAgentSessionPrompted, isLinearEnabled, initLinear, loadGlobalConfigsIntoEnv, reloadGlobalConfigsAndIntegrations, scheduleIntegrationsReload, handleCore, canClaim, emitBudgetRefusalSideEffects, taskActionInputSchema, taskActionOutputSchema, taskActionHandler, hasCapability, getEnabledCapabilities, createServer, writeSkillsToFilesystem };
|