@desplega.ai/agent-swarm 1.116.0 → 1.118.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 (113) hide show
  1. package/README.md +3 -2
  2. package/dist/{actions-brtj223f.js → actions-fsdthmby.js} +10 -8
  3. package/dist/{app-00sngzz7.js → app-d48rk7pp.js} +3 -3
  4. package/dist/{artifact-0874w6js.js → artifact-x7e6f41t.js} +1 -1
  5. package/dist/{assistant-j2ser3vg.js → assistant-nqwh132h.js} +13 -11
  6. package/dist/{boot-reembed-v4n0jphx.js → boot-reembed-vztw3kmn.js} +4 -4
  7. package/dist/{boot-reembed-fv3r1kqg.js → boot-reembed-z229xeg8.js} +3 -3
  8. package/dist/{boot-scrub-logs-72sy5z1r.js → boot-scrub-logs-yqpj4x5g.js} +2 -2
  9. package/dist/{cli-96nd7vgs.js → cli-20zj8pm5.js} +15 -10
  10. package/dist/{cli-cjmz7pp9.js → cli-2xp7h939.js} +3 -3
  11. package/dist/{cli-0h9yzvhe.js → cli-48qxfdv4.js} +5 -5
  12. package/dist/{cli-3ys55x7x.js → cli-4j0y68we.js} +2 -2
  13. package/dist/{cli-et9ab56s.js → cli-4xc1mc5x.js} +1 -1
  14. package/dist/{cli-rmyjzd8s.js → cli-60z5nrne.js} +1 -1
  15. package/dist/{cli-vb7g0zgk.js → cli-7g2e06cd.js} +2 -2
  16. package/dist/{cli-tw7xjzk7.js → cli-8j4fe76y.js} +1 -1
  17. package/dist/{cli-50czs4qy.js → cli-er0djds9.js} +1 -1
  18. package/dist/{cli-k4yp6w9w.js → cli-f5qm42kd.js} +995 -112
  19. package/dist/{cli-x9q6bghe.js → cli-g97ggbfg.js} +2 -2
  20. package/dist/{cli-xj6zntcq.js → cli-gwyaqc05.js} +264 -68
  21. package/dist/{cli-9ys4kdd4.js → cli-hy7qcdk0.js} +1 -1
  22. package/dist/{cli-5g3hx9cc.js → cli-jzyjzstk.js} +2 -2
  23. package/dist/{cli-vggz87j8.js → cli-k01wm82x.js} +10 -8
  24. package/dist/{cli-n9kkkmtm.js → cli-m3ybbmsm.js} +2 -76
  25. package/dist/{cli-qnnbe59j.js → cli-nh866k5t.js} +1 -1
  26. package/dist/{cli-xm2tyqfe.js → cli-ppwm8c6e.js} +11 -0
  27. package/dist/{cli-2rxckty2.js → cli-r6yjwx5h.js} +2 -2
  28. package/dist/{cli-vnvv6hvm.js → cli-sqp2bsy0.js} +1 -1
  29. package/dist/{cli-ty7qe0vp.js → cli-st3frvaf.js} +4 -4
  30. package/dist/{cli-b4hf0g0x.js → cli-tzs999rv.js} +1 -1
  31. package/dist/{cli-0pwtr9f0.js → cli-xck0rrm4.js} +1 -1
  32. package/dist/{cli-x4trwrcr.js → cli-yqc51axp.js} +9 -6
  33. package/dist/cli.js +10 -10
  34. package/dist/{commands-4jt6hvc9.js → commands-f6n33dnc.js} +2 -2
  35. package/dist/{db-1p5e9zj8.js → db-h2rfvs2w.js} +16 -2
  36. package/dist/{handlers-5n9gq3ek.js → handlers-tr8hkw59.js} +13 -11
  37. package/dist/{hook-ny4kv3r0.js → hook-h5337q9c.js} +1 -1
  38. package/dist/{http-bb3pva4d.js → http-6q6znnpp.js} +450 -84
  39. package/dist/{index-nzghbv06.js → index-5mxp943z.js} +10 -10
  40. package/dist/{index-ajkj0aaf.js → index-bm63k39y.js} +9 -9
  41. package/dist/{index-dpcmhqy0.js → index-qps3frqq.js} +45 -24
  42. package/dist/{index-mheeg078.js → index-sqkw2pk3.js} +8 -8
  43. package/dist/{keepalive-vbgjzp3y.js → keepalive-zajb254h.js} +4 -4
  44. package/dist/{lead-98bg3kfz.js → lead-k73hvgv5.js} +18 -18
  45. package/dist/{maintenance-1fp5fz08.js → maintenance-8s5pwjje.js} +4 -4
  46. package/dist/{oauth-refresh-sweep-91s6j3dq.js → oauth-refresh-sweep-5qaqbyfx.js} +3 -3
  47. package/dist/{onboard-n32hf2t4.js → onboard-eds531mw.js} +2 -2
  48. package/dist/{otel-impl-j42cnqry.js → otel-impl-93kctmj7.js} +1 -1
  49. package/dist/{pricing-refresh-56gkeqaw.js → pricing-refresh-gn72ypzm.js} +4 -4
  50. package/dist/{rbac-roles-c1ky22v2.js → rbac-roles-26qs5ckt.js} +2 -2
  51. package/dist/{rbac-roles-xwt8319h.js → rbac-roles-r01mzk0t.js} +3 -3
  52. package/dist/{seed-pricing-qagjzjqj.js → seed-pricing-299zvwxd.js} +3 -3
  53. package/dist/{setup-1kk84v0w.js → setup-fhysh4dg.js} +2 -2
  54. package/dist/{worker-bmjqg3c6.js → worker-ykxg0ttd.js} +18 -18
  55. package/openapi.json +369 -4
  56. package/package.json +1 -1
  57. package/plugin/skills/pages/SKILL.md +7 -0
  58. package/src/artifact-sdk/browser-sdk.ts +12 -0
  59. package/src/assets/key.ts +115 -0
  60. package/src/be/asset-key-audit.ts +210 -0
  61. package/src/be/asset-key-auth.ts +44 -0
  62. package/src/be/db.ts +810 -155
  63. package/src/be/migrations/115_asset_namespace_keys.sql +400 -0
  64. package/src/be/migrations/116_favorite_principal_scope.sql +38 -0
  65. package/src/be/seed-skills/index.ts +7 -0
  66. package/src/http/all-routes.ts +1 -0
  67. package/src/http/assets.ts +238 -0
  68. package/src/http/favorite-owner.ts +39 -0
  69. package/src/http/favorites.ts +17 -16
  70. package/src/http/index.ts +2 -0
  71. package/src/http/pages.ts +54 -13
  72. package/src/http/schedules.ts +44 -5
  73. package/src/http/stats.ts +3 -3
  74. package/src/http/tasks.ts +25 -1
  75. package/src/http/workflows.ts +52 -6
  76. package/src/scheduler/scheduler.test.ts +1 -0
  77. package/src/scheduler/scheduler.ts +1 -0
  78. package/src/server-user.ts +1 -0
  79. package/src/tasks/slack-routing.ts +147 -0
  80. package/src/tests/approval-requests.test.ts +36 -0
  81. package/src/tests/artifact-sdk.test.ts +9 -1
  82. package/src/tests/asset-key-api.test.ts +277 -0
  83. package/src/tests/asset-key-invariants.test.ts +233 -0
  84. package/src/tests/asset-key-mcp.test.ts +168 -0
  85. package/src/tests/asset-key-migration.test.ts +336 -0
  86. package/src/tests/asset-key-normalization.test.ts +68 -0
  87. package/src/tests/favorites.test.ts +37 -0
  88. package/src/tests/http-api-integration.test.ts +38 -0
  89. package/src/tests/pages-complete-fetch.test.ts +54 -0
  90. package/src/tests/pool-affinity.test.ts +3 -1
  91. package/src/tests/script-connections.test.ts +2 -0
  92. package/src/tests/send-task-slack-routing-guard.test.ts +355 -0
  93. package/src/tests/slack-metadata-inheritance.test.ts +279 -1
  94. package/src/tests/slack-routing-guard.test.ts +164 -0
  95. package/src/tests/system-default-skills.test.ts +6 -0
  96. package/src/tests/ui-swarm-sdk.test.ts +81 -0
  97. package/src/tests/workflow-workspace.test.ts +2 -0
  98. package/src/tools/create-page.ts +32 -1
  99. package/src/tools/get-tasks.ts +11 -1
  100. package/src/tools/schedules/create-schedule.ts +14 -1
  101. package/src/tools/schedules/list-schedules.ts +8 -0
  102. package/src/tools/schedules/patch-schedule.ts +11 -1
  103. package/src/tools/schedules/update-schedule.ts +11 -1
  104. package/src/tools/send-task.ts +174 -81
  105. package/src/tools/task-action.ts +24 -0
  106. package/src/tools/workflows/create-workflow.ts +19 -1
  107. package/src/tools/workflows/list-workflows.ts +5 -3
  108. package/src/tools/workflows/patch-workflow.ts +7 -2
  109. package/src/tools/workflows/update-workflow.ts +6 -0
  110. package/src/types.ts +64 -2
  111. package/src/workflows/executors/agent-task.ts +4 -1
  112. package/templates/skills/asset-namespaces/config.json +14 -0
  113. package/templates/skills/asset-namespaces/content.md +60 -0
