@sentry/junior 0.155.0 → 0.157.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 (94) hide show
  1. package/dist/{agent-hooks-AWVTLAV2.js → agent-hooks-QI5EPOKQ.js} +10 -10
  2. package/dist/api/conversations/attachments.d.ts +32 -0
  3. package/dist/api/conversations/priority.d.ts +21 -0
  4. package/dist/api/conversations/routes.d.ts +4 -1
  5. package/dist/api/locations/detail.d.ts +4 -1
  6. package/dist/api/schema/conversation.d.ts +33 -8
  7. package/dist/api/schema/location.d.ts +4 -1
  8. package/dist/api/schema/person.d.ts +4 -1
  9. package/dist/api/schema/task.d.ts +8 -8
  10. package/dist/api/schema.js +1 -1
  11. package/dist/api.js +182 -48
  12. package/dist/app.js +128 -44
  13. package/dist/{catalog-runtime-V6YD6KH5.js → catalog-runtime-HUGHAFUN.js} +5 -5
  14. package/dist/chat/agent/types.d.ts +2 -0
  15. package/dist/chat/agent-invocations/types.d.ts +12 -12
  16. package/dist/chat/attachments/storage.d.ts +14 -0
  17. package/dist/chat/attachments/store.d.ts +68 -0
  18. package/dist/chat/attachments/vercel.d.ts +3 -0
  19. package/dist/chat/conversations/retention.d.ts +4 -0
  20. package/dist/chat/event-tasks/tool-support.d.ts +1 -1
  21. package/dist/chat/event-tasks/types.d.ts +1 -1
  22. package/dist/chat/ingress/bot-mention.d.ts +13 -0
  23. package/dist/chat/plugins/unfinished-work.d.ts +11 -0
  24. package/dist/chat/runtime/agent-runner.d.ts +2 -0
  25. package/dist/chat/scheduled-tasks/tool-support.d.ts +5 -5
  26. package/dist/chat/scheduled-tasks/tools/create.d.ts +2 -2
  27. package/dist/chat/scheduled-tasks/tools/delete.d.ts +2 -2
  28. package/dist/chat/scheduled-tasks/tools/list.d.ts +2 -2
  29. package/dist/chat/scheduled-tasks/tools/run-now.d.ts +2 -2
  30. package/dist/chat/scheduled-tasks/tools/update.d.ts +2 -2
  31. package/dist/chat/services/conversation-memory.d.ts +1 -1
  32. package/dist/chat/slack/tools/send-files.d.ts +17 -11
  33. package/dist/chat/state/conversation.d.ts +2 -1
  34. package/dist/chat/task-execution/slack-work.d.ts +2 -2
  35. package/dist/chat/tools/types.d.ts +2 -0
  36. package/dist/{chunk-773XC7P4.js → chunk-3GS7O66Z.js} +159 -128
  37. package/dist/{chunk-JFZBGFNX.js → chunk-46ADYIPE.js} +2 -1
  38. package/dist/{chunk-KTVHAHUV.js → chunk-4M25PSFB.js} +1 -1
  39. package/dist/{chunk-4K5N6HTX.js → chunk-5TIH5GOE.js} +5 -5
  40. package/dist/{chunk-TVXAN43I.js → chunk-6MI43QZO.js} +1 -1
  41. package/dist/{chunk-RN3AWYEP.js → chunk-77QB5FNI.js} +1 -1
  42. package/dist/{chunk-Y7ZUBP2K.js → chunk-7J65DOBL.js} +6 -6
  43. package/dist/{chunk-2AHVM3HK.js → chunk-AFQ47JQ7.js} +10 -8
  44. package/dist/{chunk-ELOTIL5F.js → chunk-BLCVTEE7.js} +4 -4
  45. package/dist/{chunk-ZLSFOSDK.js → chunk-CZCMFRUQ.js} +18 -1
  46. package/dist/{chunk-2WLJDM23.js → chunk-D7BNWRYL.js} +1 -1
  47. package/dist/{chunk-EORR34UB.js → chunk-F7VOY4VQ.js} +1 -1
  48. package/dist/{chunk-JJX77MB7.js → chunk-G2FN3I66.js} +3 -3
  49. package/dist/{chunk-J2SYI36O.js → chunk-GEY6IFXH.js} +4 -4
  50. package/dist/{chunk-37G5HJ2X.js → chunk-GO2W3VZY.js} +4 -4
  51. package/dist/{chunk-LQT3SWFA.js → chunk-HJ3EZHIX.js} +351 -5
  52. package/dist/{chunk-SDRJ2LE3.js → chunk-I3OQVVMV.js} +1 -1
  53. package/dist/{chunk-TKJ7277U.js → chunk-MDZQ62PB.js} +2 -2
  54. package/dist/{chunk-V57LZDHN.js → chunk-MGJW3SW4.js} +6 -6
  55. package/dist/{chunk-7NYEB2TM.js → chunk-MKA3T2R6.js} +1 -1
  56. package/dist/{chunk-RQ62VKM7.js → chunk-MXO6VFB3.js} +1 -1
  57. package/dist/{chunk-AENPRXCG.js → chunk-OWASYHHV.js} +68 -39
  58. package/dist/{chunk-UWTXAYBP.js → chunk-PP4UQTH3.js} +9 -9
  59. package/dist/{chunk-JIC33O5L.js → chunk-RQBRGPUF.js} +2 -2
  60. package/dist/{chunk-NWSKFYOH.js → chunk-S4PFOWRA.js} +1 -1
  61. package/dist/{chunk-NKFRRZIT.js → chunk-TA5R6BME.js} +19 -18
  62. package/dist/{chunk-XFZLQANQ.js → chunk-UJYA75N5.js} +2 -2
  63. package/dist/{chunk-Y6MVGU7L.js → chunk-VBKFO65J.js} +35 -10
  64. package/dist/{chunk-KBT2I5ER.js → chunk-VDAEXEBB.js} +6 -6
  65. package/dist/{chunk-J7MWZFFP.js → chunk-VIBHFGQC.js} +4 -4
  66. package/dist/{chunk-APHDJYN7.js → chunk-VYDM7HUF.js} +1 -1
  67. package/dist/{chunk-CPQBTTI6.js → chunk-WNNYOPXX.js} +5 -5
  68. package/dist/{chunk-MLBH7J7J.js → chunk-XRERMHPN.js} +1 -1
  69. package/dist/cli/chat.js +30 -30
  70. package/dist/cli/check.js +6 -6
  71. package/dist/cli/plugins.js +13 -13
  72. package/dist/cli/snapshot-create.js +9 -9
  73. package/dist/cli/upgrade.js +7 -7
  74. package/dist/db/schema/agent-invocations.d.ts +4 -4
  75. package/dist/db/schema/attachments.d.ts +178 -0
  76. package/dist/db/schema.d.ts +183 -5
  77. package/dist/{db-WT7P56AH.js → db-VQAV4PJ3.js} +7 -7
  78. package/dist/handlers/retention.d.ts +4 -1
  79. package/dist/instrumentation.js +4 -4
  80. package/dist/nitro.js +2 -2
  81. package/dist/{oauth-callback-server-R2PK2MCA.js → oauth-callback-server-K5KQAAAX.js} +23 -23
  82. package/dist/{runner-O7YYUIHT.js → runner-U7QI7P4S.js} +14 -15
  83. package/dist/{skills-S6HLQRH7.js → skills-ENKRBZ7K.js} +6 -6
  84. package/dist/spawn-4XGNONLS.js +37 -0
  85. package/dist/{validation-46WD2OAR.js → validation-VKCDVGA6.js} +6 -6
  86. package/dist/version.js +1 -1
  87. package/dist/{work-YT5QUBS3.js → work-IBD6PWP2.js} +17 -17
  88. package/dist/worker-TJFXOZRC.js +24 -0
  89. package/migrations/0027_attachment_storage.sql +16 -0
  90. package/migrations/meta/0027_snapshot.json +2554 -0
  91. package/migrations/meta/_journal.json +8 -1
  92. package/package.json +5 -4
  93. package/dist/spawn-B4SBFFA2.js +0 -37
  94. package/dist/worker-4C5BEDWN.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-ZLSFOSDK.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-Y6MVGU7L.js";
