@desplega.ai/agent-swarm 1.112.0 → 1.113.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/dist/{actions-eckb4g8r.js → actions-q4n7cz6e.js} +6 -6
  2. package/dist/{app-b05sgm02.js → app-es4nzc71.js} +3 -3
  3. package/dist/{assistant-7255sbyw.js → assistant-0x8ey0vs.js} +9 -9
  4. package/dist/{boot-reembed-y1bt3ttn.js → boot-reembed-69agkhv6.js} +4 -4
  5. package/dist/{boot-reembed-yv7awg4y.js → boot-reembed-p0ny05t8.js} +3 -3
  6. package/dist/{boot-scrub-logs-ddyaeqar.js → boot-scrub-logs-yybrkmvr.js} +2 -2
  7. package/dist/{cli-s9pj3c7z.js → cli-30bbaveh.js} +2 -2
  8. package/dist/{cli-r1btc895.js → cli-36ymmpmp.js} +2 -2
  9. package/dist/{cli-15598a4k.js → cli-4xyj9jtq.js} +1215 -235
  10. package/dist/{cli-52tbq17n.js → cli-bgef578c.js} +1 -1
  11. package/dist/{cli-76d1vtvq.js → cli-bgvskydy.js} +1 -1
  12. package/dist/{cli-pt7c7qxz.js → cli-bnzbn8j8.js} +3 -3
  13. package/dist/{cli-8f0dwca2.js → cli-c3frqf65.js} +4 -3
  14. package/dist/{cli-9p0qhead.js → cli-c9mtm09b.js} +1 -1
  15. package/dist/{cli-709t753c.js → cli-f3qa069v.js} +2 -2
  16. package/dist/{cli-94vzr3nq.js → cli-gtkqc144.js} +4 -4
  17. package/dist/{cli-7xkwgkrs.js → cli-h3k622d0.js} +1 -1
  18. package/dist/{cli-h9jx5mag.js → cli-hjhvekf4.js} +4 -4
  19. package/dist/{cli-tp7e60s3.js → cli-hsetkkd3.js} +2 -2
  20. package/dist/{cli-tch0ypp6.js → cli-mq580e06.js} +4 -4
  21. package/dist/{cli-951w7c79.js → cli-ncfgsqbd.js} +63 -7
  22. package/dist/{cli-30d2ct3k.js → cli-pwc5e83c.js} +3 -3
  23. package/dist/{cli-3r5y6ayj.js → cli-qwgtacd6.js} +2 -2
  24. package/dist/{cli-56s4ehzw.js → cli-r5g6ngtn.js} +1 -1
  25. package/dist/{cli-rp8pca98.js → cli-tbw13sx8.js} +1 -1
  26. package/dist/{cli-hm27prrc.js → cli-trmapn9k.js} +5 -5
  27. package/dist/{cli-0c3n0eba.js → cli-y4ycrnjc.js} +1 -1
  28. package/dist/{cli-a5e8n5hg.js → cli-y6mrptcn.js} +1 -1
  29. package/dist/{cli-tg0f2wmz.js → cli-z46pnksz.js} +5 -5
  30. package/dist/cli.js +11 -10
  31. package/dist/{commands-14g7taf4.js → commands-z5dbwta3.js} +2 -2
  32. package/dist/{db-3tge4jrn.js → db-bbgahh4z.js} +2 -2
  33. package/dist/{handlers-sy64egg6.js → handlers-6b44317z.js} +9 -9
  34. package/dist/{hook-rnkzw8vp.js → hook-sbp5fmps.js} +1 -1
  35. package/dist/{http-xx9xptcs.js → http-2xz43gz3.js} +354 -94
  36. package/dist/{index-zfx5p959.js → index-0gzmqj4k.js} +8 -8
  37. package/dist/{index-3wr6v2mm.js → index-5ghxn8s6.js} +7 -7
  38. package/dist/{index-brma88d4.js → index-nhat35em.js} +9 -9
  39. package/dist/{index-ha8e9xn2.js → index-vfedgdw6.js} +26 -15
  40. package/dist/{keepalive-rbz05zad.js → keepalive-62gfj91d.js} +4 -4
  41. package/dist/{lead-s790v7r0.js → lead-ag0akn0v.js} +17 -17
  42. package/dist/{maintenance-420rmadb.js → maintenance-xxrrzk5x.js} +4 -4
  43. package/dist/{onboard-jgqamqnw.js → onboard-64zyfcbs.js} +2 -2
  44. package/dist/{otel-impl-ch3n3783.js → otel-impl-b86qseaa.js} +1 -1
  45. package/dist/{pricing-refresh-rmtrby6h.js → pricing-refresh-cgtkffpa.js} +4 -4
  46. package/dist/{seed-pricing-1szjzfj1.js → seed-pricing-1r1y1xkq.js} +3 -3
  47. package/dist/{setup-8jfzkyzv.js → setup-mkcgkjf7.js} +2 -2
  48. package/dist/{worker-40vq0pm8.js → worker-351sze63.js} +17 -17
  49. package/openapi.json +314 -3
  50. package/package.json +4 -3
  51. package/src/be/db.ts +43 -3
  52. package/src/be/memory/providers/sqlite-store.ts +5 -1
  53. package/src/be/migrations/108_rbac_permission_audit.sql +26 -0
  54. package/src/be/rbac-audit.ts +218 -0
  55. package/src/http/all-routes.ts +58 -0
  56. package/src/http/config.ts +55 -0
  57. package/src/http/favorites.ts +5 -0
  58. package/src/http/fs.ts +27 -7
  59. package/src/http/index.ts +26 -0
  60. package/src/http/kv.ts +21 -4
  61. package/src/http/route-def.ts +9 -0
  62. package/src/http/schedules.ts +56 -22
  63. package/src/http/scripts.ts +33 -0
  64. package/src/http/workflows.ts +13 -2
  65. package/src/prompts/base-prompt.ts +7 -6
  66. package/src/prompts/session-templates.ts +38 -11
  67. package/src/rbac/can.ts +44 -0
  68. package/src/rbac/index.ts +13 -0
  69. package/src/rbac/legacy-policy.ts +185 -0
  70. package/src/rbac/permissions.ts +182 -0
  71. package/src/rbac/types.ts +45 -0
  72. package/src/scripts-runtime/sdk-allowlist.ts +1 -0
  73. package/src/scripts-runtime/swarm-sdk.ts +81 -0
  74. package/src/scripts-runtime/types/stdlib.d.ts +18 -2
  75. package/src/scripts-runtime/types/swarm-sdk.d.ts +18 -2
  76. package/src/server.ts +2 -0
  77. package/src/stdio.ts +43 -0
  78. package/src/tests/base-prompt.test.ts +7 -4
  79. package/src/tests/harness-provider-resolution.test.ts +5 -0
  80. package/src/tests/http-api-integration.test.ts +8 -1
  81. package/src/tests/prompt-template-session.test.ts +21 -8
  82. package/src/tests/rbac-audit.test.ts +345 -0
  83. package/src/tests/rbac-charact-http.test.ts +272 -0
  84. package/src/tests/rbac-charact-misc-tools.test.ts +428 -0
  85. package/src/tests/rbac-charact-skills.test.ts +492 -0
  86. package/src/tests/rbac-charact-slack.test.ts +283 -0
  87. package/src/tests/rbac-e2e-helpers.ts +305 -0
  88. package/src/tests/rbac-engine.test.ts +433 -0
  89. package/src/tests/rbac-lifecycle-e2e.test.ts +262 -0
  90. package/src/tests/rbac-wire-e2e.test.ts +574 -0
  91. package/src/tests/schedule-http-triage-tools.test.ts +121 -0
  92. package/src/tests/scheduled-tasks.test.ts +34 -0
  93. package/src/tests/scripts-http.test.ts +56 -7
  94. package/src/tests/swarm-config-reserved-keys.test.ts +17 -0
  95. package/src/tests/tool-annotations.test.ts +1 -0
  96. package/src/tests/update-schedule-mcp-tool.test.ts +57 -0
  97. package/src/tests/workflow-http-v2.test.ts +80 -0
  98. package/src/tools/cancel-task.ts +13 -3
  99. package/src/tools/context-diff.ts +12 -1
  100. package/src/tools/context-history.ts +12 -1
  101. package/src/tools/credential-bindings/tool.ts +12 -1
  102. package/src/tools/delete-channel.ts +12 -1
  103. package/src/tools/get-task-details.ts +1 -1
  104. package/src/tools/inject-learning.ts +12 -1
  105. package/src/tools/kv/kv-delete.ts +3 -18
  106. package/src/tools/kv/kv-incr.ts +3 -18
  107. package/src/tools/kv/kv-set.ts +3 -20
  108. package/src/tools/kv/kv-write-auth.ts +40 -0
  109. package/src/tools/manage-user.ts +12 -1
  110. package/src/tools/mcp-servers/mcp-server-create.ts +12 -1
  111. package/src/tools/mcp-servers/mcp-server-delete.ts +12 -1
  112. package/src/tools/mcp-servers/mcp-server-install.ts +12 -1
  113. package/src/tools/mcp-servers/mcp-server-uninstall.ts +12 -1
  114. package/src/tools/mcp-servers/mcp-server-update.ts +12 -1
  115. package/src/tools/memory-delete.ts +12 -4
  116. package/src/tools/register-kapso-number.ts +23 -2
  117. package/src/tools/schedules/create-schedule.ts +4 -3
  118. package/src/tools/schedules/index.ts +1 -0
  119. package/src/tools/schedules/list-schedules.ts +36 -2
  120. package/src/tools/schedules/patch-schedule.ts +405 -0
  121. package/src/tools/schedules/update-schedule.ts +2 -2
  122. package/src/tools/script-connections/tool.ts +12 -1
  123. package/src/tools/skills/skill-create.ts +12 -1
  124. package/src/tools/skills/skill-delete.ts +12 -1
  125. package/src/tools/skills/skill-install-remote.ts +12 -1
  126. package/src/tools/skills/skill-install.ts +12 -1
  127. package/src/tools/skills/skill-uninstall.ts +12 -1
  128. package/src/tools/skills/skill-update.ts +25 -2
  129. package/src/tools/slack-delete.ts +8 -1
  130. package/src/tools/slack-post.ts +8 -1
  131. package/src/tools/slack-read.ts +8 -1
  132. package/src/tools/slack-start-thread.ts +8 -1
  133. package/src/tools/slack-update.ts +8 -1
  134. package/src/tools/slack-upload-file.ts +8 -1
  135. package/src/tools/swarm-config/delete-config.ts +28 -1
  136. package/src/tools/swarm-config/get-config.ts +32 -5
  137. package/src/tools/swarm-config/list-config.ts +31 -5
  138. package/src/tools/swarm-config/set-config.ts +38 -1
  139. package/src/tools/task-action.ts +1 -1
  140. package/src/tools/task-tool-ctx.ts +19 -3
  141. package/src/tools/tool-config.ts +2 -1
  142. package/src/tools/update-profile.ts +8 -1
  143. package/src/tools/workflows/list-workflows.ts +14 -2
  144. package/templates/skills/swarm-scripts/SKILL.md +4 -9
  145. package/templates/skills/swarm-scripts/content.md +20 -9
