@sentry/junior 0.156.0 → 0.158.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 (81) hide show
  1. package/dist/{agent-hooks-CCAR46PQ.js → agent-hooks-BOWFTDE5.js} +10 -10
  2. package/dist/api/conversations/attachments.d.ts +32 -0
  3. package/dist/api/conversations/priority.d.ts +22 -0
  4. package/dist/api/conversations/routes.d.ts +4 -1
  5. package/dist/api/conversations/user-message-activity.d.ts +3 -0
  6. package/dist/api/locations/detail.d.ts +3 -1
  7. package/dist/api/schema/conversation.d.ts +14 -4
  8. package/dist/api/schema/location.d.ts +3 -1
  9. package/dist/api/schema/person.d.ts +3 -1
  10. package/dist/api/schema/task.d.ts +8 -8
  11. package/dist/api/schema.js +1 -1
  12. package/dist/api.js +350 -187
  13. package/dist/app.js +35 -54
  14. package/dist/{catalog-runtime-3SNKYCBG.js → catalog-runtime-7GZ25WTR.js} +5 -5
  15. package/dist/chat/agent-invocations/types.d.ts +12 -12
  16. package/dist/chat/attachments/storage.d.ts +2 -0
  17. package/dist/chat/attachments/store.d.ts +16 -0
  18. package/dist/chat/event-tasks/tool-support.d.ts +1 -1
  19. package/dist/chat/event-tasks/types.d.ts +1 -1
  20. package/dist/chat/plugins/unfinished-work.d.ts +6 -1
  21. package/dist/chat/scheduled-tasks/tool-support.d.ts +5 -5
  22. package/dist/chat/scheduled-tasks/tools/create.d.ts +2 -2
  23. package/dist/chat/scheduled-tasks/tools/delete.d.ts +2 -2
  24. package/dist/chat/scheduled-tasks/tools/list.d.ts +2 -2
  25. package/dist/chat/scheduled-tasks/tools/run-now.d.ts +2 -2
  26. package/dist/chat/scheduled-tasks/tools/update.d.ts +2 -2
  27. package/dist/chat/task-execution/slack-work.d.ts +2 -2
  28. package/dist/{chunk-4ZZDTHFC.js → chunk-3A7VXSPJ.js} +8 -8
  29. package/dist/{chunk-7RAETIYO.js → chunk-4JAOLUDB.js} +6 -6
  30. package/dist/{chunk-KCA5MKHU.js → chunk-4Q6QVTKA.js} +351 -5
  31. package/dist/{chunk-TN4LVILT.js → chunk-4XJ2WSTI.js} +5 -5
  32. package/dist/{chunk-OGZPRMZA.js → chunk-6GJCOUTA.js} +1 -1
  33. package/dist/{chunk-6CINHXIL.js → chunk-A7EDLNGU.js} +4 -4
  34. package/dist/{chunk-IIO5PFZX.js → chunk-AF4PHIP6.js} +3 -3
  35. package/dist/{chunk-IZEBHYI4.js → chunk-BNFGY66A.js} +1 -1
  36. package/dist/{chunk-3L2PMOSR.js → chunk-C3FYJZSA.js} +4 -4
  37. package/dist/{chunk-X5NZQH5U.js → chunk-CZCMFRUQ.js} +16 -0
  38. package/dist/{chunk-CK2H7CFO.js → chunk-DTVTBZNP.js} +1 -1
  39. package/dist/{chunk-WD7SEUZO.js → chunk-EQ6Y37MD.js} +1 -1
  40. package/dist/{chunk-53W3LZEW.js → chunk-EXITAVHO.js} +64 -361
  41. package/dist/{chunk-ZNWRYNCA.js → chunk-FA32YRZ3.js} +5 -5
  42. package/dist/{chunk-GKC43TLS.js → chunk-FAJ6SMEE.js} +2 -2
  43. package/dist/{chunk-GSBPZHLQ.js → chunk-IEM3GFXL.js} +9 -9
  44. package/dist/{chunk-KBGMYLSV.js → chunk-IXYGGJRA.js} +1 -1
  45. package/dist/{chunk-OMQJB7YN.js → chunk-JEXQQLEC.js} +18 -18
  46. package/dist/{chunk-PMFGVLDU.js → chunk-KAFYW3O6.js} +35 -10
  47. package/dist/{chunk-X7W45B5G.js → chunk-KSZXVR5G.js} +1 -1
  48. package/dist/{chunk-SRJA6QLM.js → chunk-LUSAAJJA.js} +2 -2
  49. package/dist/{chunk-3FFUC32L.js → chunk-NOEERHHR.js} +6 -6
  50. package/dist/{chunk-MC57E5U5.js → chunk-PHQDXC27.js} +1 -1
  51. package/dist/{chunk-A6WPDFHQ.js → chunk-RCIKTZZF.js} +1 -1
  52. package/dist/{chunk-NTNU6T2D.js → chunk-ROOTB7J7.js} +2 -2
  53. package/dist/{chunk-TNXGMFWV.js → chunk-SAJPQLTY.js} +1 -1
  54. package/dist/{chunk-SE3D225X.js → chunk-SSU65ZTZ.js} +1 -1
  55. package/dist/{chunk-6JUU7J2B.js → chunk-UJ6N32ZM.js} +4 -4
  56. package/dist/{chunk-MJE7XKWP.js → chunk-UPPU3FQU.js} +1 -1
  57. package/dist/{chunk-JUJR6WTL.js → chunk-UQJO35BP.js} +4 -4
  58. package/dist/{chunk-AIJRG227.js → chunk-VURJ2NSF.js} +1 -1
  59. package/dist/{chunk-CN2XSVKF.js → chunk-X4BLYIMV.js} +6 -6
  60. package/dist/{chunk-EDJ64P2P.js → chunk-YEZS4TEY.js} +1 -1
  61. package/dist/cli/chat.js +30 -30
  62. package/dist/cli/check.js +6 -6
  63. package/dist/cli/plugins.js +13 -13
  64. package/dist/cli/snapshot-create.js +9 -9
  65. package/dist/cli/upgrade.js +7 -7
  66. package/dist/db/schema/agent-invocations.d.ts +4 -4
  67. package/dist/db/schema.d.ts +4 -4
  68. package/dist/{db-TYUUKGJX.js → db-HFWTPYT7.js} +7 -7
  69. package/dist/instrumentation.js +4 -4
  70. package/dist/nitro.js +2 -2
  71. package/dist/{oauth-callback-server-AWVDKPR5.js → oauth-callback-server-J3NW4QL5.js} +23 -23
  72. package/dist/{runner-JUQXMACC.js → runner-CL6QHML5.js} +14 -15
  73. package/dist/{skills-4GZTZX4V.js → skills-WZWKZIFQ.js} +6 -6
  74. package/dist/spawn-TQJWADG4.js +37 -0
  75. package/dist/{validation-DRLNJRND.js → validation-4HVSSLJV.js} +6 -6
  76. package/dist/version.js +1 -1
  77. package/dist/{work-TGIXRRJY.js → work-UF4KXPVG.js} +17 -17
  78. package/dist/worker-HI7I2W2U.js +24 -0
  79. package/package.json +5 -5
  80. package/dist/spawn-32SSRWU4.js +0 -37
  81. package/dist/worker-IFXZOYED.js +0 -24
package/dist/api.js CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  apiErrorSchema,
7
7
  archiveConversationBodySchema,
8
8
  archiveConversationResponseSchema,
9
+ conversationAttachmentParamsSchema,
9
10
  conversationDetailQuerySchema,
10
11
  conversationDetailReportSchema,
11
12
  conversationEventPageSchema,
@@ -33,23 +34,24 @@ import {
33
34
  taskListSchema,
34
35
  taskParamsSchema,
35
36
  taskRunListSchema
36
- } from "./chunk-X5NZQH5U.js";
37
+ } from "./chunk-CZCMFRUQ.js";
37
38
  import {
38
39
  appendAndEnqueueApiConversationMessage,
39
40
  createAndEnqueueApiConversation,
41
+ createVercelAttachmentStorage,
40
42
  formatSlackConversationRedactedLabel,
41
43
  lookupSlackUser,
42
44
  readHealthReport,
43
45
  resolveSlackConversationContextFromThreadId,
44
46
  webActorFromEmail
45
- } from "./chunk-PMFGVLDU.js";
47
+ } from "./chunk-KAFYW3O6.js";
46
48
  import {
47
49
  buildSentryConversationUrl,
48
50
  getWebAuthorization
49
- } from "./chunk-SE3D225X.js";
51
+ } from "./chunk-SSU65ZTZ.js";
50
52
  import {
51
53
  getConversation
52
- } from "./chunk-6CINHXIL.js";
54
+ } from "./chunk-A7EDLNGU.js";
53
55
  import {
54
56
  readTaskExecutionByConversationId,
55
57
  readTaskExecutionDays,
@@ -57,8 +59,8 @@ import {
57
59
  readTaskExecutionSummaries,
58
60
  readTaskExecutions,
59
61
  readTaskRuns
60
- } from "./chunk-GSBPZHLQ.js";
61
- import "./chunk-TN4LVILT.js";
62
+ } from "./chunk-IEM3GFXL.js";
63
+ import "./chunk-4XJ2WSTI.js";
62
64
  import {
63
65
  healthReportSchema,
64
66
  pluginOperationalReportFeedSchema,
@@ -81,17 +83,18 @@ import {
81
83
  listEventTasksCreatedBy,
82
84
  listPublicEventTasksForTeams,
83
85
  listPublicScheduledTasksForTeams,
86
+ readLiveAttachment,
84
87
  readStats
85
- } from "./chunk-KCA5MKHU.js";
88
+ } from "./chunk-4Q6QVTKA.js";
86
89
  import {
87
90
  parseSlackThreadId
88
- } from "./chunk-CK2H7CFO.js";
91
+ } from "./chunk-DTVTBZNP.js";
89
92
  import {
90
93
  agentTurnUsageSchema,
91
94
  fallbackShortTitle,
92
95
  hasAgentTurnUsage,
93
96
  renderJuniorNativeConversationEvent
94
- } from "./chunk-3L2PMOSR.js";
97
+ } from "./chunk-C3FYJZSA.js";
95
98
  import "./chunk-3Y47VMHV.js";