47
+ } from "./chunk-VBKFO65J.js";
46
48
  import {
47
49
  buildSentryConversationUrl,
48
50
  getWebAuthorization
49
- } from "./chunk-KTVHAHUV.js";
51
+ } from "./chunk-4M25PSFB.js";
50
52
  import {
51
53
  getConversation
52
- } from "./chunk-ELOTIL5F.js";
54
+ } from "./chunk-BLCVTEE7.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-UWTXAYBP.js";
61
- import "./chunk-4K5N6HTX.js";
62
+ } from "./chunk-PP4UQTH3.js";
63
+ import "./chunk-5TIH5GOE.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-LQT3SWFA.js";
88
+ } from "./chunk-HJ3EZHIX.js";
86
89
  import {
87
90
  parseSlackThreadId
88
- } from "./chunk-APHDJYN7.js";
91
+ } from "./chunk-VYDM7HUF.js";
89
92
  import {
90
93
  agentTurnUsageSchema,
91
94
  fallbackShortTitle,
92
95
  hasAgentTurnUsage,
93
96
  renderJuniorNativeConversationEvent
94
- } from "./chunk-J2SYI36O.js";
97
+ } from "./chunk-GEY6IFXH.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-V57LZDHN.js";
107
+ } from "./chunk-MGJW3SW4.js";
105
108
  import {
106
109
  getSlackClient,
107
110
  withSlackRetries
108
- } from "./chunk-TKJ7277U.js";
111
+ } from "./chunk-MDZQ62PB.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-JJX77MB7.js";
119
+ } from "./chunk-G2FN3I66.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-773XC7P4.js";
127
+ } from "./chunk-3GS7O66Z.js";
125
128
  import "./chunk-EM225ITJ.js";