@@ -8,11 +8,13 @@ import {
8
8
  agentWithCapacity,
9
9
  applyRating,
10
10
  assertSelectOnlyQuery,
11
+ can,
11
12
  canClaim,
12
13
  canReadMemory,
13
14
  cancelTaskHandler,
14
15
  cancelTaskInputSchema,
15
16
  cancelTaskOutputSchema,
17
+ clearAuditSink,
16
18
  clearJiraMetadata,
17
19
  createEvent,
18
20
  createEventsBatch,
@@ -93,6 +95,7 @@ import {
93
95
  sendKapsoReaction,
94
96
  sendTaskHandler,
95
97
  sendTaskOutputSchema,
98
+ setAuditSink,
96
99
  setCorsHeaders,
97
100
  shouldPersistAutomaticTaskMemory,
98
101
  snapshotMetric,
@@ -111,7 +114,7 @@ import {
111
114
  withRemoteContext,
112
115
  withSpanContext,
113
116
  writeSkillsToFilesystem
114
- } from "./cli-15598a4k.js";
117
+ } from "./cli-4xyj9jtq.js";
115
118
  import {
116
119
  BROWSER_SDK_JS,
117
120
  SWARM_UI_JS
@@ -144,11 +147,11 @@ import {
144
147
  getOAuthApp,
145
148
  getOAuthTokens,
146
149
  releaseOAuthRefreshLock
147
- } from "./cli-52tbq17n.js";
148
- import"./cli-r1btc895.js";
150
+ } from "./cli-bgef578c.js";
151
+ import"./cli-36ymmpmp.js";
149
152
  import {
150
153
  normalizeModelKey
151
- } from "./cli-rp8pca98.js";
154
+ } from "./cli-tbw13sx8.js";
152
155
  import {
153
156
  SCRIPT_STDLIB_TYPES,
154
157
  ensureAgentFsCredentialsForAgent,
@@ -156,16 +159,16 @@ import {
156
159
  extractScriptSignature,
157
160
  scriptSdkTypesWithGeneratedApis,
158
161
  typecheckScript
159
- } from "./cli-tp7e60s3.js";
162
+ } from "./cli-hsetkkd3.js";
160
163
  import {
161
164
  checkHeartbeatChecklist,
162
165
  runHeartbeatSweep,
163
166
  stopHeartbeat
164
- } from "./cli-pt7c7qxz.js";
167
+ } from "./cli-bnzbn8j8.js";
165
168
  import {
166
169
  createResumeFollowUp,
167
170
  createWorkerTaskFollowUp
168
- } from "./cli-56s4ehzw.js";
171
+ } from "./cli-r5g6ngtn.js";
169
172
  import"./cli-fad8m16k.js";