@@ -26,7 +26,7 @@ import {
26
26
  import {
27
27
  init_package,
28
28
  package_default
29
- } from "./cli-qnnbe59j.js";
29
+ } from "./cli-nh866k5t.js";
30
30
  import {
31
31
  init_secret_scrubber,
32
32
  scrubSecrets
@@ -46,6 +46,81 @@ function parseProviderMeta(rawProvider, rawMeta) {
46
46
  }
47
47
  var init_provider_metadata = () => {};
48
48
 
49
+ // src/assets/key.ts
50
+ function normalizeAssetKey(input) {
51
+ if (typeof input !== "string")
52
+ throw new AssetKeyError("Asset key must be a string");
53
+ if (input.includes("\x00"))
54
+ throw new AssetKeyError("Asset key cannot contain NUL bytes");
55
+ if (input.includes("\\"))
56
+ throw new AssetKeyError("Asset key must use forward slashes");
57
+ const normalized = input.trim().normalize("NFKC").toLowerCase();
58
+ if (!normalized)
59
+ throw new AssetKeyError("Asset key cannot be empty");
60
+ if (normalized.startsWith("/"))
61
+ throw new AssetKeyError("Asset key must be relative");
62
+ const withTrailingSlash = normalized.endsWith("/") ? normalized : `${normalized}/`;
63
+ if (withTrailingSlash.length > ASSET_KEY_MAX_LENGTH) {
64
+ throw new AssetKeyError(`Asset key cannot exceed ${ASSET_KEY_MAX_LENGTH} characters`);
65
+ }
66
+ const segments = withTrailingSlash.slice(0, -1).split("/");
67
+ if (segments.some((segment) => segment === "" || segment === "." || segment === "..")) {
68
+ throw new AssetKeyError("Asset key contains an empty or traversal segment");
69
+ }
70
+ const root = segments[0];
71
+ if (root !== "shared" && root !== "personal") {
72
+ throw new AssetKeyError("Asset key root must be shared or personal");
73
+ }
74
+ if (root === "personal" && !segments[1]) {
75
+ throw new AssetKeyError("Personal asset keys require a canonical user ID");
76
+ }
77
+ return withTrailingSlash;
78
+ }
79
+ function defaultAssetKey(resource, id) {
80
+ if (!id.trim())
81
+ throw new AssetKeyError("Asset key resource ID cannot be empty");
82
+ return normalizeAssetKey(`shared/${resource}:${id}/`);
83
+ }
84
+ function parseAssetKey(input) {
85
+ const key = normalizeAssetKey(input);
86
+ const segments = key.slice(0, -1).split("/");
87
+ if (segments[0] === "personal") {
88
+ return {
89
+ root: "personal",
90
+ key,
91
+ segments,
92
+ userId: segments[1],
93
+ relativeSegments: segments.slice(2)
94
+ };
95
+ }
96
+ return {
97
+ root: "shared",
98
+ key,
99
+ segments,
100
+ relativeSegments: segments.slice(1)
101
+ };
102
+ }
103
+ function isCanonicalAssetKey(input) {
104
+ try {
105
+ return normalizeAssetKey(input) === input;
106
+ } catch {
107
+ return false;
108
+ }
109
+ }
110
+ function canWriteAssetKey(input, trustedUserId) {
111
+ const parsed = parseAssetKey(input);
112
+ return parsed.root === "shared" || !!trustedUserId && parsed.userId === trustedUserId;
113
+ }
114
+ var ASSET_KEY_MAX_LENGTH = 255, AssetKeyError;
115
+ var init_key = __esm(() => {
116
+ AssetKeyError = class AssetKeyError extends Error {
117
+ constructor(message) {
118
+ super(message);
119
+ this.name = "AssetKeyError";
120
+ }
121
+ };
122
+ });
123
+
49
124
  // src/utils/template.ts
50
125
  function interpolate(template, ctx) {
51
126
  const unresolved = [];
@@ -259,6 +334,264 @@ var init_resolver = __esm(() => {
259
334
  TEMPLATE_REF_REGEX = /\{\{@template\[([^\]]+)\]\}\}/g;
260
335
  });
261
336
 
337
+ // src/tasks/context-key.ts
338
+ function assertSafePart(value, label) {
339
+ if (value === null || value === undefined) {
340
+ throw new Error(`context-key: "${label}" is required`);
341
+ }
342
+ const str = typeof value === "string" ? value : String(value);
343
+ if (str.length === 0) {
344
+ throw new Error(`context-key: "${label}" must be non-empty`);
345
+ }
346
+ if (str.includes(SEPARATOR)) {
347
+ throw new Error(`context-key: "${label}" must not contain "${SEPARATOR}"; caller must sanitize (got ${JSON.stringify(str)})`);
348
+ }
349
+ return str;
350
+ }
351
+ function slackContextKey(input) {
352
+ const channelId = assertSafePart(input.channelId, "channelId");
353
+ const threadTs = assertSafePart(input.threadTs, "threadTs");
354
+ return ["task", "slack", channelId, threadTs].join(SEPARATOR);
355
+ }
356
+ function agentmailContextKey(input) {
357
+ const threadId = assertSafePart(input.threadId, "threadId");
358
+ return ["task", "agentmail", threadId].join(SEPARATOR);
359
+ }
360
+ function githubContextKey(input) {
361
+ const owner = assertSafePart(input.owner, "owner");
362
+ const repo = assertSafePart(input.repo, "repo");
363
+ const kind = assertSafePart(input.kind, "kind").toLowerCase();
364
+ if (kind !== "issue" && kind !== "pr") {
365
+ throw new Error(`context-key: github "kind" must be "issue" or "pr" (got ${JSON.stringify(kind)})`);
366
+ }
367
+ const number2 = assertSafePart(input.number, "number");
368
+ if (!/^\d+$/.test(number2)) {
369
+ throw new Error(`context-key: github "number" must be a positive integer (got ${JSON.stringify(number2)})`);
370
+ }
371
+ return ["task", "trackers", "github", owner, repo, kind, number2].join(SEPARATOR);
372
+ }
373
+ function gitlabContextKey(input) {
374
+ const projectId = assertSafePart(input.projectId, "projectId");
375
+ const kind = assertSafePart(input.kind, "kind").toLowerCase();
376
+ if (kind !== "mr" && kind !== "issue") {
377
+ throw new Error(`context-key: gitlab "kind" must be "mr" or "issue" (got ${JSON.stringify(kind)})`);
378
+ }
379
+ const iid = assertSafePart(input.iid, "iid");
380
+ if (!/^\d+$/.test(iid)) {
381
+ throw new Error(`context-key: gitlab "iid" must be a positive integer (got ${JSON.stringify(iid)})`);
382
+ }
383
+ return ["task", "trackers", "gitlab", projectId, kind, iid].join(SEPARATOR);
384
+ }
385
+ function linearContextKey(input) {
386
+ const issueIdentifier = assertSafePart(input.issueIdentifier, "issueIdentifier");
387
+ return ["task", "trackers", "linear", issueIdentifier].join(SEPARATOR);
388
+ }
389
+ function buildJiraContextKey(issueIdentifier) {
390
+ const id = assertSafePart(issueIdentifier, "issueIdentifier");
391
+ return ["task", "trackers", "jira", id].join(SEPARATOR);
392
+ }
393
+ function scheduleContextKey(input) {
394
+ const scheduleId = assertSafePart(input.scheduleId, "scheduleId");
395
+ return ["task", "schedule", scheduleId].join(SEPARATOR);
396
+ }
397
+ function workflowContextKey(input) {
398
+ const workflowRunId = assertSafePart(input.workflowRunId, "workflowRunId");
399
+ return ["task", "workflow", workflowRunId].join(SEPARATOR);
400
+ }
401
+ function agentContextKey(input) {
402
+ const agentId = assertSafePart(input.agentId, "agentId");
403
+ return ["task", "agent", agentId].join(SEPARATOR);
404
+ }
405
+ function pageContextKey(input) {
406
+ const pageId = assertSafePart(input.pageId, "pageId");
407
+ return ["task", "page", pageId].join(SEPARATOR);
408
+ }
409
+ function parseContextKey(key) {
410
+ if (typeof key !== "string" || key.length === 0) {
411
+ throw new Error("context-key: key must be a non-empty string");
412
+ }
413
+ const parts = key.split(SEPARATOR);
414
+ if (parts.length < 3 || parts[0] !== "task") {
415
+ throw new Error(`context-key: malformed key (expected "task:..."): ${JSON.stringify(key)}`);
416
+ }
417
+ const family = parts[1];
418
+ switch (family) {
419
+ case "slack": {
420
+ if (parts.length !== 4) {
421
+ throw new Error(`context-key: malformed slack key: ${JSON.stringify(key)}`);
422
+ }
423
+ return {
424
+ family: "slack",
425
+ parts: { channelId: parts[2], threadTs: parts[3] }
426
+ };
427
+ }
428
+ case "agentmail": {
429
+ if (parts.length !== 3) {
430
+ throw new Error(`context-key: malformed agentmail key: ${JSON.stringify(key)}`);
431
+ }
432
+ return { family: "agentmail", parts: { threadId: parts[2] } };
433
+ }
434
+ case "schedule": {
435
+ if (parts.length !== 3) {
436
+ throw new Error(`context-key: malformed schedule key: ${JSON.stringify(key)}`);
437
+ }
438
+ return { family: "schedule", parts: { scheduleId: parts[2] } };
439
+ }
440
+ case "workflow": {
441
+ if (parts.length !== 3) {
442
+ throw new Error(`context-key: malformed workflow key: ${JSON.stringify(key)}`);
443
+ }
444
+ return { family: "workflow", parts: { workflowRunId: parts[2] } };
445
+ }
446
+ case "trackers": {
447
+ const subFamily = parts[2];
448
+ if (subFamily === "github") {
449
+ if (parts.length !== 7) {
450
+ throw new Error(`context-key: malformed github key: ${JSON.stringify(key)}`);
451
+ }
452
+ const owner = parts[3];
453
+ const repo = parts[4];
454
+ const kind = parts[5];
455
+ const numberStr = parts[6];
456
+ if (kind !== "issue" && kind !== "pr") {
457
+ throw new Error(`context-key: malformed github kind "${kind}": ${JSON.stringify(key)}`);
458
+ }
459
+ const number2 = Number.parseInt(numberStr, 10);
460
+ if (!Number.isFinite(number2)) {
461
+ throw new Error(`context-key: malformed github number "${numberStr}": ${JSON.stringify(key)}`);
462
+ }
463
+ return {
464
+ family: "trackers",
465
+ subFamily: "github",
466
+ parts: { owner, repo, kind, number: number2 }
467
+ };
468
+ }
469
+ if (subFamily === "gitlab") {
470
+ if (parts.length !== 6) {
471
+ throw new Error(`context-key: malformed gitlab key: ${JSON.stringify(key)}`);
472
+ }
473
+ const projectId = parts[3];
474
+ const kind = parts[4];
475
+ const iidStr = parts[5];
476
+ if (kind !== "mr" && kind !== "issue") {
477
+ throw new Error(`context-key: malformed gitlab kind "${kind}": ${JSON.stringify(key)}`);
478
+ }
479
+ const iid = Number.parseInt(iidStr, 10);
480
+ if (!Number.isFinite(iid)) {
481
+ throw new Error(`context-key: malformed gitlab iid "${iidStr}": ${JSON.stringify(key)}`);
482
+ }
483
+ return {
484
+ family: "trackers",
485
+ subFamily: "gitlab",
486
+ parts: { projectId, kind, iid }
487
+ };
488
+ }
489
+ if (subFamily === "linear") {
490
+ if (parts.length !== 4) {
491
+ throw new Error(`context-key: malformed linear key: ${JSON.stringify(key)}`);
492
+ }
493
+ return {
494
+ family: "trackers",
495
+ subFamily: "linear",
496
+ parts: { issueIdentifier: parts[3] }
497
+ };
498
+ }
499
+ if (subFamily === "jira") {
500
+ if (parts.length !== 4) {
501
+ throw new Error(`context-key: malformed jira key: ${JSON.stringify(key)}`);
502
+ }
503
+ return {
504
+ family: "trackers",
505
+ subFamily: "jira",
506
+ parts: { issueIdentifier: parts[3] }
507
+ };
508
+ }
509
+ throw new Error(`context-key: unknown trackers sub-family "${subFamily}": ${JSON.stringify(key)}`);
510
+ }
511
+ default:
512
+ throw new Error(`context-key: unknown family "${family}": ${JSON.stringify(key)}`);
513
+ }
514
+ }
515
+ var SEPARATOR = ":";
516
+ var init_context_key = () => {};
517
+
518
+ // src/tasks/slack-routing.ts
519
+ function slackChannelFromContextKey(key) {
520
+ if (!key)
521
+ return null;
522
+ try {
523
+ const parsed = parseContextKey(key);
524
+ if (parsed.family !== "slack")
525
+ return null;
526
+ return { channelId: parsed.parts.channelId, threadTs: parsed.parts.threadTs };
527
+ } catch {
528
+ return null;
529
+ }
530
+ }
531
+ function checkSlackRoutingCoherence(input) {
532
+ const { explicit, parent, inheritedContextKey } = input;
533
+ const hasChannel = !!explicit.channelId;
534
+ const hasThread = !!explicit.threadTs;
535
+ if (hasChannel !== hasThread) {
536
+ return {
537
+ verdict: "partial-unit",
538
+ detail: hasChannel ? `slackChannelId "${explicit.channelId}" was passed without slackThreadTs — both are required together for Slack delivery to work.` : `slackThreadTs "${explicit.threadTs}" was passed without slackChannelId — both are required together for Slack delivery to work.`
539
+ };
540
+ }
541
+ if (!hasChannel) {
542
+ return { verdict: "ok" };
543
+ }
544
+ const channelId = explicit.channelId;
545
+ const threadTs = explicit.threadTs;
546
+ if (parent?.slackChannelId && channelId !== parent.slackChannelId) {
547
+ return {
548
+ verdict: "mismatch",
549
+ field: "slackChannelId",
550
+ expected: parent.slackChannelId,
551
+ expectedSource: "parent",
552
+ got: channelId,
553
+ detail: `explicit slackChannelId "${channelId}" does not match the parent task's slackChannelId "${parent.slackChannelId}".`
554
+ };
555
+ }
556
+ if (parent?.slackThreadTs && threadTs !== parent.slackThreadTs) {
557
+ return {
558
+ verdict: "mismatch",
559
+ field: "slackThreadTs",
560
+ expected: parent.slackThreadTs,
561
+ expectedSource: "parent",
562
+ got: threadTs,
563
+ detail: `explicit slackThreadTs "${threadTs}" does not match the parent task's slackThreadTs "${parent.slackThreadTs}" (same channel "${parent.slackChannelId}").`
564
+ };
565
+ }
566
+ const contextKeySlack = slackChannelFromContextKey(inheritedContextKey);
567
+ if (contextKeySlack) {
568
+ if (channelId !== contextKeySlack.channelId) {
569
+ return {
570
+ verdict: "mismatch",
571
+ field: "slackChannelId",
572
+ expected: contextKeySlack.channelId,
573
+ expectedSource: "contextKey",
574
+ got: channelId,
575
+ detail: `explicit slackChannelId "${channelId}" does not match the channel encoded in contextKey "${inheritedContextKey}" ("${contextKeySlack.channelId}").`
576
+ };
577
+ }
578
+ if (threadTs !== contextKeySlack.threadTs) {
579
+ return {
580
+ verdict: "mismatch",
581
+ field: "slackThreadTs",
582
+ expected: contextKeySlack.threadTs,
583
+ expectedSource: "contextKey",
584
+ got: threadTs,
585
+ detail: `explicit slackThreadTs "${threadTs}" does not match the thread encoded in contextKey "${inheritedContextKey}" ("${contextKeySlack.threadTs}").`
586
+ };
587
+ }
588
+ }
589
+ return { verdict: "ok" };
590
+ }
591
+ var init_slack_routing = __esm(() => {
592
+ init_context_key();
593
+ });
594
+
262
595
  // src/telemetry.ts
263
596
  import { randomUUID } from "node:crypto";
264
597
  function isEnabled() {
@@ -470,9 +803,49 @@ function resolveTaskModelSelection(opts) {
470
803
  function isTerminalTaskStatus(status) {
471
804
  return TERMINAL_TASK_STATUSES.includes(status);
472
805
  }
473
- var ModelTierSchema, MODEL_TIERS, ReasoningEffortSchema, LEGACY_MODEL_TO_TIER, DEFAULT_MODEL_TIER_MAP, AgentTaskStatusSchema, TERMINAL_TASK_STATUSES, InboxMessageStatusSchema, InboxMessageSchema, AgentTaskSourceSchema, ProviderNameSchema, FollowUpConfigSchema, RoutingAffinitySchema, AgentTaskSchema, TaskAttachmentKindSchema, attachmentCommonFields, AttachmentInputSchema, TaskAttachmentSchema, UserSchema, IdentityEventTypeSchema, InboxItemTypeSchema, InboxItemStatusSchema, InboxItemStateSchema, FavoriteItemTypeSchema, UserFavoriteSchema, TaskTemplateKindSchema, TaskTemplateSchema, AgentStatusSchema, AgentSchema, AgentCredStatusLiveTestSchema, AgentLatestModelSchema, AgentBedrockStatusSchema, AgentCredStatusSchema, AgentWithTasksSchema, ChangeSourceSchema, VersionableFieldSchema, ContextVersionSchema, ChannelTypeSchema, ChannelSchema, ChannelMessageSchema, ServiceStatusSchema, ServiceSchema, AgentLogEventTypeSchema, ResumeReasonSchema, AgentLogSchema, SessionLogSchema, SessionCostSourceSchema, SessionCostSchema, EventCategorySchema, EventStatusSchema, EventSourceSchema, EventNameSchema, SwarmEventSchema, ScheduledTaskTargetTypeSchema, ScheduledTaskSchema, SwarmConfigScopeSchema, SwarmConfigSchema, RepoGuidelinesSchema, RepoHooksSchema, SwarmRepoSchema, AgentMemoryScopeSchema, AgentMemorySourceSchema, AgentMemorySchema, ActiveSessionSchema, RetryPolicySchema, ExecutorMetaSchema, ValidationResultSchema, StepValidationConfigSchema, SwarmScriptNodeConfigSchema, WorkflowNodeSchema, WorkflowEdgeSchema, WorkflowDefinitionSchema, WorkflowNodePatchSchema, WorkflowPatchSchema, WebhookVerificationSchema, TriggerConfigSchema, CooldownConfigSchema, InputValueSchema, WorkflowTemplateSchema, WorkflowSnapshotSchema, WorkflowSchema, WorkflowVersionSchema, PageContentTypeSchema, PageAuthModeSchema, PageSnapshotSchema, PageSchema, PageVersionSchema, MetricParamSchema, MetricVariableTypeSchema, MetricFormatSchema, MetricVisualizationSchema, MetricQuerySchema, MetricVariableSchema, MetricVizConfigSchema, MetricWidgetSchema, MetricDefinitionSchema, MetricSnapshotSchema, MetricSchema, MetricVersionSchema, WorkflowRunStatusSchema, WorkflowRunSchema, ScriptRunStatusSchema, TERMINAL_SCRIPT_RUN_STATUSES, ScriptRunKindSchema, ScriptRunSchema, ScriptRunListItemSchema, ScriptRunJournalEntrySchema, WorkflowRunStepStatusSchema, WorkflowRunStepSchema, WaitModeSchema, WaitStateStatusSchema, WaitStateRowSchema, PromptTemplateScopeSchema, PromptTemplateStateSchema, PromptTemplateSchema, PromptTemplateHistorySchema, ScriptScopeSchema, ScriptFsModeSchema, ScriptRecordSchema, ScriptVersionRecordSchema, ScriptApiAuthModeSchema, ScriptApiRecordSchema, SkillTypeSchema, SkillScopeSchema, SkillSchema, AgentSkillSchema, SkillWithInstallInfoSchema, SkillFileSchema, McpServerTransportSchema, McpServerScopeSchema, McpAuthMethodSchema, McpServerSchema, AgentMcpServerSchema, McpServerWithInstallInfoSchema, ContextSnapshotEventTypeSchema, ContextFormulaSchema, ContextSnapshotSchema, BudgetScopeSchema, BudgetSchema, PricingProviderSchema, PricingTokenClassSchema, PricingRowSchema, BudgetRefusalCauseSchema, BudgetRefusalNotificationSchema, BudgetRefusedTriggerSchema, KvValueTypeSchema, KV_NAME_REGEX, KvNamespaceSchema, KvKeySchema, KvEntrySchema;
806
+ var AssetKeySchema, AssetEntityTypeSchema, AssetProviderRefSchema, AssetKeyMappingSchema, AssetSummarySchema, ModelTierSchema, MODEL_TIERS, ReasoningEffortSchema, LEGACY_MODEL_TO_TIER, DEFAULT_MODEL_TIER_MAP, AgentTaskStatusSchema, TERMINAL_TASK_STATUSES, InboxMessageStatusSchema, InboxMessageSchema, AgentTaskSourceSchema, ProviderNameSchema, FollowUpConfigSchema, RoutingAffinitySchema, AgentTaskSchema, TaskAttachmentKindSchema, attachmentCommonFields, AttachmentInputSchema, TaskAttachmentSchema, UserSchema, IdentityEventTypeSchema, InboxItemTypeSchema, InboxItemStatusSchema, InboxItemStateSchema, FavoriteItemTypeSchema, UserFavoriteSchema, TaskTemplateKindSchema, TaskTemplateSchema, AgentStatusSchema, AgentSchema, AgentCredStatusLiveTestSchema, AgentLatestModelSchema, AgentBedrockStatusSchema, AgentCredStatusSchema, AgentWithTasksSchema, ChangeSourceSchema, VersionableFieldSchema, ContextVersionSchema, ChannelTypeSchema, ChannelSchema, ChannelMessageSchema, ServiceStatusSchema, ServiceSchema, AgentLogEventTypeSchema, ResumeReasonSchema, AgentLogSchema, SessionLogSchema, SessionCostSourceSchema, SessionCostSchema, EventCategorySchema, EventStatusSchema, EventSourceSchema, EventNameSchema, SwarmEventSchema, ScheduledTaskTargetTypeSchema, ScheduledTaskSchema, SwarmConfigScopeSchema, SwarmConfigSchema, RepoGuidelinesSchema, RepoHooksSchema, SwarmRepoSchema, AgentMemoryScopeSchema, AgentMemorySourceSchema, AgentMemorySchema, ActiveSessionSchema, RetryPolicySchema, ExecutorMetaSchema, ValidationResultSchema, StepValidationConfigSchema, SwarmScriptNodeConfigSchema, WorkflowNodeSchema, WorkflowEdgeSchema, WorkflowDefinitionSchema, WorkflowNodePatchSchema, WorkflowPatchSchema, WebhookVerificationSchema, TriggerConfigSchema, CooldownConfigSchema, InputValueSchema, WorkflowTemplateSchema, WorkflowSnapshotSchema, WorkflowSchema, WorkflowVersionSchema, PageContentTypeSchema, PageAuthModeSchema, PageSnapshotSchema, PageSchema, PageVersionSchema, MetricParamSchema, MetricVariableTypeSchema, MetricFormatSchema, MetricVisualizationSchema, MetricQuerySchema, MetricVariableSchema, MetricVizConfigSchema, MetricWidgetSchema, MetricDefinitionSchema, MetricSnapshotSchema, MetricSchema, MetricVersionSchema, WorkflowRunStatusSchema, WorkflowRunSchema, ScriptRunStatusSchema, TERMINAL_SCRIPT_RUN_STATUSES, ScriptRunKindSchema, ScriptRunSchema, ScriptRunListItemSchema, ScriptRunJournalEntrySchema, WorkflowRunStepStatusSchema, WorkflowRunStepSchema, WaitModeSchema, WaitStateStatusSchema, WaitStateRowSchema, PromptTemplateScopeSchema, PromptTemplateStateSchema, PromptTemplateSchema, PromptTemplateHistorySchema, ScriptScopeSchema, ScriptFsModeSchema, ScriptRecordSchema, ScriptVersionRecordSchema, ScriptApiAuthModeSchema, ScriptApiRecordSchema, SkillTypeSchema, SkillScopeSchema, SkillSchema, AgentSkillSchema, SkillWithInstallInfoSchema, SkillFileSchema, McpServerTransportSchema, McpServerScopeSchema, McpAuthMethodSchema, McpServerSchema, AgentMcpServerSchema, McpServerWithInstallInfoSchema, ContextSnapshotEventTypeSchema, ContextFormulaSchema, ContextSnapshotSchema, BudgetScopeSchema, BudgetSchema, PricingProviderSchema, PricingTokenClassSchema, PricingRowSchema, BudgetRefusalCauseSchema, BudgetRefusalNotificationSchema, BudgetRefusedTriggerSchema, KvValueTypeSchema, KV_NAME_REGEX, KvNamespaceSchema, KvKeySchema, KvEntrySchema;
474
807
  var init_types = __esm(() => {
475
808
  init_zod();
809
+ init_key();
810
+ AssetKeySchema = string().min(1).max(255).superRefine((value, ctx) => {
811
+ try {
812
+ normalizeAssetKey(value);
813
+ } catch (error) {
814
+ ctx.addIssue({
815
+ code: "custom",
816
+ message: error instanceof Error ? error.message : "Invalid asset namespace key"
817
+ });
818
+ }
819
+ }).describe("Non-unique asset directory namespace (for example shared/ or personal/<user-id>/drafts/). Runtime write boundaries normalize and validate the canonical form.");
820
+ AssetEntityTypeSchema = _enum(["task", "workflow", "schedule", "page", "file"]);
821
+ AssetProviderRefSchema = object({
822
+ providerId: string(),
823
+ orgId: string().optional(),
824
+ driveId: string().optional(),
825
+ providerKey: string()
826
+ });
827
+ AssetKeyMappingSchema = object({
828
+ id: string(),
829
+ providerId: string(),
830
+ providerOrgId: string().optional(),
831
+ providerDriveId: string().optional(),
832
+ providerKey: string(),
833
+ key: AssetKeySchema,
834
+ sourceEntityType: _enum(["task-attachment", "external"]).optional(),
835
+ sourceEntityId: string().optional(),
836
+ createdAt: exports_iso.datetime(),
837
+ updatedAt: exports_iso.datetime(),
838
+ createdBy: string().optional(),
839
+ updatedBy: string().optional()
840
+ });
841
+ AssetSummarySchema = object({
842
+ entityType: AssetEntityTypeSchema,
843
+ id: string(),
844
+ key: AssetKeySchema,
845
+ label: string(),
846
+ updatedAt: string(),
847
+ providerRef: AssetProviderRefSchema.optional()
848
+ });
476
849
  ModelTierSchema = _enum(["smol", "regular", "smart", "ultra"]);
477
850
  MODEL_TIERS = ModelTierSchema.options;
478
851
  ReasoningEffortSchema = _enum(["off", "low", "medium", "high", "xhigh", "max"]);
@@ -591,6 +964,7 @@ var init_types = __esm(() => {
591
964
  });
592
965
  AgentTaskSchema = object({
593
966
  id: uuid(),
967
+ key: AssetKeySchema,
594
968
  agentId: uuid().nullable(),
595
969
  creatorAgentId: uuid().optional(),
596
970
  task: string().min(1),
@@ -771,7 +1145,7 @@ var init_types = __esm(() => {
771
1145
  FavoriteItemTypeSchema = _enum(["page", "workflow", "schedule"]);
772
1146
  UserFavoriteSchema = object({
773
1147
  id: string(),
774
- userId: string(),
1148
+ userId: string().optional(),
775
1149
  itemType: FavoriteItemTypeSchema,
776
1150
  itemId: string(),
777
1151
  createdAt: string(),
@@ -1042,6 +1416,7 @@ var init_types = __esm(() => {
1042
1416
  ScheduledTaskTargetTypeSchema = _enum(["agent-task", "workflow", "script"]);
1043
1417
  ScheduledTaskSchema = object({
1044
1418
  id: uuid(),
1419
+ key: AssetKeySchema,
1045
1420
  name: string().min(1).max(100),
1046
1421
  description: string().optional(),
1047
1422
  cronExpression: string().optional(),
@@ -1310,6 +1685,7 @@ var init_types = __esm(() => {
1310
1685
  });
1311
1686
  WorkflowSchema = object({
1312
1687
  id: string().uuid(),
1688
+ key: AssetKeySchema,
1313
1689
  name: string(),
1314
1690
  description: string().optional(),
1315
1691
  enabled: boolean(),
@@ -1348,6 +1724,7 @@ var init_types = __esm(() => {
1348
1724
  });
1349
1725
  PageSchema = object({
1350
1726
  id: string(),
1727
+ key: AssetKeySchema,
1351
1728
  agentId: string(),
1352
1729
  slug: string(),
1353
1730
  title: string(),
@@ -1854,6 +2231,153 @@ var init_request_auth_context = __esm(() => {
1854
2231
  authStorage = new AsyncLocalStorage;
1855
2232
  });
1856
2233
 
2234
+ // src/be/asset-key-audit.ts
2235
+ function auditKeyRow(db, entityType, row) {
2236
+ if (!row.key) {
2237
+ return [
2238
+ {
2239
+ severity: "fatal",
2240
+ code: "missing-key",
2241
+ entityType,
2242
+ entityId: row.id,
2243
+ message: "Asset key is missing or empty."
2244
+ }
2245
+ ];
2246
+ }
2247
+ if (!isCanonicalAssetKey(row.key)) {
2248
+ return [
2249
+ {
2250
+ severity: "fatal",
2251
+ code: "noncanonical-key",
2252
+ entityType,
2253
+ entityId: row.id,
2254
+ message: "Asset key is not in canonical v1 form."
2255
+ }
2256
+ ];
2257
+ }
2258
+ const parsed = parseAssetKey(row.key);
2259
+ if (parsed.root === "personal") {
2260
+ const user = db.prepare("SELECT EXISTS(SELECT 1 FROM users WHERE id = ?) AS present").get(parsed.userId);
2261
+ if (!user?.present) {
2262
+ return [
2263
+ {
2264
+ severity: "warning",
2265
+ code: "unknown-personal-user",
2266
+ entityType,
2267
+ entityId: row.id,
2268
+ message: "Personal namespace references a user that no longer exists."
2269
+ }
2270
+ ];
2271
+ }
2272
+ }
2273
+ return [];
2274
+ }
2275
+ function auditAssetKeys(db) {
2276
+ const issues = [];
2277
+ let checked = 0;
2278
+ for (const table of KEY_TABLES) {
2279
+ const rows = db.prepare(table.sql).all();
2280
+ checked += rows.length;
2281
+ for (const row of rows)
2282
+ issues.push(...auditKeyRow(db, table.entityType, row));
2283
+ }
2284
+ const attachmentMappings = db.prepare(`SELECT
2285
+ a.id AS attachment_id,
2286
+ t."key" AS task_key,
2287
+ m.id AS mapping_id,
2288
+ m."key" AS mapping_key
2289
+ FROM task_attachments a
2290
+ JOIN agent_tasks t ON t.id = a.task_id
2291
+ LEFT JOIN asset_key_mappings m
2292
+ ON m.provider_id = COALESCE(NULLIF(a.provider_id, ''), 'agent-fs')
2293
+ AND m.provider_org_id = COALESCE(a.agent_fs_org_id, '')
2294
+ AND m.provider_drive_id = COALESCE(a.agent_fs_drive_id, '')
2295
+ AND m.provider_key = COALESCE(NULLIF(a.provider_key, ''), a.path)
2296
+ WHERE a.kind = 'agent-fs'
2297
+ AND COALESCE(NULLIF(a.provider_key, ''), a.path) IS NOT NULL`).all();
2298
+ for (const row of attachmentMappings) {
2299
+ if (!row.mapping_id) {
2300
+ issues.push({
2301
+ severity: "warning",
2302
+ code: "missing-provider-mapping",
2303
+ entityType: "file",
2304
+ entityId: row.attachment_id,
2305
+ message: "Agent-fs attachment has no logical namespace mapping."
2306
+ });
2307
+ } else if (row.mapping_key !== row.task_key) {
2308
+ issues.push({
2309
+ severity: "warning",
2310
+ code: "provider-mapping-drift",
2311
+ entityType: "file",
2312
+ entityId: row.mapping_id,
2313
+ message: "Provider mapping namespace differs from its parent task namespace."
2314
+ });
2315
+ }
2316
+ }
2317
+ const sourcedMappings = db.prepare(`SELECT
2318
+ m.id AS mapping_id,
2319
+ m.source_entity_id,
2320
+ a.id AS attachment_id,
2321
+ t."key" AS task_key,
2322
+ m."key" AS mapping_key,
2323
+ CASE WHEN a.id IS NOT NULL
2324
+ AND m.provider_id = COALESCE(NULLIF(a.provider_id, ''), 'agent-fs')
2325
+ AND m.provider_org_id = COALESCE(a.agent_fs_org_id, '')
2326
+ AND m.provider_drive_id = COALESCE(a.agent_fs_drive_id, '')
2327
+ AND m.provider_key = COALESCE(NULLIF(a.provider_key, ''), a.path)
2328
+ THEN 1 ELSE 0 END AS tuple_matches
2329
+ FROM asset_key_mappings m
2330
+ LEFT JOIN task_attachments a ON a.id = m.source_entity_id
2331
+ LEFT JOIN agent_tasks t ON t.id = a.task_id
2332
+ WHERE m.source_entity_type = 'task-attachment'`).all();
2333
+ for (const row of sourcedMappings) {
2334
+ if (!row.attachment_id || row.tuple_matches !== 1 || row.task_key !== row.mapping_key) {
2335
+ issues.push({
2336
+ severity: "warning",
2337
+ code: "provider-mapping-drift",
2338
+ entityType: "file",
2339
+ entityId: row.mapping_id,
2340
+ message: "Provider mapping source or tuple has drifted from its attachment."
2341
+ });
2342
+ }
2343
+ }
2344
+ const fatalCount = issues.filter((issue) => issue.severity === "fatal").length;
2345
+ const warningCount = issues.length - fatalCount;
2346
+ return {
2347
+ ok: issues.length === 0,
2348
+ structuralValid: fatalCount === 0,
2349
+ checked,
2350
+ fatalCount,
2351
+ warningCount,
2352
+ issues
2353
+ };
2354
+ }
2355
+ function enforceAssetKeyStartupAudit(db) {
2356
+ const result = auditAssetKeys(db);
2357
+ if (result.warningCount > 0) {
2358
+ console.warn(`[asset-keys] Startup audit found ${result.warningCount} repairable warning(s)`);
2359
+ }
2360
+ if (result.structuralValid)
2361
+ return result;
2362
+ const message = `[asset-keys] Startup audit found ${result.fatalCount} structurally invalid row(s)`;
2363
+ if (process.env.ASSET_KEY_AUDIT_DISABLE_STARTUP_HARD_FAIL === "true") {
2364
+ console.warn(`${message}; hard-fail temporarily disabled`);
2365
+ return result;
2366
+ }
2367
+ throw new Error(message);
2368
+ }
2369
+ var KEY_TABLES;
2370
+ var init_asset_key_audit = __esm(() => {
2371
+ init_key();
2372
+ KEY_TABLES = [
2373
+ { entityType: "task", sql: 'SELECT id, "key" as key FROM agent_tasks' },
2374
+ { entityType: "workflow", sql: 'SELECT id, "key" as key FROM workflows' },
2375
+ { entityType: "schedule", sql: 'SELECT id, "key" as key FROM scheduled_tasks' },
2376
+ { entityType: "page", sql: 'SELECT id, "key" as key FROM pages' },
2377
+ { entityType: "file", sql: 'SELECT id, "key" as key FROM asset_key_mappings' }
2378
+ ];
2379
+ });
2380
+
1857
2381
  // src/be/crypto/secrets-cipher.ts
1858
2382
  import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
1859
2383
  function assertKey(key) {
@@ -3580,6 +4104,7 @@ __export(exports_db, {
3580
4104
  upsertInboxState: () => upsertInboxState,
3581
4105
  upsertChannelActivityCursor: () => upsertChannelActivityCursor,
3582
4106
  upsertBudget: () => upsertBudget,
4107
+ upsertAssetKeyMapping: () => upsertAssetKeyMapping,
3583
4108
  updateWorkflowRunStep: () => updateWorkflowRunStep,
3584
4109
  updateWorkflowRun: () => updateWorkflowRun,
3585
4110
  updateWorkflow: () => updateWorkflow,
@@ -3616,6 +4141,7 @@ __export(exports_db, {
3616
4141
  shouldBlockPolling: () => shouldBlockPolling,
3617
4142
  setUserFavorite: () => setUserFavorite,
3618
4143
  setSlackMessageTracking: () => setSlackMessageTracking,
4144
+ setFavorite: () => setFavorite,
3619
4145
  setBudgetRefusalFollowUpTaskId: () => setBudgetRefusalFollowUpTaskId,
3620
4146
  setApiKeyName: () => setApiKeyName,
3621
4147
  setAgentHarnessProvider: () => setAgentHarnessProvider,
@@ -3646,6 +4172,7 @@ __export(exports_db, {
3646
4172
  normalizeSkillFilePath: () => normalizeSkillFilePath,
3647
4173
  moveTaskToBacklog: () => moveTaskToBacklog,
3648
4174
  moveTaskFromBacklog: () => moveTaskFromBacklog,
4175
+ moveAssetKey: () => moveAssetKey,
3649
4176
  maskSecrets: () => maskSecrets,
3650
4177
  markTasksNotified: () => markTasksNotified,
3651
4178
  markTaskSlackReplySent: () => markTaskSlackReplySent,
@@ -3668,6 +4195,8 @@ __export(exports_db, {
3668
4195
  listMcpServers: () => listMcpServers,
3669
4196
  listKv: () => listKv,
3670
4197
  listInboxState: () => listInboxState,
4198
+ listFavorites: () => listFavorites,
4199
+ listAssetSummaries: () => listAssetSummaries,
3671
4200
  listApprovalRequests: () => listApprovalRequests,
3672
4201
  listAllPages: () => listAllPages,
3673
4202
  listAllMetrics: () => listAllMetrics,
@@ -3835,6 +4364,8 @@ __export(exports_db, {
3835
4364
  getBudgetRefusalNotification: () => getBudgetRefusalNotification,
3836
4365
  getBudget: () => getBudget,
3837
4366
  getAvailableKeyIndices: () => getAvailableKeyIndices,
4367
+ getAssetKeyMappingByProvider: () => getAssetKeyMappingByProvider,
4368
+ getAssetKeyMapping: () => getAssetKeyMapping,
3838
4369
  getApprovalRequestByStepId: () => getApprovalRequestByStepId,
3839
4370
  getApprovalRequestById: () => getApprovalRequestById,
3840
4371
  getAllUsers: () => getAllUsers,
@@ -3961,6 +4492,10 @@ __export(exports_db, {
3961
4492
  KvTypeCollisionError: () => KvTypeCollisionError
3962
4493
  });
3963
4494
  import { Database } from "bun:sqlite";
4495
+ function assetKeyPrefixPattern(input) {
4496
+ const canonical = normalizeAssetKey(input);
4497
+ return `${canonical.replace(/[\\%_]/g, "\\$&")}%`;
4498
+ }
3964
4499
  function emitTaskLifecycleTelemetryAfterCommit(event, props, verify) {
3965
4500
  queueMicrotask(() => {
3966
4501
  if (verify && !verify(getTaskById(props.taskId)))
@@ -4011,6 +4546,7 @@ function initDb(dbPath = "./agent-swarm-db.sqlite") {
4011
4546
  db.run("PRAGMA foreign_keys = ON;");
4012
4547
  loadSqliteVec(db);
4013
4548
  configureDbResolver(resolvePromptTemplate);
4549
+ enforceAssetKeyStartupAudit(db);
4014
4550
  resolveEncryptionKey(dbPath);
4015
4551
  return db;
4016
4552
  }
@@ -4036,81 +4572,25 @@ function initDb(dbPath = "./agent-swarm-db.sqlite") {
4036
4572
  if (needsStatusMigration) {
4037
4573
  console.log("[Migration] Removing restrictive CHECK constraint on agent_tasks.status");
4038
4574
  db.run("PRAGMA foreign_keys=off");
4039
- db.run(`
4040
- CREATE TABLE agent_tasks_new (
4041
- id TEXT PRIMARY KEY,
4042
- agentId TEXT,
4043
- creatorAgentId TEXT,
4044
- task TEXT NOT NULL,
4045
- status TEXT NOT NULL DEFAULT 'pending',
4046
- source TEXT NOT NULL DEFAULT 'mcp' CHECK(source IN ('mcp', 'slack', 'api', 'github', 'agentmail', 'system', 'schedule')),
4047
- taskType TEXT,
4048
- tags TEXT DEFAULT '[]',
4049
- priority INTEGER DEFAULT 50,
4050
- dependsOn TEXT DEFAULT '[]',
4051
- offeredTo TEXT,
4052
- offeredAt TEXT,
4053
- acceptedAt TEXT,
4054
- rejectionReason TEXT,
4055
- slackChannelId TEXT,
4056
- slackThreadTs TEXT,
4057
- slackUserId TEXT,
4058
- createdAt TEXT NOT NULL,
4059
- lastUpdatedAt TEXT NOT NULL,
4060
- finishedAt TEXT,
4061
- failureReason TEXT,
4062
- output TEXT,
4063
- progress TEXT,
4064
- notifiedAt TEXT,
4065
- mentionMessageId TEXT,
4066
- mentionChannelId TEXT,
4067
- githubRepo TEXT,
4068
- githubEventType TEXT,
4069
- githubNumber INTEGER,
4070
- githubCommentId INTEGER,
4071
- githubAuthor TEXT,
4072
- githubUrl TEXT,
4073
- parentTaskId TEXT,
4074
- claudeSessionId TEXT,
4075
- agentmailInboxId TEXT,
4076
- agentmailMessageId TEXT,
4077
- agentmailThreadId TEXT,
4078
- model TEXT,
4079
- scheduleId TEXT
4080
- )
4081
- `);
4082
- db.run(`
4083
- INSERT INTO agent_tasks_new (
4084
- id, agentId, creatorAgentId, task, status, source, taskType, tags,
4085
- priority, dependsOn, offeredTo, offeredAt, acceptedAt, rejectionReason,
4086
- slackChannelId, slackThreadTs, slackUserId, createdAt, lastUpdatedAt,
4087
- finishedAt, failureReason, output, progress, notifiedAt,
4088
- mentionMessageId, mentionChannelId, githubRepo, githubEventType,
4089
- githubNumber, githubCommentId, githubAuthor, githubUrl,
4090
- parentTaskId, claudeSessionId,
4091
- agentmailInboxId, agentmailMessageId, agentmailThreadId,
4092
- model, scheduleId
4093
- )
4094
- SELECT
4095
- id, agentId, creatorAgentId, task, status, source, taskType, tags,
4096
- priority, dependsOn, offeredTo, offeredAt, acceptedAt, rejectionReason,
4097
- slackChannelId, slackThreadTs, slackUserId, createdAt, lastUpdatedAt,
4098
- finishedAt, failureReason, output, progress, notifiedAt,
4099
- mentionMessageId, mentionChannelId, githubRepo, githubEventType,
4100
- githubNumber, githubCommentId, githubAuthor, githubUrl,
4101
- parentTaskId, claudeSessionId,
4102
- agentmailInboxId, agentmailMessageId, agentmailThreadId,
4103
- model, scheduleId
4104
- FROM agent_tasks
4105
- `);
4106
- db.run("DROP TABLE agent_tasks");
4107
- db.run("ALTER TABLE agent_tasks_new RENAME TO agent_tasks");
4108
- db.run("CREATE INDEX IF NOT EXISTS idx_agent_tasks_agentId ON agent_tasks(agentId)");
4109
- db.run("CREATE INDEX IF NOT EXISTS idx_agent_tasks_status ON agent_tasks(status)");
4110
- db.run("CREATE INDEX IF NOT EXISTS idx_agent_tasks_offeredTo ON agent_tasks(offeredTo)");
4111
- db.run("CREATE INDEX IF NOT EXISTS idx_agent_tasks_taskType ON agent_tasks(taskType)");
4112
- db.run("CREATE INDEX IF NOT EXISTS idx_agent_tasks_agentmailThreadId ON agent_tasks(agentmailThreadId)");
4113
- db.run("CREATE INDEX IF NOT EXISTS idx_agent_tasks_schedule_id ON agent_tasks(scheduleId)");
4575
+ const rebuiltSchemaSql = schemaSql.replace(/^(CREATE TABLE\s+(?:IF NOT EXISTS\s+)?)(?:"agent_tasks"|agent_tasks)/i, "$1agent_tasks_new").replace(/\s+CHECK\s*\(\s*status\s+IN\s*\([^)]*\)\s*\)/i, "");
4576
+ if (rebuiltSchemaSql === schemaSql || !/agent_tasks_new/i.test(rebuiltSchemaSql)) {
4577
+ throw new Error("Could not derive agent_tasks schema without legacy status CHECK");
4578
+ }
4579
+ const columns = db.prepare('PRAGMA table_info("agent_tasks")').all().map((column) => `"${column.name.replaceAll('"', '""')}"`).join(", ");
4580
+ const schemaObjects = db.prepare(`SELECT sql FROM sqlite_master
4581
+ WHERE tbl_name = 'agent_tasks'
4582
+ AND type IN ('index', 'trigger')
4583
+ AND sql IS NOT NULL
4584
+ ORDER BY type, name`).all().map((row) => row.sql);
4585
+ database.transaction(() => {
4586
+ database.run("DROP TABLE IF EXISTS agent_tasks_new");
4587
+ database.run(rebuiltSchemaSql);
4588
+ database.run(`INSERT INTO agent_tasks_new (${columns}) SELECT ${columns} FROM agent_tasks`);
4589
+ database.run("DROP TABLE agent_tasks");
4590
+ database.run("ALTER TABLE agent_tasks_new RENAME TO agent_tasks");
4591
+ for (const sql of schemaObjects)
4592
+ database.run(sql);
4593
+ })();
4114
4594
  db.run("PRAGMA foreign_keys=on");
4115
4595
  console.log("[Migration] Successfully removed CHECK constraint on agent_tasks.status");
4116
4596
  }
@@ -4123,6 +4603,7 @@ function initDb(dbPath = "./agent-swarm-db.sqlite") {
4123
4603
  }
4124
4604
  throw e;
4125
4605
  }
4606
+ enforceAssetKeyStartupAudit(database);
4126
4607
  seedContextVersions();
4127
4608
  configureDbResolver(resolvePromptTemplate);
4128
4609
  seedDefaultTemplates();
@@ -4468,6 +4949,7 @@ function rowToAgentTask(row) {
4468
4949
  }
4469
4950
  return {
4470
4951
  id: row.id,
4952
+ key: row.key,
4471
4953
  agentId: row.agentId,
4472
4954
  creatorAgentId: row.creatorAgentId ?? undefined,
4473
4955
  task: row.task,
@@ -4541,6 +5023,7 @@ function rowToAgentTaskSummary(row) {
4541
5023
  const t = rowToAgentTask(row);
4542
5024
  return {
4543
5025
  id: t.id,
5026
+ key: t.key,
4544
5027
  agentId: t.agentId,
4545
5028
  creatorAgentId: t.creatorAgentId,
4546
5029
  task: previewText(t.task, TASK_PREVIEW_LENGTH),
@@ -4570,7 +5053,7 @@ function rowToAgentTaskSummary(row) {
4570
5053
  function createTask(agentId, task, options) {
4571
5054
  const id = crypto.randomUUID();
4572
5055
  const source2 = options?.source ?? "mcp";
4573
- const row = taskQueries.insert().get(id, agentId, task, "pending", source2, options?.slackChannelId ?? null, options?.slackThreadTs ?? null, options?.slackUserId ?? null, package_default.version);
5056
+ const row = taskQueries.insert().get(id, defaultAssetKey("task", id), agentId, task, "pending", source2, options?.slackChannelId ?? null, options?.slackThreadTs ?? null, options?.slackUserId ?? null, package_default.version);
4574
5057
  if (!row)
4575
5058
  throw new Error("Failed to create task");
4576
5059
  try {
@@ -4799,6 +5282,13 @@ function getAllTasks(filters, opts) {
4799
5282
  conditions.push("scheduleId = ?");
4800
5283
  params.push(filters.scheduleId);
4801
5284
  }
5285
+ if (filters?.key) {
5286
+ conditions.push('"key" = ?');
5287
+ params.push(normalizeAssetKey(filters.key));
5288
+ } else if (filters?.keyPrefix) {
5289
+ conditions.push(`"key" LIKE ? ESCAPE '\\'`);
5290
+ params.push(assetKeyPrefixPattern(filters.keyPrefix));
5291
+ }
4802
5292
  if (filters?.source && filters.source.length > 0) {
4803
5293
  const placeholders = filters.source.map(() => "?").join(", ");
4804
5294
  conditions.push(`source IN (${placeholders})`);
@@ -4894,6 +5384,13 @@ function getTasksCount(filters) {
4894
5384
  conditions.push("scheduleId = ?");
4895
5385
  params.push(filters.scheduleId);
4896
5386
  }
5387
+ if (filters?.key) {
5388
+ conditions.push('"key" = ?');
5389
+ params.push(normalizeAssetKey(filters.key));
5390
+ } else if (filters?.keyPrefix) {
5391
+ conditions.push(`"key" LIKE ? ESCAPE '\\'`);
5392
+ params.push(assetKeyPrefixPattern(filters.keyPrefix));
5393
+ }
4897
5394
  if (filters?.source && filters.source.length > 0) {
4898
5395
  const placeholders = filters.source.map(() => "?").join(", ");
4899
5396
  conditions.push(`source IN (${placeholders})`);
@@ -5481,6 +5978,28 @@ function stringifyAttachmentCapabilities(value) {
5481
5978
  return value ? JSON.stringify(value) : null;
5482
5979
  }
5483
5980
  function insertTaskAttachment(input) {
5981
+ return getDb().transaction(() => {
5982
+ const attachment = insertTaskAttachmentRow(input);
5983
+ if (attachment.kind === "agent-fs" && attachment.providerId && attachment.providerKey) {
5984
+ const task = getTaskById(input.taskId);
5985
+ if (!task)
5986
+ throw new Error(`Task not found while mapping attachment: ${input.taskId}`);
5987
+ upsertAssetKeyMapping({
5988
+ providerId: attachment.providerId,
5989
+ providerOrgId: attachment.orgId,
5990
+ providerDriveId: attachment.driveId,
5991
+ providerKey: attachment.providerKey,
5992
+ key: task.key,
5993
+ sourceEntityType: "task-attachment",
5994
+ sourceEntityId: attachment.id,
5995
+ createdBy: input.createdBy,
5996
+ updatedBy: input.updatedBy
5997
+ });
5998
+ }
5999
+ return attachment;
6000
+ })();
6001
+ }
6002
+ function insertTaskAttachmentRow(input) {
5484
6003
  const db2 = getDb();
5485
6004
  if (input.sha256) {
5486
6005
  const existing = db2.prepare("SELECT * FROM task_attachments WHERE task_id = ? AND sha256 = ? LIMIT 1").get(input.taskId, input.sha256);
@@ -5526,6 +6045,249 @@ function defaultProviderKey(input) {
5526
6045
  return input.pageId ?? null;
5527
6046
  return null;
5528
6047
  }
6048
+ function rowToAssetKeyMapping(row) {
6049
+ return {
6050
+ id: row.id,
6051
+ providerId: row.provider_id,
6052
+ providerOrgId: row.provider_org_id || undefined,
6053
+ providerDriveId: row.provider_drive_id || undefined,
6054
+ providerKey: row.provider_key,
6055
+ key: row.key,
6056
+ sourceEntityType: row.source_entity_type ?? undefined,
6057
+ sourceEntityId: row.source_entity_id ?? undefined,
6058
+ createdAt: row.created_at,
6059
+ updatedAt: row.updated_at,
6060
+ createdBy: row.created_by ?? undefined,
6061
+ updatedBy: row.updated_by ?? undefined
6062
+ };
6063
+ }
6064
+ function insertAssetKeyHistory(input) {
6065
+ getDb().run(`INSERT INTO asset_key_history
6066
+ (id, entity_type, entity_id, previous_key, new_key, changed_by)
6067
+ VALUES (?, ?, ?, ?, ?, ?)`, [
6068
+ crypto.randomUUID(),
6069
+ input.entityType,
6070
+ input.entityId,
6071
+ input.previousKey ?? null,
6072
+ input.newKey,
6073
+ input.changedBy ?? null
6074
+ ]);
6075
+ }
6076
+ function getAssetKeyMappingByProvider(input) {
6077
+ const row = getDb().prepare(`SELECT * FROM asset_key_mappings
6078
+ WHERE provider_id = ? AND provider_org_id = ?
6079
+ AND provider_drive_id = ? AND provider_key = ?`).get(input.providerId, input.providerOrgId ?? "", input.providerDriveId ?? "", input.providerKey);
6080
+ return row ? rowToAssetKeyMapping(row) : null;
6081
+ }
6082
+ function upsertAssetKeyMapping(input) {
6083
+ if (!input.providerId.trim())
6084
+ throw new Error("providerId is required");
6085
+ if (!input.providerKey.trim())
6086
+ throw new Error("providerKey is required");
6087
+ const now = new Date().toISOString();
6088
+ return getDb().transaction(() => {
6089
+ const existing = getAssetKeyMappingByProvider(input);
6090
+ if (existing) {
6091
+ const key2 = normalizeAssetKey(input.key ?? existing.key);
6092
+ const row2 = getDb().prepare(`UPDATE asset_key_mappings
6093
+ SET "key" = ?, source_entity_type = ?, source_entity_id = ?,
6094
+ updated_at = ?, updated_by = ?
6095
+ WHERE id = ? RETURNING *`).get(key2, input.sourceEntityType ?? existing.sourceEntityType ?? null, input.sourceEntityId ?? existing.sourceEntityId ?? null, now, input.updatedBy ?? input.createdBy ?? existing.updatedBy ?? null, existing.id);
6096
+ if (!row2)
6097
+ throw new Error("Failed to update asset key mapping");
6098
+ if (existing.key !== key2) {
6099
+ insertAssetKeyHistory({
6100
+ entityType: "file",
6101
+ entityId: existing.id,
6102
+ previousKey: existing.key,
6103
+ newKey: key2,
6104
+ changedBy: input.updatedBy ?? input.createdBy
6105
+ });
6106
+ }
6107
+ return rowToAssetKeyMapping(row2);
6108
+ }
6109
+ const id = crypto.randomUUID();
6110
+ const key = normalizeAssetKey(input.key ?? defaultAssetKey(`fs:${input.providerId.trim()}`, id));
6111
+ const row = getDb().prepare(`INSERT INTO asset_key_mappings (
6112
+ id, provider_id, provider_org_id, provider_drive_id, provider_key,
6113
+ "key", source_entity_type, source_entity_id,
6114
+ created_at, updated_at, created_by, updated_by
6115
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(id, input.providerId.trim(), input.providerOrgId ?? "", input.providerDriveId ?? "", input.providerKey, key, input.sourceEntityType ?? "external", input.sourceEntityId ?? null, now, now, input.createdBy ?? null, input.updatedBy ?? input.createdBy ?? null);
6116
+ if (!row)
6117
+ throw new Error("Failed to create asset key mapping");
6118
+ insertAssetKeyHistory({
6119
+ entityType: "file",
6120
+ entityId: id,
6121
+ newKey: key,
6122
+ changedBy: input.createdBy
6123
+ });
6124
+ return rowToAssetKeyMapping(row);
6125
+ })();
6126
+ }
6127
+ function getAssetKeyMapping(id) {
6128
+ const row = getDb().prepare("SELECT * FROM asset_key_mappings WHERE id = ?").get(id);
6129
+ return row ? rowToAssetKeyMapping(row) : null;
6130
+ }
6131
+ function assetSummaryQueries(types) {
6132
+ const queries = [];
6133
+ if (types.has("task")) {
6134
+ queries.push(`SELECT 'task' AS entityType, id, "key", 'Task ' || substr(id, 1, 8) AS label,
6135
+ lastUpdatedAt AS updatedAt, NULL AS providerId, NULL AS providerOrgId,
6136
+ NULL AS providerDriveId, NULL AS providerKey
6137
+ FROM agent_tasks`);
6138
+ }
6139
+ if (types.has("workflow")) {
6140
+ queries.push(`SELECT 'workflow' AS entityType, id, "key", name AS label,
6141
+ lastUpdatedAt AS updatedAt, NULL AS providerId, NULL AS providerOrgId,
6142
+ NULL AS providerDriveId, NULL AS providerKey
6143
+ FROM workflows`);
6144
+ }
6145
+ if (types.has("schedule")) {
6146
+ queries.push(`SELECT 'schedule' AS entityType, id, "key", name AS label,
6147
+ lastUpdatedAt AS updatedAt, NULL AS providerId, NULL AS providerOrgId,
6148
+ NULL AS providerDriveId, NULL AS providerKey
6149
+ FROM scheduled_tasks`);
6150
+ }
6151
+ if (types.has("page")) {
6152
+ queries.push(`SELECT 'page' AS entityType, id, "key", title AS label,
6153
+ updatedAt, NULL AS providerId, NULL AS providerOrgId,
6154
+ NULL AS providerDriveId, NULL AS providerKey
6155
+ FROM pages`);
6156
+ }
6157
+ if (types.has("file")) {
6158
+ queries.push(`SELECT 'file' AS entityType, id, "key", provider_key AS label,
6159
+ updated_at AS updatedAt, provider_id AS providerId,
6160
+ NULLIF(provider_org_id, '') AS providerOrgId,
6161
+ NULLIF(provider_drive_id, '') AS providerDriveId,
6162
+ provider_key AS providerKey
6163
+ FROM asset_key_mappings`);
6164
+ }
6165
+ return queries;
6166
+ }
6167
+ function listAssetSummaries(filters) {
6168
+ const requestedTypes = new Set(filters?.types?.length ? filters.types : ["task", "workflow", "schedule", "page", "file"]);
6169
+ const queries = assetSummaryQueries(requestedTypes);
6170
+ if (queries.length === 0)
6171
+ return [];
6172
+ const limit = Math.min(Math.max(filters?.limit ?? 500, 1), 1000);
6173
+ const params = [];
6174
+ let query = `SELECT * FROM (${queries.join(" UNION ALL ")})`;
6175
+ if (filters?.keyPrefix) {
6176
+ query += ` WHERE "key" LIKE ? ESCAPE '\\'`;
6177
+ params.push(assetKeyPrefixPattern(filters.keyPrefix));
6178
+ }
6179
+ query += ' ORDER BY "key" ASC, updatedAt DESC LIMIT ?';
6180
+ params.push(limit);
6181
+ const rows = getDb().prepare(query).all(...params);
6182
+ return rows.map((row) => ({
6183
+ entityType: row.entityType,
6184
+ id: row.id,
6185
+ key: row.key,
6186
+ label: row.label,
6187
+ updatedAt: row.updatedAt,
6188
+ providerRef: row.providerId && row.providerKey ? {
6189
+ providerId: row.providerId,
6190
+ orgId: row.providerOrgId ?? undefined,
6191
+ driveId: row.providerDriveId ?? undefined,
6192
+ providerKey: row.providerKey
6193
+ } : undefined
6194
+ }));
6195
+ }
6196
+ function currentAssetKey(entityType, id) {
6197
+ switch (entityType) {
6198
+ case "task":
6199
+ return getDb().prepare('SELECT "key" AS key FROM agent_tasks WHERE id = ?').get(id)?.key ?? null;
6200
+ case "workflow":
6201
+ return getDb().prepare('SELECT "key" AS key FROM workflows WHERE id = ?').get(id)?.key ?? null;
6202
+ case "schedule":
6203
+ return getDb().prepare('SELECT "key" AS key FROM scheduled_tasks WHERE id = ?').get(id)?.key ?? null;
6204
+ case "page":
6205
+ return getDb().prepare('SELECT "key" AS key FROM pages WHERE id = ?').get(id)?.key ?? null;
6206
+ case "file":
6207
+ return getAssetKeyMapping(id)?.key ?? null;
6208
+ }
6209
+ }
6210
+ function moveAssetKey(input) {
6211
+ const key = normalizeAssetKey(input.key);
6212
+ const audit = auditAssetKeys(getDb());
6213
+ if (!audit.ok) {
6214
+ throw new Error("Asset namespace moves are blocked until structural errors, unknown personal users, or provider mapping drift are repaired.");
6215
+ }
6216
+ const previousKey = currentAssetKey(input.entityType, input.id);
6217
+ if (!previousKey)
6218
+ return false;
6219
+ if (previousKey === key)
6220
+ return true;
6221
+ if (input.entityType === "file") {
6222
+ const mapping = getAssetKeyMapping(input.id);
6223
+ if (mapping?.sourceEntityType === "task-attachment") {
6224
+ throw new Error("Task attachment namespaces move with their parent task; move the task instead.");
6225
+ }
6226
+ }
6227
+ const now = new Date().toISOString();
6228
+ getDb().transaction(() => {
6229
+ switch (input.entityType) {
6230
+ case "task": {
6231
+ const mappedFiles = getDb().prepare(`SELECT DISTINCT m.id, m."key" AS key
6232
+ FROM asset_key_mappings m
6233
+ JOIN task_attachments a
6234
+ ON m.provider_id = COALESCE(NULLIF(a.provider_id, ''), 'agent-fs')
6235
+ AND m.provider_org_id = COALESCE(a.agent_fs_org_id, '')
6236
+ AND m.provider_drive_id = COALESCE(a.agent_fs_drive_id, '')
6237
+ AND m.provider_key = COALESCE(NULLIF(a.provider_key, ''), a.path)
6238
+ WHERE a.task_id = ?`).all(input.id);
6239
+ getDb().run('UPDATE agent_tasks SET "key" = ?, lastUpdatedAt = ?, updated_by = ? WHERE id = ?', [key, now, input.changedBy ?? null, input.id]);
6240
+ getDb().run(`UPDATE asset_key_mappings
6241
+ SET "key" = ?, updated_at = ?, updated_by = ?
6242
+ WHERE EXISTS (
6243
+ SELECT 1 FROM task_attachments a
6244
+ WHERE a.task_id = ?
6245
+ AND asset_key_mappings.provider_id = COALESCE(NULLIF(a.provider_id, ''), 'agent-fs')
6246
+ AND asset_key_mappings.provider_org_id = COALESCE(a.agent_fs_org_id, '')
6247
+ AND asset_key_mappings.provider_drive_id = COALESCE(a.agent_fs_drive_id, '')
6248
+ AND asset_key_mappings.provider_key = COALESCE(NULLIF(a.provider_key, ''), a.path)
6249
+ )`, [key, now, input.changedBy ?? null, input.id]);
6250
+ for (const mapping of mappedFiles) {
6251
+ if (mapping.key === key)
6252
+ continue;
6253
+ insertAssetKeyHistory({
6254
+ entityType: "file",
6255
+ entityId: mapping.id,
6256
+ previousKey: mapping.key,
6257
+ newKey: key,
6258
+ changedBy: input.changedBy
6259
+ });
6260
+ }
6261
+ break;
6262
+ }
6263
+ case "workflow":
6264
+ getDb().run('UPDATE workflows SET "key" = ?, lastUpdatedAt = ?, updated_by = ? WHERE id = ?', [key, now, input.changedBy ?? null, input.id]);
6265
+ break;
6266
+ case "schedule":
6267
+ getDb().run('UPDATE scheduled_tasks SET "key" = ?, lastUpdatedAt = ?, updated_by = ? WHERE id = ?', [key, now, input.changedBy ?? null, input.id]);
6268
+ break;
6269
+ case "page":
6270
+ getDb().run('UPDATE pages SET "key" = ?, updatedAt = ?, updated_by = ? WHERE id = ?', [
6271
+ key,
6272
+ now,
6273
+ input.changedBy ?? null,
6274
+ input.id
6275
+ ]);
6276
+ break;
6277
+ case "file":
6278
+ getDb().run('UPDATE asset_key_mappings SET "key" = ?, updated_at = ?, updated_by = ? WHERE id = ?', [key, now, input.changedBy ?? null, input.id]);
6279
+ break;
6280
+ }
6281
+ insertAssetKeyHistory({
6282
+ entityType: input.entityType,
6283
+ entityId: input.id,
6284
+ previousKey,
6285
+ newKey: key,
6286
+ changedBy: input.changedBy
6287
+ });
6288
+ })();
6289
+ return true;
6290
+ }
5529
6291
  function deleteTaskAttachment(id) {
5530
6292
  const result = getDb().run("DELETE FROM task_attachments WHERE id = ?", [id]);
5531
6293
  return result.changes > 0;
@@ -5639,13 +6401,14 @@ function createTaskExtended(task, options) {
5639
6401
  if (options?.parentTaskId) {
5640
6402
  const parent = getTaskById(options.parentTaskId);
5641
6403
  if (parent) {
6404
+ const explicitForeignChannel = !!options.slackChannelId && !!parent.slackChannelId && options.slackChannelId !== parent.slackChannelId;
5642
6405
  if (parent.slackChannelId && !options.slackChannelId) {
5643
6406
  options.slackChannelId = parent.slackChannelId;
5644
6407
  }
5645
- if (parent.slackThreadTs && !options.slackThreadTs) {
6408
+ if (parent.slackThreadTs && !options.slackThreadTs && !explicitForeignChannel) {
5646
6409
  options.slackThreadTs = parent.slackThreadTs;
5647
6410
  }
5648
- if (parent.slackUserId && !options.slackUserId) {
6411
+ if (parent.slackUserId && !options.slackUserId && !explicitForeignChannel) {
5649
6412
  options.slackUserId = parent.slackUserId;
5650
6413
  }
5651
6414
  if (parent.agentmailInboxId && !options.agentmailInboxId) {
@@ -5699,6 +6462,9 @@ function createTaskExtended(task, options) {
5699
6462
  if (parent.requestedByUserId && !options.requestedByUserId) {
5700
6463
  options.requestedByUserId = parent.requestedByUserId;
5701
6464
  }
6465
+ if (parent.key && !options.key) {
6466
+ options.key = parent.key;
6467
+ }
5702
6468
  if (parent.contextKey && !options.contextKey) {
5703
6469
  options.contextKey = parent.contextKey;
5704
6470
  }
@@ -5723,9 +6489,41 @@ function createTaskExtended(task, options) {
5723
6489
  options.slackUserId = sourceTask.slackUserId;
5724
6490
  }
5725
6491
  }
6492
+ if (!options?.slackChannelId) {
6493
+ const backfill = slackChannelFromContextKey(options?.contextKey);
6494
+ if (backfill && options) {
6495
+ options.slackChannelId = backfill.channelId;
6496
+ options.slackThreadTs = backfill.threadTs;
6497
+ }
6498
+ } else if (!options.slackThreadTs) {
6499
+ const backfill = slackChannelFromContextKey(options.contextKey);
6500
+ if (backfill && backfill.channelId === options.slackChannelId) {
6501
+ options.slackThreadTs = backfill.threadTs;
6502
+ }
6503
+ }
6504
+ const finalSlackContext = slackChannelFromContextKey(options?.contextKey);
6505
+ if (finalSlackContext && options?.slackChannelId) {
6506
+ if (options.slackChannelId !== finalSlackContext.channelId) {
6507
+ if (options.overrideSlackContext) {
6508
+ console.log(`[slack-routing] override: keeping slackChannelId="${options.slackChannelId}" despite disagreeing with contextKey channel "${finalSlackContext.channelId}" (contextKey=${options.contextKey}, sourceTaskId=${options.sourceTaskId ?? "n/a"}, parentTaskId=${options.parentTaskId ?? "n/a"})`);
6509
+ } else {
6510
+ console.warn(`[slack-routing] MISMATCH task creation: normalizing slackChannelId="${options.slackChannelId}" to contextKey channel "${finalSlackContext.channelId}" (contextKey=${options.contextKey}, sourceTaskId=${options.sourceTaskId ?? "n/a"}, parentTaskId=${options.parentTaskId ?? "n/a"})`);
6511
+ options.slackChannelId = finalSlackContext.channelId;
6512
+ options.slackThreadTs = finalSlackContext.threadTs;
6513
+ }
6514
+ } else if (options.slackThreadTs && options.slackThreadTs !== finalSlackContext.threadTs) {
6515
+ if (options.overrideSlackContext) {
6516
+ console.log(`[slack-routing] override: keeping slackThreadTs="${options.slackThreadTs}" despite disagreeing with contextKey thread "${finalSlackContext.threadTs}" (contextKey=${options.contextKey}, sourceTaskId=${options.sourceTaskId ?? "n/a"}, parentTaskId=${options.parentTaskId ?? "n/a"})`);
6517
+ } else {
6518
+ console.warn(`[slack-routing] MISMATCH task creation: normalizing slackThreadTs="${options.slackThreadTs}" to contextKey thread "${finalSlackContext.threadTs}" (contextKey=${options.contextKey}, sourceTaskId=${options.sourceTaskId ?? "n/a"}, parentTaskId=${options.parentTaskId ?? "n/a"})`);
6519
+ options.slackThreadTs = finalSlackContext.threadTs;
6520
+ }
6521
+ }
6522
+ }
5726
6523
  const auditUserId = getCurrentRequestUserId() ?? null;
6524
+ const assetKey = normalizeAssetKey(options?.key ?? defaultAssetKey("task", id));
5727
6525
  const row = getDb().prepare(`INSERT INTO agent_tasks (
5728
- id, agentId, creatorAgentId, task, status, source,
6526
+ id, "key", agentId, creatorAgentId, task, status, source,
5729
6527
  taskType, tags, priority, dependsOn, offeredTo, offeredAt,
5730
6528
  slackChannelId, slackThreadTs, slackUserId,
5731
6529
  vcsProvider, vcsRepo, vcsEventType, vcsNumber, vcsCommentId, vcsAuthor, vcsUrl,
@@ -5733,7 +6531,7 @@ function createTaskExtended(task, options) {
5733
6531
  agentmailInboxId, agentmailMessageId, agentmailThreadId,
5734
6532
  mentionMessageId, mentionChannelId, dir, parentTaskId, model, modelTier, effort, scheduleId,
5735
6533
  workflowRunId, workflowRunStepId, outputSchema, followUpConfig, requestedByUserId, contextKey, routingAffinity, swarmVersion, createdAt, lastUpdatedAt, created_by, updated_by
5736
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(id, options?.agentId ?? null, options?.creatorAgentId ?? null, task, status, options?.source ?? "mcp", options?.taskType ?? null, JSON.stringify(options?.tags ?? []), options?.priority ?? 50, JSON.stringify(options?.dependsOn ?? []), options?.offeredTo ?? null, options?.offeredTo ? now : null, options?.slackChannelId ?? null, options?.slackThreadTs ?? null, options?.slackUserId ?? null, options?.vcsProvider ?? null, options?.vcsRepo ?? null, options?.vcsEventType ?? null, options?.vcsNumber ?? null, options?.vcsCommentId ?? null, options?.vcsAuthor ?? null, options?.vcsUrl ?? null, options?.vcsInstallationId ?? null, options?.vcsNodeId ?? null, options?.agentmailInboxId ?? null, options?.agentmailMessageId ?? null, options?.agentmailThreadId ?? null, options?.mentionMessageId ?? null, options?.mentionChannelId ?? null, options?.dir ?? null, options?.parentTaskId ?? null, options?.model ?? null, options?.modelTier ?? null, options?.effort ?? null, options?.scheduleId ?? null, options?.workflowRunId ?? null, options?.workflowRunStepId ?? null, options?.outputSchema ? JSON.stringify(options.outputSchema) : null, options?.followUpConfig ? JSON.stringify(options.followUpConfig) : null, options?.requestedByUserId ?? null, options?.contextKey ?? null, options?.routingAffinity ? JSON.stringify(options.routingAffinity) : null, package_default.version, now, now, auditUserId, auditUserId);
6534
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(id, assetKey, options?.agentId ?? null, options?.creatorAgentId ?? null, task, status, options?.source ?? "mcp", options?.taskType ?? null, JSON.stringify(options?.tags ?? []), options?.priority ?? 50, JSON.stringify(options?.dependsOn ?? []), options?.offeredTo ?? null, options?.offeredTo ? now : null, options?.slackChannelId ?? null, options?.slackThreadTs ?? null, options?.slackUserId ?? null, options?.vcsProvider ?? null, options?.vcsRepo ?? null, options?.vcsEventType ?? null, options?.vcsNumber ?? null, options?.vcsCommentId ?? null, options?.vcsAuthor ?? null, options?.vcsUrl ?? null, options?.vcsInstallationId ?? null, options?.vcsNodeId ?? null, options?.agentmailInboxId ?? null, options?.agentmailMessageId ?? null, options?.agentmailThreadId ?? null, options?.mentionMessageId ?? null, options?.mentionChannelId ?? null, options?.dir ?? null, options?.parentTaskId ?? null, options?.model ?? null, options?.modelTier ?? null, options?.effort ?? null, options?.scheduleId ?? null, options?.workflowRunId ?? null, options?.workflowRunStepId ?? null, options?.outputSchema ? JSON.stringify(options.outputSchema) : null, options?.followUpConfig ? JSON.stringify(options.followUpConfig) : null, options?.requestedByUserId ?? null, options?.contextKey ?? null, options?.routingAffinity ? JSON.stringify(options.routingAffinity) : null, package_default.version, now, now, auditUserId, auditUserId);
5737
6535
  if (!row)
5738
6536
  throw new Error("Failed to create task");
5739
6537
  try {
@@ -6762,6 +7560,7 @@ function previewText(text, maxChars) {
6762
7560
  function rowToScheduledTask(row) {
6763
7561
  return {
6764
7562
  id: row.id,
7563
+ key: row.key,
6765
7564
  name: row.name,
6766
7565
  description: row.description ?? undefined,
6767
7566
  cronExpression: row.cronExpression ?? undefined,
@@ -6826,6 +7625,13 @@ function getScheduledTasks(filters, opts) {
6826
7625
  query += " AND scriptName = ?";
6827
7626
  params.push(filters.scriptName);
6828
7627
  }
7628
+ if (filters?.key) {
7629
+ query += ' AND "key" = ?';
7630
+ params.push(normalizeAssetKey(filters.key));
7631
+ } else if (filters?.keyPrefix) {
7632
+ query += ` AND "key" LIKE ? ESCAPE '\\'`;
7633
+ params.push(assetKeyPrefixPattern(filters.keyPrefix));
7634
+ }
6829
7635
  if (filters?.consecutiveErrorsMin !== undefined) {
6830
7636
  query += " AND consecutiveErrors >= ?";
6831
7637
  params.push(filters.consecutiveErrorsMin);
@@ -6854,12 +7660,12 @@ function createScheduledTask(data) {
6854
7660
  const id = crypto.randomUUID();
6855
7661
  const now = new Date().toISOString();
6856
7662
  const row = getDb().prepare(`INSERT INTO scheduled_tasks (
6857
- id, name, description, cronExpression, intervalMs, taskTemplate,
7663
+ id, "key", name, description, cronExpression, intervalMs, taskTemplate,
6858
7664
  taskType, tags, priority, targetAgentId, enabled, nextRunAt,
6859
7665
  createdByAgentId, timezone, model, modelTier, scheduleType, targetType,
6860
7666
  workflowId, scriptName, scriptArgs, createdAt, lastUpdatedAt,
6861
7667
  created_by, updated_by
6862
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(id, data.name, data.description ?? null, data.cronExpression ?? null, data.intervalMs ?? null, data.taskTemplate ?? null, data.taskType ?? null, JSON.stringify(data.tags ?? []), data.priority ?? 50, data.targetAgentId ?? null, data.enabled !== false ? 1 : 0, data.nextRunAt ?? null, data.createdByAgentId ?? null, data.timezone ?? "UTC", data.model ?? null, data.modelTier ?? null, data.scheduleType ?? "recurring", data.targetType ?? "agent-task", data.workflowId ?? null, data.scriptName ?? null, data.scriptArgs !== undefined ? JSON.stringify(data.scriptArgs) : "{}", now, now, data.createdBy ?? null, data.createdBy ?? null);
7668
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(id, normalizeAssetKey(data.key ?? defaultAssetKey("schedule", id)), data.name, data.description ?? null, data.cronExpression ?? null, data.intervalMs ?? null, data.taskTemplate ?? null, data.taskType ?? null, JSON.stringify(data.tags ?? []), data.priority ?? 50, data.targetAgentId ?? null, data.enabled !== false ? 1 : 0, data.nextRunAt ?? null, data.createdByAgentId ?? null, data.timezone ?? "UTC", data.model ?? null, data.modelTier ?? null, data.scheduleType ?? "recurring", data.targetType ?? "agent-task", data.workflowId ?? null, data.scriptName ?? null, data.scriptArgs !== undefined ? JSON.stringify(data.scriptArgs) : "{}", now, now, data.createdBy ?? null, data.createdBy ?? null);
6863
7669
  if (!row)
6864
7670
  throw new Error("Failed to create scheduled task");
6865
7671
  return rowToScheduledTask(row);
@@ -6867,6 +7673,10 @@ function createScheduledTask(data) {
6867
7673
  function updateScheduledTask(id, data) {
6868
7674
  const updates = [];
6869
7675
  const params = [];
7676
+ if (data.key !== undefined) {
7677
+ updates.push('"key" = ?');
7678
+ params.push(normalizeAssetKey(data.key));
7679
+ }
6870
7680
  if (data.name !== undefined) {
6871
7681
  updates.push("name = ?");
6872
7682
  params.push(data.name);
@@ -7447,6 +8257,7 @@ function getRecentFailedCount(hours = 24) {
7447
8257
  function rowToWorkflow(row) {
7448
8258
  return {
7449
8259
  id: row.id,
8260
+ key: row.key,
7450
8261
  name: row.name,
7451
8262
  description: row.description ?? undefined,
7452
8263
  enabled: row.enabled === 1,
@@ -7466,8 +8277,8 @@ function rowToWorkflow(row) {
7466
8277
  }
7467
8278
  function createWorkflow(data) {
7468
8279
  const id = crypto.randomUUID();
7469
- const row = getDb().prepare(`INSERT INTO workflows (id, name, description, definition, triggers, cooldown, input, triggerSchema, dir, vcs_repo, createdByAgentId, created_by, updated_by)
7470
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(id, data.name, data.description ?? null, JSON.stringify(data.definition), JSON.stringify(data.triggers ?? []), data.cooldown ? JSON.stringify(data.cooldown) : null, data.input ? JSON.stringify(data.input) : null, data.triggerSchema ? JSON.stringify(data.triggerSchema) : null, data.dir ?? null, data.vcsRepo ?? null, data.createdByAgentId ?? null, data.createdBy ?? null, data.createdBy ?? null);
8280
+ const row = getDb().prepare(`INSERT INTO workflows (id, "key", name, description, definition, triggers, cooldown, input, triggerSchema, dir, vcs_repo, createdByAgentId, created_by, updated_by)
8281
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(id, normalizeAssetKey(data.key ?? defaultAssetKey("workflow", id)), data.name, data.description ?? null, JSON.stringify(data.definition), JSON.stringify(data.triggers ?? []), data.cooldown ? JSON.stringify(data.cooldown) : null, data.input ? JSON.stringify(data.input) : null, data.triggerSchema ? JSON.stringify(data.triggerSchema) : null, data.dir ?? null, data.vcsRepo ?? null, data.createdByAgentId ?? null, data.createdBy ?? null, data.createdBy ?? null);
7471
8282
  if (!row)
7472
8283
  throw new Error("Failed to create workflow");
7473
8284
  return rowToWorkflow(row);
@@ -7486,6 +8297,7 @@ function rowToWorkflowSummary(row) {
7486
8297
  }
7487
8298
  return {
7488
8299
  id: row.id,
8300
+ key: row.key,
7489
8301
  name: row.name,
7490
8302
  description: row.description ?? undefined,
7491
8303
  enabled: row.enabled === 1,
@@ -7504,6 +8316,13 @@ function listWorkflows(filters, opts) {
7504
8316
  query += " AND enabled = ?";
7505
8317
  params.push(filters.enabled ? 1 : 0);
7506
8318
  }
8319
+ if (filters?.key) {
8320
+ query += ' AND "key" = ?';
8321
+ params.push(normalizeAssetKey(filters.key));
8322
+ } else if (filters?.keyPrefix) {
8323
+ query += ` AND "key" LIKE ? ESCAPE '\\'`;
8324
+ params.push(assetKeyPrefixPattern(filters.keyPrefix));
8325
+ }
7507
8326
  if (filters?.lastRunStatus !== undefined) {
7508
8327
  query += " AND (SELECT status FROM workflow_runs WHERE workflowId = workflows.id ORDER BY startedAt DESC LIMIT 1) = ?";
7509
8328
  params.push(filters.lastRunStatus);
@@ -7531,6 +8350,10 @@ function listWorkflows(filters, opts) {
7531
8350
  function updateWorkflow(id, data) {
7532
8351
  const updates = [];
7533
8352
  const params = [];
8353
+ if (data.key !== undefined) {
8354
+ updates.push('"key" = ?');
8355
+ params.push(normalizeAssetKey(data.key));
8356
+ }
7534
8357
  if (data.name !== undefined) {
7535
8358
  updates.push("name = ?");
7536
8359
  params.push(data.name);
@@ -7841,6 +8664,7 @@ function getWorkflowVersion(workflowId, version) {
7841
8664
  function rowToPage(row) {
7842
8665
  return {
7843
8666
  id: row.id,
8667
+ key: row.key,
7844
8668
  agentId: row.agentId,
7845
8669
  slug: row.slug,
7846
8670
  title: row.title,
@@ -7856,8 +8680,9 @@ function rowToPage(row) {
7856
8680
  };
7857
8681
  }
7858
8682
  function createPage(data) {
7859
- const row = getDb().prepare(`INSERT INTO pages (agentId, slug, title, description, contentType, authMode, passwordHash, body, needsCredentials)
7860
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(data.agentId, data.slug, data.title, data.description ?? null, data.contentType, data.authMode ?? "authed", data.passwordHash ?? null, data.body, data.needsCredentials ? JSON.stringify(data.needsCredentials) : null);
8683
+ const id = crypto.randomUUID().replace(/-/g, "");
8684
+ const row = getDb().prepare(`INSERT INTO pages (id, "key", agentId, slug, title, description, contentType, authMode, passwordHash, body, needsCredentials)
8685
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(id, normalizeAssetKey(data.key ?? defaultAssetKey("page", id)), data.agentId, data.slug, data.title, data.description ?? null, data.contentType, data.authMode ?? "authed", data.passwordHash ?? null, data.body, data.needsCredentials ? JSON.stringify(data.needsCredentials) : null);
7861
8686
  if (!row)
7862
8687
  throw new Error("Failed to create page");
7863
8688
  return rowToPage(row);
@@ -7877,6 +8702,7 @@ function getLatestPageBySlug(slug) {
7877
8702
  function rowToPageSummary(row) {
7878
8703
  return {
7879
8704
  id: row.id,
8705
+ key: row.key,
7880
8706
  agentId: row.agentId,
7881
8707
  slug: row.slug,
7882
8708
  title: row.title,
@@ -7890,24 +8716,68 @@ function rowToPageSummary(row) {
7890
8716
  };
7891
8717
  }
7892
8718
  function listPagesByAgent(agentId, limit = 100, offset = 0, opts) {
7893
- const rows = getDb().prepare("SELECT * FROM pages WHERE agentId = ? ORDER BY updatedAt DESC LIMIT ? OFFSET ?").all(agentId, limit, offset);
8719
+ let query = "SELECT * FROM pages WHERE agentId = ?";
8720
+ const params = [agentId];
8721
+ if (opts?.key) {
8722
+ query += ' AND "key" = ?';
8723
+ params.push(normalizeAssetKey(opts.key));
8724
+ } else if (opts?.keyPrefix) {
8725
+ query += ` AND "key" LIKE ? ESCAPE '\\'`;
8726
+ params.push(assetKeyPrefixPattern(opts.keyPrefix));
8727
+ }
8728
+ query += " ORDER BY updatedAt DESC LIMIT ? OFFSET ?";
8729
+ params.push(limit, offset);
8730
+ const rows = getDb().prepare(query).all(...params);
7894
8731
  return opts?.slim ? rows.map(rowToPageSummary) : rows.map(rowToPage);
7895
8732
  }
7896
8733
  function listAllPages(limit = 100, offset = 0, opts) {
7897
- const rows = getDb().prepare("SELECT * FROM pages ORDER BY updatedAt DESC LIMIT ? OFFSET ?").all(limit, offset);
8734
+ let query = "SELECT * FROM pages WHERE 1=1";
8735
+ const params = [];
8736
+ if (opts?.key) {
8737
+ query += ' AND "key" = ?';
8738
+ params.push(normalizeAssetKey(opts.key));
8739
+ } else if (opts?.keyPrefix) {
8740
+ query += ` AND "key" LIKE ? ESCAPE '\\'`;
8741
+ params.push(assetKeyPrefixPattern(opts.keyPrefix));
8742
+ }
8743
+ query += " ORDER BY updatedAt DESC LIMIT ? OFFSET ?";
8744
+ params.push(limit, offset);
8745
+ const rows = getDb().prepare(query).all(...params);
7898
8746
  return opts?.slim ? rows.map(rowToPageSummary) : rows.map(rowToPage);
7899
8747
  }
7900
- function countAllPages() {
7901
- const row = getDb().prepare("SELECT COUNT(*) AS count FROM pages").get();
8748
+ function countAllPages(filters) {
8749
+ let query = "SELECT COUNT(*) AS count FROM pages WHERE 1=1";
8750
+ const params = [];
8751
+ if (filters?.key) {
8752
+ query += ' AND "key" = ?';
8753
+ params.push(normalizeAssetKey(filters.key));
8754
+ } else if (filters?.keyPrefix) {
8755
+ query += ` AND "key" LIKE ? ESCAPE '\\'`;
8756
+ params.push(assetKeyPrefixPattern(filters.keyPrefix));
8757
+ }
8758
+ const row = getDb().prepare(query).get(...params);
7902
8759
  return row?.count ?? 0;
7903
8760
  }
7904
- function countPagesByAgent(agentId) {
7905
- const row = getDb().prepare("SELECT COUNT(*) AS count FROM pages WHERE agentId = ?").get(agentId);
8761
+ function countPagesByAgent(agentId, filters) {
8762
+ let query = "SELECT COUNT(*) AS count FROM pages WHERE agentId = ?";
8763
+ const params = [agentId];
8764
+ if (filters?.key) {
8765
+ query += ' AND "key" = ?';
8766
+ params.push(normalizeAssetKey(filters.key));
8767
+ } else if (filters?.keyPrefix) {
8768
+ query += ` AND "key" LIKE ? ESCAPE '\\'`;
8769
+ params.push(assetKeyPrefixPattern(filters.keyPrefix));
8770
+ }
8771
+ const row = getDb().prepare(query).get(...params);
7906
8772
  return row?.count ?? 0;
7907
8773
  }
7908
8774
  function updatePage(id, data) {
7909
8775
  const updates = [];
7910
8776
  const params = [];
8777
+ if (data.key !== undefined) {
8778
+ updates.push('"key" = ?');
8779
+ params.push(normalizeAssetKey(data.key));
8780
+ }
7911
8781
  if (data.title !== undefined) {
7912
8782
  updates.push("title = ?");
7913
8783
  params.push(data.title);
@@ -9435,7 +10305,7 @@ function upsertInboxState(opts) {
9435
10305
  function rowToUserFavorite(row) {
9436
10306
  return {
9437
10307
  id: row.id,
9438
- userId: row.userId,
10308
+ userId: row.userId ?? undefined,
9439
10309
  itemType: row.itemType,
9440
10310
  itemId: row.itemId,
9441
10311
  createdAt: row.createdAt,
@@ -9444,9 +10314,9 @@ function rowToUserFavorite(row) {
9444
10314
  updatedBy: row.updated_by ?? undefined
9445
10315
  };
9446
10316
  }
9447
- function listUserFavorites(opts) {
9448
- const conditions = ["userId = ?"];
9449
- const params = [opts.userId];
10317
+ function listFavorites(opts) {
10318
+ const conditions = ["favoriteScope = ?"];
10319
+ const params = [opts.favoriteScope];
9450
10320
  if (opts.itemType) {
9451
10321
  conditions.push("itemType = ?");
9452
10322
  params.push(opts.itemType);
@@ -9458,34 +10328,44 @@ function listUserFavorites(opts) {
9458
10328
  const rows = getDb().prepare(`SELECT * FROM user_favorites WHERE ${conditions.join(" AND ")} ORDER BY lastUpdatedAt DESC`).all(...params);
9459
10329
  return rows.map(rowToUserFavorite);
9460
10330
  }
10331
+ function listUserFavorites(opts) {
10332
+ return listFavorites({ ...opts, favoriteScope: `user:${opts.userId}` });
10333
+ }
9461
10334
  function getFavoriteItemIdSet(opts) {
9462
- return new Set(listUserFavorites(opts).map((favorite) => favorite.itemId));
10335
+ return new Set(listFavorites(opts).map((favorite) => favorite.itemId));
9463
10336
  }
9464
- function setUserFavorite(opts) {
10337
+ function setFavorite(opts) {
9465
10338
  if (!opts.favorite) {
9466
- getDb().prepare("DELETE FROM user_favorites WHERE userId = ? AND itemType = ? AND itemId = ?").run(opts.userId, opts.itemType, opts.itemId);
10339
+ getDb().prepare("DELETE FROM user_favorites WHERE favoriteScope = ? AND itemType = ? AND itemId = ?").run(opts.favoriteScope, opts.itemType, opts.itemId);
9467
10340
  return null;
9468
10341
  }
9469
10342
  const now = new Date().toISOString();
9470
- const actor = opts.actorUserId ?? opts.userId;
10343
+ const actor = opts.actorId ?? opts.userId ?? opts.favoriteScope;
9471
10344
  const row = getDb().prepare(`INSERT INTO user_favorites (
9472
- userId, itemType, itemId, createdAt, lastUpdatedAt, created_by, updated_by
10345
+ favoriteScope, userId, itemType, itemId, createdAt, lastUpdatedAt, created_by, updated_by
9473
10346
  )
9474
- VALUES (?, ?, ?, ?, ?, ?, ?)
9475
- ON CONFLICT(userId, itemType, itemId) DO UPDATE SET
10347
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
10348
+ ON CONFLICT(favoriteScope, itemType, itemId) DO UPDATE SET
9476
10349
  lastUpdatedAt = excluded.lastUpdatedAt,
9477
10350
  updated_by = excluded.updated_by
9478
- RETURNING *`).get(opts.userId, opts.itemType, opts.itemId, now, now, actor, actor);
10351
+ RETURNING *`).get(opts.favoriteScope, opts.userId ?? null, opts.itemType, opts.itemId, now, now, actor, actor);
9479
10352
  if (!row)
9480
10353
  throw new Error("Failed to set favorite");
9481
10354
  return rowToUserFavorite(row);
9482
10355
  }
10356
+ function setUserFavorite(opts) {
10357
+ return setFavorite({
10358
+ ...opts,
10359
+ favoriteScope: `user:${opts.userId}`,
10360
+ actorId: opts.actorUserId
10361
+ });
10362
+ }
9483
10363
  function withFavoriteFlags(rows, opts) {
9484
- if (!opts.userId || rows.length === 0) {
10364
+ if (!opts.favoriteScope || rows.length === 0) {
9485
10365
  return rows.map((row) => ({ ...row, favorite: false }));
9486
10366
  }
9487
10367
  const favoriteIds = getFavoriteItemIdSet({
9488
- userId: opts.userId,
10368
+ favoriteScope: opts.favoriteScope,
9489
10369
  itemType: opts.itemType,
9490
10370
  itemIds: rows.map((row) => row.id)
9491
10371
  });
@@ -10200,12 +11080,15 @@ var db = null, sqliteVecAvailable = false, VERSIONABLE_FIELDS, agentQueries, MAX
10200
11080
  var init_db = __esm(() => {
10201
11081
  init_provider_metadata();
10202
11082
  init_package();
11083
+ init_key();
10203
11084
  init_resolver();
11085
+ init_slack_routing();
10204
11086
  init_telemetry();
10205
11087
  init_types();
10206
11088
  init_credentials();
10207
11089
  init_request_auth_context();
10208
11090
  init_secret_scrubber();
11091
+ init_asset_key_audit();
10209
11092
  init_crypto();
10210
11093
  init_date_utils();
10211
11094
  init_runner();
@@ -10230,8 +11113,8 @@ var init_db = __esm(() => {
10230
11113
  delete: () => getDb().prepare("DELETE FROM agents WHERE id = ?")
10231
11114
  };
10232
11115
  taskQueries = {
10233
- insert: () => getDb().prepare(`INSERT INTO agent_tasks (id, agentId, task, status, source, slackChannelId, slackThreadTs, slackUserId, swarmVersion, createdAt, lastUpdatedAt)
10234
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) RETURNING *`),
11116
+ insert: () => getDb().prepare(`INSERT INTO agent_tasks (id, "key", agentId, task, status, source, slackChannelId, slackThreadTs, slackUserId, swarmVersion, createdAt, lastUpdatedAt)
11117
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) RETURNING *`),
10235
11118
  getById: () => getDb().prepare("SELECT * FROM agent_tasks WHERE id = ?"),
10236
11119
  getByAgentId: () => getDb().prepare("SELECT * FROM agent_tasks WHERE agentId = ? ORDER BY createdAt DESC"),
10237
11120
  getByStatus: () => getDb().prepare("SELECT * FROM agent_tasks WHERE status = ? ORDER BY createdAt DESC"),
@@ -10306,4 +11189,4 @@ var init_db = __esm(() => {
10306
11189
  };
10307
11190
  });
10308
11191
 
10309
- export { interpolate, deepInterpolate, init_template, registerTemplate, getTemplateDefinition, getAllTemplateDefinitions, init_registry, configureHttpResolver, resolveTemplate, resolveTemplateAsync, init_resolver, initTelemetry, telemetry, init_telemetry, ModelTierSchema, ReasoningEffortSchema, splitLegacyModelAlias, resolveTaskModelSelection, AgentTaskStatusSchema, isTerminalTaskStatus, AgentTaskSourceSchema, ProviderNameSchema, FollowUpConfigSchema, AgentTaskSchema, AttachmentInputSchema, TaskAttachmentSchema, InboxItemTypeSchema, InboxItemStatusSchema, FavoriteItemTypeSchema, TaskTemplateKindSchema, AgentSchema, AgentLatestModelSchema, AgentCredStatusSchema, ChannelTypeSchema, ChannelSchema, ChannelMessageSchema, ServiceStatusSchema, ServiceSchema, ResumeReasonSchema, AgentLogSchema, EventCategorySchema, EventStatusSchema, EventSourceSchema, EventNameSchema, ScheduledTaskTargetTypeSchema, SwarmConfigScopeSchema, SwarmConfigSchema, RepoGuidelinesSchema, RepoHooksSchema, SwarmRepoSchema, AgentMemoryScopeSchema, AgentMemorySourceSchema, AgentMemorySchema, WorkflowDefinitionSchema, WorkflowNodePatchSchema, WorkflowPatchSchema, TriggerConfigSchema, CooldownConfigSchema, InputValueSchema, PageContentTypeSchema, PageAuthModeSchema, MetricDefinitionSchema, MetricVersionSchema, WorkflowRunStatusSchema, ScriptRunStatusSchema, TERMINAL_SCRIPT_RUN_STATUSES, PromptTemplateScopeSchema, PromptTemplateStateSchema, PromptTemplateSchema, PromptTemplateHistorySchema, ScriptScopeSchema, ScriptFsModeSchema, ScriptApiAuthModeSchema, ContextSnapshotEventTypeSchema, ContextFormulaSchema, BudgetScopeSchema, BudgetSchema, PricingProviderSchema, PricingTokenClassSchema, PricingRowSchema, BudgetRefusalCauseSchema, BudgetRefusalNotificationSchema, KvValueTypeSchema, KvNamespaceSchema, KvKeySchema, KvEntrySchema, init_types, setRequestAuth, getRequestAuth, getCurrentRequestAuth, init_request_auth_context, encryptSecret, decryptSecret, getEncryptionKey, init_crypto, normalizeDate, normalizeDateRequired, init_date_utils, init_session_templates, init_templates, init_templates2 as init_templates1, isReservedConfigKey, reservedKeyError, validateConfigValue, init_swarm_config_guard, onTaskStarted, init_task_lifecycle_events, generateDefaultClaudeMd, generateDefaultSoulMd, generateDefaultIdentityMd, generateDefaultToolsMd, init_defaults, isSqliteVecAvailable, initDb, getDb, closeDb, computeContentHash, createContextVersion, getLatestContextVersion, getContextVersion, getContextVersionHistory, agentQueries, updateAgentCredentialState, createAgent, getAgentById, getAllAgents, getLeadAgent, updateAgentStatus, updateAgentMaxTasks, updateAgentProvider, setAgentHarnessProvider, updateAgentCredStatus, listAgentsWithCredStatusByProvider, getAgentHarnessProviders, updateAgentActivity, MAX_EMPTY_POLLS, incrementEmptyPollCount, resetEmptyPollCount, shouldBlockPolling, deleteAgent, getActiveTaskCount, hasCapacity, getRemainingCapacity, updateAgentStatusFromCapacity, isPoolAffinityEnforcementEnabled, buildRoutingAffinityFromAgent, isAgentEligibleForTask, taskQueries, createTask, getPendingTaskForAgent, assignUnassignedTaskPending, startTask, getTaskById, markTaskSlackReplySent, setSlackMessageTracking, getChildTasks, hasNonTerminalResumeChild, hasNonTerminalRerouteDecisionChild, updateTaskClaudeSessionId, updateTaskVcs, getTasksByAgentId, getAgentCurrentTask, getTasksByStatus, findTaskByVcs, findTaskByGitHub, getAllTasks, getTasksCount, getTaskStats, getCompletedSlackTasks, getRecentlyFinishedWorkerTasks, markTasksNotified, resetTasksNotified, getInProgressSlackTasks, getInProgressTasksByContextKey, findExistingLinearTrackerContextWork, getLatestTaskByContextKey, getAgentWorkingOnThread, getLatestActiveTaskInThread, getMostRecentTaskInThread, findCompletedTaskInThread, findRecentCancelledTaskInThread, completeTask, failTask, cancelTask, supersedeTask, backfillSupersedeTaskResumeTaskId, pauseTask, resumeTask, getPausedTasksForAgent, getOrphanedInProgressTasksForAgent, resetOrphanedInProgressTasksForAgent, getRecentlyCancelledTasksForAgent, deleteTask, updateTaskProgress, insertTaskAttachment, deleteTaskAttachment, replaceTaskAttachment, getTaskAttachments, getAgentWithTasks, getAllAgentsWithTasks, logQueries, createLogEntry, getLogsByAgentId, getLogsByTaskId, getLogsByTaskIdChronological, getLogsByEventType, getAllLogs, findRecentSimilarTasks, createTaskExtended, claimTask, releaseTask, acceptTask, rejectTask, moveTaskToBacklog, moveTaskFromBacklog, releaseStaleReviewingTasks, getOfferedTasksForAgent, claimOfferedTask, getUnassignedTasksCount, getUnassignedTaskIds, getUnassignedTaskIdsForAgent, checkDependencies, getDependentTasks, cascadeFailDependents, updateAgentProfile, updateAgentName, createChannel, getMessageById, getChannelById, getChannelByName, getAllChannels, deleteChannel, postMessage, getChannelMessages, updateReadState, getLastReadAt, getUnreadMessages, getMentionsForAgent, getInboxSummary, claimMentions, releaseMentionProcessing, releaseStaleMentionProcessing, createService, getServiceById, getServiceByAgentAndName, getServicesByAgentId, getAllServices, updateServiceStatus, deleteService, upsertService, deleteServicesByAgentId, sessionLogQueries, createSessionLogs, getSessionLogsByTaskId, getSessionLogsBySession, createSessionCost, getSessionCostsByTaskId, getSessionCostsByAgentId, getAllSessionCosts, getSessionCostsFiltered, getSessionCostSummary, getDashboardCostSummary, createInboxMessage, getInboxMessageById, getUnreadInboxMessages, claimInboxMessages, markInboxMessageRead, markInboxMessageResponded, markInboxMessageDelegated, releaseStaleProcessingInbox, getConcurrentContext, getScheduledTasks, getScheduledTaskById, getScheduledTaskByName, createScheduledTask, updateScheduledTask, deleteScheduledTask, getDueScheduledTasks, autoEncryptLegacyPlaintextSecrets, maskSecrets, getSwarmConfigs, getInjectableGlobalConfigs, getSwarmConfigById, getSwarmConfigLookupById, upsertSwarmConfig, deleteSwarmConfig, deleteSwarmConfigByKey, getResolvedConfig, getSwarmRepos, getSwarmRepoById, getSwarmRepoByName, getSwarmRepoByUrl, createSwarmRepo, updateSwarmRepo, deleteSwarmRepo, getAgentMailInboxMapping, getAgentMailInboxMappingsByAgent, getAllAgentMailInboxMappings, createAgentMailInboxMapping, deleteAgentMailInboxMapping, findTaskByAgentMailThread, insertActiveSession, deleteActiveSession, deleteActiveSessionById, getActiveSessions, heartbeatActiveSession, cleanupStaleSessions, cleanupAgentSessions, updateActiveSessionProviderSessionId, getActiveSessionForTask, reassociateSessionLogs, getStalledInProgressTasks, getStalePinnedResumes, failPendingResumeIfUnclaimed, getIdleWorkersWithCapacity, getUnassignedPoolTasks, getStaleUnassignedAffinityTasks, getRecentFailedTasks, getRecentCompletedCount, getRecentFailedCount, createWorkflow, getWorkflow, listWorkflows, updateWorkflow, deleteWorkflow, getWorkflowsByScheduleId, createWorkflowRun, getWorkflowRun, updateWorkflowRun, listWorkflowRuns, createWorkflowRunStep, getWorkflowRunStep, updateWorkflowRunStep, getWorkflowRunStepsByRunId, getStuckWorkflowRuns, getLastSuccessfulRun, getLastRunStart, getRetryableSteps, getCompletedStepNodeIds, getTaskByWorkflowRunStepId, getStepByIdempotencyKey, getStepCountForNode, getLatestStepForNode, createWorkflowVersion, getWorkflowVersions, getWorkflowVersion, createPage, getPage, getPageBySlug, getLatestPageBySlug, listPagesByAgent, listAllPages, countAllPages, countPagesByAgent, updatePage, deletePage, incrementPageViewCount, createPageVersion, getPageVersions, getPageVersion, createMetric, getMetric, getMetricBySlug, listMetricsByAgent, listAllMetrics, countAllMetrics, countMetricsByAgent, updateMetric, deleteMetric, createMetricVersion, getMetricVersions, getMetricVersion, getPromptTemplates, getPromptTemplateById, upsertPromptTemplate, deletePromptTemplate, resetPromptTemplateToDefault, getPromptTemplateHistory, resolvePromptTemplate, checkoutPromptTemplate, getAllChannelActivityCursors, getChannelActivityCursor, upsertChannelActivityCursor, createApprovalRequest, getApprovalRequestById, resolveApprovalRequest, updateApprovalRequestNotifications, listApprovalRequests, getStuckApprovalRuns, getApprovalRequestByStepId, getExpiredPendingApprovals, createWaitState, getWaitStateById, getWaitStateByStepId, getDueWaitStates, getPendingEventWaitNames, getPendingWaitsByEvent, resolveWaitState, getStuckWaitRuns, SKILL_FILE_LIMITS, normalizeSkillFilePath, createSkill, updateSkill, listSkillFileManifest, getSkillFiles, getSkillFile, upsertSkillFile, upsertSkillFiles, deleteSkillFile, deleteSkill, getSkillById, getSkillByName, listSkills, searchSkills, installSkill, getSystemDefaultSkills, installSystemDefaultSkillsForAgent, uninstallSkill, getAgentSkills, toggleAgentSkill, createMcpServer, updateMcpServer, deleteMcpServer, getMcpServerById, getMcpServerByName, listMcpServers, installMcpServer, uninstallMcpServer, getAgentMcpServers, createContextSnapshot, getContextSnapshotsByTaskId, getContextSnapshotsBySessionId, getContextSummaryByTaskId, getAvailableKeyIndices, recordKeyUsage, markKeyRateLimited, recordKeyRateLimitWindows, setApiKeyName, clearKeyRateLimit, getKeyStatuses, getKeyCostSummary, getUserById, getAllUsers, createUser, updateUser, deleteUser, listInboxState, upsertInboxState, listUserFavorites, getFavoriteItemIdSet, setUserFavorite, withFavoriteFlags, listTaskTemplates, getRootTaskChain, listRecentSessions, countSessions, getBudget, getBudgets, upsertBudget, deleteBudget, getAllPricingRows, getPricingRows, getActivePricingRow, insertPricingRow, deletePricingRow, getDailySpendForAgent, getDailySpendGlobal, getDailySpendForUser, recordBudgetRefusalNotification, getBudgetRefusalNotification, getRecentBudgetRefusalNotifications, hasBudgetRefusalNotificationToday, setBudgetRefusalFollowUpTaskId, getLiveAgentCounts, getInstanceActivity, getSwarmMetrics, hasFirstCompletedTask, getKv, upsertKv, deleteKv, KvTypeCollisionError, incrKv, listKv, countKv, createScriptRun, recordInlineScriptRun, getScriptRun, getScriptRunByIdempotencyKey, listScriptRuns, countScriptRuns, countActiveScriptRuns, updateScriptRun, getRunningScriptRuns, getScriptRunJournalStep, upsertScriptRunJournalStep, listScriptRunJournalSteps, countScriptRunJournalSteps, countScriptRunJournalAgentTaskSteps, exports_db, init_db };
11192
+ export { normalizeAssetKey, parseAssetKey, canWriteAssetKey, init_key, interpolate, deepInterpolate, init_template, registerTemplate, getTemplateDefinition, getAllTemplateDefinitions, init_registry, configureHttpResolver, resolveTemplate, resolveTemplateAsync, init_resolver, slackContextKey, agentmailContextKey, githubContextKey, gitlabContextKey, linearContextKey, buildJiraContextKey, scheduleContextKey, workflowContextKey, agentContextKey, pageContextKey, init_context_key, checkSlackRoutingCoherence, init_slack_routing, initTelemetry, telemetry, init_telemetry, AssetKeySchema, AssetEntityTypeSchema, ModelTierSchema, ReasoningEffortSchema, splitLegacyModelAlias, resolveTaskModelSelection, AgentTaskStatusSchema, isTerminalTaskStatus, AgentTaskSourceSchema, ProviderNameSchema, FollowUpConfigSchema, AgentTaskSchema, AttachmentInputSchema, TaskAttachmentSchema, InboxItemTypeSchema, InboxItemStatusSchema, FavoriteItemTypeSchema, TaskTemplateKindSchema, AgentSchema, AgentLatestModelSchema, AgentCredStatusSchema, ChannelTypeSchema, ChannelSchema, ChannelMessageSchema, ServiceStatusSchema, ServiceSchema, ResumeReasonSchema, AgentLogSchema, EventCategorySchema, EventStatusSchema, EventSourceSchema, EventNameSchema, ScheduledTaskTargetTypeSchema, SwarmConfigScopeSchema, SwarmConfigSchema, RepoGuidelinesSchema, RepoHooksSchema, SwarmRepoSchema, AgentMemoryScopeSchema, AgentMemorySourceSchema, AgentMemorySchema, WorkflowDefinitionSchema, WorkflowNodePatchSchema, WorkflowPatchSchema, TriggerConfigSchema, CooldownConfigSchema, InputValueSchema, PageContentTypeSchema, PageAuthModeSchema, MetricDefinitionSchema, MetricVersionSchema, WorkflowRunStatusSchema, ScriptRunStatusSchema, TERMINAL_SCRIPT_RUN_STATUSES, PromptTemplateScopeSchema, PromptTemplateStateSchema, PromptTemplateSchema, PromptTemplateHistorySchema, ScriptScopeSchema, ScriptFsModeSchema, ScriptApiAuthModeSchema, ContextSnapshotEventTypeSchema, ContextFormulaSchema, BudgetScopeSchema, BudgetSchema, PricingProviderSchema, PricingTokenClassSchema, PricingRowSchema, BudgetRefusalCauseSchema, BudgetRefusalNotificationSchema, KvValueTypeSchema, KvNamespaceSchema, KvKeySchema, KvEntrySchema, init_types, setRequestAuth, getRequestAuth, getCurrentRequestAuth, init_request_auth_context, auditAssetKeys, init_asset_key_audit, encryptSecret, decryptSecret, getEncryptionKey, init_crypto, normalizeDate, normalizeDateRequired, init_date_utils, init_session_templates, init_templates, init_templates2 as init_templates1, isReservedConfigKey, reservedKeyError, validateConfigValue, init_swarm_config_guard, onTaskStarted, init_task_lifecycle_events, generateDefaultClaudeMd, generateDefaultSoulMd, generateDefaultIdentityMd, generateDefaultToolsMd, init_defaults, isSqliteVecAvailable, initDb, getDb, closeDb, computeContentHash, createContextVersion, getLatestContextVersion, getContextVersion, getContextVersionHistory, agentQueries, updateAgentCredentialState, createAgent, getAgentById, getAllAgents, getLeadAgent, updateAgentStatus, updateAgentMaxTasks, updateAgentProvider, setAgentHarnessProvider, updateAgentCredStatus, listAgentsWithCredStatusByProvider, getAgentHarnessProviders, updateAgentActivity, MAX_EMPTY_POLLS, incrementEmptyPollCount, resetEmptyPollCount, shouldBlockPolling, deleteAgent, getActiveTaskCount, hasCapacity, getRemainingCapacity, updateAgentStatusFromCapacity, isPoolAffinityEnforcementEnabled, buildRoutingAffinityFromAgent, isAgentEligibleForTask, taskQueries, createTask, getPendingTaskForAgent, assignUnassignedTaskPending, startTask, getTaskById, markTaskSlackReplySent, setSlackMessageTracking, getChildTasks, hasNonTerminalResumeChild, hasNonTerminalRerouteDecisionChild, updateTaskClaudeSessionId, updateTaskVcs, getTasksByAgentId, getAgentCurrentTask, getTasksByStatus, findTaskByVcs, findTaskByGitHub, getAllTasks, getTasksCount, getTaskStats, getCompletedSlackTasks, getRecentlyFinishedWorkerTasks, markTasksNotified, resetTasksNotified, getInProgressSlackTasks, getInProgressTasksByContextKey, findExistingLinearTrackerContextWork, getLatestTaskByContextKey, getAgentWorkingOnThread, getLatestActiveTaskInThread, getMostRecentTaskInThread, findCompletedTaskInThread, findRecentCancelledTaskInThread, completeTask, failTask, cancelTask, supersedeTask, backfillSupersedeTaskResumeTaskId, pauseTask, resumeTask, getPausedTasksForAgent, getOrphanedInProgressTasksForAgent, resetOrphanedInProgressTasksForAgent, getRecentlyCancelledTasksForAgent, deleteTask, updateTaskProgress, insertTaskAttachment, getAssetKeyMappingByProvider, upsertAssetKeyMapping, getAssetKeyMapping, listAssetSummaries, moveAssetKey, deleteTaskAttachment, replaceTaskAttachment, getTaskAttachments, getAgentWithTasks, getAllAgentsWithTasks, logQueries, createLogEntry, getLogsByAgentId, getLogsByTaskId, getLogsByTaskIdChronological, getLogsByEventType, getAllLogs, findRecentSimilarTasks, createTaskExtended, claimTask, releaseTask, acceptTask, rejectTask, moveTaskToBacklog, moveTaskFromBacklog, releaseStaleReviewingTasks, getOfferedTasksForAgent, claimOfferedTask, getUnassignedTasksCount, getUnassignedTaskIds, getUnassignedTaskIdsForAgent, checkDependencies, getDependentTasks, cascadeFailDependents, updateAgentProfile, updateAgentName, createChannel, getMessageById, getChannelById, getChannelByName, getAllChannels, deleteChannel, postMessage, getChannelMessages, updateReadState, getLastReadAt, getUnreadMessages, getMentionsForAgent, getInboxSummary, claimMentions, releaseMentionProcessing, releaseStaleMentionProcessing, createService, getServiceById, getServiceByAgentAndName, getServicesByAgentId, getAllServices, updateServiceStatus, deleteService, upsertService, deleteServicesByAgentId, sessionLogQueries, createSessionLogs, getSessionLogsByTaskId, getSessionLogsBySession, createSessionCost, getSessionCostsByTaskId, getSessionCostsByAgentId, getAllSessionCosts, getSessionCostsFiltered, getSessionCostSummary, getDashboardCostSummary, createInboxMessage, getInboxMessageById, getUnreadInboxMessages, claimInboxMessages, markInboxMessageRead, markInboxMessageResponded, markInboxMessageDelegated, releaseStaleProcessingInbox, getConcurrentContext, getScheduledTasks, getScheduledTaskById, getScheduledTaskByName, createScheduledTask, updateScheduledTask, deleteScheduledTask, getDueScheduledTasks, autoEncryptLegacyPlaintextSecrets, maskSecrets, getSwarmConfigs, getInjectableGlobalConfigs, getSwarmConfigById, getSwarmConfigLookupById, upsertSwarmConfig, deleteSwarmConfig, deleteSwarmConfigByKey, getResolvedConfig, getSwarmRepos, getSwarmRepoById, getSwarmRepoByName, getSwarmRepoByUrl, createSwarmRepo, updateSwarmRepo, deleteSwarmRepo, getAgentMailInboxMapping, getAgentMailInboxMappingsByAgent, getAllAgentMailInboxMappings, createAgentMailInboxMapping, deleteAgentMailInboxMapping, findTaskByAgentMailThread, insertActiveSession, deleteActiveSession, deleteActiveSessionById, getActiveSessions, heartbeatActiveSession, cleanupStaleSessions, cleanupAgentSessions, updateActiveSessionProviderSessionId, getActiveSessionForTask, reassociateSessionLogs, getStalledInProgressTasks, getStalePinnedResumes, failPendingResumeIfUnclaimed, getIdleWorkersWithCapacity, getUnassignedPoolTasks, getStaleUnassignedAffinityTasks, getRecentFailedTasks, getRecentCompletedCount, getRecentFailedCount, createWorkflow, getWorkflow, listWorkflows, updateWorkflow, deleteWorkflow, getWorkflowsByScheduleId, createWorkflowRun, getWorkflowRun, updateWorkflowRun, listWorkflowRuns, createWorkflowRunStep, getWorkflowRunStep, updateWorkflowRunStep, getWorkflowRunStepsByRunId, getStuckWorkflowRuns, getLastSuccessfulRun, getLastRunStart, getRetryableSteps, getCompletedStepNodeIds, getTaskByWorkflowRunStepId, getStepByIdempotencyKey, getStepCountForNode, getLatestStepForNode, createWorkflowVersion, getWorkflowVersions, getWorkflowVersion, createPage, getPage, getPageBySlug, getLatestPageBySlug, listPagesByAgent, listAllPages, countAllPages, countPagesByAgent, updatePage, deletePage, incrementPageViewCount, createPageVersion, getPageVersions, getPageVersion, createMetric, getMetric, getMetricBySlug, listMetricsByAgent, listAllMetrics, countAllMetrics, countMetricsByAgent, updateMetric, deleteMetric, createMetricVersion, getMetricVersions, getMetricVersion, getPromptTemplates, getPromptTemplateById, upsertPromptTemplate, deletePromptTemplate, resetPromptTemplateToDefault, getPromptTemplateHistory, resolvePromptTemplate, checkoutPromptTemplate, getAllChannelActivityCursors, getChannelActivityCursor, upsertChannelActivityCursor, createApprovalRequest, getApprovalRequestById, resolveApprovalRequest, updateApprovalRequestNotifications, listApprovalRequests, getStuckApprovalRuns, getApprovalRequestByStepId, getExpiredPendingApprovals, createWaitState, getWaitStateById, getWaitStateByStepId, getDueWaitStates, getPendingEventWaitNames, getPendingWaitsByEvent, resolveWaitState, getStuckWaitRuns, SKILL_FILE_LIMITS, normalizeSkillFilePath, createSkill, updateSkill, listSkillFileManifest, getSkillFiles, getSkillFile, upsertSkillFile, upsertSkillFiles, deleteSkillFile, deleteSkill, getSkillById, getSkillByName, listSkills, searchSkills, installSkill, getSystemDefaultSkills, installSystemDefaultSkillsForAgent, uninstallSkill, getAgentSkills, toggleAgentSkill, createMcpServer, updateMcpServer, deleteMcpServer, getMcpServerById, getMcpServerByName, listMcpServers, installMcpServer, uninstallMcpServer, getAgentMcpServers, createContextSnapshot, getContextSnapshotsByTaskId, getContextSnapshotsBySessionId, getContextSummaryByTaskId, getAvailableKeyIndices, recordKeyUsage, markKeyRateLimited, recordKeyRateLimitWindows, setApiKeyName, clearKeyRateLimit, getKeyStatuses, getKeyCostSummary, getUserById, getAllUsers, createUser, updateUser, deleteUser, listInboxState, upsertInboxState, listFavorites, listUserFavorites, getFavoriteItemIdSet, setFavorite, setUserFavorite, withFavoriteFlags, listTaskTemplates, getRootTaskChain, listRecentSessions, countSessions, getBudget, getBudgets, upsertBudget, deleteBudget, getAllPricingRows, getPricingRows, getActivePricingRow, insertPricingRow, deletePricingRow, getDailySpendForAgent, getDailySpendGlobal, getDailySpendForUser, recordBudgetRefusalNotification, getBudgetRefusalNotification, getRecentBudgetRefusalNotifications, hasBudgetRefusalNotificationToday, setBudgetRefusalFollowUpTaskId, getLiveAgentCounts, getInstanceActivity, getSwarmMetrics, hasFirstCompletedTask, getKv, upsertKv, deleteKv, KvTypeCollisionError, incrKv, listKv, countKv, createScriptRun, recordInlineScriptRun, getScriptRun, getScriptRunByIdempotencyKey, listScriptRuns, countScriptRuns, countActiveScriptRuns, updateScriptRun, getRunningScriptRuns, getScriptRunJournalStep, upsertScriptRunJournalStep, listScriptRunJournalSteps, countScriptRunJournalSteps, countScriptRunJournalAgentTaskSteps, exports_db, init_db };