@desplega.ai/agent-swarm 1.111.0 → 1.113.0

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