96
99
  import "./chunk-PDO5BLNM.js";
97
100
  import {
@@ -101,11 +104,11 @@ import {
101
104
  renderPluginConversationEvent,
102
105
  resolveViewerUser,
103
106
  updateViewerDisplayName
104
- } from "./chunk-3FFUC32L.js";
107
+ } from "./chunk-NOEERHHR.js";
105
108
  import {
106
109
  getSlackClient,
107
110
  withSlackRetries
108
- } from "./chunk-NTNU6T2D.js";
111
+ } from "./chunk-ROOTB7J7.js";
109
112
  import {
110
113
  decodeStoredConversationEvent,
111
114
  getConversationStore,
@@ -113,7 +116,7 @@ import {
113
116
  getSqlExecutor,
114
117
  locationFromRow,
115
118
  parseSessionSource
116
- } from "./chunk-IIO5PFZX.js";
119
+ } from "./chunk-AF4PHIP6.js";
117
120
  import {
118
121
  juniorApiTokens,
119
122
  juniorConversationEvents,
@@ -121,16 +124,16 @@ import {
121
124
  juniorConversations,
122
125
  juniorIdentities,
123
126
  juniorUsers
124
- } from "./chunk-AIJRG227.js";
127
+ } from "./chunk-VURJ2NSF.js";
125
128
  import "./chunk-EM225ITJ.js";
126
- import "./chunk-MC57E5U5.js";
127
- import "./chunk-GKC43TLS.js";
129
+ import "./chunk-PHQDXC27.js";
130
+ import "./chunk-FAJ6SMEE.js";
128
131
  import {
129
132
  juniorDestinations
130
133
  } from "./chunk-VFUK3X5B.js";
131
134
  import {
132
135
  canExposeConversationPayload
133
- } from "./chunk-MJE7XKWP.js";
136
+ } from "./chunk-UPPU3FQU.js";
134
137
  import "./chunk-BDUENQD3.js";
135
138
  import {
136
139
  parseSlackChannelId,
@@ -140,10 +143,10 @@ import {
140
143
  import {
141
144
  logException,
142
145
  logWarn
143
- } from "./chunk-X7W45B5G.js";
146
+ } from "./chunk-KSZXVR5G.js";
144
147
  import "./chunk-6GWA276C.js";
145
- import "./chunk-OGZPRMZA.js";
146
- import "./chunk-KBGMYLSV.js";
148
+ import "./chunk-6GJCOUTA.js";
149
+ import "./chunk-IXYGGJRA.js";
147
150
  import "./chunk-MLKGABMK.js";
148
151
 
149
152
  // src/api.ts
@@ -353,6 +356,47 @@ async function readConversationAccessFromSql(db, conversationIds, viewer) {
353
356
  );
354
357
  }
355
358
 
