@desplega.ai/agent-swarm 1.112.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.
Files changed (145) hide show
  1. package/dist/{actions-eckb4g8r.js → actions-q4n7cz6e.js} +6 -6
  2. package/dist/{app-b05sgm02.js → app-es4nzc71.js} +3 -3
  3. package/dist/{assistant-7255sbyw.js → assistant-0x8ey0vs.js} +9 -9
  4. package/dist/{boot-reembed-y1bt3ttn.js → boot-reembed-69agkhv6.js} +4 -4
  5. package/dist/{boot-reembed-yv7awg4y.js → boot-reembed-p0ny05t8.js} +3 -3
  6. package/dist/{boot-scrub-logs-ddyaeqar.js → boot-scrub-logs-yybrkmvr.js} +2 -2
  7. package/dist/{cli-s9pj3c7z.js → cli-30bbaveh.js} +2 -2
  8. package/dist/{cli-r1btc895.js → cli-36ymmpmp.js} +2 -2
  9. package/dist/{cli-15598a4k.js → cli-4xyj9jtq.js} +1215 -235
  10. package/dist/{cli-52tbq17n.js → cli-bgef578c.js} +1 -1
  11. package/dist/{cli-76d1vtvq.js → cli-bgvskydy.js} +1 -1
  12. package/dist/{cli-pt7c7qxz.js → cli-bnzbn8j8.js} +3 -3
  13. package/dist/{cli-8f0dwca2.js → cli-c3frqf65.js} +4 -3
  14. package/dist/{cli-9p0qhead.js → cli-c9mtm09b.js} +1 -1
  15. package/dist/{cli-709t753c.js → cli-f3qa069v.js} +2 -2
  16. package/dist/{cli-94vzr3nq.js → cli-gtkqc144.js} +4 -4
  17. package/dist/{cli-7xkwgkrs.js → cli-h3k622d0.js} +1 -1
  18. package/dist/{cli-h9jx5mag.js → cli-hjhvekf4.js} +4 -4
  19. package/dist/{cli-tp7e60s3.js → cli-hsetkkd3.js} +2 -2
  20. package/dist/{cli-tch0ypp6.js → cli-mq580e06.js} +4 -4
  21. package/dist/{cli-951w7c79.js → cli-ncfgsqbd.js} +63 -7
  22. package/dist/{cli-30d2ct3k.js → cli-pwc5e83c.js} +3 -3
  23. package/dist/{cli-3r5y6ayj.js → cli-qwgtacd6.js} +2 -2
  24. package/dist/{cli-56s4ehzw.js → cli-r5g6ngtn.js} +1 -1
  25. package/dist/{cli-rp8pca98.js → cli-tbw13sx8.js} +1 -1
  26. package/dist/{cli-hm27prrc.js → cli-trmapn9k.js} +5 -5
  27. package/dist/{cli-0c3n0eba.js → cli-y4ycrnjc.js} +1 -1
  28. package/dist/{cli-a5e8n5hg.js → cli-y6mrptcn.js} +1 -1
  29. package/dist/{cli-tg0f2wmz.js → cli-z46pnksz.js} +5 -5
  30. package/dist/cli.js +11 -10
  31. package/dist/{commands-14g7taf4.js → commands-z5dbwta3.js} +2 -2
  32. package/dist/{db-3tge4jrn.js → db-bbgahh4z.js} +2 -2
  33. package/dist/{handlers-sy64egg6.js → handlers-6b44317z.js} +9 -9
  34. package/dist/{hook-rnkzw8vp.js → hook-sbp5fmps.js} +1 -1
  35. package/dist/{http-xx9xptcs.js → http-2xz43gz3.js} +354 -94
  36. package/dist/{index-zfx5p959.js → index-0gzmqj4k.js} +8 -8
  37. package/dist/{index-3wr6v2mm.js → index-5ghxn8s6.js} +7 -7
  38. package/dist/{index-brma88d4.js → index-nhat35em.js} +9 -9
  39. package/dist/{index-ha8e9xn2.js → index-vfedgdw6.js} +26 -15
  40. package/dist/{keepalive-rbz05zad.js → keepalive-62gfj91d.js} +4 -4
  41. package/dist/{lead-s790v7r0.js → lead-ag0akn0v.js} +17 -17
  42. package/dist/{maintenance-420rmadb.js → maintenance-xxrrzk5x.js} +4 -4
  43. package/dist/{onboard-jgqamqnw.js → onboard-64zyfcbs.js} +2 -2
  44. package/dist/{otel-impl-ch3n3783.js → otel-impl-b86qseaa.js} +1 -1
  45. package/dist/{pricing-refresh-rmtrby6h.js → pricing-refresh-cgtkffpa.js} +4 -4
  46. package/dist/{seed-pricing-1szjzfj1.js → seed-pricing-1r1y1xkq.js} +3 -3
  47. package/dist/{setup-8jfzkyzv.js → setup-mkcgkjf7.js} +2 -2
  48. package/dist/{worker-40vq0pm8.js → worker-351sze63.js} +17 -17
  49. package/openapi.json +314 -3
  50. package/package.json +4 -3
  51. package/src/be/db.ts +43 -3
  52. package/src/be/memory/providers/sqlite-store.ts +5 -1
  53. package/src/be/migrations/108_rbac_permission_audit.sql +26 -0
  54. package/src/be/rbac-audit.ts +218 -0
  55. package/src/http/all-routes.ts +58 -0
  56. package/src/http/config.ts +55 -0
  57. package/src/http/favorites.ts +5 -0
  58. package/src/http/fs.ts +27 -7
  59. package/src/http/index.ts +26 -0
  60. package/src/http/kv.ts +21 -4
  61. package/src/http/route-def.ts +9 -0
  62. package/src/http/schedules.ts +56 -22
  63. package/src/http/scripts.ts +33 -0
  64. package/src/http/workflows.ts +13 -2
  65. package/src/prompts/base-prompt.ts +7 -6
  66. package/src/prompts/session-templates.ts +38 -11
  67. package/src/rbac/can.ts +44 -0
  68. package/src/rbac/index.ts +13 -0
  69. package/src/rbac/legacy-policy.ts +185 -0
  70. package/src/rbac/permissions.ts +182 -0
  71. package/src/rbac/types.ts +45 -0
  72. package/src/scripts-runtime/sdk-allowlist.ts +1 -0
  73. package/src/scripts-runtime/swarm-sdk.ts +81 -0
  74. package/src/scripts-runtime/types/stdlib.d.ts +18 -2
  75. package/src/scripts-runtime/types/swarm-sdk.d.ts +18 -2
  76. package/src/server.ts +2 -0
  77. package/src/stdio.ts +43 -0
  78. package/src/tests/base-prompt.test.ts +7 -4
  79. package/src/tests/harness-provider-resolution.test.ts +5 -0
  80. package/src/tests/http-api-integration.test.ts +8 -1
  81. package/src/tests/prompt-template-session.test.ts +21 -8
  82. package/src/tests/rbac-audit.test.ts +345 -0
  83. package/src/tests/rbac-charact-http.test.ts +272 -0
  84. package/src/tests/rbac-charact-misc-tools.test.ts +428 -0
  85. package/src/tests/rbac-charact-skills.test.ts +492 -0
  86. package/src/tests/rbac-charact-slack.test.ts +283 -0
  87. package/src/tests/rbac-e2e-helpers.ts +305 -0
  88. package/src/tests/rbac-engine.test.ts +433 -0
  89. package/src/tests/rbac-lifecycle-e2e.test.ts +262 -0
  90. package/src/tests/rbac-wire-e2e.test.ts +574 -0
  91. package/src/tests/schedule-http-triage-tools.test.ts +121 -0
  92. package/src/tests/scheduled-tasks.test.ts +34 -0
  93. package/src/tests/scripts-http.test.ts +56 -7
  94. package/src/tests/swarm-config-reserved-keys.test.ts +17 -0
  95. package/src/tests/tool-annotations.test.ts +1 -0
  96. package/src/tests/update-schedule-mcp-tool.test.ts +57 -0
  97. package/src/tests/workflow-http-v2.test.ts +80 -0
  98. package/src/tools/cancel-task.ts +13 -3
  99. package/src/tools/context-diff.ts +12 -1
  100. package/src/tools/context-history.ts +12 -1
  101. package/src/tools/credential-bindings/tool.ts +12 -1
  102. package/src/tools/delete-channel.ts +12 -1
  103. package/src/tools/get-task-details.ts +1 -1
  104. package/src/tools/inject-learning.ts +12 -1
  105. package/src/tools/kv/kv-delete.ts +3 -18
  106. package/src/tools/kv/kv-incr.ts +3 -18
  107. package/src/tools/kv/kv-set.ts +3 -20
  108. package/src/tools/kv/kv-write-auth.ts +40 -0
  109. package/src/tools/manage-user.ts +12 -1
  110. package/src/tools/mcp-servers/mcp-server-create.ts +12 -1
  111. package/src/tools/mcp-servers/mcp-server-delete.ts +12 -1
  112. package/src/tools/mcp-servers/mcp-server-install.ts +12 -1
  113. package/src/tools/mcp-servers/mcp-server-uninstall.ts +12 -1
  114. package/src/tools/mcp-servers/mcp-server-update.ts +12 -1
  115. package/src/tools/memory-delete.ts +12 -4
  116. package/src/tools/register-kapso-number.ts +23 -2
  117. package/src/tools/schedules/create-schedule.ts +4 -3
  118. package/src/tools/schedules/index.ts +1 -0
  119. package/src/tools/schedules/list-schedules.ts +36 -2
  120. package/src/tools/schedules/patch-schedule.ts +405 -0
  121. package/src/tools/schedules/update-schedule.ts +2 -2
  122. package/src/tools/script-connections/tool.ts +12 -1
  123. package/src/tools/skills/skill-create.ts +12 -1
  124. package/src/tools/skills/skill-delete.ts +12 -1
  125. package/src/tools/skills/skill-install-remote.ts +12 -1
  126. package/src/tools/skills/skill-install.ts +12 -1
  127. package/src/tools/skills/skill-uninstall.ts +12 -1
  128. package/src/tools/skills/skill-update.ts +25 -2
  129. package/src/tools/slack-delete.ts +8 -1
  130. package/src/tools/slack-post.ts +8 -1
  131. package/src/tools/slack-read.ts +8 -1
  132. package/src/tools/slack-start-thread.ts +8 -1
  133. package/src/tools/slack-update.ts +8 -1
  134. package/src/tools/slack-upload-file.ts +8 -1
  135. package/src/tools/swarm-config/delete-config.ts +28 -1
  136. package/src/tools/swarm-config/get-config.ts +32 -5
  137. package/src/tools/swarm-config/list-config.ts +31 -5
  138. package/src/tools/swarm-config/set-config.ts +38 -1
  139. package/src/tools/task-action.ts +1 -1
  140. package/src/tools/task-tool-ctx.ts +19 -3
  141. package/src/tools/tool-config.ts +2 -1
  142. package/src/tools/update-profile.ts +8 -1
  143. package/src/tools/workflows/list-workflows.ts +14 -2
  144. package/templates/skills/swarm-scripts/SKILL.md +4 -9
  145. 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-52tbq17n.js";