126
- import "./chunk-SDRJ2LE3.js";
127
- import "./chunk-XFZLQANQ.js";
129
+ import "./chunk-I3OQVVMV.js";
130
+ import "./chunk-UJYA75N5.js";
128
131
  import {
129
132
  juniorDestinations
130
133
  } from "./chunk-VFUK3X5B.js";
131
134
  import {
132
135
  canExposeConversationPayload
133
- } from "./chunk-2WLJDM23.js";
136
+ } from "./chunk-D7BNWRYL.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-7NYEB2TM.js";
146
+ } from "./chunk-MKA3T2R6.js";
144
147
  import "./chunk-6GWA276C.js";
145
- import "./chunk-EORR34UB.js";
146
- import "./chunk-JFZBGFNX.js";
148
+ import "./chunk-F7VOY4VQ.js";
149
+ import "./chunk-46ADYIPE.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();
@@ -909,7 +953,7 @@ function reportEventData(args) {
909
953
  type: "message",
910
954
  messageId: data.messageId,
911
955
  role: data.role,
912
- ...data.meta?.source === "web" ? { source: "web" } : {},
956
+ ...data.meta?.source === "web" || data.meta?.source === "slack" ? { source: data.meta.source } : {},
913
957
  ...actorIdentity ? { actorIdentity } : {},
914
958
  ...typeof data.meta?.eventType === "string" ? { eventType: data.meta.eventType } : {},
915
959
  ...typeof data.meta?.explicitMention === "boolean" ? { explicitMention: data.meta.explicitMention } : {},
@@ -1605,9 +1649,13 @@ async function readConversationAuxiliaryCostsFromSql(db, conversationIds, option
1605
1649
  }
1606
1650
 
1607
1651
  // src/chat/plugins/unfinished-work.ts
1608
- async function listUnfinishedWork(conversationIds) {
1609
- if (conversationIds.length === 0) return [];
1652
+ async function listConversationWork(conversationIds) {
1653
+ if (conversationIds.length === 0) {
1654
+ return { assignedIds: [], finishedAtById: {}, unfinishedIds: [] };
1655
+ }
1610
1656
  const candidates = new Set(conversationIds);
1657
+ const assigned = /* @__PURE__ */ new Set();
1658
+ const finishedAtById = /* @__PURE__ */ new Map();
1611
1659
  const unfinished = /* @__PURE__ */ new Set();
1612
1660
  for (const plugin of getPlugins()) {
1613
1661
  const hook = plugin.hooks?.unfinishedWork;
@@ -1620,21 +1668,61 @@ async function listUnfinishedWork(conversationIds) {
1620
1668
  plugin: { name: plugin.manifest.name }
1621
1669
  });
1622
1670
  for (const conversationId of result.conversationIds) {
1623
- if (candidates.has(conversationId)) unfinished.add(conversationId);
1671
+ if (!candidates.has(conversationId)) continue;
1672
+ unfinished.add(conversationId);
1673
+ assigned.add(conversationId);
1674
+ }
1675
+ for (const conversationId of result.assignedConversationIds ?? []) {
1676
+ if (candidates.has(conversationId)) assigned.add(conversationId);
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
+ }
1624
1688
  }
1625
1689
  } catch (error) {
1626
1690
  logWarn("plugin.unfinished_work.hook.failed", {
1627
1691
  "app.plugin.name": plugin.manifest.name,
1628
1692
  "exception.message": error instanceof Error ? error.message : String(error)
1629
1693
  });
1630
- for (const conversationId of conversationIds) {
1631
- unfinished.add(conversationId);
1632
- }
1633
1694
  }
1634
1695
  }