170
173
  import {
171
174
  calculateNextRun,
@@ -173,7 +176,7 @@ import {
173
176
  ensure,
174
177
  initialize,
175
178
  require_dist
176
- } from "./cli-94vzr3nq.js";
179
+ } from "./cli-gtkqc144.js";
177
180
  import {
178
181
  RawLlmConfigSchema,
179
182
  TriggerSchemaError,
@@ -193,7 +196,7 @@ import {
193
196
  validateDefinition,
194
197
  validateJsonSchema,
195
198
  verifyHmacSignature
196
- } from "./cli-tg0f2wmz.js";
199
+ } from "./cli-z46pnksz.js";
197
200
  import {
198
201
  getApiKey
199
202
  } from "./cli-f14fvzag.js";
@@ -214,10 +217,10 @@ import {
214
217
  rotateScriptApiSecret,
215
218
  updateScriptApi,
216
219
  upsertScriptByName
217
- } from "./cli-3r5y6ayj.js";
220
+ } from "./cli-qwgtacd6.js";
218
221
  import {
219
222
  searchScripts
220
- } from "./cli-s9pj3c7z.js";
223
+ } from "./cli-30bbaveh.js";
221
224
  import {
222
225
  CANDIDATE_SET_MULTIPLIER,
223
226
  getEmbeddingProvider,
@@ -225,19 +228,19 @@ import {
225
228
  init_constants,
226
229
  init_reranker,
227
230
  rerank
228
- } from "./cli-709t753c.js";
231
+ } from "./cli-f3qa069v.js";
229
232
  import"./cli-q25ejbyn.js";
230
- import"./cli-76d1vtvq.js";
233
+ import"./cli-bgvskydy.js";
231
234
  import {
232
235
  getSlackApp,
233
236
  startSlackApp,
234
237
  stopSlackApp
235
- } from "./cli-hm27prrc.js";
238
+ } from "./cli-trmapn9k.js";
236
239
  import"./cli-rkndnn90.js";
237
240
  import"./cli-b0p7rfnd.js";
238
241
  import {
239
242
  recordUnmappedIdentity
240
- } from "./cli-9p0qhead.js";
243
+ } from "./cli-c9mtm09b.js";
241
244
  import {
242
245
  findOrCreateUserByEmail,
243
246
  findUserByExternalId,
@@ -251,7 +254,7 @@ import {
251
254
  resolveUserByToken,
252
255
  revokeToken,
253
256
  unlinkIdentity
254
- } from "./cli-7xkwgkrs.js";
257
+ } from "./cli-h3k622d0.js";
255
258
  import"./cli-6xd0bvf8.js";
256
259
  import"./cli-wspgs9bt.js";
257
260
  import {
@@ -259,7 +262,7 @@ import {
259
262
  createTaskWithSiblingAwareness,
260
263
  gitlabContextKey,
261
264
  pageContextKey
262
- } from "./cli-0c3n0eba.js";
265
+ } from "./cli-y4ycrnjc.js";
263
266
  import {
264
267
  getAppUrl,
265
268
  getConfiguredAppUrls,
@@ -571,7 +574,7 @@ import {
571
574
  upsertSwarmConfig,
572
575
  validateConfigValue,
573
576
  withFavoriteFlags
574
- } from "./cli-951w7c79.js";
577
+ } from "./cli-ncfgsqbd.js";
575
578
  import"./cli-z2zcxes1.js";