17
+ } from "./cli-bgef578c.js";
18
18
  import {
19
19
  startPricingRefreshLoop
20
- } from "./cli-r1btc895.js";
20
+ } from "./cli-36ymmpmp.js";
21
21
  import {
22
22
  seedPricingFromModelsDev
23
- } from "./cli-rp8pca98.js";
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-56s4ehzw.js";
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-94vzr3nq.js";
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-tg0f2wmz.js";
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-3r5y6ayj.js";
76
+ } from "./cli-qwgtacd6.js";
77
77
  import {
78
78
  CANDIDATE_SET_MULTIPLIER,
79
79
  computeScore,
@@ -85,17 +85,17 @@ import {
85
85
  isGraphExpansionEnabled,
86
86
  rerank,
87
87
  rowToCandidate
88
- } from "./cli-709t753c.js";
88
+ } from "./cli-f3qa069v.js";
89
89
  import {
90
90
  createAdditiveBuffer,
91
91
  extractSlackMessageText,
92
92
  parseSlackTs
93
- } from "./cli-76d1vtvq.js";
93
+ } from "./cli-bgvskydy.js";
94
94
  import {
95
95
  getSlackApp,
96
96
  startSlackApp,
97
97
  stopSlackApp
98
- } from "./cli-hm27prrc.js";
98
+ } from "./cli-trmapn9k.js";
99
99
  import {
100
100
  findOrCreateUserByEmail,
101
101
  findUserByEmail,
@@ -107,7 +107,7 @@ import {
107
107
  recordIdentityEvent,
108
108
  resolveUserByToken,
109
109
  unlinkIdentity
110
- } from "./cli-7xkwgkrs.js";
110
+ } from "./cli-h3k622d0.js";
111
111
  import {
112
112
  markdownToSlack
113
113
  } from "./cli-6xd0bvf8.js";
@@ -118,7 +118,7 @@ import {
118
118
  createTaskWithSiblingAwareness,
119
119
  githubContextKey,
120
120
  linearContextKey
121
- } from "./cli-0c3n0eba.js";
121
+ } from "./cli-y4ycrnjc.js";
122
122
  import {
123
123
  getAppUrl,
124
124
  getMcpBaseUrl,
@@ -347,7 +347,7 @@ import {
347
347
  upsertService,
348
348
  upsertSwarmConfig,
349
349
  validateConfigValue
350
- } from "./cli-951w7c79.js";
350
+ } from "./cli-ncfgsqbd.js";
351
351
  import {
352
352
  init_zod
353
353
  } from "./cli-q21d49ac.js";