359
+ // src/api/conversations/attachments.ts
360
+ function mayDisplayInline(contentType) {
361
+ return contentType === "image/gif" || contentType === "image/jpeg" || contentType === "image/png" || contentType === "image/webp";
362
+ }
363
+ function contentDisposition(filename, contentType) {
364
+ const disposition = mayDisplayInline(contentType) ? "inline" : "attachment";
365
+ const fallback = filename.replace(/[^\x20-\x7e]|["\\]/g, "_");
366
+ return `${disposition}; filename="${fallback}"; filename*=UTF-8''${encodeURIComponent(filename)}`;
367
+ }
368
+ function conversationAttachmentHeaders(args) {
369
+ return new Headers({
370
+ "cache-control": "private, no-store",
371
+ "content-disposition": contentDisposition(args.filename, args.contentType),
372
+ "content-length": String(args.bytes),
373
+ "content-type": args.contentType,
374
+ "x-content-type-options": "nosniff"
375
+ });
376
+ }
377
+ async function openConversationAttachment(args) {
378
+ const access = (await readConversationAccessFromSql(
379
+ getDb(),
380
+ [args.conversationId],
381
+ args.viewer
382
+ )).get(args.conversationId);
383
+ if (!access?.canViewPrivateContent) return null;
384
+ const attachment = await readLiveAttachment({
385
+ attachmentId: args.attachmentId,
386
+ conversationId: args.conversationId,
387
+ db: getSqlExecutor()
388
+ });
389
+ if (!attachment) return null;
390
+ if (attachment.provider !== args.storage.provider) return null;
391
+ const body = await args.storage.get(attachment.storageKey);
392
+ return body ? { attachment, body } : null;
393
+ }
394
+ async function requireConversationAttachment(args) {
395
+ const opened = await openConversationAttachment(args);
396
+ if (!opened) throwApiError(404, "Attachment not found.");
397
+ return opened;
398
+ }
399
+
356
400
  // src/api/conversations/create.ts
357
401
  function actorFromViewer(viewer) {
358
402
  const normalized = viewer.email.trim().toLowerCase();
@@ -1210,7 +1254,7 @@ async function readConversationEventPage(executor, args) {
1210
1254
  }
1211
1255
 
1212
1256
  // src/api/conversations/list.ts
1213
- import { and as and7, asc as asc2, desc as desc2, eq as eq8, isNull as isNull3 } from "drizzle-orm";
1257
+ import { and as and8, asc as asc2, desc as desc2, eq as eq9, isNull as isNull3 } from "drizzle-orm";
1214
1258
 
1215
1259
  // src/api/conversations/shared.ts
1216
1260
  function slackStatsLocationLabel(input) {
@@ -1607,10 +1651,11 @@ async function readConversationAuxiliaryCostsFromSql(db, conversationIds, option
1607
1651
  // src/chat/plugins/unfinished-work.ts
1608
1652
  async function listConversationWork(conversationIds) {
1609
1653
  if (conversationIds.length === 0) {
1610
- return { assignedIds: [], unfinishedIds: [] };
1654
+ return { assignedIds: [], finishedAtById: {}, unfinishedIds: [] };
1611
1655
  }
1612
1656
  const candidates = new Set(conversationIds);
1613
1657
  const assigned = /* @__PURE__ */ new Set();
1658
+ const finishedAtById = /* @__PURE__ */ new Map();
1614
1659
  const unfinished = /* @__PURE__ */ new Set();
1615
1660
  for (const plugin of getPlugins()) {
1616
1661
  const hook = plugin.hooks?.unfinishedWork;
@@ -1630,6 +1675,17 @@ async function listConversationWork(conversationIds) {
1630
1675
  for (const conversationId of result.assignedConversationIds ?? []) {
1631
1676
  if (candidates.has(conversationId)) assigned.add(conversationId);
1632
1677
  }
1678
+ for (const [conversationId, finishedAt] of Object.entries(
1679
+ result.finishedWorkAtByConversationId ?? {}
1680
+ )) {
1681
+ if (!candidates.has(conversationId)) continue;
1682
+ const time = Date.parse(finishedAt);
1683
+ if (!Number.isFinite(time)) continue;
1684
+ const current = finishedAtById.get(conversationId);
1685
+ if (!current || time > Date.parse(current)) {
1686
+ finishedAtById.set(conversationId, finishedAt);
1687
+ }
1688
+ }
1633
1689
  } catch (error) {
1634
1690
  logWarn("plugin.unfinished_work.hook.failed", {
1635
1691
  "app.plugin.name": plugin.manifest.name,
@@ -1641,24 +1697,82 @@ async function listConversationWork(conversationIds) {
1641
1697
  assignedIds: conversationIds.filter(
1642
1698
  (conversationId) => assigned.has(conversationId)
1643
1699
  ),
1700
+ finishedAtById: Object.fromEntries(
1701
+ conversationIds.flatMap((conversationId) => {
1702
+ const finishedAt = finishedAtById.get(conversationId);
1703
+ return finishedAt ? [[conversationId, finishedAt]] : [];
1704
+ })
1705
+ ),
1644
1706
  unfinishedIds: conversationIds.filter(
1645
1707
  (conversationId) => unfinished.has(conversationId)
1646
1708
  )
1647
1709
  };
1648
1710
  }
1649
1711
 
1712
+ // src/api/conversations/priority.ts
1713
+ var UNASSIGNED_PRIORITY_WINDOW_MS = 3 * 60 * 60 * 1e3;
1714
+ var UNFINISHED_PRIORITY_WINDOW_MS = 48 * 60 * 60 * 1e3;
1715
+ function isConversationPriority(conversation, nowMs) {
1716
+ const lastSeenAt = Date.parse(conversation.lastSeenAt);
1717
+ if (!Number.isFinite(lastSeenAt)) return false;
1718
+ if (conversation.unfinishedWork) {
1719
+ return nowMs - lastSeenAt <= UNFINISHED_PRIORITY_WINDOW_MS;
1720
+ }
1721
+ if (conversation.assignedWork) {
1722
+ const finishedAt = Date.parse(conversation.finishedWorkAt ?? "");
1723
+ const lastUserMessageAt = Date.parse(conversation.lastUserMessageAt ?? "");
1724
+ return Number.isFinite(finishedAt) && Number.isFinite(lastUserMessageAt) && lastUserMessageAt > finishedAt;
1725
+ }
1726
+ return nowMs - lastSeenAt <= UNASSIGNED_PRIORITY_WINDOW_MS;
1727
+ }
1728
+
1729
+ // src/api/conversations/user-message-activity.ts
1730
+ import { and as and7, eq as eq8, inArray as inArray6, isNotNull as isNotNull3, or as or3, sql as sql5 } from "drizzle-orm";
1731
+ async function readLastUserMessageAtByConversation(db, conversationIds) {
1732
+ if (conversationIds.length === 0) return /* @__PURE__ */ new Map();
1733
+ const authority = sql5`${juniorConversationEvents.payload}->'provenance'->>'authority'`;
1734
+ const actorPlatform = sql5`${juniorConversationEvents.payload}->'provenance'->'actor'->>'platform'`;
1735
+ const role = sql5`${juniorConversationEvents.payload}->>'role'`;
1736
+ const rows = await db.select({
1737
+ conversationId: juniorConversationEvents.conversationId,
1738
+ lastUserMessageAt: sql5`max(${juniorConversationEvents.createdAt})`
1739
+ }).from(juniorConversationEvents).where(
1740
+ and7(
1741
+ inArray6(juniorConversationEvents.conversationId, conversationIds),
1742
+ or3(
1743
+ and7(
1744
+ eq8(juniorConversationEvents.type, "user_message"),
1745
+ eq8(authority, "instruction"),
1746
+ sql5`${actorPlatform} in ('slack', 'local', 'web')`
1747
+ ),
1748
+ and7(
1749
+ eq8(juniorConversationEvents.type, "message"),
1750
+ eq8(role, "user"),
1751
+ isNotNull3(juniorConversationEvents.actorIdentityId)
1752
+ )
1753
+ )
1754
+ )
1755
+ ).groupBy(juniorConversationEvents.conversationId);
1756
+ return new Map(
1757
+ rows.map((row) => [
1758
+ row.conversationId,
1759
+ new Date(row.lastUserMessageAt).toISOString()
1760
+ ])
1761
+ );
1762
+ }
1763
+
1650
1764
  // src/api/conversations/list.ts
1651
1765
  var CONVERSATION_FEED_LIMIT = 50;
1652
1766
  function conversationFeedMembershipFilter(filter) {
1653
1767
  if (!filter) return void 0;
1654
1768
  if (filter.kind === "viewer") {
1655
1769
  return viewerConversationMembership({
1656
- actorMatch: eq8(juniorIdentities.userId, filter.userId),
1770
+ actorMatch: eq9(juniorIdentities.userId, filter.userId),
1657
1771
  participantMatch: conversationHasParticipantUser(filter.userId)
1658
1772
  });
1659
1773
  }
1660
1774
  return viewerConversationMembership({
1661
- actorMatch: eq8(juniorUsers.primaryEmailNormalized, filter.email),
1775
+ actorMatch: eq9(juniorUsers.primaryEmailNormalized, filter.email),
1662
1776
  participantMatch: conversationHasParticipantEmail(filter.email)
1663
1777
  });
1664
1778
  }
@@ -1675,12 +1789,12 @@ async function conversationRows(db, limit, filter) {
1675
1789
  userDisplayName: juniorUsers.displayName
1676
1790
  }).from(juniorConversations).leftJoin(
1677
1791
  juniorDestinations,
1678
- eq8(juniorDestinations.id, juniorConversations.destinationId)
1792
+ eq9(juniorDestinations.id, juniorConversations.destinationId)
1679
1793
  ).leftJoin(
1680
1794
  juniorIdentities,
1681
- eq8(juniorIdentities.id, juniorConversations.actorIdentityId)
1682
- ).leftJoin(juniorUsers, eq8(juniorUsers.id, juniorIdentities.userId)).where(
1683
- and7(
1795
+ eq9(juniorIdentities.id, juniorConversations.actorIdentityId)
1796
+ ).leftJoin(juniorUsers, eq9(juniorUsers.id, juniorIdentities.userId)).where(
1797
+ and8(
1684
1798
  isNull3(juniorConversations.parentConversationId),
1685
1799
  isNull3(juniorConversations.archivedAt),
1686
1800
  conversationFeedMembershipFilter(filter)
@@ -1741,11 +1855,11 @@ async function readConversationRecordFromSql(conversationId) {
1741
1855
  userDisplayName: juniorUsers.displayName
1742
1856
  }).from(juniorConversations).leftJoin(
1743
1857
  juniorDestinations,
1744
- eq8(juniorDestinations.id, juniorConversations.destinationId)
1858
+ eq9(juniorDestinations.id, juniorConversations.destinationId)
1745
1859
  ).leftJoin(
1746
1860
  juniorIdentities,
1747
- eq8(juniorIdentities.id, juniorConversations.actorIdentityId)
1748
- ).leftJoin(juniorUsers, eq8(juniorUsers.id, juniorIdentities.userId)).where(eq8(juniorConversations.conversationId, conversationId)).limit(1);
1861
+ eq9(juniorIdentities.id, juniorConversations.actorIdentityId)
1862
+ ).leftJoin(juniorUsers, eq9(juniorUsers.id, juniorIdentities.userId)).where(eq9(juniorConversations.conversationId, conversationId)).limit(1);
1749
1863
  const row = rows[0];
1750
1864
  return row ? {
1751
1865
  conversation: conversationFromRow(row),
@@ -1787,7 +1901,8 @@ async function readConversationFeedFromSql(options = {}) {
1787
1901
  auxiliaryCostsByRoot,
1788
1902
  metricsByRoot,
1789
1903
  teamDomainByTeamId,
1790
- conversationWork
1904
+ conversationWork,
1905
+ lastUserMessageAtByConversation
1791
1906
  ] = await Promise.all([
1792
1907
  readConversationAccessFromSql(db, conversationIds, options.viewer),
1793
1908
  readConversationAuxiliaryCostsFromSql(db, conversationIds, {
@@ -1799,7 +1914,8 @@ async function readConversationFeedFromSql(options = {}) {
1799
1914
  (conversation) => conversation.sessionSource?.platform === "slack" ? [conversation.sessionSource.teamId] : []
1800
1915
  )
1801
1916
  ),
1802
- listConversationWork(conversationIds)
1917
+ listConversationWork(conversationIds),
1918
+ readLastUserMessageAtByConversation(db, conversationIds)
1803
1919
  ]);
1804
1920
  const assignedWork = new Set(conversationWork.assignedIds);
1805
1921
  const unfinishedWork = new Set(conversationWork.unfinishedIds);
@@ -1807,19 +1923,36 @@ async function readConversationFeedFromSql(options = {}) {
1807
1923
  conversations: conversations.map((conversation, index) => {
1808
1924
  const row = rows[index];
1809
1925
  const metrics = metricsByRoot.get(conversation.conversationId);
1810
- return {
1811
- ...conversationSummaryFromStoredConversation({
1812
- conversation,
1813
- access: accessByConversation.get(conversation.conversationId),
1814
- auxiliaryCosts: auxiliaryCostsByRoot.get(conversation.conversationId),
1815
- durationMs: metrics?.durationMs ?? row.conversation.durationMs,
1816
- teamDomainByTeamId,
1817
- ...row.destination?.visibility === "public" ? { locationId: row.destination.id } : {},
1818
- usage: metrics?.usage ?? row.conversation.usage ?? void 0
1819
- }),
1926
+ const summary = conversationSummaryFromStoredConversation({
1927
+ conversation,
1928
+ access: accessByConversation.get(conversation.conversationId),
1929
+ auxiliaryCosts: auxiliaryCostsByRoot.get(conversation.conversationId),
1930
+ durationMs: metrics?.durationMs ?? row.conversation.durationMs,
1931
+ teamDomainByTeamId,
1932
+ ...row.destination?.visibility === "public" ? { locationId: row.destination.id } : {},
1933
+ usage: metrics?.usage ?? row.conversation.usage ?? void 0
1934
+ });
1935
+ const work = {
1820
1936
  ...assignedWork.has(conversation.conversationId) ? { assignedWork: true } : {},
1937
+ ...conversationWork.finishedAtById[conversation.conversationId] ? {
1938
+ finishedWorkAt: conversationWork.finishedAtById[conversation.conversationId]
1939
+ } : {},
1821
1940
  ...unfinishedWork.has(conversation.conversationId) ? { unfinishedWork: true } : {}
1822
1941
  };
1942
+ return {
1943
+ ...summary,
1944
+ ...work,
1945
+ isPriority: isConversationPriority(
1946
+ {
1947
+ lastSeenAt: summary.lastSeenAt,
1948
+ lastUserMessageAt: lastUserMessageAtByConversation.get(
1949
+ conversation.conversationId
1950
+ ),
1951
+ ...work
1952
+ },
1953
+ nowMs
1954
+ )
1955
+ };
1823
1956
  }),
1824
1957
  generatedAt: new Date(nowMs).toISOString(),
1825
1958
  source: "conversation_index"
@@ -1832,7 +1965,7 @@ async function readConversationFeed(options = {}) {
1832
1965
  }
1833
1966
 
1834
1967
  // src/chat/tasks/read.ts
1835
- import { and as and8, eq as eq9, or as or3 } from "drizzle-orm";
1968
+ import { and as and9, eq as eq10, or as or4 } from "drizzle-orm";
1836
1969
 
1837
1970
  // src/chat/scheduled-tasks/personal.ts
1838
1971
  import { z as z3 } from "zod";
@@ -1942,16 +2075,16 @@ async function creatorProfileEmails(candidates) {
1942
2075
  email: juniorUsers.primaryEmailNormalized,
1943
2076
  slackUserId: juniorIdentities.providerSubjectId,
1944
2077
  teamId: juniorIdentities.providerTenantId
1945
- }).from(juniorIdentities).innerJoin(juniorUsers, eq9(juniorUsers.id, juniorIdentities.userId)).where(
1946
- and8(
1947
- eq9(juniorIdentities.kind, "user"),
1948
- eq9(juniorIdentities.provider, "slack"),
1949
- eq9(juniorIdentities.emailVerified, true),
1950
- or3(
2078
+ }).from(juniorIdentities).innerJoin(juniorUsers, eq10(juniorUsers.id, juniorIdentities.userId)).where(
2079
+ and9(
2080
+ eq10(juniorIdentities.kind, "user"),
2081
+ eq10(juniorIdentities.provider, "slack"),
2082
+ eq10(juniorIdentities.emailVerified, true),
2083
+ or4(
1951
2084
  ...[...selectors.values()].map(
1952
- (selector) => and8(
1953
- eq9(juniorIdentities.providerTenantId, selector.teamId),
1954
- eq9(juniorIdentities.providerSubjectId, selector.slackUserId)
2085
+ (selector) => and9(
2086
+ eq10(juniorIdentities.providerTenantId, selector.teamId),
2087
+ eq10(juniorIdentities.providerSubjectId, selector.slackUserId)
1955
2088
  )
1956
2089
  )
1957
2090
  )
@@ -1987,12 +2120,12 @@ async function destinationDetails(destinations) {
1987
2120
  providerTenantId: juniorDestinations.providerTenantId,
1988
2121
  visibility: juniorDestinations.visibility
1989
2122
  }).from(juniorDestinations).where(
1990
- or3(
2123
+ or4(
1991
2124
  ...[...selectors.values()].map(
1992
- (destination) => and8(
1993
- eq9(juniorDestinations.provider, "slack"),
1994
- eq9(juniorDestinations.providerTenantId, destination.teamId),
1995
- eq9(juniorDestinations.providerDestinationId, destination.channelId)
2125
+ (destination) => and9(
2126
+ eq10(juniorDestinations.provider, "slack"),
2127
+ eq10(juniorDestinations.providerTenantId, destination.teamId),
2128
+ eq10(juniorDestinations.providerDestinationId, destination.channelId)
1996
2129
  )
1997
2130
  )
1998
2131
  )
@@ -2645,20 +2778,20 @@ async function requireConversationPendingMessages(conversationId, options = {})
2645
2778
  }
2646
2779
 
2647
2780
  // src/api/conversations/stats.query.ts
2648
- import { and as and9, eq as eq10, gte, isNull as isNull4, lte, sql as sql6 } from "drizzle-orm";
2781
+ import { and as and10, eq as eq11, gte, isNull as isNull4, lte, sql as sql7 } from "drizzle-orm";
2649
2782
  import { alias as alias4 } from "drizzle-orm/pg-core";
2650
2783
 
2651
2784
  // src/api/conversations/aggregate.ts
2652
- import { sql as sql5 } from "drizzle-orm";
2785
+ import { sql as sql6 } from "drizzle-orm";
2653
2786
  function usageTokenValue(source, field) {
2654
- return sql5`CASE
2787
+ return sql6`CASE
2655
2788
  WHEN ${source.usage}->>${field} IS NOT NULL
2656
2789
  THEN (${source.usage}->>${field})::double precision
2657
2790
  ELSE NULL
2658
2791
  END`;
2659
2792
  }
2660
2793
  function tokenValue(source) {
2661
- return sql5`
2794
+ return sql6`
2662
2795
  CASE
2663
2796
  WHEN COALESCE(
2664
2797
  ${source.usage}->>'inputTokens',
@@ -2677,7 +2810,7 @@ function tokenValue(source) {
2677
2810
  `;
2678
2811
  }
2679
2812
  function costValue(source) {
2680
- return sql5`
2813
+ return sql6`
2681
2814
  CASE
2682
2815
  WHEN ${source.usage}->'cost'->>'total' IS NOT NULL
2683
2816
  THEN (${source.usage}->'cost'->>'total')::double precision
@@ -2698,34 +2831,34 @@ function costValue(source) {
2698
2831
  function conversationAggregateColumns(sources) {
2699
2832
  const metrics = sources?.metrics ?? juniorConversations;
2700
2833
  const roots = sources?.roots ?? juniorConversations;
2701
- const conversationCount = sources ? sql5`COUNT(DISTINCT ${roots.conversationId})` : sql5`COUNT(*)`;
2834
+ const conversationCount = sources ? sql6`COUNT(DISTINCT ${roots.conversationId})` : sql6`COUNT(*)`;
2702
2835
  return {
2703
- active: sql5`${conversationCount} FILTER (
2836
+ active: sql6`${conversationCount} FILTER (
2704
2837
  WHERE ${roots.executionStatus} NOT IN ('idle', 'failed')
2705
2838
  )::integer`,
2706
- conversations: sql5`${conversationCount}::integer`,
2707
- cachedInputTokens: sql5`SUM(${usageTokenValue(metrics, "cachedInputTokens")})::double precision`,
2708
- costUsd: sql5`SUM(${costValue(metrics)})::double precision`,
2709
- durationMs: sql5`COALESCE(SUM(${metrics.durationMs}), 0)::double precision`,
2710
- failed: sql5`${conversationCount} FILTER (
2839
+ conversations: sql6`${conversationCount}::integer`,
2840
+ cachedInputTokens: sql6`SUM(${usageTokenValue(metrics, "cachedInputTokens")})::double precision`,
2841
+ costUsd: sql6`SUM(${costValue(metrics)})::double precision`,
2842
+ durationMs: sql6`COALESCE(SUM(${metrics.durationMs}), 0)::double precision`,
2843
+ failed: sql6`${conversationCount} FILTER (
2711
2844
  WHERE ${roots.executionStatus} = 'failed'
2712
2845
  )::integer`,
2713
- inputTokens: sql5`SUM(${usageTokenValue(metrics, "inputTokens")})::double precision`,
2714
- tokens: sql5`SUM(${tokenValue(metrics)})::double precision`
2846
+ inputTokens: sql6`SUM(${usageTokenValue(metrics, "inputTokens")})::double precision`,
2847
+ tokens: sql6`SUM(${tokenValue(metrics)})::double precision`
2715
2848
  };
2716
2849
  }
2717
2850
  function conversationRangeColumns() {
2718
2851
  return {
2719
- firstSeenAt: sql5`MIN(${juniorConversations.createdAt})`.mapWith(
2852
+ firstSeenAt: sql6`MIN(${juniorConversations.createdAt})`.mapWith(
2720
2853
  juniorConversations.createdAt
2721
2854
  ),
2722
- lastSeenAt: sql5`MAX(${juniorConversations.lastActivityAt})`.mapWith(
2855
+ lastSeenAt: sql6`MAX(${juniorConversations.lastActivityAt})`.mapWith(
2723
2856
  juniorConversations.lastActivityAt
2724
2857
  )
2725
2858
  };
2726
2859
  }
2727
2860
  function conversationActiveDaysColumn() {
2728
- return sql5`COUNT(DISTINCT (${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC')::date)::integer`;
2861
+ return sql6`COUNT(DISTINCT (${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC')::date)::integer`;
2729
2862
  }
2730
2863
 
2731
2864
  // src/api/conversations/stats.query.ts
@@ -2789,7 +2922,7 @@ function statsItems(map) {
2789
2922
  );
2790
2923
  }
2791
2924
  function statsWhere(start, end) {
2792
- return and9(
2925
+ return and10(
2793
2926
  isNull4(juniorConversations.parentConversationId),
2794
2927
  gte(juniorConversations.lastActivityAt, start),
2795
2928
  lte(juniorConversations.lastActivityAt, end)
@@ -2866,14 +2999,14 @@ function guardianStats(rows, endMs) {
2866
2999
  }
2867
3000
  async function aggregateStats(db, start, end) {
2868
3001
  const where = statsWhere(start, end);
2869
- const activityDate = sql6`TO_CHAR(
3002
+ const activityDate = sql7`TO_CHAR(
2870
3003
  ${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC',
2871
3004
  'YYYY-MM-DD'
2872
3005
  )`;
2873
3006
  const [totalsRows, actorRows, locationRows, metricRows] = await Promise.all([
2874
3007
  db.select(treeAggregateColumns).from(juniorConversations).innerJoin(
2875
3008
  treeConversation,
2876
- eq10(
3009
+ eq11(
2877
3010
  treeConversation.rootConversationId,
2878
3011
  juniorConversations.conversationId
2879
3012
  )
@@ -2888,14 +3021,14 @@ async function aggregateStats(db, start, end) {
2888
3021
  ...treeAggregateColumns
2889
3022
  }).from(juniorConversations).innerJoin(
2890
3023
  treeConversation,
2891
- eq10(
3024
+ eq11(
2892
3025
  treeConversation.rootConversationId,
2893
3026
  juniorConversations.conversationId
2894
3027
  )
2895
3028
  ).leftJoin(
2896
3029
  juniorIdentities,
2897
- eq10(juniorIdentities.id, juniorConversations.actorIdentityId)
2898
- ).leftJoin(juniorUsers, eq10(juniorUsers.id, juniorIdentities.userId)).where(where).groupBy(
3030
+ eq11(juniorIdentities.id, juniorConversations.actorIdentityId)
3031
+ ).leftJoin(juniorUsers, eq11(juniorUsers.id, juniorIdentities.userId)).where(where).groupBy(
2899
3032
  juniorIdentities.displayName,
2900
3033
  juniorIdentities.emailNormalized,
2901
3034
  juniorIdentities.handle,
@@ -2913,13 +3046,13 @@ async function aggregateStats(db, start, end) {
2913
3046
  ...treeAggregateColumns
2914
3047
  }).from(juniorConversations).innerJoin(
2915
3048
  treeConversation,
2916
- eq10(
3049
+ eq11(
2917
3050
  treeConversation.rootConversationId,
2918
3051
  juniorConversations.conversationId
2919
3052
  )
2920
3053
  ).leftJoin(
2921
3054
  juniorDestinations,
2922
- eq10(juniorDestinations.id, juniorConversations.destinationId)
3055
+ eq11(juniorDestinations.id, juniorConversations.destinationId)
2923
3056
  ).where(where).groupBy(
2924
3057
  juniorConversations.channelName,
2925
3058
  juniorConversations.source,
@@ -2938,7 +3071,7 @@ async function aggregateStats(db, start, end) {
2938
3071
  tokens: treeAggregateColumns.tokens
2939
3072
  }).from(juniorConversations).innerJoin(
2940
3073
  treeConversation,
2941
- eq10(
3074
+ eq11(
2942
3075
  treeConversation.rootConversationId,
2943
3076
  juniorConversations.conversationId
2944
3077
  )
@@ -2947,26 +3080,26 @@ async function aggregateStats(db, start, end) {
2947
3080
  return { actorRows, locationRows, metricRows, totals: totalsRows[0] };
2948
3081
  }
2949
3082
  async function aggregateGuardianStats(db, start, end) {
2950
- const date = sql6`TO_CHAR(
3083
+ const date = sql7`TO_CHAR(
2951
3084
  ${juniorConversationEvents.createdAt} AT TIME ZONE 'UTC',
2952
3085
  'YYYY-MM-DD'
2953
3086
  )`;
2954
- const decision = sql6`${juniorConversationEvents.payload}->>'decision'`;
2955
- const cost2 = sql6`CASE
3087
+ const decision = sql7`${juniorConversationEvents.payload}->>'decision'`;
3088
+ const cost2 = sql7`CASE
2956
3089
  WHEN jsonb_typeof(${juniorConversationEvents.payload}->'costUsd') = 'number'
2957
3090
  THEN (${juniorConversationEvents.payload}->>'costUsd')::double precision
2958
3091
  ELSE NULL
2959
3092
  END`;
2960
3093
  return await db.select({
2961
- allow: sql6`COUNT(*) FILTER (WHERE ${decision} = 'allow')::integer`,
2962
- ask: sql6`COUNT(*) FILTER (WHERE ${decision} = 'ask')::integer`,
2963
- costUsd: sql6`SUM(${cost2})::double precision`,
3094
+ allow: sql7`COUNT(*) FILTER (WHERE ${decision} = 'allow')::integer`,
3095
+ ask: sql7`COUNT(*) FILTER (WHERE ${decision} = 'ask')::integer`,
3096
+ costUsd: sql7`SUM(${cost2})::double precision`,
2964
3097
  date,
2965
- deny: sql6`COUNT(*) FILTER (WHERE ${decision} = 'deny')::integer`,
2966
- requests: sql6`COUNT(*)::integer`
3098
+ deny: sql7`COUNT(*) FILTER (WHERE ${decision} = 'deny')::integer`,
3099
+ requests: sql7`COUNT(*)::integer`
2967
3100
  }).from(juniorConversationEvents).where(
2968
- and9(
2969
- eq10(juniorConversationEvents.type, "guardian_action_reviewed"),
3101
+ and10(
3102
+ eq11(juniorConversationEvents.type, "guardian_action_reviewed"),
2970
3103
  gte(juniorConversationEvents.createdAt, start),
2971
3104
  lte(juniorConversationEvents.createdAt, end)
2972
3105
  )
@@ -3016,7 +3149,7 @@ async function readConversationStats() {
3016
3149
  }
3017
3150
 
3018
3151
  // src/api/conversations/routes.ts
3019
- function createConversationRoutes() {
3152
+ function createConversationRoutes(options) {
3020
3153
  const app = new Hono();
3021
3154
  app.get(
3022
3155
  "/",
@@ -3144,6 +3277,31 @@ function createConversationRoutes() {
3144
3277
  );
3145
3278
  }
3146
3279
  );
3280
+ app.get(
3281
+ "/:conversationId/attachments/:attachmentId",
3282
+ validateRequest(
3283
+ "param",
3284
+ conversationAttachmentParamsSchema,
3285
+ "Invalid route parameters."
3286
+ ),
3287
+ async (context) => {
3288
+ const { attachmentId, conversationId } = context.req.valid("param");
3289
+ const viewer = context.get("viewer");
3290
+ const opened = await requireConversationAttachment({
3291
+ attachmentId,
3292
+ conversationId,
3293
+ storage: options.attachmentStorage,
3294
+ ...viewer ? { viewer } : {}
3295
+ });
3296
+ return new Response(opened.body, {
3297
+ headers: conversationAttachmentHeaders({
3298
+ bytes: opened.attachment.bytes,
3299
+ contentType: opened.attachment.contentType,
3300
+ filename: opened.attachment.filename
3301
+ })
3302
+ });
3303
+ }
3304
+ );
3147
3305
  app.get(
3148
3306
  "/:conversationId",
3149
3307
  validateRequest(
@@ -3175,7 +3333,7 @@ function createConversationRoutes() {
3175
3333
  import { Hono as Hono2 } from "hono";
3176
3334
 
3177
3335
  // src/api/locations/query.ts
3178
- import { and as and10, asc as asc3, desc as desc3, eq as eq11, gte as gte2, isNull as isNull5, sql as sql7 } from "drizzle-orm";
3336
+ import { and as and11, asc as asc3, desc as desc3, eq as eq12, gte as gte2, isNull as isNull5, sql as sql8 } from "drizzle-orm";
3179
3337
  import { alias as alias5 } from "drizzle-orm/pg-core";
3180
3338
 
3181
3339
  // src/api/conversations/reporting.ts
@@ -3283,10 +3441,10 @@ function topLevelWhere() {
3283
3441
  return isNull5(juniorConversations.parentConversationId);
3284
3442
  }
3285
3443
  function publicLocationWhere(destinationId) {
3286
- return and10(
3444
+ return and11(
3287
3445
  topLevelWhere(),
3288
- eq11(juniorDestinations.id, destinationId),
3289
- eq11(juniorDestinations.visibility, "public")
3446
+ eq12(juniorDestinations.id, destinationId),
3447
+ eq12(juniorDestinations.visibility, "public")
3290
3448
  );
3291
3449
  }
3292
3450
  function locationColumns() {
@@ -3337,28 +3495,28 @@ async function directoryRows(db) {
3337
3495
  ...conversationRangeColumns()
3338
3496
  }).from(juniorConversations).innerJoin(
3339
3497
  treeConversation2,
3340
- eq11(
3498
+ eq12(
3341
3499
  treeConversation2.rootConversationId,
3342
3500
  juniorConversations.conversationId
3343
3501
  )
3344
3502
  ).leftJoin(
3345
3503
  juniorDestinations,
3346
- eq11(juniorDestinations.id, juniorConversations.destinationId)
3504
+ eq12(juniorDestinations.id, juniorConversations.destinationId)
3347
3505
  ).where(topLevelWhere()).groupBy(...locationGroupBy());
3348
3506
  }
3349
3507
  async function directoryActivityRows(db, start) {
3350
- const date = sql7`TO_CHAR(
3508
+ const date = sql8`TO_CHAR(
3351
3509
  ${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC',
3352
3510
  'YYYY-MM-DD'
3353
3511
  )`;
3354
3512
  return db.select({
3355
- conversations: sql7`COUNT(*)::integer`,
3513
+ conversations: sql8`COUNT(*)::integer`,
3356
3514
  date,
3357
3515
  visibility: juniorDestinations.visibility
3358
3516
  }).from(juniorConversations).leftJoin(
3359
3517
  juniorDestinations,
3360
- eq11(juniorDestinations.id, juniorConversations.destinationId)
3361
- ).where(and10(topLevelWhere(), gte2(juniorConversations.lastActivityAt, start))).groupBy(date, juniorDestinations.visibility);
3518
+ eq12(juniorDestinations.id, juniorConversations.destinationId)
3519
+ ).where(and11(topLevelWhere(), gte2(juniorConversations.lastActivityAt, start))).groupBy(date, juniorDestinations.visibility);
3362
3520
  }
3363
3521
  function directoryActivityDays(rows, nowMs) {
3364
3522
  const days = /* @__PURE__ */ new Map();
@@ -3431,7 +3589,7 @@ async function recentLocationRows(db, locationId) {
3431
3589
  createdAt: juniorConversations.createdAt,
3432
3590
  destinationId: juniorDestinations.id,
3433
3591
  durationMs: juniorConversations.durationMs,
3434
- email: sql7`COALESCE(
3592
+ email: sql8`COALESCE(
3435
3593
  ${juniorUsers.primaryEmailNormalized},
3436
3594
  ${juniorIdentities.email}
3437
3595
  )`,
@@ -3440,7 +3598,7 @@ async function recentLocationRows(db, locationId) {
3440
3598
  fullName: juniorUsers.displayName,
3441
3599
  handle: juniorIdentities.handle,
3442
3600
  lastActivityAt: juniorConversations.lastActivityAt,
3443
- providerSubjectId: sql7`CASE
3601
+ providerSubjectId: sql8`CASE
3444
3602
  WHEN ${juniorIdentities.provider} = 'slack'
3445
3603
  THEN ${juniorIdentities.providerSubjectId}
3446
3604
  ELSE NULL
@@ -3451,12 +3609,12 @@ async function recentLocationRows(db, locationId) {
3451
3609
  usage: juniorConversations.usage
3452
3610
  }).from(juniorConversations).innerJoin(
3453
3611
  juniorDestinations,
3454
- eq11(juniorDestinations.id, juniorConversations.destinationId)
3612
+ eq12(juniorDestinations.id, juniorConversations.destinationId)
3455
3613
  ).leftJoin(
3456
3614
  juniorIdentities,
3457
- eq11(juniorIdentities.id, juniorConversations.actorIdentityId)
3458
- ).leftJoin(juniorUsers, eq11(juniorUsers.id, juniorIdentities.userId)).where(
3459
- and10(
3615
+ eq12(juniorIdentities.id, juniorConversations.actorIdentityId)
3616
+ ).leftJoin(juniorUsers, eq12(juniorUsers.id, juniorIdentities.userId)).where(
3617
+ and11(
3460
3618
  publicLocationWhere(locationId),
3461
3619
  isNull5(juniorConversations.archivedAt)
3462
3620
  )
@@ -3472,7 +3630,7 @@ async function readLocationDetailFromSql(locationId, options = {}) {
3472
3630
  const start = new Date(end);
3473
3631
  start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS - 1));
3474
3632
  const where = publicLocationWhere(locationId);
3475
- const activityDate = sql7`TO_CHAR(
3633
+ const activityDate = sql8`TO_CHAR(
3476
3634
  ${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC',
3477
3635
  'YYYY-MM-DD'
3478
3636
  )`;
@@ -3483,27 +3641,27 @@ async function readLocationDetailFromSql(locationId, options = {}) {
3483
3641
  ...conversationRangeColumns()
3484
3642
  }).from(juniorConversations).innerJoin(
3485
3643
  treeConversation2,
3486
- eq11(
3644
+ eq12(
3487
3645
  treeConversation2.rootConversationId,
3488
3646
  juniorConversations.conversationId
3489
3647
  )
3490
3648
  ).innerJoin(
3491
3649
  juniorDestinations,
3492
- eq11(juniorDestinations.id, juniorConversations.destinationId)
3650
+ eq12(juniorDestinations.id, juniorConversations.destinationId)
3493
3651
  ).where(where).groupBy(...locationGroupBy()),
3494
3652
  getDb().select({
3495
3653
  date: activityDate,
3496
3654
  ...treeAggregateColumns2
3497
3655
  }).from(juniorConversations).innerJoin(
3498
3656
  treeConversation2,
3499
- eq11(
3657
+ eq12(
3500
3658
  treeConversation2.rootConversationId,
3501
3659
  juniorConversations.conversationId
3502
3660
  )
3503
3661
  ).innerJoin(
3504
3662
  juniorDestinations,
3505
- eq11(juniorDestinations.id, juniorConversations.destinationId)
3506
- ).where(and10(where, gte2(juniorConversations.lastActivityAt, start))).groupBy(activityDate),
3663
+ eq12(juniorDestinations.id, juniorConversations.destinationId)
3664
+ ).where(and11(where, gte2(juniorConversations.lastActivityAt, start))).groupBy(activityDate),
3507
3665
  getDb().select({
3508
3666
  actorIdentityId: juniorConversations.actorIdentityId,
3509
3667
  email: juniorUsers.primaryEmailNormalized,
@@ -3515,17 +3673,17 @@ async function readLocationDetailFromSql(locationId, options = {}) {
3515
3673
  ...treeAggregateColumns2
3516
3674
  }).from(juniorConversations).innerJoin(
3517
3675
  treeConversation2,
3518
- eq11(
3676
+ eq12(
3519
3677
  treeConversation2.rootConversationId,
3520
3678
  juniorConversations.conversationId
3521
3679
  )
3522
3680
  ).innerJoin(
3523
3681
  juniorDestinations,
3524
- eq11(juniorDestinations.id, juniorConversations.destinationId)
3682
+ eq12(juniorDestinations.id, juniorConversations.destinationId)
3525
3683
  ).leftJoin(
3526
3684
  juniorIdentities,
3527
- eq11(juniorIdentities.id, juniorConversations.actorIdentityId)
3528
- ).leftJoin(juniorUsers, eq11(juniorUsers.id, juniorIdentities.userId)).where(where).groupBy(
3685
+ eq12(juniorIdentities.id, juniorConversations.actorIdentityId)
3686
+ ).leftJoin(juniorUsers, eq12(juniorUsers.id, juniorIdentities.userId)).where(where).groupBy(
3529
3687
  juniorConversations.actorIdentityId,
3530
3688
  juniorUsers.primaryEmailNormalized,
3531
3689
  juniorUsers.displayName,
@@ -3638,10 +3796,10 @@ function createLocationRoutes() {
3638
3796
  import { Hono as Hono3 } from "hono";
3639
3797
 
3640
3798
  // src/api/people/list.query.ts
3641
- import { and as and12, eq as eq13, gte as gte3, sql as sql9 } from "drizzle-orm";
3799
+ import { and as and13, eq as eq14, gte as gte3, sql as sql10 } from "drizzle-orm";
3642
3800
 
3643
3801
  // src/api/people/shared.ts
3644
- import { and as and11, asc as asc4, desc as desc4, eq as eq12, isNull as isNull6, notExists, or as or4, sql as sql8 } from "drizzle-orm";
3802
+ import { and as and12, asc as asc4, desc as desc4, eq as eq13, isNull as isNull6, notExists, or as or5, sql as sql9 } from "drizzle-orm";
3645
3803
  import { alias as alias6 } from "drizzle-orm/pg-core";
3646
3804
  var RECENT_LIMIT2 = 25;
3647
3805
  var ACTIVITY_DAYS2 = 365;
@@ -3661,37 +3819,37 @@ var peopleTreeAggregateColumns = conversationAggregateColumns({
3661
3819
  function peopleTreeMetricsJoin() {
3662
3820
  const rootOwnedByActor = getDb().select({ conversationId: peopleRootConversation.conversationId }).from(peopleRootConversation).innerJoin(
3663
3821
  peopleRootIdentity,
3664
- eq12(peopleRootIdentity.id, peopleRootConversation.actorIdentityId)
3822
+ eq13(peopleRootIdentity.id, peopleRootConversation.actorIdentityId)
3665
3823
  ).where(
3666
- and11(
3667
- eq12(
3824
+ and12(
3825
+ eq13(
3668
3826
  peopleRootConversation.conversationId,
3669
3827
  juniorConversations.rootConversationId
3670
3828
  ),
3671
3829
  isNull6(peopleRootConversation.parentConversationId),
3672
- eq12(
3830
+ eq13(
3673
3831
  peopleRootConversation.rootConversationId,
3674
3832
  peopleRootConversation.conversationId
3675
3833
  ),
3676
- eq12(
3834
+ eq13(
3677
3835
  peopleRootIdentity.emailNormalized,
3678
3836
  juniorUsers.primaryEmailNormalized
3679
3837
  ),
3680
- eq12(peopleRootIdentity.emailVerified, true)
3838
+ eq13(peopleRootIdentity.emailVerified, true)
3681
3839
  )
3682
3840
  );
3683
- return and11(
3684
- eq12(
3841
+ return and12(
3842
+ eq13(
3685
3843
  peopleTreeConversation.rootConversationId,
3686
3844
  juniorConversations.rootConversationId
3687
3845
  ),
3688
- or4(
3689
- eq12(
3846
+ or5(
3847
+ eq13(
3690
3848
  juniorConversations.rootConversationId,
3691
3849
  juniorConversations.conversationId
3692
3850
  ),
3693
- and11(
3694
- eq12(
3851
+ and12(
3852
+ eq13(
3695
3853
  peopleTreeConversation.conversationId,
3696
3854
  juniorConversations.conversationId
3697
3855
  ),
@@ -3741,11 +3899,11 @@ function statsItems2(map) {
3741
3899
  }
3742
3900
  function verifiedActorWhere(email) {
3743
3901
  const normalizedEmail = normalizeEmail(email);
3744
- return and11(
3745
- eq12(juniorIdentities.provider, "slack"),
3746
- eq12(juniorIdentities.emailVerified, true),
3747
- sql8`${juniorUsers.primaryEmailNormalized} IS NOT NULL`,
3748
- normalizedEmail ? eq12(juniorUsers.primaryEmailNormalized, normalizedEmail) : void 0
3902
+ return and12(
3903
+ eq13(juniorIdentities.provider, "slack"),
3904
+ eq13(juniorIdentities.emailVerified, true),
3905
+ sql9`${juniorUsers.primaryEmailNormalized} IS NOT NULL`,
3906
+ normalizedEmail ? eq13(juniorUsers.primaryEmailNormalized, normalizedEmail) : void 0
3749
3907
  );
3750
3908
  }
3751
3909
  async function recentActorRows(email) {
@@ -3768,12 +3926,12 @@ async function recentActorRows(email) {
3768
3926
  usage: juniorConversations.usage
3769
3927
  }).from(juniorConversations).innerJoin(
3770
3928
  juniorIdentities,
3771
- eq12(juniorIdentities.id, juniorConversations.actorIdentityId)
3772
- ).innerJoin(juniorUsers, eq12(juniorUsers.id, juniorIdentities.userId)).leftJoin(
3929
+ eq13(juniorIdentities.id, juniorConversations.actorIdentityId)
3930
+ ).innerJoin(juniorUsers, eq13(juniorUsers.id, juniorIdentities.userId)).leftJoin(
3773
3931
  juniorDestinations,
3774
- eq12(juniorDestinations.id, juniorConversations.destinationId)
3932
+ eq13(juniorDestinations.id, juniorConversations.destinationId)
3775
3933
  ).where(
3776
- and11(verifiedActorWhere(email), isNull6(juniorConversations.archivedAt))
3934
+ and12(verifiedActorWhere(email), isNull6(juniorConversations.archivedAt))
3777
3935
  ).orderBy(
3778
3936
  desc4(juniorConversations.lastActivityAt),
3779
3937
  asc4(juniorConversations.conversationId)
@@ -3802,7 +3960,7 @@ function directoryActivityDays2(rows, nowMs) {
3802
3960
  async function readPeopleListFromSql() {
3803
3961
  const nowMs = Date.now();
3804
3962
  const { end, start } = activityWindow(nowMs);
3805
- const activityDate = sql9`TO_CHAR(
3963
+ const activityDate = sql10`TO_CHAR(
3806
3964
  ${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC',
3807
3965
  'YYYY-MM-DD'
3808
3966
  )`;
@@ -3810,24 +3968,24 @@ async function readPeopleListFromSql() {
3810
3968
  getDb().select({
3811
3969
  email: juniorUsers.primaryEmailNormalized,
3812
3970
  fullName: juniorUsers.displayName,
3813
- slackUserId: sql9`MAX(${juniorIdentities.providerSubjectId})`,
3814
- slackUserName: sql9`MAX(${juniorIdentities.handle})`,
3971
+ slackUserId: sql10`MAX(${juniorIdentities.providerSubjectId})`,
3972
+ slackUserName: sql10`MAX(${juniorIdentities.handle})`,
3815
3973
  activeDays: conversationActiveDaysColumn(),
3816
3974
  ...peopleTreeAggregateColumns,
3817
3975
  ...conversationRangeColumns()
3818
3976
  }).from(juniorConversations).innerJoin(
3819
3977
  juniorIdentities,
3820
- eq13(juniorIdentities.id, juniorConversations.actorIdentityId)
3821
- ).innerJoin(juniorUsers, eq13(juniorUsers.id, juniorIdentities.userId)).leftJoin(peopleTreeConversation, peopleTreeMetricsJoin()).where(verifiedActorWhere()).groupBy(juniorUsers.primaryEmailNormalized, juniorUsers.displayName),
3978
+ eq14(juniorIdentities.id, juniorConversations.actorIdentityId)
3979
+ ).innerJoin(juniorUsers, eq14(juniorUsers.id, juniorIdentities.userId)).leftJoin(peopleTreeConversation, peopleTreeMetricsJoin()).where(verifiedActorWhere()).groupBy(juniorUsers.primaryEmailNormalized, juniorUsers.displayName),
3822
3980
  getDb().select({
3823
- activePeople: sql9`COUNT(DISTINCT ${juniorUsers.id})::int`,
3824
- conversations: sql9`COUNT(*)::int`,
3981
+ activePeople: sql10`COUNT(DISTINCT ${juniorUsers.id})::int`,
3982
+ conversations: sql10`COUNT(*)::int`,
3825
3983
  date: activityDate
3826
3984
  }).from(juniorConversations).innerJoin(
3827
3985
  juniorIdentities,
3828
- eq13(juniorIdentities.id, juniorConversations.actorIdentityId)
3829
- ).innerJoin(juniorUsers, eq13(juniorUsers.id, juniorIdentities.userId)).where(
3830
- and12(
3986
+ eq14(juniorIdentities.id, juniorConversations.actorIdentityId)
3987
+ ).innerJoin(juniorUsers, eq14(juniorUsers.id, juniorIdentities.userId)).where(
3988
+ and13(
3831
3989
  verifiedActorWhere(),
3832
3990
  gte3(juniorConversations.lastActivityAt, start)
3833
3991
  )
@@ -3870,7 +4028,7 @@ async function readPeopleList() {
3870
4028
  }
3871
4029
 
3872
4030
  // src/api/people/profile.query.ts
3873
- import { and as and13, eq as eq14, gte as gte4, sql as sql10 } from "drizzle-orm";
4031
+ import { and as and14, eq as eq15, gte as gte4, sql as sql11 } from "drizzle-orm";
3874
4032
  function emptyProfile(email, nowMs) {
3875
4033
  const end = new Date(nowMs);
3876
4034
  end.setUTCHours(0, 0, 0, 0);
@@ -3905,7 +4063,7 @@ function addAggregate2(map, label, row) {
3905
4063
  map.set(label, item);
3906
4064
  }
3907
4065
  function surfaceExpression() {
3908
- return sql10`CASE
4066
+ return sql11`CASE
3909
4067
  WHEN ${juniorConversations.source} IN ('api', 'scheduler', 'slack')
3910
4068
  THEN ${juniorConversations.source}
3911
4069
  WHEN ${juniorConversations.conversationId} LIKE 'slack:%' THEN 'slack'
@@ -3934,30 +4092,30 @@ async function readPeopleProfileFromSql(email, options = {}) {
3934
4092
  const where = verifiedActorWhere(normalizedEmail);
3935
4093
  const metricsJoin = peopleTreeMetricsJoin();
3936
4094
  const surface = surfaceExpression();
3937
- const activityDate = sql10`TO_CHAR(
4095
+ const activityDate = sql11`TO_CHAR(
3938
4096
  ${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC',
3939
4097
  'YYYY-MM-DD'
3940
4098
  )`;
3941
- const channel = sql10`SPLIT_PART(${juniorConversations.conversationId}, ':', 2)`;
4099
+ const channel = sql11`SPLIT_PART(${juniorConversations.conversationId}, ':', 2)`;
3942
4100
  const [totalsRows, dayRows, locationRows, surfaceRows, recentRows] = await Promise.all([
3943
4101
  getDb().select({
3944
4102
  email: juniorUsers.primaryEmailNormalized,
3945
4103
  fullName: juniorUsers.displayName,
3946
- slackUserId: sql10`MAX(${juniorIdentities.providerSubjectId})`,
3947
- slackUserName: sql10`MAX(${juniorIdentities.handle})`,
4104
+ slackUserId: sql11`MAX(${juniorIdentities.providerSubjectId})`,
4105
+ slackUserName: sql11`MAX(${juniorIdentities.handle})`,
3948
4106
  activeDays: conversationActiveDaysColumn(),
3949
4107
  ...peopleTreeAggregateColumns
3950
4108
  }).from(juniorConversations).innerJoin(
3951
4109
  juniorIdentities,
3952
- eq14(juniorIdentities.id, juniorConversations.actorIdentityId)
3953
- ).innerJoin(juniorUsers, eq14(juniorUsers.id, juniorIdentities.userId)).leftJoin(peopleTreeConversation, metricsJoin).where(where).groupBy(juniorUsers.primaryEmailNormalized, juniorUsers.displayName),
4110
+ eq15(juniorIdentities.id, juniorConversations.actorIdentityId)
4111
+ ).innerJoin(juniorUsers, eq15(juniorUsers.id, juniorIdentities.userId)).leftJoin(peopleTreeConversation, metricsJoin).where(where).groupBy(juniorUsers.primaryEmailNormalized, juniorUsers.displayName),
3954
4112
  getDb().select({
3955
4113
  date: activityDate,
3956
4114
  ...peopleTreeAggregateColumns
3957
4115
  }).from(juniorConversations).innerJoin(
3958
4116
  juniorIdentities,
3959
- eq14(juniorIdentities.id, juniorConversations.actorIdentityId)
3960
- ).innerJoin(juniorUsers, eq14(juniorUsers.id, juniorIdentities.userId)).leftJoin(peopleTreeConversation, metricsJoin).where(and13(where, gte4(juniorConversations.lastActivityAt, start))).groupBy(activityDate),
4117
+ eq15(juniorIdentities.id, juniorConversations.actorIdentityId)
4118
+ ).innerJoin(juniorUsers, eq15(juniorUsers.id, juniorIdentities.userId)).leftJoin(peopleTreeConversation, metricsJoin).where(and14(where, gte4(juniorConversations.lastActivityAt, start))).groupBy(activityDate),
3961
4119
  getDb().select({
3962
4120
  channel,
3963
4121
  channelName: juniorConversations.channelName,
@@ -3966,10 +4124,10 @@ async function readPeopleProfileFromSql(email, options = {}) {
3966
4124
  ...peopleTreeAggregateColumns
3967
4125
  }).from(juniorConversations).innerJoin(
3968
4126
  juniorIdentities,
3969
- eq14(juniorIdentities.id, juniorConversations.actorIdentityId)
3970
- ).innerJoin(juniorUsers, eq14(juniorUsers.id, juniorIdentities.userId)).leftJoin(peopleTreeConversation, metricsJoin).leftJoin(
4127
+ eq15(juniorIdentities.id, juniorConversations.actorIdentityId)
4128
+ ).innerJoin(juniorUsers, eq15(juniorUsers.id, juniorIdentities.userId)).leftJoin(peopleTreeConversation, metricsJoin).leftJoin(
3971
4129
  juniorDestinations,
3972
- eq14(juniorDestinations.id, juniorConversations.destinationId)
4130
+ eq15(juniorDestinations.id, juniorConversations.destinationId)
3973
4131
  ).where(where).groupBy(
3974
4132
  channel,
3975
4133
  juniorConversations.channelName,
@@ -3978,8 +4136,8 @@ async function readPeopleProfileFromSql(email, options = {}) {
3978
4136
  ),
3979
4137
  getDb().select({ surface, ...peopleTreeAggregateColumns }).from(juniorConversations).innerJoin(
3980
4138
  juniorIdentities,
3981
- eq14(juniorIdentities.id, juniorConversations.actorIdentityId)
3982
- ).innerJoin(juniorUsers, eq14(juniorUsers.id, juniorIdentities.userId)).leftJoin(peopleTreeConversation, metricsJoin).where(where).groupBy(surface),
4139
+ eq15(juniorIdentities.id, juniorConversations.actorIdentityId)
4140
+ ).innerJoin(juniorUsers, eq15(juniorUsers.id, juniorIdentities.userId)).leftJoin(peopleTreeConversation, metricsJoin).where(where).groupBy(surface),
3983
4141
  recentActorRows(normalizedEmail)
3984
4142
  ]);
3985
4143
  const totalsRow = totalsRows[0];
@@ -4057,7 +4215,7 @@ async function readPeopleProfile(email, options = {}) {
4057
4215
  }
4058
4216
 
4059
4217
  // src/api/people/spend.query.ts
4060
- import { and as and14, eq as eq15, gte as gte5, lte as lte2, sql as sql11 } from "drizzle-orm";
4218
+ import { and as and15, eq as eq16, gte as gte5, lte as lte2, sql as sql12 } from "drizzle-orm";
4061
4219
  var SEVEN_DAY_OFFSET = 6;
4062
4220
  var THIRTY_DAY_OFFSET = 29;
4063
4221
  function addUsd2(current, next) {
@@ -4076,7 +4234,7 @@ function spendWindow(nowMs) {
4076
4234
  async function readPersonalSpendFromSql(email, nowMs = Date.now()) {
4077
4235
  const normalizedEmail = normalizeEmail(email);
4078
4236
  const { end, sevenDayStart, thirtyDayStart } = spendWindow(nowMs);
4079
- const activityDate = sql11`TO_CHAR(
4237
+ const activityDate = sql12`TO_CHAR(
4080
4238
  ${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC',
4081
4239
  'YYYY-MM-DD'
4082
4240
  )`;
@@ -4086,9 +4244,9 @@ async function readPersonalSpendFromSql(email, nowMs = Date.now()) {
4086
4244
  date: activityDate
4087
4245
  }).from(juniorConversations).innerJoin(
4088
4246
  juniorIdentities,
4089
- eq15(juniorIdentities.id, juniorConversations.actorIdentityId)
4090
- ).innerJoin(juniorUsers, eq15(juniorUsers.id, juniorIdentities.userId)).leftJoin(peopleTreeConversation, metricsJoin).where(
4091
- and14(
4247
+ eq16(juniorIdentities.id, juniorConversations.actorIdentityId)
4248
+ ).innerJoin(juniorUsers, eq16(juniorUsers.id, juniorIdentities.userId)).leftJoin(peopleTreeConversation, metricsJoin).where(
4249
+ and15(
4092
4250
  verifiedActorWhere(normalizedEmail),
4093
4251
  gte5(juniorConversations.lastActivityAt, thirtyDayStart),
4094
4252
  lte2(juniorConversations.lastActivityAt, end)
@@ -4176,7 +4334,7 @@ import { Hono as Hono4 } from "hono";
4176
4334
 
4177
4335
  // src/personal-tokens/store.ts
4178
4336
  import { createHash, randomBytes, randomUUID } from "crypto";
4179
- import { and as and15, desc as desc5, eq as eq16, gt, isNull as isNull7 } from "drizzle-orm";
4337
+ import { and as and16, desc as desc5, eq as eq17, gt, isNull as isNull7 } from "drizzle-orm";
4180
4338
  var TOKEN_PREFIX = "jr_pat_";
4181
4339
  var TOKEN_SUFFIX_LENGTH = 8;
4182
4340
  var DEFAULT_TOKEN_LIFETIME_MS = 90 * 24 * 60 * 60 * 1e3;
@@ -4213,8 +4371,8 @@ async function createPersonalToken(args) {
4213
4371
  }
4214
4372
  async function listPersonalTokens(email, now = /* @__PURE__ */ new Date()) {
4215
4373
  const rows = await getDb().select().from(juniorApiTokens).where(
4216
- and15(
4217
- eq16(juniorApiTokens.ownerEmailNormalized, normalizeEmail2(email)),
4374
+ and16(
4375
+ eq17(juniorApiTokens.ownerEmailNormalized, normalizeEmail2(email)),
4218
4376
  isNull7(juniorApiTokens.revokedAt),
4219
4377
  gt(juniorApiTokens.expiresAt, now)
4220
4378
  )
@@ -4223,9 +4381,9 @@ async function listPersonalTokens(email, now = /* @__PURE__ */ new Date()) {
4223
4381
  }
4224
4382
  async function revokePersonalToken(args) {
4225
4383
  const rows = await getDb().update(juniorApiTokens).set({ revokedAt: args.now ?? /* @__PURE__ */ new Date() }).where(
4226
- and15(
4227
- eq16(juniorApiTokens.id, args.id),
4228
- eq16(juniorApiTokens.ownerEmailNormalized, normalizeEmail2(args.email)),
4384
+ and16(
4385
+ eq17(juniorApiTokens.id, args.id),
4386
+ eq17(juniorApiTokens.ownerEmailNormalized, normalizeEmail2(args.email)),
4229
4387
  isNull7(juniorApiTokens.revokedAt)
4230
4388
  )
4231
4389
  ).returning({ id: juniorApiTokens.id });
@@ -4237,15 +4395,15 @@ async function authenticatePersonalToken(token2, now = /* @__PURE__ */ new Date(
4237
4395
  id: juniorApiTokens.id,
4238
4396
  ownerEmailNormalized: juniorApiTokens.ownerEmailNormalized
4239
4397
  }).from(juniorApiTokens).where(
4240
- and15(
4241
- eq16(juniorApiTokens.tokenHash, hashToken(token2)),
4398
+ and16(
4399
+ eq17(juniorApiTokens.tokenHash, hashToken(token2)),
4242
4400
  isNull7(juniorApiTokens.revokedAt),
4243
4401
  gt(juniorApiTokens.expiresAt, now)
4244
4402
  )
4245
4403
  ).limit(1);
4246
4404
  const match = rows[0];
4247
4405
  if (!match) return void 0;
4248
- await getDb().update(juniorApiTokens).set({ lastUsedAt: now }).where(eq16(juniorApiTokens.id, match.id));
4406
+ await getDb().update(juniorApiTokens).set({ lastUsedAt: now }).where(eq17(juniorApiTokens.id, match.id));
4249
4407
  return match.ownerEmailNormalized;
4250
4408
  }
4251
4409
 
@@ -4502,7 +4660,7 @@ function readDescriptionText(home) {
4502
4660
  }
4503
4661
  }
4504
4662
  async function readSkillReports() {
4505
- const { discoverSkills } = await import("./skills-4GZTZX4V.js");
4663
+ const { discoverSkills } = await import("./skills-WZWKZIFQ.js");
4506
4664
  const skills = await discoverSkills();
4507
4665
  return skillReportsSchema.parse(
4508
4666
  skills.map((skill) => ({
@@ -4512,7 +4670,7 @@ async function readSkillReports() {
4512
4670
  );
4513
4671
  }
4514
4672
  async function readPlugins() {
4515
- const { pluginCatalogRuntime } = await import("./catalog-runtime-3SNKYCBG.js");
4673
+ const { pluginCatalogRuntime } = await import("./catalog-runtime-7GZ25WTR.js");
4516
4674
  return pluginsSchema.parse(
4517
4675
  pluginCatalogRuntime.getProviders().map((plugin) => ({
4518
4676
  configKeys: plugin.manifest.configKeys ?? [],
@@ -4525,7 +4683,7 @@ async function readPlugins() {
4525
4683
  async function readRuntimeInfoReport() {
4526
4684
  const [{ homeDir }, { pluginCatalogRuntime }, plugins, skills] = await Promise.all([
4527
4685
  import("./discovery-GUFTFBTL.js"),
4528
- import("./catalog-runtime-3SNKYCBG.js"),
4686
+ import("./catalog-runtime-7GZ25WTR.js"),
4529
4687
  readPlugins(),
4530
4688
  readSkillReports()
4531
4689
  ]);
@@ -4541,7 +4699,7 @@ async function readRuntimeInfoReport() {
4541
4699
  }
4542
4700
  async function readPluginOperationalReportFeed() {
4543
4701
  const nowMs = Date.now();
4544
- const { getPluginOperationalReports } = await import("./agent-hooks-CCAR46PQ.js");
4702
+ const { getPluginOperationalReports } = await import("./agent-hooks-BOWFTDE5.js");
4545
4703
  return pluginOperationalReportFeedSchema.parse({
4546
4704
  source: "plugins",
4547
4705
  generatedAt: new Date(nowMs).toISOString(),
@@ -4579,7 +4737,12 @@ function createJuniorApi() {
4579
4737
  "/api/stats",
4580
4738
  async () => jsonResponse(statsReportSchema, await readStatsReport())
4581
4739
  );
4582
- app.route("/api/conversations", createConversationRoutes());
4740
+ app.route(
4741
+ "/api/conversations",
4742
+ createConversationRoutes({
4743
+ attachmentStorage: createVercelAttachmentStorage()
4744
+ })
4745
+ );
4583
4746
  app.route("/api/personal-tokens", createPersonalTokenRoutes());
4584
4747
  app.route("/api/people", createPeopleRoutes());
4585
4748
  app.route("/api/locations", createLocationRoutes());