1635
- return conversationIds.filter(
1636
- (conversationId) => unfinished.has(conversationId)
1637
- );
1696
+ return {
1697
+ assignedIds: conversationIds.filter(
1698
+ (conversationId) => assigned.has(conversationId)
1699
+ ),
1700
+ finishedAtById: Object.fromEntries(
1701
+ conversationIds.flatMap((conversationId) => {
1702
+ const finishedAt = finishedAtById.get(conversationId);
1703
+ return finishedAt ? [[conversationId, finishedAt]] : [];
1704
+ })
1705
+ ),
1706
+ unfinishedIds: conversationIds.filter(
1707
+ (conversationId) => unfinished.has(conversationId)
1708
+ )
1709
+ };
1710
+ }
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
+ return Number.isFinite(finishedAt) && lastSeenAt > finishedAt;
1724
+ }
1725
+ return nowMs - lastSeenAt <= UNASSIGNED_PRIORITY_WINDOW_MS;
1638
1726
  }
1639
1727
 
1640
1728
  // src/api/conversations/list.ts
@@ -1777,7 +1865,7 @@ async function readConversationFeedFromSql(options = {}) {
1777
1865
  auxiliaryCostsByRoot,
1778
1866
  metricsByRoot,
1779
1867
  teamDomainByTeamId,
1780
- unfinishedWorkIds
1868
+ conversationWork
1781
1869
  ] = await Promise.all([
1782
1870
  readConversationAccessFromSql(db, conversationIds, options.viewer),
1783
1871
  readConversationAuxiliaryCostsFromSql(db, conversationIds, {
@@ -1789,25 +1877,41 @@ async function readConversationFeedFromSql(options = {}) {
1789
1877
  (conversation) => conversation.sessionSource?.platform === "slack" ? [conversation.sessionSource.teamId] : []
1790
1878
  )
1791
1879
  ),
1792
- listUnfinishedWork(conversationIds)
1880
+ listConversationWork(conversationIds)
1793
1881
  ]);
1794
- const unfinishedWork = new Set(unfinishedWorkIds);
1882
+ const assignedWork = new Set(conversationWork.assignedIds);
1883
+ const unfinishedWork = new Set(conversationWork.unfinishedIds);
1795
1884
  return {
1796
1885
  conversations: conversations.map((conversation, index) => {
1797
1886
  const row = rows[index];
1798
1887
  const metrics = metricsByRoot.get(conversation.conversationId);
1799
- return {
1800
- ...conversationSummaryFromStoredConversation({
1801
- conversation,
1802
- access: accessByConversation.get(conversation.conversationId),
1803
- auxiliaryCosts: auxiliaryCostsByRoot.get(conversation.conversationId),
1804
- durationMs: metrics?.durationMs ?? row.conversation.durationMs,
1805
- teamDomainByTeamId,
1806
- ...row.destination?.visibility === "public" ? { locationId: row.destination.id } : {},
1807
- usage: metrics?.usage ?? row.conversation.usage ?? void 0
1808
- }),
1888
+ const summary = conversationSummaryFromStoredConversation({
1889
+ conversation,
1890
+ access: accessByConversation.get(conversation.conversationId),
1891
+ auxiliaryCosts: auxiliaryCostsByRoot.get(conversation.conversationId),
1892
+ durationMs: metrics?.durationMs ?? row.conversation.durationMs,
1893
+ teamDomainByTeamId,
1894
+ ...row.destination?.visibility === "public" ? { locationId: row.destination.id } : {},
1895
+ usage: metrics?.usage ?? row.conversation.usage ?? void 0
1896
+ });
1897
+ const work = {
1898
+ ...assignedWork.has(conversation.conversationId) ? { assignedWork: true } : {},
1899
+ ...conversationWork.finishedAtById[conversation.conversationId] ? {
1900
+ finishedWorkAt: conversationWork.finishedAtById[conversation.conversationId]
1901
+ } : {},
1809
1902
  ...unfinishedWork.has(conversation.conversationId) ? { unfinishedWork: true } : {}
1810
1903
  };
1904
+ return {
1905
+ ...summary,
1906
+ ...work,
1907
+ isPriority: isConversationPriority(
1908
+ {
1909
+ lastSeenAt: summary.lastSeenAt,
1910
+ ...work
1911
+ },
1912
+ nowMs
1913
+ )
1914
+ };
1811
1915
  }),
1812
1916
  generatedAt: new Date(nowMs).toISOString(),
1813
1917
  source: "conversation_index"
@@ -3004,7 +3108,7 @@ async function readConversationStats() {
3004
3108
  }
3005
3109
 
3006
3110
  // src/api/conversations/routes.ts
3007
- function createConversationRoutes() {
3111
+ function createConversationRoutes(options) {
3008
3112
  const app = new Hono();
3009
3113
  app.get(
3010
3114
  "/",
@@ -3132,6 +3236,31 @@ function createConversationRoutes() {
3132
3236
  );
3133
3237
  }
3134
3238
  );
3239
+ app.get(
3240
+ "/:conversationId/attachments/:attachmentId",
3241
+ validateRequest(
3242
+ "param",
3243
+ conversationAttachmentParamsSchema,
3244
+ "Invalid route parameters."
3245
+ ),
3246
+ async (context) => {
3247
+ const { attachmentId, conversationId } = context.req.valid("param");
3248
+ const viewer = context.get("viewer");
3249
+ const opened = await requireConversationAttachment({
3250
+ attachmentId,
3251
+ conversationId,
3252
+ storage: options.attachmentStorage,
3253
+ ...viewer ? { viewer } : {}
3254
+ });
3255
+ return new Response(opened.body, {
3256
+ headers: conversationAttachmentHeaders({
3257
+ bytes: opened.attachment.bytes,
3258
+ contentType: opened.attachment.contentType,
3259
+ filename: opened.attachment.filename
3260
+ })
3261
+ });
3262
+ }
3263
+ );
3135
3264
  app.get(
3136
3265
  "/:conversationId",
3137
3266
  validateRequest(
@@ -4490,7 +4619,7 @@ function readDescriptionText(home) {
4490
4619
  }
4491
4620
  }
4492
4621
  async function readSkillReports() {
4493
- const { discoverSkills } = await import("./skills-S6HLQRH7.js");
4622
+ const { discoverSkills } = await import("./skills-ENKRBZ7K.js");
4494
4623
  const skills = await discoverSkills();
4495
4624
  return skillReportsSchema.parse(
4496
4625
  skills.map((skill) => ({
@@ -4500,7 +4629,7 @@ async function readSkillReports() {
4500
4629
  );
4501
4630
  }
4502
4631
  async function readPlugins() {
4503
- const { pluginCatalogRuntime } = await import("./catalog-runtime-V6YD6KH5.js");
4632
+ const { pluginCatalogRuntime } = await import("./catalog-runtime-HUGHAFUN.js");
4504
4633
  return pluginsSchema.parse(
4505
4634
  pluginCatalogRuntime.getProviders().map((plugin) => ({
4506
4635
  configKeys: plugin.manifest.configKeys ?? [],
@@ -4513,7 +4642,7 @@ async function readPlugins() {
4513
4642
  async function readRuntimeInfoReport() {
4514
4643
  const [{ homeDir }, { pluginCatalogRuntime }, plugins, skills] = await Promise.all([
4515
4644
  import("./discovery-GUFTFBTL.js"),
4516
- import("./catalog-runtime-V6YD6KH5.js"),
4645
+ import("./catalog-runtime-HUGHAFUN.js"),
4517
4646
  readPlugins(),
4518
4647
  readSkillReports()
4519
4648
  ]);
@@ -4529,7 +4658,7 @@ async function readRuntimeInfoReport() {
4529
4658
  }
4530
4659
  async function readPluginOperationalReportFeed() {
4531
4660
  const nowMs = Date.now();
4532
- const { getPluginOperationalReports } = await import("./agent-hooks-AWVTLAV2.js");
4661
+ const { getPluginOperationalReports } = await import("./agent-hooks-QI5EPOKQ.js");
4533
4662
  return pluginOperationalReportFeedSchema.parse({
4534
4663
  source: "plugins",
4535
4664
  generatedAt: new Date(nowMs).toISOString(),
@@ -4567,7 +4696,12 @@ function createJuniorApi() {
4567
4696
  "/api/stats",
4568
4697
  async () => jsonResponse(statsReportSchema, await readStatsReport())
4569
4698
  );
4570
- app.route("/api/conversations", createConversationRoutes());
4699
+ app.route(
4700
+ "/api/conversations",
4701
+ createConversationRoutes({
4702
+ attachmentStorage: createVercelAttachmentStorage()
4703
+ })
4704
+ );
4571
4705
  app.route("/api/personal-tokens", createPersonalTokenRoutes());
4572
4706
  app.route("/api/people", createPeopleRoutes());
4573
4707
  app.route("/api/locations", createLocationRoutes());