@@ -391,7 +391,7 @@ import {
391
391
  import {
392
392
  init_package,
393
393
  package_default
394
- } from "./cli-8f0dwca2.js";
394
+ } from "./cli-c3frqf65.js";
395
395
  import {
396
396
  init_secret_scrubber,
397
397
  refreshSecretScrubberCache,
@@ -19613,6 +19613,300 @@ function registerGithubTaskReactions() {
19613
19613
  init_zod();
19614
19614
  init_db();
19615
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);
19616
19910
  // src/tools/task-tool-ctx.ts
19617
19911
  function ownerCtx(info) {
19618
19912
  return {
@@ -19630,8 +19924,14 @@ function userCtx(user, sessionId) {
19630
19924
  sessionId
19631
19925
  };
19632
19926
  }
19633
- function assertOwnsTask(ctx, task) {
19634
- if (ctx.kind === "owner" || task.requestedByUserId === ctx.userId) {
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) {
19635
19935
  return null;
19636
19936
  }
19637
19937
  const message = `Forbidden: this task is not yours (task ${task.id}).`;
@@ -19673,7 +19973,7 @@ async function initOtel(serviceRole = process.env.AGENT_ROLE || "api") {
19673
19973
  return;
19674
19974
  initialized2 = true;
19675
19975
  try {
19676
- const impl = await import("./otel-impl-ch3n3783.js");
19976
+ const impl = await import("./otel-impl-b86qseaa.js");
19677
19977
  await impl.boot(serviceRole);
19678
19978
  realWithSpan = impl.withSpan;
19679
19979
  realStartSpan = impl.startSpan;
@@ -19853,8 +20153,17 @@ async function cancelTaskHandler(ctx, { taskId, reason }) {
19853
20153
  message: `Task "${taskId}" not found.`
19854
20154
  };
19855
20155
  }
19856
- const canCancel = callerAgent.isLead || existingTask2.creatorAgentId === ownerAgentId;
19857
- if (!canCancel) {
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) {
19858
20167
  return {
19859
20168
  success: false,
19860
20169
  message: "Only the lead or task creator can cancel tasks."
@@ -19883,7 +20192,7 @@ async function cancelTaskHandler(ctx, { taskId, reason }) {
19883
20192
  message: `Task "${taskId}" not found.`
19884
20193
  };
19885
20194
  }
19886
- const ownershipError = assertOwnsTask(ctx, existingTask);
20195
+ const ownershipError = assertOwnsTask(ctx, existingTask, "task.cancel.own");
19887
20196
  if (ownershipError)
19888
20197
  return ownershipError;
19889
20198
  const cancelled = cancelTask(taskId, reason);
@@ -20000,7 +20309,17 @@ var registerContextDiffTool = (server) => {
20000
20309
  }
20001
20310
  if (version.agentId !== requestInfo.agentId) {
20002
20311
  const callerAgent = getAgentById(requestInfo.agentId);
20003
- if (!callerAgent?.isLead) {
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) {
20004
20323
  return {
20005
20324
  content: [
20006
20325
  {
@@ -20126,7 +20445,17 @@ var registerContextHistoryTool = (server) => {
20126
20445
  }
20127
20446
  if (targetAgentId !== requestInfo.agentId) {
20128
20447
  const callerAgent = getAgentById(requestInfo.agentId);
20129
- if (!callerAgent?.isLead) {
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) {
20130
20459
  return {
20131
20460
  content: [
20132
20461
  {
@@ -20954,7 +21283,17 @@ var registerCredentialBindingsTool = (server) => {
20954
21283
  };
20955
21284
  }
20956
21285
  const agent = getAgentById(requestInfo.agentId);
20957
- if (!agent?.isLead) {
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) {
20958
21297
  return {
20959
21298
  content: [{ type: "text", text: "Only the lead can manage credential bindings." }],
20960
21299
  structuredContent: {
@@ -21211,7 +21550,17 @@ var registerDeleteChannelTool = (server) => {
21211
21550
  };
21212
21551
  }
21213
21552
  const callingAgent = getAgentById(requestInfo.agentId);
21214
- if (!callingAgent?.isLead) {
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) {
21215
21564
  return {
21216
21565
  content: [
21217
21566
  { type: "text", text: "Not authorized. Only the lead agent can delete channels." }
@@ -21390,7 +21739,7 @@ async function getTaskDetailsHandler(ctx, { taskId }) {
21390
21739
  }
21391
21740
  };
21392
21741
  }
21393
- const ownershipError = assertOwnsTask(ctx, task);
21742
+ const ownershipError = assertOwnsTask(ctx, task, "task.read.own");
21394
21743
  if (ownershipError)
21395
21744
  return ownershipError;
21396
21745
  const logs = getLogsByTaskIdChronological(taskId);
@@ -21593,7 +21942,17 @@ var registerInjectLearningTool = (server) => {
21593
21942
  };
21594
21943
  }
21595
21944
  const callerAgent = getAgentById(requestInfo.agentId);
21596
- if (!callerAgent || !callerAgent.isLead) {
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) {
21597
21956
  return {
21598
21957
  content: [{ type: "text", text: "Only the lead agent can inject learnings." }],
21599
21958
  structuredContent: {
@@ -21772,6 +22131,28 @@ init_zod();
21772
22131
  init_db();
21773
22132
  init_types();
21774
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
+
21775
22156
  // src/tools/kv/resolve-namespace.ts
21776
22157
  init_db();
21777
22158
  function resolveNamespace(explicit, info) {
@@ -21800,23 +22181,6 @@ function resolveNamespace(explicit, info) {
21800
22181
  }
21801
22182
 
21802
22183
  // src/tools/kv/kv-delete.ts
21803
- function authError(namespace, info) {
21804
- if (namespace.startsWith("task:page:")) {
21805
- return "task:page:* writes require a page-proxy request, not an MCP call";
21806
- }
21807
- if (namespace.startsWith("task:agent:")) {
21808
- const target = namespace.slice("task:agent:".length);
21809
- if (info.agentId && target === info.agentId)
21810
- return null;
21811
- if (info.agentId) {
21812
- const agent = getAgentById(info.agentId);
21813
- if (agent?.isLead)
21814
- return null;
21815
- }
21816
- return "writes to another agent's namespace require lead";
21817
- }
21818
- return null;
21819
- }
21820
22184
  var registerKvDeleteTool = (server) => {
21821
22185
  createToolRegistrar(server)("kv-delete", {
21822
22186
  title: "KV Delete",
@@ -21845,7 +22209,7 @@ var registerKvDeleteTool = (server) => {
21845
22209
  }
21846
22210
  };
21847
22211
  }
21848
- const authErr = authError(resolved.namespace, { agentId: requestInfo.agentId });
22212
+ const authErr = kvWriteAuthError(resolved.namespace, { agentId: requestInfo.agentId });
21849
22213
  if (authErr) {
21850
22214
  return {
21851
22215
  content: [{ type: "text", text: authErr }],
@@ -21929,23 +22293,6 @@ var registerKvGetTool = (server) => {
21929
22293
  init_zod();
21930
22294
  init_db();
21931
22295
  init_types();
21932
- function authError2(namespace, info) {
21933
- if (namespace.startsWith("task:page:")) {
21934
- return "task:page:* writes require a page-proxy request, not an MCP call";
21935
- }
21936
- if (namespace.startsWith("task:agent:")) {
21937
- const target = namespace.slice("task:agent:".length);
21938
- if (info.agentId && target === info.agentId)
21939
- return null;
21940
- if (info.agentId) {
21941
- const agent = getAgentById(info.agentId);
21942
- if (agent?.isLead)
21943
- return null;
21944
- }
21945
- return "writes to another agent's namespace require lead";
21946
- }
21947
- return null;
21948
- }
21949
22296
  var registerKvIncrTool = (server) => {
21950
22297
  createToolRegistrar(server)("kv-incr", {
21951
22298
  title: "KV Incr",
@@ -21975,7 +22322,7 @@ var registerKvIncrTool = (server) => {
21975
22322
  }
21976
22323
  };
21977
22324
  }
21978
- const authErr = authError2(resolved.namespace, { agentId: requestInfo.agentId });
22325
+ const authErr = kvWriteAuthError(resolved.namespace, { agentId: requestInfo.agentId });
21979
22326
  if (authErr) {
21980
22327
  return {
21981
22328
  content: [{ type: "text", text: authErr }],
@@ -22096,23 +22443,6 @@ init_zod();
22096
22443
  init_db();
22097
22444
  init_types();
22098
22445
  var MAX_KV_BODY_BYTES = 2 * 1024 * 1024;
22099
- function authError3(namespace, info) {
22100
- if (namespace.startsWith("task:page:")) {
22101
- return "task:page:* writes require a page-proxy request, not an MCP call";
22102
- }
22103
- if (namespace.startsWith("task:agent:")) {
22104
- const target = namespace.slice("task:agent:".length);
22105
- if (info.agentId && target === info.agentId)
22106
- return null;
22107
- if (info.agentId) {
22108
- const agent = getAgentById(info.agentId);
22109
- if (agent?.isLead)
22110
- return null;
22111
- }
22112
- return "writes to another agent's namespace require lead";
22113
- }
22114
- return null;
22115
- }
22116
22446
  var registerKvSetTool = (server) => {
22117
22447
  createToolRegistrar(server)("kv-set", {
22118
22448
  title: "KV Set",
@@ -22144,7 +22474,7 @@ var registerKvSetTool = (server) => {
22144
22474
  }
22145
22475
  };
22146
22476
  }
22147
- const authErr = authError3(resolved.namespace, { agentId: requestInfo.agentId });
22477
+ const authErr = kvWriteAuthError(resolved.namespace, { agentId: requestInfo.agentId });
22148
22478
  if (authErr) {
22149
22479
  return {
22150
22480
  content: [{ type: "text", text: authErr }],
@@ -22413,7 +22743,17 @@ var registerManageUserTool = (server) => {
22413
22743
  inputSchema: InputSchema
22414
22744
  }, async (input, requestInfo) => {
22415
22745
  const callerAgent = requestInfo.agentId ? getAgentById(requestInfo.agentId) : null;
22416
- if (!callerAgent?.isLead) {
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) {
22417
22757
  return {
22418
22758
  content: [
22419
22759
  { type: "text", text: "Only the lead agent can manage user profiles." }
@@ -22635,7 +22975,17 @@ var registerMcpServerCreateTool = (server) => {
22635
22975
  const scope = args.scope ?? "agent";
22636
22976
  if (scope === "swarm" || scope === "global") {
22637
22977
  const agent = getAgentById(requestInfo.agentId);
22638
- if (!agent?.isLead) {
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) {
22639
22989
  return {
22640
22990
  content: [
22641
22991
  {
@@ -22723,7 +23073,17 @@ var registerMcpServerDeleteTool = (server) => {
22723
23073
  };
22724
23074
  }
22725
23075
  const agent = getAgentById(requestInfo.agentId);
22726
- if (existing.ownerAgentId !== requestInfo.agentId && !agent?.isLead) {
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) {
22727
23087
  return {
22728
23088
  content: [
22729
23089
  { type: "text", text: "Only the owning agent or lead can delete this MCP server." }
@@ -22842,7 +23202,17 @@ var registerMcpServerInstallTool = (server) => {
22842
23202
  const targetAgentId = args.agentId ?? requestInfo.agentId;
22843
23203
  if (targetAgentId !== requestInfo.agentId) {
22844
23204
  const agent = getAgentById(requestInfo.agentId);
22845
- if (!agent?.isLead) {
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) {
22846
23216
  return {
22847
23217
  content: [
22848
23218
  {
@@ -22988,7 +23358,17 @@ var registerMcpServerUninstallTool = (server) => {
22988
23358
  const targetAgentId = args.agentId ?? requestInfo.agentId;
22989
23359
  if (targetAgentId !== requestInfo.agentId) {
22990
23360
  const agent = getAgentById(requestInfo.agentId);
22991
- if (!agent?.isLead) {
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) {
22992
23372
  return {
22993
23373
  content: [
22994
23374
  {
@@ -23068,7 +23448,17 @@ var registerMcpServerUpdateTool = (server) => {
23068
23448
  };
23069
23449
  }
23070
23450
  const agent = getAgentById(requestInfo.agentId);
23071
- if (existing.ownerAgentId !== requestInfo.agentId && !agent?.isLead) {
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) {
23072
23462
  return {
23073
23463
  content: [
23074
23464
  { type: "text", text: "Only the owning agent or lead can update this MCP server." }
@@ -23180,10 +23570,18 @@ var registerMemoryDeleteTool = (server) => {
23180
23570
  }
23181
23571
  };
23182
23572
  }
23183
- const isOwner = memory.agentId === requestInfo.agentId;
23184
23573
  const agent = getAgentById(requestInfo.agentId);
23185
- const isLeadDeletingSwarm = agent?.isLead && memory.scope === "swarm";
23186
- if (!isOwner && !isLeadDeletingSwarm) {
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) {
23187
23585
  return {
23188
23586
  content: [{ type: "text", text: "You don't have permission to delete this memory." }],
23189
23587
  structuredContent: {
@@ -25412,7 +25810,17 @@ var registerRegisterKapsoNumberTool = (server) => {
25412
25810
  }, async ({ phoneNumberId, agentId, workflowId, name }, requestInfo) => {
25413
25811
  try {
25414
25812
  const callerAgent = requestInfo.agentId ? getAgentById(requestInfo.agentId) : null;
25415
- if (!callerAgent?.isLead) {
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) {
25416
25824
  const msg = "Permission denied. Only the lead can register a Kapso number.";
25417
25825
  return {
25418
25826
  content: [{ type: "text", text: msg }],
@@ -25490,7 +25898,17 @@ var registerUnregisterKapsoNumberTool = (server) => {
25490
25898
  }, async ({ phoneNumberId }, requestInfo) => {
25491
25899
  try {
25492
25900
  const callerAgent = requestInfo.agentId ? getAgentById(requestInfo.agentId) : null;
25493
- if (!callerAgent?.isLead) {
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) {
25494
25912
  const msg = "Permission denied. Only the lead can unregister a Kapso number.";
25495
25913
  return {
25496
25914
  content: [{ type: "text", text: msg }],
@@ -25918,7 +26336,7 @@ init_types();
25918
26336
  var createScheduleInputSchema = object({
25919
26337
  name: string().min(1).max(100).describe("Unique name for the schedule (e.g., 'daily-cleanup')"),
25920
26338
  taskTemplate: string().min(1).optional().describe("The task description that will be created each time. Required when targetType is 'agent-task' (the default)."),
25921
- targetType: ScheduledTaskTargetTypeSchema.default("agent-task").optional().describe("Execution target: 'agent-task' (default, creates an agent task from taskTemplate), " + "'workflow' (directly triggers a workflow run, no agent in the loop), or " + "'script' (directly runs a catalog script, no agent in the loop)."),
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."),
25922
26340
  workflowId: string().uuid().optional().describe("Workflow ID to trigger. Required when targetType is 'workflow'."),
25923
26341
  scriptName: string().optional().describe("Catalog script name (global scope). Required when targetType is 'script'."),
25924
26342
  scriptArgs: record(string(), unknown()).optional().describe("JSON args passed to the script. Used when targetType is 'script'."),
@@ -26494,6 +26912,8 @@ var registerListSchedulesTool = (server) => {
26494
26912
  name: string().optional().describe("Filter by name (partial match)"),
26495
26913
  scheduleType: _enum(["recurring", "one_time"]).optional().describe("Filter by schedule type"),
26496
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."),
26497
26917
  includeFull: boolean().optional().describe("Return the full `taskTemplate` instead of a short `taskTemplatePreview`. Default false.")
26498
26918
  }),
26499
26919
  outputSchema: object({
@@ -26517,13 +26937,24 @@ var registerListSchedulesTool = (server) => {
26517
26937
  nextRunAt: string().optional(),
26518
26938
  createdByAgentId: string().optional(),
26519
26939
  timezone: string(),
26940
+ consecutiveErrors: number().optional(),
26941
+ lastErrorAt: string().optional(),
26942
+ lastErrorMessage: string().optional(),
26520
26943
  scheduleType: string(),
26521
26944
  createdAt: string(),
26522
26945
  lastUpdatedAt: string()
26523
26946
  })),
26524
26947
  count: number()
26525
26948
  })
26526
- }, async ({ enabled: enabled2, name, scheduleType, hideCompleted, includeFull }, requestInfo, _meta) => {
26949
+ }, async ({
26950
+ enabled: enabled2,
26951
+ name,
26952
+ scheduleType,
26953
+ hideCompleted,
26954
+ consecutiveErrorsMin,
26955
+ lastRunStatus,
26956
+ includeFull
26957
+ }, requestInfo, _meta) => {
26527
26958
  if (!requestInfo.agentId) {
26528
26959
  return {
26529
26960
  content: [{ type: "text", text: 'Agent ID not found. Set the "X-Agent-ID" header.' }],
@@ -26536,7 +26967,14 @@ var registerListSchedulesTool = (server) => {
26536
26967
  };
26537
26968
  }
26538
26969
  try {
26539
- const filters = { enabled: enabled2, name, scheduleType, hideCompleted };
26970
+ const filters = {
26971
+ enabled: enabled2,
26972
+ name,
26973
+ scheduleType,
26974
+ hideCompleted,
26975
+ consecutiveErrorsMin,
26976
+ lastRunStatus
26977
+ };
26540
26978
  const schedules = includeFull ? getScheduledTasks(filters) : getScheduledTasks(filters, { slim: true });
26541
26979
  const count = schedules.length;
26542
26980
  const statusSummary = count === 0 ? "No schedules found." : `Found ${count} schedule${count === 1 ? "" : "s"}.`;
@@ -26580,106 +27018,7 @@ ${scheduleList}`
26580
27018
  }
26581
27019
  });
26582
27020
  };
26583
- // src/tools/schedules/run-schedule-now.ts
26584
- init_zod();
26585
- init_db();
26586
- var registerRunScheduleNowTool = (server) => {
26587
- createToolRegistrar(server)("run-schedule-now", {
26588
- title: "Run Schedule Now",
26589
- annotations: { destructiveHint: false },
26590
- description: "Immediately execute a scheduled task, creating a task right away. Does not affect the regular schedule timing.",
26591
- inputSchema: object({
26592
- scheduleId: string().uuid().optional().describe("Schedule ID to run"),
26593
- name: string().optional().describe("Schedule name to run (alternative to ID)")
26594
- }),
26595
- outputSchema: object({
26596
- yourAgentId: string().uuid().optional(),
26597
- success: boolean(),
26598
- message: string(),
26599
- schedule: object({
26600
- id: string(),
26601
- name: string(),
26602
- nextRunAt: string().optional()
26603
- }).optional()
26604
- })
26605
- }, async ({ scheduleId, name }, requestInfo, _meta) => {
26606
- if (!requestInfo.agentId) {
26607
- return {
26608
- content: [{ type: "text", text: 'Agent ID not found. Set the "X-Agent-ID" header.' }],
26609
- structuredContent: {
26610
- success: false,
26611
- message: 'Agent ID not found. Set the "X-Agent-ID" header.'
26612
- }
26613
- };
26614
- }
26615
- if (!scheduleId && !name) {
26616
- return {
26617
- content: [{ type: "text", text: "Either scheduleId or name must be provided." }],
26618
- structuredContent: {
26619
- success: false,
26620
- message: "Either scheduleId or name must be provided."
26621
- }
26622
- };
26623
- }
26624
- const schedule = scheduleId ? getScheduledTaskById(scheduleId) : name ? getScheduledTaskByName(name) : null;
26625
- if (!schedule) {
26626
- return {
26627
- content: [{ type: "text", text: "Schedule not found." }],
26628
- structuredContent: {
26629
- success: false,
26630
- message: "Schedule not found."
26631
- }
26632
- };
26633
- }
26634
- if (!schedule.enabled) {
26635
- return {
26636
- content: [
26637
- {
26638
- type: "text",
26639
- text: `Schedule "${schedule.name}" is disabled. Enable it first or use it as a template.`
26640
- }
26641
- ],
26642
- structuredContent: {
26643
- success: false,
26644
- message: `Schedule "${schedule.name}" is disabled.`
26645
- }
26646
- };
26647
- }
26648
- try {
26649
- await runScheduleNow(schedule.id);
26650
- const updated = getScheduledTaskById(schedule.id);
26651
- return {
26652
- content: [
26653
- {
26654
- type: "text",
26655
- text: `Executed schedule "${schedule.name}". Task created. Next regular run: ${updated?.nextRunAt || "not scheduled"}`
26656
- }
26657
- ],
26658
- structuredContent: {
26659
- yourAgentId: requestInfo.agentId,
26660
- success: true,
26661
- message: `Executed schedule "${schedule.name}".`,
26662
- schedule: updated ? {
26663
- id: updated.id,
26664
- name: updated.name,
26665
- nextRunAt: updated.nextRunAt
26666
- } : undefined
26667
- }
26668
- };
26669
- } catch (error) {
26670
- const message = error instanceof Error ? error.message : "Unknown error";
26671
- return {
26672
- content: [{ type: "text", text: `Failed to run schedule: ${message}` }],
26673
- structuredContent: {
26674
- yourAgentId: requestInfo.agentId,
26675
- success: false,
26676
- message: `Failed to run schedule: ${message}`
26677
- }
26678
- };
26679
- }
26680
- });
26681
- };
26682
- // src/tools/schedules/update-schedule.ts
27021
+ // src/tools/schedules/patch-schedule.ts
26683
27022
  init_zod();
26684
27023
  var import_cron_parser2 = __toESM(require_dist(), 1);
26685
27024
  init_db();
@@ -26698,11 +27037,11 @@ function validateRecurringTiming(merged) {
26698
27037
  return null;
26699
27038
  }
26700
27039
 
26701
- // src/tools/schedules/update-schedule.ts
27040
+ // src/tools/schedules/patch-schedule.ts
26702
27041
  init_types();
26703
- var updateScheduleInputSchema = object({
26704
- scheduleId: string().uuid().optional().describe("Schedule ID to update"),
26705
- name: string().optional().describe("Schedule name to update (alternative to ID)"),
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)"),
26706
27045
  newName: string().min(1).max(100).optional().describe("New name for the schedule"),
26707
27046
  taskTemplate: string().min(1).optional().describe("New task template"),
26708
27047
  targetType: ScheduledTaskTargetTypeSchema.optional().describe("Change the execution target: 'agent-task', 'workflow', or 'script'."),
@@ -26721,12 +27060,12 @@ var updateScheduleInputSchema = object({
26721
27060
  model: string().trim().min(1).nullable().optional().describe("Concrete model override for tasks created by this schedule. Set to null to clear."),
26722
27061
  modelTier: ModelTierSchema.nullable().optional().describe("Portable model tier for tasks created by this schedule. Set to null to clear.")
26723
27062
  });
26724
- var registerUpdateScheduleTool = (server) => {
26725
- createToolRegistrar(server)("update-schedule", {
26726
- title: "Update Scheduled Task",
27063
+ var registerPatchScheduleTool = (server) => {
27064
+ createToolRegistrar(server)("patch-schedule", {
27065
+ title: "Patch Scheduled Task",
26727
27066
  annotations: { idempotentHint: true },
26728
- description: "Update an existing scheduled task. Any registered agent can update schedules.",
26729
- inputSchema: updateScheduleInputSchema,
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,
26730
27069
  outputSchema: object({
26731
27070
  yourAgentId: string().uuid().optional(),
26732
27071
  success: boolean(),
@@ -26838,6 +27177,429 @@ var registerUpdateScheduleTool = (server) => {
26838
27177
  };
26839
27178
  }
26840
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
+ }
26841
27603
  if (targetAgentId && targetAgentId !== null) {
26842
27604
  const agent = getAgentById(targetAgentId);
26843
27605
  if (!agent) {
@@ -26947,7 +27709,7 @@ var registerUpdateScheduleTool = (server) => {
26947
27709
  }
26948
27710
  if (schedule.scheduleType === "one_time") {
26949
27711
  if (enabled2 === false) {
26950
- updateData.nextRunAt = undefined;
27712
+ updateData.nextRunAt = null;
26951
27713
  }
26952
27714
  } else {
26953
27715
  const timing = mergeScheduleTiming({
@@ -26978,7 +27740,7 @@ var registerUpdateScheduleTool = (server) => {
26978
27740
  timezone: mergedTimezone
26979
27741
  }, new Date);
26980
27742
  } else if (enabled2 === false) {
26981
- updateData.nextRunAt = undefined;
27743
+ updateData.nextRunAt = null;
26982
27744
  }
26983
27745
  }
26984
27746
  const updatedBy = resolveTaskAuditUserId(requestInfo.sourceTaskId, requestInfo.agentId) ?? undefined;
@@ -27257,7 +28019,17 @@ var registerScriptConnectionsTool = (server) => {
27257
28019
  };
27258
28020
  }
27259
28021
  const agent = getAgentById(requestInfo.agentId);
27260
- if (!agent?.isLead) {
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) {
27261
28033
  return {
27262
28034
  content: [{ type: "text", text: "Only the lead can manage script connections." }],
27263
28035
  structuredContent: {
@@ -27983,7 +28755,17 @@ var registerSkillCreateTool = (server) => {
27983
28755
  const parsed = parseSkillContent(args.content);
27984
28756
  if (args.scope === "swarm") {
27985
28757
  const agent = getAgentById(requestInfo.agentId);
27986
- if (!agent?.isLead) {
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) {
27987
28769
  return {
27988
28770
  content: [
27989
28771
  {
@@ -28073,7 +28855,17 @@ var registerSkillDeleteTool = (server) => {
28073
28855
  };
28074
28856
  }
28075
28857
  const agent = getAgentById(requestInfo.agentId);
28076
- if (existing.ownerAgentId !== requestInfo.agentId && !agent?.isLead) {
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) {
28077
28869
  return {
28078
28870
  content: [{ type: "text", text: "Only the owning agent or lead can delete this skill." }],
28079
28871
  structuredContent: {
@@ -28268,7 +29060,17 @@ var registerSkillInstallTool = (server) => {
28268
29060
  const targetAgentId = args.agentId ?? requestInfo.agentId;
28269
29061
  if (targetAgentId !== requestInfo.agentId) {
28270
29062
  const agent = getAgentById(requestInfo.agentId);
28271
- if (!agent?.isLead) {
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) {
28272
29074
  return {
28273
29075
  content: [{ type: "text", text: "Only leads can install skills for other agents." }],
28274
29076
  structuredContent: {
@@ -28354,7 +29156,17 @@ var registerSkillInstallRemoteTool = (server) => {
28354
29156
  };
28355
29157
  }
28356
29158
  const agent = getAgentById(requestInfo.agentId);
28357
- if (!agent?.isLead) {
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) {
28358
29170
  return {
28359
29171
  content: [{ type: "text", text: "Only lead agents can install remote skills." }],
28360
29172
  structuredContent: {
@@ -28778,7 +29590,17 @@ var registerSkillUninstallTool = (server) => {
28778
29590
  const targetAgentId = args.agentId ?? requestInfo.agentId;
28779
29591
  if (targetAgentId !== requestInfo.agentId) {
28780
29592
  const agent = getAgentById(requestInfo.agentId);
28781
- if (!agent?.isLead) {
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) {
28782
29604
  return {
28783
29605
  content: [{ type: "text", text: "Only leads can uninstall skills for other agents." }],
28784
29606
  structuredContent: {
@@ -28853,7 +29675,17 @@ var registerSkillUpdateTool = (server) => {
28853
29675
  };
28854
29676
  }
28855
29677
  const agent = getAgentById(requestInfo.agentId);
28856
- if (existing.ownerAgentId !== requestInfo.agentId && !agent?.isLead) {
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) {
28857
29689
  return {
28858
29690
  content: [
28859
29691
  { type: "text", text: "Only the owning agent or lead can update this skill." }
@@ -28893,7 +29725,16 @@ var registerSkillUpdateTool = (server) => {
28893
29725
  updates.isEnabled = args.isEnabled;
28894
29726
  }
28895
29727
  if (args.scope !== undefined && args.scope !== existing.scope) {
28896
- if (args.scope === "swarm" && !agent?.isLead) {
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) {
28897
29738
  return {
28898
29739
  content: [
28899
29740
  {
@@ -28973,7 +29814,13 @@ var registerSlackDeleteTool = (server) => {
28973
29814
  structuredContent: { success: false, message: "Agent not found." }
28974
29815
  };
28975
29816
  }
28976
- if (!agent.isLead) {
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) {
28977
29824
  return {
28978
29825
  content: [{ type: "text", text: "Deleting Slack messages requires lead privileges." }],
28979
29826
  structuredContent: {
@@ -29497,7 +30344,13 @@ var registerSlackPostTool = (server) => {
29497
30344
  structuredContent: { success: false, message: "Agent not found." }
29498
30345
  };
29499
30346
  }
29500
- if (!agent.isLead) {
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) {
29501
30354
  return {
29502
30355
  content: [{ type: "text", text: "Posting to Slack channels requires lead privileges." }],
29503
30356
  structuredContent: {
@@ -29657,7 +30510,13 @@ var registerSlackReadTool = (server) => {
29657
30510
  slackChannelId = task.slackChannelId;
29658
30511
  slackThreadTs = task.slackThreadTs;
29659
30512
  } else if (channelId) {
29660
- if (!agent.isLead) {
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) {
29661
30520
  return {
29662
30521
  content: [{ type: "text", text: "Direct channel access requires lead privileges." }],
29663
30522
  structuredContent: {
@@ -29982,7 +30841,13 @@ var registerSlackStartThreadTool = (server) => {
29982
30841
  structuredContent: { success: false, message: "Agent not found." }
29983
30842
  };
29984
30843
  }
29985
- if (!agent.isLead) {
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) {
29986
30851
  return {
29987
30852
  content: [{ type: "text", text: "Posting to Slack channels requires lead privileges." }],
29988
30853
  structuredContent: {
@@ -30089,7 +30954,13 @@ var registerSlackUpdateTool = (server) => {
30089
30954
  structuredContent: { success: false, message: "Agent not found." }
30090
30955
  };
30091
30956
  }
30092
- if (!agent.isLead) {
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) {
30093
30964
  return {
30094
30965
  content: [{ type: "text", text: "Editing Slack messages requires lead privileges." }],
30095
30966
  structuredContent: {
@@ -30282,7 +31153,13 @@ var registerSlackUploadFileTool = (server) => {
30282
31153
  slackChannelId = task.slackChannelId;
30283
31154
  slackThreadTs = resolveTaskUploadThreadTs(task);
30284
31155
  } else if (channelId) {
30285
- if (!agent.isLead) {
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) {
30286
31163
  return {
30287
31164
  content: [{ type: "text", text: "Direct channel access requires lead privileges." }],
30288
31165
  structuredContent: {
@@ -33430,7 +34307,7 @@ async function notifySlack(text) {
33430
34307
  return;
33431
34308
  }
33432
34309
  try {
33433
- const { getSlackApp: getSlackApp2 } = await import("./app-b05sgm02.js");
34310
+ const { getSlackApp: getSlackApp2 } = await import("./app-es4nzc71.js");
33434
34311
  const app = getSlackApp2();
33435
34312
  if (!app) {
33436
34313
  console.warn("[Jira webhook keepalive] Slack not available, cannot send notification");
@@ -33499,7 +34376,7 @@ async function deleteJiraWebhook(webhookId) {
33499
34376
  console.log(`[Jira webhook keepalive] Deleted webhook id=${webhookId}`);
33500
34377
  }
33501
34378
  async function overwriteWebhookIds(next) {
33502
- const { getDb: getDb2 } = await import("./db-3tge4jrn.js");
34379
+ const { getDb: getDb2 } = await import("./db-bbgahh4z.js");
33503
34380
  const db = getDb2();
33504
34381
  const txn = db.transaction(() => {
33505
34382
  const row = db.query("SELECT metadata FROM oauth_apps WHERE provider = 'jira'").get();
@@ -107948,6 +108825,28 @@ var registerDeleteConfigTool = (server) => {
107948
108825
  }
107949
108826
  };
107950
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
+ }
107951
108850
  try {
107952
108851
  const existing = getSwarmConfigLookupById(id);
107953
108852
  if (!existing) {
@@ -108040,8 +108939,27 @@ var registerGetConfigTool = (server) => {
108040
108939
  if (key) {
108041
108940
  configs = configs.filter((c) => c.key === key);
108042
108941
  }
108043
- const result = includeSecrets ? configs : maskSecrets(configs);
108942
+ let effectiveIncludeSecrets = includeSecrets ?? false;
108943
+ let secretsNote = "";
108044
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) {
108045
108963
  for (const c of result) {
108046
108964
  if (c.isSecret && c.value) {
108047
108965
  registerVolatileSecret(c.value, `config:${c.key}`);
@@ -108049,7 +108967,7 @@ var registerGetConfigTool = (server) => {
108049
108967
  }
108050
108968
  }
108051
108969
  const count = result.length;
108052
- const configList = count === 0 ? "No configs found." : result.map((c) => `- ${c.key}=${c.isSecret && !includeSecrets ? "********" : c.value} (scope: ${c.scope}${c.scopeId ? `, scopeId: ${c.scopeId}` : ""})`).join(`
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(`
108053
108971
  `);
108054
108972
  return {
108055
108973
  content: [
@@ -108057,13 +108975,13 @@ var registerGetConfigTool = (server) => {
108057
108975
  type: "text",
108058
108976
  text: count === 0 ? "No configs found." : `Found ${count} config(s):
108059
108977
 
108060
- ${configList}`
108978
+ ${configList}${secretsNote}`
108061
108979
  }
108062
108980
  ],
108063
108981
  structuredContent: {
108064
108982
  yourAgentId: requestInfo.agentId,
108065
108983
  success: true,
108066
- message: count === 0 ? "No configs found." : `Found ${count} config(s).`,
108984
+ message: count === 0 ? "No configs found." : `Found ${count} config(s).${secretsNote}`,
108067
108985
  configs: result,
108068
108986
  count
108069
108987
  }
@@ -108124,8 +109042,27 @@ var registerListConfigTool = (server) => {
108124
109042
  scopeId,
108125
109043
  key
108126
109044
  });
108127
- const result = includeSecrets ? configs : maskSecrets(configs);
109045
+ let effectiveIncludeSecrets = includeSecrets ?? false;
109046
+ let secretsNote = "";
108128
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) {
108129
109066
  for (const c of result) {
108130
109067
  if (c.isSecret && c.value) {
108131
109068
  registerVolatileSecret(c.value, `config:${c.key}`);
@@ -108133,7 +109070,7 @@ var registerListConfigTool = (server) => {
108133
109070
  }
108134
109071
  }
108135
109072
  const count = result.length;
108136
- const configList = count === 0 ? "No configs found." : result.map((c) => `- [${c.scope}${c.scopeId ? `:${c.scopeId}` : ""}] ${c.key}=${c.isSecret && !includeSecrets ? "********" : c.value}${c.description ? ` — ${c.description}` : ""}`).join(`
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(`
108137
109074
  `);
108138
109075
  return {
108139
109076
  content: [
@@ -108141,13 +109078,13 @@ var registerListConfigTool = (server) => {
108141
109078
  type: "text",
108142
109079
  text: count === 0 ? "No configs found." : `Found ${count} config(s):
108143
109080
 
108144
- ${configList}`
109081
+ ${configList}${secretsNote}`
108145
109082
  }
108146
109083
  ],
108147
109084
  structuredContent: {
108148
109085
  yourAgentId: requestInfo.agentId,
108149
109086
  success: true,
108150
- message: count === 0 ? "No configs found." : `Found ${count} config(s).`,
109087
+ message: count === 0 ? "No configs found." : `Found ${count} config(s).${secretsNote}`,
108151
109088
  configs: result,
108152
109089
  count
108153
109090
  }
@@ -108231,7 +109168,17 @@ var registerSetConfigTool = (server) => {
108231
109168
  }
108232
109169
  if (key.toUpperCase() === CREDENTIAL_BINDINGS_CONFIG_KEY) {
108233
109170
  const agent = getAgentById(requestInfo.agentId);
108234
- if (!agent?.isLead) {
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) {
108235
109182
  const message = "Only the lead can manage SCRIPT_CREDENTIAL_BINDINGS. Use the credential-bindings tool.";
108236
109183
  return {
108237
109184
  content: [{ type: "text", text: message }],
@@ -108242,6 +109189,29 @@ var registerSetConfigTool = (server) => {
108242
109189
  }
108243
109190
  };
108244
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
+ }
108245
109215
  }
108246
109216
  const validationError = validateConfigValue(key, value);
108247
109217
  if (validationError) {
@@ -108595,7 +109565,7 @@ async function taskActionHandler(ctx, input) {
108595
109565
  if (!existingTask) {
108596
109566
  return { success: false, message: `Task "${taskId}" not found.` };
108597
109567
  }
108598
- const ownershipError = assertOwnsTask(ctx, existingTask);
109568
+ const ownershipError = assertOwnsTask(ctx, existingTask, "task.action.own");
108599
109569
  if (ownershipError)
108600
109570
  return ownershipError;
108601
109571
  if (action === "to_backlog") {
@@ -109331,7 +110301,13 @@ var registerUpdateProfileTool = (server) => {
109331
110301
  }
109332
110302
  };
109333
110303
  }
109334
- if (!callingAgent.isLead) {
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) {
109335
110311
  return {
109336
110312
  content: [
109337
110313
  {
@@ -110017,6 +110993,7 @@ var registerListWorkflowRunsTool = (server) => {
110017
110993
  // src/tools/workflows/list-workflows.ts
110018
110994
  init_zod();
110019
110995
  init_db();
110996
+ init_types();
110020
110997
  var registerListWorkflowsTool = (server) => {
110021
110998
  createToolRegistrar(server)("list-workflows", {
110022
110999
  title: "List Workflows",
@@ -110024,6 +111001,8 @@ var registerListWorkflowsTool = (server) => {
110024
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.",
110025
111002
  inputSchema: exports_external.object({
110026
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."),
110027
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.")
110028
111007
  }),
110029
111008
  outputSchema: exports_external.object({
@@ -110031,9 +111010,9 @@ var registerListWorkflowsTool = (server) => {
110031
111010
  message: exports_external.string(),
110032
111011
  workflows: exports_external.array(exports_external.unknown())
110033
111012
  })
110034
- }, async ({ enabled: enabled2, includeFull }) => {
111013
+ }, async ({ enabled: enabled2, consecutiveErrorsMin, lastRunStatus, includeFull }) => {
110035
111014
  try {
110036
- const filters = enabled2 !== undefined ? { enabled: enabled2 } : undefined;
111015
+ const filters = { enabled: enabled2, consecutiveErrorsMin, lastRunStatus };
110037
111016
  const workflows = includeFull ? listWorkflows(filters) : listWorkflows(filters, { slim: true });
110038
111017
  return {
110039
111018
  content: [{ type: "text", text: `Found ${workflows.length} workflow(s).` }],
@@ -110583,6 +111562,7 @@ function createServer() {
110583
111562
  registerListSchedulesTool(server);
110584
111563
  registerCreateScheduleTool(server);
110585
111564
  registerUpdateScheduleTool(server);
111565
+ registerPatchScheduleTool(server);
110586
111566
  registerDeleteScheduleTool(server);
110587
111567
  registerRunScheduleNowTool(server);
110588
111568
  }
@@ -110788,4 +111768,4 @@ function writeSkillsToFilesystem(entries, harnessType = "all", home) {
110788
111768
  return { synced, removed, errors };
110789
111769
  }
110790
111770
 
110791
- 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 };