576
579
  import {
577
580
  init_zod
@@ -586,7 +589,7 @@ import {
586
589
  import {
587
590
  init_package,
588
591
  package_default
589
- } from "./cli-8f0dwca2.js";
592
+ } from "./cli-c3frqf65.js";
590
593
  import {
591
594
  init_secret_scrubber,
592
595
  registerVolatileSecret,
@@ -636,6 +639,146 @@ import {
636
639
  } from "node:http";
637
640
  init_db();
638
641
 
642
+ // src/be/rbac-audit.ts
643
+ init_db();
644
+ var FLUSH_INTERVAL_MS = 2000;
645
+ var FLUSH_MAX_ROWS = 200;
646
+ var AUDIT_GC_INTERVAL_MS = 24 * 60 * 60 * 1000;
647
+ var DEFAULT_RETENTION_DAYS = 30;
648
+ var buffer = [];
649
+ var flushTimer = null;
650
+ var auditGcTimer = null;
651
+ var thresholdFlushScheduled = false;
652
+ function isAuditDisabled() {
653
+ return process.env.RBAC_AUDIT_DISABLED === "true";
654
+ }
655
+ function principalIdOf(principal) {
656
+ switch (principal.kind) {
657
+ case "agent":
658
+ return principal.agentId;
659
+ case "user":
660
+ return principal.userId;
661
+ case "operator":
662
+ return null;
663
+ }
664
+ }
665
+ function resourceIdOf(resource) {
666
+ if (!resource)
667
+ return null;
668
+ switch (resource.kind) {
669
+ case "task":
670
+ return resource.taskId;
671
+ case "agent":
672
+ return resource.agentId;
673
+ case "kv-namespace":
674
+ return resource.namespace;
675
+ case "owned":
676
+ return resource.ownerAgentId ?? null;
677
+ case "none":
678
+ return null;
679
+ }
680
+ }
681
+ function toRow(check, decision) {
682
+ return {
683
+ principalType: check.principal.kind,
684
+ principalId: principalIdOf(check.principal),
685
+ originatorUserId: null,
686
+ verb: check.verb,
687
+ resourceType: check.resource?.kind ?? null,
688
+ resourceId: resourceIdOf(check.resource),
689
+ decision: decision.allow ? "allow" : "deny",
690
+ reason: decision.allow ? null : decision.reason,
691
+ source: check.source
692
+ };
693
+ }
694
+ function enqueueAuditRow(check, decision) {
695
+ if (isAuditDisabled())
696
+ return;
697
+ try {
698
+ buffer.push(toRow(check, decision));
699
+ if (buffer.length >= FLUSH_MAX_ROWS && !thresholdFlushScheduled) {
700
+ thresholdFlushScheduled = true;
701
+ const t = setTimeout(() => {
702
+ thresholdFlushScheduled = false;
703
+ flushAuditBuffer();
704
+ }, 0);
705
+ if (typeof t.unref === "function")
706
+ t.unref();
707
+ }
708
+ } catch (err) {
709
+ console.warn("[rbac-audit] enqueue failed, dropping row:", err.message);
710
+ }
711
+ }
712
+ function flushAuditBuffer() {
713
+ if (buffer.length === 0)
714
+ return;
715
+ const rows = buffer;
716
+ buffer = [];
717
+ try {
718
+ const db = getDb();
719
+ const stmt = db.prepare(`INSERT INTO permission_audit
720
+ (principalType, principalId, originatorUserId, verb, resourceType, resourceId, decision, reason, source)
721
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`);
722
+ const insertAll = db.transaction((batch) => {
723
+ for (const r of batch) {
724
+ stmt.run(r.principalType, r.principalId, r.originatorUserId, r.verb, r.resourceType, r.resourceId, r.decision, r.reason, r.source);
725
+ }
726
+ });
727
+ insertAll(rows);
728
+ } catch (err) {
729
+ console.warn(`[rbac-audit] flush failed, dropping ${rows.length} row(s):`, err.message);
730
+ }
731
+ }
732
+ function flushIntervalMs() {
733
+ const v = Number(process.env.RBAC_AUDIT_FLUSH_MS);
734
+ return Number.isFinite(v) && v > 0 ? v : FLUSH_INTERVAL_MS;
735
+ }
736
+ function startAuditWriter(intervalMs = flushIntervalMs()) {
737
+ if (flushTimer)
738
+ return;
739
+ flushTimer = setInterval(() => flushAuditBuffer(), intervalMs);
740
+ if (typeof flushTimer?.unref === "function")
741
+ flushTimer.unref();
742
+ }
743
+ function stopAuditWriter() {
744
+ if (flushTimer) {
745
+ clearInterval(flushTimer);
746
+ flushTimer = null;
747
+ }
748
+ }
749
+ function purgeExpiredAuditRows() {
750
+ try {
751
+ const days = Number(process.env.RBAC_AUDIT_RETENTION_DAYS) || DEFAULT_RETENTION_DAYS;
752
+ const result = getDb().prepare("DELETE FROM permission_audit WHERE ts < datetime('now', ?)").run(`-${days} days`);
753
+ return result.changes;
754
+ } catch (err) {
755
+ console.warn("[rbac-audit] retention purge failed:", err.message);
756
+ return 0;
757
+ }
758
+ }
759
+ function startAuditGc(intervalMs = AUDIT_GC_INTERVAL_MS) {
760
+ if (auditGcTimer)
761
+ return;
762
+ const purged = purgeExpiredAuditRows();
763
+ if (purged > 0) {
764
+ console.log(`[rbac-audit] Initial retention purge removed ${purged} audit row(s)`);
765
+ }
766
+ auditGcTimer = setInterval(() => {
767
+ const n = purgeExpiredAuditRows();
768
+ if (n > 0) {
769
+ console.log(`[rbac-audit] Retention purge removed ${n} audit row(s)`);
770
+ }
771
+ }, intervalMs);
772
+ if (typeof auditGcTimer?.unref === "function")
773
+ auditGcTimer.unref();
774
+ }
775
+ function stopAuditGc() {
776
+ if (auditGcTimer) {
777
+ clearInterval(auditGcTimer);
778
+ auditGcTimer = null;
779
+ }
780
+ }
781
+
639
782
  // src/gitlab/auth.ts
640
783
  var initialized = false;
641
784
  var webhookSecret = null;
@@ -2845,12 +2988,35 @@ async function handleCodexOAuthKeepWarm(req, res, pathSegments) {
2845
2988
  init_zod();
2846
2989
  init_db();
2847
2990
  init_swarm_config_guard();
2991
+ init_request_auth_context();
2848
2992
  init_secret_scrubber();
2849
2993
  var MAX_ENV_PRESENCE_KEYS = 200;
2850
2994
  var API_ONLY_CONFIG_KEYS = new Set(["API_AGENT_FS_API_KEY"]);
2851
2995
  function stripApiOnlyKeys(configs) {
2852
2996
  return configs.filter((config) => !API_ONLY_CONFIG_KEYS.has(config.key));
2853
2997
  }
2998
+ function singleHeader(req, name) {
2999
+ const raw = req.headers[name];
3000
+ return Array.isArray(raw) ? raw[0] : raw;
3001
+ }
3002
+ function ensureConfigAdmin(req, res, verb) {
3003
+ const auth = getRequestAuth(req);
3004
+ if (auth?.kind === "operator" || auth?.kind === "user")
3005
+ return true;
3006
+ const agentId = singleHeader(req, "x-agent-id");
3007
+ const agent = agentId ? getAgentById(agentId) : undefined;
3008
+ const decision = can({
3009
+ principal: { kind: "agent", agentId: agentId ?? "", isLead: agent?.isLead ?? false },
3010
+ verb,
3011
+ resource: { kind: "none" },
3012
+ source: "http"
3013
+ });
3014
+ if (!decision.allow) {
3015
+ jsonError(res, verb === "config.write.any" ? "Writing swarm config requires the lead agent" : "Deleting swarm config requires the lead agent", 403);
3016
+ return false;
3017
+ }
3018
+ return true;
3019
+ }
2854
3020
  var getResolvedConfigRoute = route({
2855
3021
  method: "get",
2856
3022
  path: "/api/config/resolved",
@@ -2928,6 +3094,7 @@ var upsertConfig = route({
2928
3094
  pattern: ["api", "config"],
2929
3095
  summary: "Create or update a config entry (reserved env-only keys are rejected). Global-scope writes auto-trigger an integrations reload (debounced ~250ms) so Slack/GitHub/Linear/Jira/AgentMail pick up new credentials without an explicit /api/config/reload call.",
2930
3096
  tags: ["Config"],
3097
+ rbac: { permission: "config.write.any" },
2931
3098
  body: exports_external.object({
2932
3099
  scope: exports_external.enum(["global", "agent", "repo"]),
2933
3100
  scopeId: exports_external.string().nullish(),
@@ -2948,6 +3115,7 @@ var deleteConfig = route({
2948
3115
  pattern: ["api", "config", null],
2949
3116
  summary: "Delete a config entry by ID (including legacy reserved rows for cleanup). Global-scope deletes auto-trigger an integrations reload.",
2950
3117
  tags: ["Config"],
3118
+ rbac: { permission: "config.delete.any" },
2951
3119
  params: exports_external.object({ id: exports_external.string() }),
2952
3120
  responses: {
2953
3121
  200: { description: "Config deleted" },
@@ -3041,6 +3209,8 @@ async function handleConfig(req, res, pathSegments, queryParams) {
3041
3209
  const parsed = await upsertConfig.parse(req, res, pathSegments, queryParams);
3042
3210
  if (!parsed)
3043
3211
  return true;
3212
+ if (!ensureConfigAdmin(req, res, "config.write.any"))
3213
+ return true;
3044
3214
  const { scope, scopeId, key, value, isSecret, envPath, description } = parsed.body;
3045
3215
  if (scope === "global" && scopeId) {
3046
3216
  jsonError(res, "Global scope must not have scopeId", 400);
@@ -3084,6 +3254,8 @@ async function handleConfig(req, res, pathSegments, queryParams) {
3084
3254
  const parsed = await deleteConfig.parse(req, res, pathSegments, queryParams);
3085
3255
  if (!parsed)
3086
3256
  return true;
3257
+ if (!ensureConfigAdmin(req, res, "config.delete.any"))
3258
+ return true;
3087
3259
  const existing = getSwarmConfigLookupById(parsed.params.id);
3088
3260
  if (!existing) {
3089
3261
  jsonError(res, "Config not found", 404);
@@ -3420,6 +3592,7 @@ var putFavorite = route({
3420
3592
  pattern: ["api", "favorites"],
3421
3593
  summary: "Set favorite state for an item",
3422
3594
  tags: ["Favorites"],
3595
+ rbac: { ungated: "self-scoped to the authenticated user; no cross-principal access" },
3423
3596
  body: exports_external.object({
3424
3597
  itemType: FavoriteItemTypeSchema,
3425
3598
  itemId: exports_external.string().min(1),
@@ -3985,7 +4158,8 @@ var uploadTaskFileRoute = route({
3985
4158
  404: { description: "Task not found" },
3986
4159
  413: { description: "Upload exceeds 50 MiB" }
3987
4160
  },
3988
- auth: { apiKey: true, agentId: true }
4161
+ auth: { apiKey: true, agentId: true },
4162
+ rbac: { permission: "task.fs.mutate" }
3989
4163
  });
3990
4164
  var getTaskFileRoute = route({
3991
4165
  method: "get",
@@ -4038,7 +4212,8 @@ var deleteTaskFileRoute = route({
4038
4212
  403: { description: "Caller cannot mutate this task" },
4039
4213
  404: { description: "Task or attachment not found" }
4040
4214
  },
4041
- auth: { apiKey: true, agentId: true }
4215
+ auth: { apiKey: true, agentId: true },
4216
+ rbac: { permission: "task.fs.mutate" }
4042
4217
  });
4043
4218
  async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
4044
4219
  if (ensureAgentCredentialsRoute.match(req.method, pathSegments)) {
@@ -4138,7 +4313,7 @@ async function sendUpload(req, res, taskId, query, agentId) {
4138
4313
  return true;
4139
4314
  }
4140
4315
  const provider = getFileStorageProvider();
4141
- const contentType = singleHeader(req, "content-type") ?? "application/octet-stream";
4316
+ const contentType = singleHeader2(req, "content-type") ?? "application/octet-stream";
4142
4317
  const scope = { taskId, name: query.name };
4143
4318
  let uploaded;
4144
4319
  try {
@@ -4273,31 +4448,39 @@ function findAttachment(taskId, attachmentId, res) {
4273
4448
  return attachment;
4274
4449
  }
4275
4450
  function canMutateTask(task, myAgentId, req) {
4451
+ const resource = {
4452
+ kind: "task",
4453
+ taskId: task.id,
4454
+ agentId: task.agentId,
4455
+ creatorAgentId: task.creatorAgentId
4456
+ };
4276
4457
  const auth = getRequestAuth(req);
4277
- if (auth?.kind === "operator")
4278
- return true;
4279
- if (auth?.kind === "user")
4280
- return true;
4281
- if (!myAgentId)
4282
- return false;
4283
- const agent = getAgentById(myAgentId);
4284
- if (agent?.isLead)
4285
- return true;
4286
- return task.agentId === myAgentId || task.creatorAgentId === myAgentId;
4458
+ let principal;
4459
+ if (auth?.kind === "operator") {
4460
+ principal = { kind: "operator" };
4461
+ } else if (auth?.kind === "user") {
4462
+ principal = { kind: "user", userId: auth.userId };
4463
+ } else {
4464
+ if (!myAgentId)
4465
+ return false;
4466
+ const agent = getAgentById(myAgentId);
4467
+ principal = { kind: "agent", agentId: myAgentId, isLead: agent?.isLead ?? false };
4468
+ }
4469
+ return can({ principal, verb: "task.fs.mutate", resource, source: "http" }).allow;
4287
4470
  }
4288
4471
  async function readRawBody(req, maxBytes) {
4289
4472
  const chunks = [];
4290
4473
  let total = 0;
4291
4474
  for await (const chunk of req) {
4292
- const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
4293
- total += buffer.byteLength;
4475
+ const buffer2 = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
4476
+ total += buffer2.byteLength;
4294
4477
  if (total > maxBytes)
4295
4478
  return BODY_TOO_LARGE;
4296
- chunks.push(buffer);
4479
+ chunks.push(buffer2);
4297
4480
  }
4298
4481
  return Buffer.concat(chunks);
4299
4482
  }
4300
- function singleHeader(req, name) {
4483
+ function singleHeader2(req, name) {
4301
4484
  const raw = req.headers[name];
4302
4485
  return Array.isArray(raw) ? raw[0] : raw;
4303
4486
  }
@@ -4589,7 +4772,8 @@ var putKvHeader = route({
4589
4772
  tags: ["KV"],
4590
4773
  params: exports_external.object({ key: KvKeySchema }),
4591
4774
  body: kvSetBodySchema,
4592
- responses: RESPONSES_PUT
4775
+ responses: RESPONSES_PUT,
4776
+ rbac: { permission: "kv.write.any" }
4593
4777
  });
4594
4778
  var deleteKvHeader = route({
4595
4779
  method: "delete",
@@ -4603,7 +4787,8 @@ var deleteKvHeader = route({
4603
4787
  404: { description: "KV entry not found" },
4604
4788
  403: { description: "Caller may not write this namespace" },
4605
4789
  400: { description: "Validation error or unresolvable namespace" }
4606
- }
4790
+ },
4791
+ rbac: { permission: "kv.write.any" }
4607
4792
  });
4608
4793
  var incrKvHeader = route({
4609
4794
  method: "post",
@@ -4613,7 +4798,8 @@ var incrKvHeader = route({
4613
4798
  tags: ["KV"],
4614
4799
  params: exports_external.object({ key: KvKeySchema }),
4615
4800
  body: kvIncrBodySchema,
4616
- responses: RESPONSES_PUT
4801
+ responses: RESPONSES_PUT,
4802
+ rbac: { permission: "kv.write.any" }
4617
4803
  });
4618
4804
  var listKvHeader = route({
4619
4805
  method: "get",
@@ -4641,7 +4827,8 @@ var putKvExplicit = route({
4641
4827
  tags: ["KV"],
4642
4828
  params: exports_external.object({ namespace: KvNamespaceSchema, key: KvKeySchema }),
4643
4829
  body: kvSetBodySchema,
4644
- responses: RESPONSES_PUT
4830
+ responses: RESPONSES_PUT,
4831
+ rbac: { permission: "kv.write.any" }
4645
4832
  });
4646
4833
  var deleteKvExplicit = route({
4647
4834
  method: "delete",
@@ -4654,7 +4841,8 @@ var deleteKvExplicit = route({
4654
4841
  204: { description: "KV entry deleted" },
4655
4842
  404: { description: "KV entry not found" },
4656
4843
  403: { description: "Caller may not write this namespace" }
4657
- }
4844
+ },
4845
+ rbac: { permission: "kv.write.any" }
4658
4846
  });
4659
4847
  var incrKvExplicit = route({
4660
4848
  method: "post",
@@ -4664,7 +4852,8 @@ var incrKvExplicit = route({
4664
4852
  tags: ["KV"],
4665
4853
  params: exports_external.object({ namespace: KvNamespaceSchema, key: KvKeySchema }),
4666
4854
  body: kvIncrBodySchema,
4667
- responses: RESPONSES_PUT
4855
+ responses: RESPONSES_PUT,
4856
+ rbac: { permission: "kv.write.any" }
4668
4857
  });
4669
4858
  var listKvExplicit = route({
4670
4859
  method: "get",
@@ -4690,14 +4879,14 @@ function decodeKvSegment(res, raw, label) {
4690
4879
  }
4691
4880
  return decoded;
4692
4881
  }
4693
- function singleHeader2(req, name) {
4882
+ function singleHeader3(req, name) {
4694
4883
  const raw = req.headers[name];
4695
4884
  if (raw === undefined)
4696
4885
  return;
4697
4886
  return Array.isArray(raw) ? raw[0] : raw;
4698
4887
  }
4699
4888
  function resolveNamespaceFromHeaders(req) {
4700
- const pageId = singleHeader2(req, "x-page-id");
4889
+ const pageId = singleHeader3(req, "x-page-id");
4701
4890
  if (pageId) {
4702
4891
  try {
4703
4892
  return pageContextKey({ pageId });
@@ -4705,7 +4894,7 @@ function resolveNamespaceFromHeaders(req) {
4705
4894
  return null;
4706
4895
  }
4707
4896
  }
4708
- const sourceTaskId = singleHeader2(req, "x-source-task-id");
4897
+ const sourceTaskId = singleHeader3(req, "x-source-task-id");
4709
4898
  if (sourceTaskId) {
4710
4899
  const task = getTaskById(sourceTaskId);
4711
4900
  if (task?.contextKey)
@@ -4716,7 +4905,7 @@ function resolveNamespaceFromHeaders(req) {
4716
4905
  } catch {}
4717
4906
  }
4718
4907
  }
4719
- const agentId = singleHeader2(req, "x-agent-id");
4908
+ const agentId = singleHeader3(req, "x-agent-id");
4720
4909
  if (agentId) {
4721
4910
  try {
4722
4911
  return agentContextKey({ agentId });
@@ -4727,8 +4916,8 @@ function resolveNamespaceFromHeaders(req) {
4727
4916
  return null;
4728
4917
  }
4729
4918
  function buildAuthCtx(req) {
4730
- const callerAgentId = singleHeader2(req, "x-agent-id");
4731
- const pageId = singleHeader2(req, "x-page-id");
4919
+ const callerAgentId = singleHeader3(req, "x-agent-id");
4920
+ const pageId = singleHeader3(req, "x-page-id");
4732
4921
  let isLead = false;
4733
4922
  if (callerAgentId) {
4734
4923
  const agent = getAgentById(callerAgentId);
@@ -4744,12 +4933,16 @@ function authorizeWrite(namespace, ctx) {
4744
4933
  return null;
4745
4934
  }
4746
4935
  if (namespace.startsWith("task:agent:")) {
4747
- const target = namespace.slice("task:agent:".length);
4748
- if (ctx.callerAgentId && target === ctx.callerAgentId)
4749
- return null;
4750
- if (ctx.isLead)
4751
- return null;
4752
- return { status: 403, message: "writes to another agent's namespace require lead" };
4936
+ const allowed = ctx.callerAgentId != null && can({
4937
+ principal: { kind: "agent", agentId: ctx.callerAgentId, isLead: ctx.isLead },
4938
+ verb: "kv.write.any",
4939
+ resource: { kind: "kv-namespace", namespace },
4940
+ source: "http"
4941
+ }).allow;
4942
+ if (!allowed) {
4943
+ return { status: 403, message: "writes to another agent's namespace require lead" };
4944
+ }
4945
+ return null;
4753
4946
  }
4754
4947
  return null;
4755
4948
  }
@@ -4789,7 +4982,7 @@ function encodeValueOrError(res, value, valueType) {
4789
4982
  }
4790
4983
  }
4791
4984
  async function handleKv(req, res, pathSegments, queryParams) {
4792
- const hasPageHeader = singleHeader2(req, "x-page-id") !== undefined;
4985
+ const hasPageHeader = singleHeader3(req, "x-page-id") !== undefined;
4793
4986
  if (hasPageHeader && pathSegments[0] === "api" && pathSegments[1] === "kv" && pathSegments[2] === "_") {
4794
4987
  pathSegments = [pathSegments[0], pathSegments[1], ...pathSegments.slice(4)];
4795
4988
  }
@@ -6286,6 +6479,7 @@ var SDK_TOOL_NAME_MAP = {
6286
6479
  schedule_list: "list-schedules",
6287
6480
  schedule_create: "create-schedule",
6288
6481
  schedule_update: "update-schedule",
6482
+ schedule_patch: "patch-schedule",
6289
6483
  schedule_delete: "delete-schedule",
6290
6484
  schedule_runNow: "run-schedule-now",
6291
6485
  script_search: "script-search",
@@ -11867,6 +12061,26 @@ init_zod();
11867
12061
  var import_cron_parser = __toESM(require_dist(), 1);
11868
12062
  init_db();
11869
12063
  init_types();
12064
+ var scheduleUpdateBodySchema = exports_external.object({
12065
+ name: exports_external.string().optional(),
12066
+ description: exports_external.string().optional(),
12067
+ cronExpression: exports_external.string().nullable().optional(),
12068
+ intervalMs: exports_external.number().int().positive().nullable().optional(),
12069
+ taskTemplate: exports_external.string().optional(),
12070
+ taskType: exports_external.string().optional(),
12071
+ tags: exports_external.array(exports_external.string()).optional(),
12072
+ priority: exports_external.number().int().optional(),
12073
+ targetAgentId: exports_external.string().uuid().nullable().optional(),
12074
+ enabled: exports_external.boolean().optional(),
12075
+ timezone: exports_external.string().optional(),
12076
+ model: exports_external.string().nullable().optional(),
12077
+ modelTier: ModelTierSchema.nullable().optional(),
12078
+ nextRunAt: exports_external.string().nullable().optional(),
12079
+ targetType: ScheduledTaskTargetTypeSchema.optional(),
12080
+ workflowId: exports_external.string().uuid().nullable().optional(),
12081
+ scriptName: exports_external.string().nullable().optional(),
12082
+ scriptArgs: exports_external.record(exports_external.string(), exports_external.unknown()).nullable().optional()
12083
+ });
11870
12084
  var createSchedule = route({
11871
12085
  method: "post",
11872
12086
  path: "/api/schedules",
@@ -11929,6 +12143,8 @@ var listSchedules = route({
11929
12143
  workflowId: exports_external.string().uuid().optional(),
11930
12144
  scriptName: exports_external.string().optional(),
11931
12145
  hideCompleted: exports_external.enum(["true", "false"]).optional().transform((v) => v === undefined ? undefined : v === "true"),
12146
+ consecutiveErrorsMin: exports_external.coerce.number().int().min(0).optional(),
12147
+ lastRunStatus: exports_external.enum(["failed", "succeeded"]).optional(),
11932
12148
  fields: exports_external.enum(["full", "slim"]).optional()
11933
12149
  }),
11934
12150
  responses: {
@@ -11954,26 +12170,7 @@ var updateSchedule = route({
11954
12170
  summary: "Update a schedule",
11955
12171
  tags: ["Schedules"],
11956
12172
  params: exports_external.object({ id: exports_external.string() }),
11957
- body: exports_external.object({
11958
- name: exports_external.string().optional(),
11959
- description: exports_external.string().optional(),
11960
- cronExpression: exports_external.string().nullable().optional(),
11961
- intervalMs: exports_external.number().int().positive().nullable().optional(),
11962
- taskTemplate: exports_external.string().optional(),
11963
- taskType: exports_external.string().optional(),
11964
- tags: exports_external.array(exports_external.string()).optional(),
11965
- priority: exports_external.number().int().optional(),
11966
- targetAgentId: exports_external.string().uuid().optional(),
11967
- enabled: exports_external.boolean().optional(),
11968
- timezone: exports_external.string().optional(),
11969
- model: exports_external.string().optional(),
11970
- modelTier: ModelTierSchema.nullable().optional(),
11971
- nextRunAt: exports_external.string().nullable().optional(),
11972
- targetType: ScheduledTaskTargetTypeSchema.optional(),
11973
- workflowId: exports_external.string().uuid().nullable().optional(),
11974
- scriptName: exports_external.string().nullable().optional(),
11975
- scriptArgs: exports_external.record(exports_external.string(), exports_external.unknown()).nullable().optional()
11976
- }),
12173
+ body: scheduleUpdateBodySchema,
11977
12174
  responses: {
11978
12175
  200: { description: "Schedule updated" },
11979
12176
  400: { description: "Validation error" },
@@ -11981,6 +12178,25 @@ var updateSchedule = route({
11981
12178
  409: { description: "Duplicate name" }
11982
12179
  }
11983
12180
  });
12181
+ var patchSchedule = route({
12182
+ method: "patch",
12183
+ path: "/api/schedules/{id}",
12184
+ pattern: ["api", "schedules", null],
12185
+ summary: "Patch a schedule",
12186
+ description: "Partially updates a schedule by shallow-merging provided fields over the existing row.",
12187
+ tags: ["Schedules"],
12188
+ params: exports_external.object({ id: exports_external.string() }),
12189
+ body: scheduleUpdateBodySchema,
12190
+ responses: {
12191
+ 200: { description: "Schedule patched" },
12192
+ 400: { description: "Validation error" },
12193
+ 404: { description: "Schedule not found" },
12194
+ 409: { description: "Duplicate name" }
12195
+ },
12196
+ rbac: {
12197
+ ungated: "matches existing schedule update/delete posture: bearer-authenticated agents may manage schedules"
12198
+ }
12199
+ });
11984
12200
  var deleteSchedule = route({
11985
12201
  method: "delete",
11986
12202
  path: "/api/schedules/{id}",
@@ -12005,7 +12221,9 @@ async function handleSchedules(req, res, pathSegments, queryParams, myAgentId) {
12005
12221
  targetType: parsed.query.targetType,
12006
12222
  workflowId: parsed.query.workflowId,
12007
12223
  scriptName: parsed.query.scriptName,
12008
- hideCompleted: parsed.query.hideCompleted
12224
+ hideCompleted: parsed.query.hideCompleted,
12225
+ consecutiveErrorsMin: parsed.query.consecutiveErrorsMin,
12226
+ lastRunStatus: parsed.query.lastRunStatus
12009
12227
  };
12010
12228
  const schedules = parsed.query.fields === "full" ? getScheduledTasks(filters) : getScheduledTasks(filters, { slim: true });
12011
12229
  const userId = resolveHttpAuditUserId(req, myAgentId);
@@ -12180,8 +12398,9 @@ async function handleSchedules(req, res, pathSegments, queryParams, myAgentId) {
12180
12398
  json(res, decorated ?? schedule);
12181
12399
  return true;
12182
12400
  }
12183
- if (updateSchedule.match(req.method, pathSegments)) {
12184
- const parsed = await updateSchedule.parse(req, res, pathSegments, queryParams);
12401
+ if (updateSchedule.match(req.method, pathSegments) || patchSchedule.match(req.method, pathSegments)) {
12402
+ const routeHandle = updateSchedule.match(req.method, pathSegments) ? updateSchedule : patchSchedule;
12403
+ const parsed = await routeHandle.parse(req, res, pathSegments, queryParams);
12185
12404
  if (!parsed)
12186
12405
  return true;
12187
12406
  const body = parsed.body;
@@ -12888,7 +13107,8 @@ var upsertRoute2 = route({
12888
13107
  200: { description: "Script upserted" },
12889
13108
  400: { description: "Validation or typecheck failure" },
12890
13109
  403: { description: "Global write requires lead agent" }
12891
- }
13110
+ },
13111
+ rbac: { permission: "script.global.write" }
12892
13112
  });
12893
13113
  var runRoute = route({
12894
13114
  method: "post",
@@ -12933,7 +13153,8 @@ var deleteRoute = route({
12933
13153
  200: { description: "Delete result" },
12934
13154
  400: { description: "Validation error" },
12935
13155
  403: { description: "Global delete requires lead agent" }
12936
- }
13156
+ },
13157
+ rbac: { permission: "script.global.delete" }
12937
13158
  });
12938
13159
  var typesRoute = route({
12939
13160
  method: "get",
@@ -13151,6 +13372,18 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
13151
13372
  const agent = requireAgent2(res, agentId);
13152
13373
  if (!agent)
13153
13374
  return true;
13375
+ if (parsed.body.scope === "global") {
13376
+ const decision = can({
13377
+ principal: { kind: "agent", agentId: agent.id, isLead: agent.isLead },
13378
+ verb: "script.global.write",
13379
+ resource: { kind: "owned", scope: "global" },
13380
+ source: "http"
13381
+ });
13382
+ if (!decision.allow) {
13383
+ jsonError(res, "Global write requires lead agent", 403);
13384
+ return true;
13385
+ }
13386
+ }
13154
13387
  const typecheck = typecheckScript(parsed.body.source, { agentId: agent.id });
13155
13388
  if (!typecheck.ok) {
13156
13389
  json(res, {
@@ -13406,6 +13639,18 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
13406
13639
  const agent = requireAgent2(res, agentId);
13407
13640
  if (!agent)
13408
13641
  return true;
13642
+ if (parsed.query.scope === "global") {
13643
+ const decision = can({
13644
+ principal: { kind: "agent", agentId: agent.id, isLead: agent.isLead },
13645
+ verb: "script.global.delete",
13646
+ resource: { kind: "owned", scope: "global" },
13647
+ source: "http"
13648
+ });
13649
+ if (!decision.allow) {
13650
+ jsonError(res, "Global delete requires lead agent", 403);
13651
+ return true;
13652
+ }
13653
+ }
13409
13654
  const deleted = deleteScript({
13410
13655
  name: parsed.params.name,
13411
13656
  scope: parsed.query.scope,
@@ -18026,6 +18271,9 @@ var listWorkflowsRoute = route({
18026
18271
  description: "Returns workflows WITHOUT the heavy `definition` (the full DAG) by default — the list view only needs a `nodeCount`, which is included. Pass `fields=full` to restore `definition` + trigger config. Fetch the full workflow via `GET /api/workflows/{id}`.",
18027
18272
  tags: ["Workflows"],
18028
18273
  query: exports_external.object({
18274
+ enabled: exports_external.enum(["true", "false"]).optional().transform((v) => v === undefined ? undefined : v === "true"),
18275
+ consecutiveErrorsMin: exports_external.coerce.number().int().min(0).optional(),
18276
+ lastRunStatus: WorkflowRunStatusSchema.optional(),
18029
18277
  fields: exports_external.enum(["full", "slim"]).optional()
18030
18278
  }),
18031
18279
  responses: {
@@ -18342,10 +18590,15 @@ async function handleWorkflows(req, res, pathSegments, queryParams, myAgentId) {
18342
18590
  if (!parsed)
18343
18591
  return true;
18344
18592
  const userId = resolveHttpAuditUserId(req, myAgentId);
18593
+ const filters = {
18594
+ enabled: parsed.query.enabled,
18595
+ consecutiveErrorsMin: parsed.query.consecutiveErrorsMin,
18596
+ lastRunStatus: parsed.query.lastRunStatus
18597
+ };
18345
18598
  if (parsed.query.fields === "full") {
18346
- json(res, withFavoriteFlags(listWorkflows(), { userId, itemType: "workflow" }));
18599
+ json(res, withFavoriteFlags(listWorkflows(filters), { userId, itemType: "workflow" }));
18347
18600
  } else {
18348
- json(res, withFavoriteFlags(listWorkflows(undefined, { slim: true }), {
18601
+ json(res, withFavoriteFlags(listWorkflows(filters, { slim: true }), {
18349
18602
  userId,
18350
18603
  itemType: "workflow"
18351
18604
  }));
@@ -19027,18 +19280,22 @@ async function shutdown() {
19027
19280
  sessionsProcessed: getServerSessionsProcessed()
19028
19281
  });
19029
19282
  if (hasCapability("scheduling")) {
19030
- const { stopScheduler } = await import("./index-zfx5p959.js");
19283
+ const { stopScheduler } = await import("./index-0gzmqj4k.js");
19031
19284
  stopScheduler();
19032
19285
  }
19033
19286
  stopHeartbeat();
19034
19287
  stopScriptRunSupervisor();
19035
19288
  await stopSlackApp();
19036
19289
  if (process.env.OAUTH_KEEPALIVE_DISABLE !== "true") {
19037
- const { stopOAuthKeepalive } = await import("./keepalive-rbz05zad.js");
19290
+ const { stopOAuthKeepalive } = await import("./keepalive-62gfj91d.js");
19038
19291
  await stopOAuthKeepalive();
19039
19292
  }
19040
19293
  stopMcpOAuthPendingGc();
19041
19294
  stopMemoryGc();
19295
+ stopAuditGc();
19296
+ stopAuditWriter();
19297
+ flushAuditBuffer();
19298
+ clearAuditSink();
19042
19299
  if (globalState.__apiGcInterval) {
19043
19300
  clearInterval(globalState.__apiGcInterval);
19044
19301
  delete globalState.__apiGcInterval;
@@ -19087,19 +19344,22 @@ try {
19087
19344
  throw err;
19088
19345
  }
19089
19346
  try {
19090
- const { seedPricingFromModelsDev } = await import("./seed-pricing-1szjzfj1.js");
19347
+ const { seedPricingFromModelsDev } = await import("./seed-pricing-1r1y1xkq.js");
19091
19348
  seedPricingFromModelsDev();
19092
- const { startPricingRefreshLoop } = await import("./pricing-refresh-rmtrby6h.js");
19349
+ const { startPricingRefreshLoop } = await import("./pricing-refresh-cgtkffpa.js");
19093
19350
  startPricingRefreshLoop();
19094
19351
  } catch (err) {
19095
19352
  console.error("[startup] Failed to seed pricing rows:", err);
19096
19353
  }
19097
19354
  try {
19098
- const { runAllSeeders } = await import("./index-ha8e9xn2.js");
19355
+ const { runAllSeeders } = await import("./index-vfedgdw6.js");
19099
19356
  await runAllSeeders({ scriptEmbeddingMode: "skip" });
19100
19357
  } catch (err) {
19101
19358
  console.error("[startup] Failed to seed built-in entities:", err);
19102
19359
  }
19360
+ setAuditSink(enqueueAuditRow);
19361
+ startAuditWriter();
19362
+ startAuditGc();
19103
19363
  initialize();
19104
19364
  await initOtel("api");
19105
19365
  httpServer.listen(port, async () => {
@@ -19128,31 +19388,31 @@ httpServer.listen(port, async () => {
19128
19388
  initWorkflows();
19129
19389
  startScriptRunSupervisor(getMcpBaseUrl());
19130
19390
  if (hasCapability("scheduling")) {
19131
- const { startScheduler } = await import("./index-zfx5p959.js");
19132
- const { getExecutorRegistry: getExecutorRegistry2 } = await import("./index-3wr6v2mm.js");
19391
+ const { startScheduler } = await import("./index-0gzmqj4k.js");
19392
+ const { getExecutorRegistry: getExecutorRegistry2 } = await import("./index-5ghxn8s6.js");
19133
19393
  const intervalMs = Number(process.env.SCHEDULER_INTERVAL_MS) || 1e4;
19134
19394
  startScheduler(getExecutorRegistry2(), intervalMs, {
19135
19395
  runId: globalState.__runId
19136
19396
  });
19137
19397
  }
19138
19398
  if (process.env.HEARTBEAT_DISABLE !== "true") {
19139
- const { startHeartbeat } = await import("./index-brma88d4.js");
19399
+ const { startHeartbeat } = await import("./index-nhat35em.js");
19140
19400
  const heartbeatMs = Number(process.env.HEARTBEAT_INTERVAL_MS) || 90000;
19141
19401
  startHeartbeat(heartbeatMs);
19142
19402
  }
19143
19403
  if (process.env.OAUTH_KEEPALIVE_DISABLE !== "true") {
19144
- const { startOAuthKeepalive } = await import("./keepalive-rbz05zad.js");
19404
+ const { startOAuthKeepalive } = await import("./keepalive-62gfj91d.js");
19145
19405
  startOAuthKeepalive();
19146
19406
  }
19147
19407
  startMcpOAuthPendingGc();
19148
19408
  startMemoryGc();
19149
- import("./boot-reembed-yv7awg4y.js").then(({ runBootReembed }) => runBootReembed()).catch((err) => {
19409
+ import("./boot-reembed-p0ny05t8.js").then(({ runBootReembed }) => runBootReembed()).catch((err) => {
19150
19410
  console.error("[boot-reembed] startup backfill failed (non-fatal):", err);
19151
19411
  });
19152
- import("./boot-reembed-y1bt3ttn.js").then(({ runBootReembedScripts }) => runBootReembedScripts()).catch((err) => {
19412
+ import("./boot-reembed-69agkhv6.js").then(({ runBootReembedScripts }) => runBootReembedScripts()).catch((err) => {
19153
19413
  console.error("[boot-reembed-scripts] startup backfill failed (non-fatal):", err);
19154
19414
  });
19155
- import("./boot-scrub-logs-ddyaeqar.js").then(({ runBootScrubLogs }) => runBootScrubLogs()).catch((err) => {
19415
+ import("./boot-scrub-logs-yybrkmvr.js").then(({ runBootScrubLogs }) => runBootScrubLogs()).catch((err) => {
19156
19416
  console.error("[boot-scrub-logs] startup scrub failed (non-fatal):", err);
19157
19417
  });
19158
19418
  }).on("error", (err) => {