@sentry/junior 0.154.0 → 0.156.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 (96) hide show
  1. package/dist/{agent-hooks-VEHV2QUA.js → agent-hooks-CCAR46PQ.js} +11 -12
  2. package/dist/api/conversations/list.d.ts +5 -3
  3. package/dist/api/conversations/membership.d.ts +16 -0
  4. package/dist/api/locations/detail.d.ts +2 -0
  5. package/dist/api/schema/conversation.d.ts +22 -4
  6. package/dist/api/schema/location.d.ts +2 -0
  7. package/dist/api/schema/person.d.ts +2 -0
  8. package/dist/api/schema.js +1 -1
  9. package/dist/api.js +331 -188
  10. package/dist/app.js +154 -51
  11. package/dist/catalog-runtime-3SNKYCBG.js +16 -0
  12. package/dist/chat/agent/types.d.ts +2 -0
  13. package/dist/chat/attachments/storage.d.ts +12 -0
  14. package/dist/chat/attachments/store.d.ts +52 -0
  15. package/dist/chat/attachments/vercel.d.ts +3 -0
  16. package/dist/chat/conversations/retention.d.ts +4 -0
  17. package/dist/chat/conversations/sql/event-actor.d.ts +14 -0
  18. package/dist/chat/conversations/sql/event-lock.d.ts +1 -1
  19. package/dist/chat/conversations/sql/participants.d.ts +17 -0
  20. package/dist/chat/ingress/bot-mention.d.ts +13 -0
  21. package/dist/chat/plugins/unfinished-work.d.ts +8 -0
  22. package/dist/chat/runtime/agent-runner.d.ts +4 -0
  23. package/dist/chat/runtime/slack-resume.d.ts +1 -1
  24. package/dist/chat/services/conversation-memory.d.ts +1 -1
  25. package/dist/chat/slack/tools/send-files.d.ts +17 -11
  26. package/dist/chat/state/conversation.d.ts +3 -1
  27. package/dist/chat/tools/types.d.ts +2 -0
  28. package/dist/{chunk-XYMCZGH6.js → chunk-3FFUC32L.js} +8 -10
  29. package/dist/{chunk-4U4MNYGP.js → chunk-3L2PMOSR.js} +6 -8
  30. package/dist/{chunk-PTJ75ZWR.js → chunk-4ZZDTHFC.js} +94 -9
  31. package/dist/{chunk-DAJZZFU7.js → chunk-53W3LZEW.js} +386 -93
  32. package/dist/{chunk-F7QUK3YP.js → chunk-6CINHXIL.js} +5 -5
  33. package/dist/{chunk-6VWMX7NS.js → chunk-6JUU7J2B.js} +4 -4
  34. package/dist/{chunk-HUO3UTAQ.js → chunk-7MA4HV4W.js} +1 -1
  35. package/dist/{chunk-O4AQDOKA.js → chunk-7RAETIYO.js} +7 -7
  36. package/dist/{chunk-4R2XCKUH.js → chunk-A6WPDFHQ.js} +1 -1
  37. package/dist/{chunk-CZD6M57V.js → chunk-AIJRG227.js} +191 -126
  38. package/dist/{chunk-RUIXF3YQ.js → chunk-BDUENQD3.js} +1 -1
  39. package/dist/{chunk-NUG3VYHL.js → chunk-CK2H7CFO.js} +2 -2
  40. package/dist/{chunk-HOGJ6TBT.js → chunk-CN2XSVKF.js} +6 -6
  41. package/dist/{chunk-3567VGCN.js → chunk-EDJ64P2P.js} +1 -1
  42. package/dist/{chunk-6PJXR4BC.js → chunk-GKC43TLS.js} +2 -2
  43. package/dist/{chunk-A6LWN4K6.js → chunk-GSBPZHLQ.js} +11 -11
  44. package/dist/{chunk-Y654CTW2.js → chunk-IIO5PFZX.js} +366 -138
  45. package/dist/{chunk-F2Q3ZHW2.js → chunk-IZEBHYI4.js} +1 -1
  46. package/dist/{chunk-QGR6QTC4.js → chunk-JUJR6WTL.js} +4 -4
  47. package/dist/{chunk-ZQT4RHUO.js → chunk-KBGMYLSV.js} +4 -1
  48. package/dist/{chunk-DK2FSQHS.js → chunk-KCA5MKHU.js} +5 -5
  49. package/dist/{chunk-NNN2WYAD.js → chunk-MC57E5U5.js} +1 -1
  50. package/dist/{chunk-6AF5GOZA.js → chunk-MJE7XKWP.js} +1 -1
  51. package/dist/{chunk-7CPN5JTA.js → chunk-NTNU6T2D.js} +3 -3
  52. package/dist/{chunk-NG4IAOVD.js → chunk-OGZPRMZA.js} +1 -1
  53. package/dist/{chunk-UCH55IGZ.js → chunk-OMQJB7YN.js} +28 -37
  54. package/dist/{chunk-2XNVS6RC.js → chunk-PMFGVLDU.js} +12 -11
  55. package/dist/{chunk-65IAU2L2.js → chunk-SE3D225X.js} +1 -1
  56. package/dist/{chunk-CCAU73CE.js → chunk-SRJA6QLM.js} +2 -2
  57. package/dist/{chunk-7RFWIHPM.js → chunk-TN4LVILT.js} +5 -5
  58. package/dist/{chunk-3ZR2R5EQ.js → chunk-TNXGMFWV.js} +1 -1
  59. package/dist/{chunk-HZF5P5UN.js → chunk-V7YE4WCP.js} +58 -7
  60. package/dist/{chunk-MSOZLASC.js → chunk-WD7SEUZO.js} +2 -2
  61. package/dist/{chunk-UG5UCHHJ.js → chunk-X5NZQH5U.js} +4 -2
  62. package/dist/{chunk-3HX67UPN.js → chunk-X7W45B5G.js} +1 -1
  63. package/dist/{chunk-FVY7AJ46.js → chunk-ZNWRYNCA.js} +7 -7
  64. package/dist/cli/chat.js +36 -36
  65. package/dist/cli/check.js +8 -9
  66. package/dist/cli/init.js +41 -7
  67. package/dist/cli/plugins.js +15 -16
  68. package/dist/cli/snapshot-create.js +11 -12
  69. package/dist/cli/upgrade.js +9 -10
  70. package/dist/db/schema/attachments.d.ts +178 -0
  71. package/dist/db/schema/conversation-events.d.ts +20 -0
  72. package/dist/db/schema/conversation-participants.d.ts +63 -0
  73. package/dist/db/schema.d.ts +255 -1
  74. package/dist/{db-SOJJLMJK.js → db-TYUUKGJX.js} +8 -8
  75. package/dist/handlers/retention.d.ts +4 -1
  76. package/dist/instrumentation.js +4 -4
  77. package/dist/nitro.js +2 -2
  78. package/dist/{oauth-callback-server-GCJROMVO.js → oauth-callback-server-AWVDKPR5.js} +26 -26
  79. package/dist/{runner-SY6KDQCN.js → runner-JUQXMACC.js} +15 -16
  80. package/dist/{sandbox-egress-signals-EKPAVWQL.js → sandbox-egress-signals-U3ZX7C5W.js} +3 -4
  81. package/dist/{skills-XJ2LJ2ML.js → skills-4GZTZX4V.js} +8 -9
  82. package/dist/spawn-32SSRWU4.js +37 -0
  83. package/dist/{validation-XQWY6QRY.js → validation-DRLNJRND.js} +8 -9
  84. package/dist/version.js +1 -1
  85. package/dist/{work-FFT5JAOU.js → work-TGIXRRJY.js} +20 -21
  86. package/dist/worker-IFXZOYED.js +24 -0
  87. package/migrations/0026_conversation_event_actor_and_participants.sql +140 -0
  88. package/migrations/0027_attachment_storage.sql +16 -0
  89. package/migrations/meta/0026_snapshot.json +2424 -0
  90. package/migrations/meta/0027_snapshot.json +2554 -0
  91. package/migrations/meta/_journal.json +15 -1
  92. package/package.json +10 -7
  93. package/dist/catalog-runtime-AK5MY63F.js +0 -17
  94. package/dist/chunk-F3B2YJ76.js +0 -57
  95. package/dist/spawn-QFCQREEP.js +0 -38
  96. package/dist/worker-JAITK72F.js +0 -25
@@ -7,7 +7,7 @@ import {
7
7
  PluginCredentialFailureError,
8
8
  agentNameSchema,
9
9
  createPluginAuthOrchestration
10
- } from "./chunk-FVY7AJ46.js";
10
+ } from "./chunk-ZNWRYNCA.js";
11
11
  import {
12
12
  authorizationId
13
13
  } from "./chunk-DIB4GX3G.js";
@@ -32,7 +32,7 @@ import {
32
32
  searchSlackUsers,
33
33
  selectPluginGrant,
34
34
  toMcpProviderError
35
- } from "./chunk-PTJ75ZWR.js";
35
+ } from "./chunk-4ZZDTHFC.js";
36
36
  import {
37
37
  RetryableDeliveryError,
38
38
  actorFromRun,
@@ -48,16 +48,13 @@ import {
48
48
  getSlackMessagePermalink,
49
49
  resolveBaseUrl,
50
50
  uploadFilesToConversation
51
- } from "./chunk-O4AQDOKA.js";
51
+ } from "./chunk-7RAETIYO.js";
52
52
  import {
53
53
  parseSandboxEgressAuthRequiredSignal,
54
54
  parseSandboxEgressPermissionDeniedSignal,
55
55
  sandboxEgressCredentialContextSchema,
56
56
  sandboxEgressCredentialLeaseSchema
57
- } from "./chunk-HUO3UTAQ.js";
58
- import {
59
- isExperimentalFeatureEnabled
60
- } from "./chunk-5IDFPCON.js";
57
+ } from "./chunk-7MA4HV4W.js";
61
58
  import {
62
59
  bindSandboxFileSystem,
63
60
  buildCommandScript,
@@ -77,7 +74,7 @@ import {
77
74
  runNonInteractiveCommand,
78
75
  throwSandboxOperationError,
79
76
  wrapSandboxSetupError
80
- } from "./chunk-6VWMX7NS.js";
77
+ } from "./chunk-6JUU7J2B.js";
81
78
  import {
82
79
  SCHEDULED_TASK_SYSTEM_ACTOR,
83
80
  compactEventTask,
@@ -109,10 +106,10 @@ import {
109
106
  saveEventTask,
110
107
  scheduledTaskAttributes,
111
108
  writableEventTask
112
- } from "./chunk-DK2FSQHS.js";
109
+ } from "./chunk-KCA5MKHU.js";
113
110
  import {
114
111
  parseSlackThreadId
115
- } from "./chunk-NUG3VYHL.js";
112
+ } from "./chunk-CK2H7CFO.js";
116
113
  import {
117
114
  AGENTS_REMOVAL_NOTICE,
118
115
  AGENTS_REPLACEMENT_NOTICE,
@@ -182,7 +179,7 @@ import {
182
179
  stripRuntimeTurnContext,
183
180
  trimTrailingAssistantMessages,
184
181
  truncateText
185
- } from "./chunk-4U4MNYGP.js";
182
+ } from "./chunk-3L2PMOSR.js";
186
183
  import {
187
184
  JUNIOR_THREAD_STATE_TTL_MS
188
185
  } from "./chunk-3Y47VMHV.js";
@@ -198,7 +195,7 @@ import {
198
195
  getSlackToolContext,
199
196
  readActorIdentity,
200
197
  resolveChannelCapabilities
201
- } from "./chunk-XYMCZGH6.js";
198
+ } from "./chunk-3FFUC32L.js";
202
199
  import {
203
200
  SlackActionError,
204
201
  downloadPrivateSlackFile,
@@ -207,7 +204,7 @@ import {
207
204
  isConversationScopedChannel,
208
205
  normalizeSlackConversationId,
209
206
  withSlackRetries
210
- } from "./chunk-7CPN5JTA.js";
207
+ } from "./chunk-NTNU6T2D.js";
211
208
  import {
212
209
  KNOWN_CONVERSATION_EVENT_TYPES,
213
210
  contextProvenance,
@@ -219,10 +216,12 @@ import {
219
216
  instructionActors,
220
217
  instructionProvenanceFor,
221
218
  sameActorIdentity
222
- } from "./chunk-Y654CTW2.js";
219
+ } from "./chunk-IIO5PFZX.js";
223
220
  import {
221
+ juniorAttachments,
222
+ juniorConversations,
224
223
  juniorIdentities
225
- } from "./chunk-CZD6M57V.js";
224
+ } from "./chunk-AIJRG227.js";
226
225
  import {
227
226
  SANDBOX_DATA_ROOT,
228
227
  SANDBOX_SKILLS_ROOT,
@@ -234,7 +233,7 @@ import {
234
233
  } from "./chunk-EM225ITJ.js";
235
234
  import {
236
235
  getStateAdapter
237
- } from "./chunk-NNN2WYAD.js";
236
+ } from "./chunk-MC57E5U5.js";
238
237
  import {
239
238
  ACTIVE_TURN_COMPACTION_SUMMARY_PREFIX,
240
239
  COMPACTION_SUMMARY_PREFIX,
@@ -267,7 +266,7 @@ import {
267
266
  resolveGatewayCredential,
268
267
  resolveGatewayModel,
269
268
  unwrapCurrentInstruction
270
- } from "./chunk-6PJXR4BC.js";
269
+ } from "./chunk-GKC43TLS.js";
271
270
  import {
272
271
  canExposeConversationPayload,
273
272
  resolveConversationPrivacy,
@@ -277,16 +276,16 @@ import {
277
276
  toGenAiMessagesTraceAttributes,
278
277
  toGenAiPayloadMetadata,
279
278
  toGenAiPayloadTraceAttributes
280
- } from "./chunk-6AF5GOZA.js";
279
+ } from "./chunk-MJE7XKWP.js";
281
280
  import {
282
281
  discoverSkills,
283
282
  findSkillByName,
284
283
  loadSkillsByName,
285
284
  parseSkillInvocation
286
- } from "./chunk-CCAU73CE.js";
285
+ } from "./chunk-SRJA6QLM.js";
287
286
  import {
288
287
  pluginCatalogRuntime
289
- } from "./chunk-3567VGCN.js";
288
+ } from "./chunk-EDJ64P2P.js";
290
289
  import {
291
290
  CredentialUnavailableError,
292
291
  OAuthProviderError,
@@ -294,18 +293,16 @@ import {
294
293
  getOAuthProviderErrorAttributes,
295
294
  resolveAuthTokenPlaceholder,
296
295
  resolvePluginCommandEnv
297
- } from "./chunk-MSOZLASC.js";
296
+ } from "./chunk-WD7SEUZO.js";
298
297
  import {
299
298
  credentialUserSubjectId
300
- } from "./chunk-RUIXF3YQ.js";
301
- import {
302
- isUserActor
303
- } from "./chunk-HZF5P5UN.js";
299
+ } from "./chunk-BDUENQD3.js";
304
300
  import {
301
+ isUserActor,
305
302
  parseSlackChannelId,
306
303
  parseSlackMessageTs,
307
304
  parseSlackUserId
308
- } from "./chunk-F3B2YJ76.js";
305
+ } from "./chunk-V7YE4WCP.js";
309
306
  import {
310
307
  extractGenAiUsageAttributes,
311
308
  extractGenAiUsageSummary,
@@ -324,7 +321,7 @@ import {
324
321
  summarizeMessageText,
325
322
  withLogContext,
326
323
  withSpan
327
- } from "./chunk-3HX67UPN.js";
324
+ } from "./chunk-X7W45B5G.js";
328
325
  import {
329
326
  startInactiveSpan,
330
327
  withActiveSpan
@@ -10540,15 +10537,327 @@ function createSlackMessageAddReactionTool(context, state) {
10540
10537
  }
10541
10538
 
10542
10539
  // src/chat/slack/tools/send-files.ts
10543
- import { createHash as createHash4 } from "crypto";
10540
+ import { createHash as createHash5 } from "crypto";
10541
+
10542
+ // src/chat/attachments/store.ts
10543
+ import { createHash as createHash4, randomUUID as randomUUID3 } from "crypto";
10544
+ import { and, asc, eq, inArray, isNotNull, isNull, or } from "drizzle-orm";
10545
+ var ATTACHMENT_GC_BATCH_LIMIT = 200;
10546
+ function attachmentDigest(file) {
10547
+ return createHash4("sha256").update(file.data).digest("hex");
10548
+ }
10549
+ function attachmentId(args) {
10550
+ return createHash4("sha256").update(
10551
+ [
10552
+ args.conversationId,
10553
+ args.provider,
10554
+ args.sha256,
10555
+ args.filename,
10556
+ args.contentType
10557
+ ].join("\0")
10558
+ ).digest("hex");
10559
+ }
10560
+ function attachmentKey(args) {
10561
+ return `conversations/${args.conversationId}/attachments/${args.attachmentId}/${args.writeId}/${args.filename}`;
10562
+ }
10563
+ async function bestEffortDelete(storage, key) {
10564
+ try {
10565
+ await storage.delete([key]);
10566
+ } catch {
10567
+ }
10568
+ }
10569
+ async function isConversationPurged(db, conversationId) {
10570
+ const [row] = await db.db().select({
10571
+ transcriptPurgedAt: juniorConversations.transcriptPurgedAt
10572
+ }).from(juniorConversations).where(eq(juniorConversations.conversationId, conversationId));
10573
+ return row?.transcriptPurgedAt != null;
10574
+ }
10575
+ function purgedConversationError(conversationId) {
10576
+ return new Error(
10577
+ `Cannot store attachment for purged conversation ${conversationId}`
10578
+ );
10579
+ }
10580
+ async function assertConversationNotPurged(db, conversationId) {
10581
+ if (await isConversationPurged(db, conversationId)) {
10582
+ throw purgedConversationError(conversationId);
10583
+ }
10584
+ }
10585
+ async function rejectPurgedAttachmentWrite(args) {
10586
+ await args.db.db().update(juniorAttachments).set({ deleteRequestedAt: args.now }).where(eq(juniorAttachments.id, args.id));
10587
+ await bestEffortDelete(args.storage, args.storageKey);
10588
+ throw purgedConversationError(args.conversationId);
10589
+ }
10590
+ async function reuseLiveAttachment(args) {
10591
+ await bestEffortDelete(args.storage, args.storageKey);
10592
+ await assertConversationNotPurged(args.db, args.conversationId);
10593
+ return { id: args.id };
10594
+ }
10595
+ async function storeAttachment(args) {
10596
+ const now = new Date(args.nowMs ?? Date.now());
10597
+ const sha256 = attachmentDigest(args.file);
10598
+ const id = attachmentId({
10599
+ conversationId: args.conversationId,
10600
+ contentType: args.file.mimeType,
10601
+ filename: args.file.filename,
10602
+ provider: args.storage.provider,
10603
+ sha256
10604
+ });
10605
+ const [existing] = await args.db.db().select({
10606
+ contentType: juniorAttachments.contentType,
10607
+ conversationId: juniorAttachments.conversationId,
10608
+ deleteRequestedAt: juniorAttachments.deleteRequestedAt,
10609
+ filename: juniorAttachments.filename,
10610
+ id: juniorAttachments.id,
10611
+ provider: juniorAttachments.provider,
10612
+ sha256: juniorAttachments.sha256,
10613
+ storageKey: juniorAttachments.storageKey
10614
+ }).from(juniorAttachments).where(eq(juniorAttachments.id, id));
10615
+ if (existing) {
10616
+ if (existing.contentType !== args.file.mimeType || existing.conversationId !== args.conversationId || existing.filename !== args.file.filename || existing.provider !== args.storage.provider || existing.sha256 !== sha256) {
10617
+ throw new Error(`Attachment write conflicts with ${existing.id}`);
10618
+ }
10619
+ if (existing.deleteRequestedAt === null) {
10620
+ await assertConversationNotPurged(args.db, args.conversationId);
10621
+ return { id: existing.id };
10622
+ }
10623
+ }
10624
+ await assertConversationNotPurged(args.db, args.conversationId);
10625
+ const storageKey = attachmentKey({
10626
+ attachmentId: id,
10627
+ conversationId: args.conversationId,
10628
+ filename: args.file.filename,
10629
+ writeId: randomUUID3()
10630
+ });
10631
+ await args.storage.put({
10632
+ body: args.file.data,
10633
+ contentType: args.file.mimeType,
10634
+ key: storageKey
10635
+ });
10636
+ if (existing?.deleteRequestedAt != null) {
10637
+ if (await isConversationPurged(args.db, args.conversationId)) {
10638
+ await bestEffortDelete(args.storage, storageKey);
10639
+ throw purgedConversationError(args.conversationId);
10640
+ }
10641
+ const oldStorageKey = existing.storageKey;
10642
+ const revived2 = await args.db.db().update(juniorAttachments).set({
10643
+ deleteRequestedAt: null,
10644
+ storageKey
10645
+ }).where(
10646
+ and(
10647
+ eq(juniorAttachments.id, id),
10648
+ isNotNull(juniorAttachments.deleteRequestedAt)
10649
+ )
10650
+ ).returning({ id: juniorAttachments.id });
10651
+ if (revived2.length > 0) {
10652
+ if (await isConversationPurged(args.db, args.conversationId)) {
10653
+ await rejectPurgedAttachmentWrite({
10654
+ conversationId: args.conversationId,
10655
+ db: args.db,
10656
+ id,
10657
+ now,
10658
+ storage: args.storage,
10659
+ storageKey
10660
+ });
10661
+ }
10662
+ if (oldStorageKey !== storageKey) {
10663
+ await bestEffortDelete(args.storage, oldStorageKey);
10664
+ }
10665
+ return { id };
10666
+ }
10667
+ const [afterRace2] = await args.db.db().select({
10668
+ deleteRequestedAt: juniorAttachments.deleteRequestedAt,
10669
+ id: juniorAttachments.id,
10670
+ storageKey: juniorAttachments.storageKey
10671
+ }).from(juniorAttachments).where(eq(juniorAttachments.id, id));
10672
+ if (afterRace2?.deleteRequestedAt === null) {
10673
+ return reuseLiveAttachment({
10674
+ conversationId: args.conversationId,
10675
+ db: args.db,
10676
+ id: afterRace2.id,
10677
+ storage: args.storage,
10678
+ storageKey
10679
+ });
10680
+ }
10681
+ }
10682
+ let inserted = [];
10683
+ try {
10684
+ inserted = await args.db.db().insert(juniorAttachments).values({
10685
+ id,
10686
+ conversationId: args.conversationId,
10687
+ provider: args.storage.provider,
10688
+ storageKey,
10689
+ filename: args.file.filename,
10690
+ contentType: args.file.mimeType,
10691
+ bytes: args.file.bytes,
10692
+ sha256,
10693
+ createdAt: now
10694
+ }).onConflictDoNothing({ target: juniorAttachments.id }).returning({ id: juniorAttachments.id });
10695
+ } catch (error) {
10696
+ await bestEffortDelete(args.storage, storageKey);
10697
+ throw error;
10698
+ }
10699
+ if (inserted.length > 0) {
10700
+ if (await isConversationPurged(args.db, args.conversationId)) {
10701
+ await rejectPurgedAttachmentWrite({
10702
+ conversationId: args.conversationId,
10703
+ db: args.db,
10704
+ id,
10705
+ now,
10706
+ storage: args.storage,
10707
+ storageKey
10708
+ });
10709
+ }
10710
+ return { id };
10711
+ }
10712
+ const [row] = await args.db.db().select({
10713
+ deleteRequestedAt: juniorAttachments.deleteRequestedAt,
10714
+ id: juniorAttachments.id,
10715
+ storageKey: juniorAttachments.storageKey
10716
+ }).from(juniorAttachments).where(eq(juniorAttachments.id, id));
10717
+ if (!row) {
10718
+ await bestEffortDelete(args.storage, storageKey);
10719
+ throw new Error(`Attachment row missing after write for ${id}`);
10720
+ }
10721
+ if (row.deleteRequestedAt === null) {
10722
+ return reuseLiveAttachment({
10723
+ conversationId: args.conversationId,
10724
+ db: args.db,
10725
+ id: row.id,
10726
+ storage: args.storage,
10727
+ storageKey
10728
+ });
10729
+ }
10730
+ if (await isConversationPurged(args.db, args.conversationId)) {
10731
+ await bestEffortDelete(args.storage, storageKey);
10732
+ throw purgedConversationError(args.conversationId);
10733
+ }
10734
+ const revived = await args.db.db().update(juniorAttachments).set({
10735
+ deleteRequestedAt: null,
10736
+ storageKey
10737
+ }).where(
10738
+ and(
10739
+ eq(juniorAttachments.id, id),
10740
+ isNotNull(juniorAttachments.deleteRequestedAt)
10741
+ )
10742
+ ).returning({ id: juniorAttachments.id });
10743
+ if (revived.length > 0) {
10744
+ if (await isConversationPurged(args.db, args.conversationId)) {
10745
+ await rejectPurgedAttachmentWrite({
10746
+ conversationId: args.conversationId,
10747
+ db: args.db,
10748
+ id,
10749
+ now,
10750
+ storage: args.storage,
10751
+ storageKey
10752
+ });
10753
+ }
10754
+ if (row.storageKey !== storageKey) {
10755
+ await bestEffortDelete(args.storage, row.storageKey);
10756
+ }
10757
+ return { id };
10758
+ }
10759
+ const [afterRace] = await args.db.db().select({
10760
+ deleteRequestedAt: juniorAttachments.deleteRequestedAt,
10761
+ id: juniorAttachments.id
10762
+ }).from(juniorAttachments).where(eq(juniorAttachments.id, id));
10763
+ if (afterRace?.deleteRequestedAt === null) {
10764
+ return reuseLiveAttachment({
10765
+ conversationId: args.conversationId,
10766
+ db: args.db,
10767
+ id: afterRace.id,
10768
+ storage: args.storage,
10769
+ storageKey
10770
+ });
10771
+ }
10772
+ await bestEffortDelete(args.storage, storageKey);
10773
+ throw new Error(`Attachment ${id} disappeared while reviving`);
10774
+ }
10775
+ async function storeAttachments(args) {
10776
+ const stored = [];
10777
+ for (const file of args.files) {
10778
+ stored.push(
10779
+ await storeAttachment({
10780
+ conversationId: args.conversationId,
10781
+ db: args.db,
10782
+ file,
10783
+ ...args.nowMs !== void 0 ? { nowMs: args.nowMs } : {},
10784
+ storage: args.storage
10785
+ })
10786
+ );
10787
+ }
10788
+ return stored;
10789
+ }
10790
+ async function requestAttachmentDeletion(db, conversationIds, nowMs) {
10791
+ if (conversationIds.length === 0) return;
10792
+ await db.db().update(juniorAttachments).set({ deleteRequestedAt: new Date(nowMs) }).where(
10793
+ and(
10794
+ inArray(juniorAttachments.conversationId, conversationIds),
10795
+ isNull(juniorAttachments.deleteRequestedAt)
10796
+ )
10797
+ );
10798
+ }
10799
+ async function collectAttachmentGarbage(args) {
10800
+ void args.nowMs;
10801
+ const rows = await args.db.db().select({
10802
+ id: juniorAttachments.id,
10803
+ storageKey: juniorAttachments.storageKey
10804
+ }).from(juniorAttachments).innerJoin(
10805
+ juniorConversations,
10806
+ eq(
10807
+ juniorAttachments.conversationId,
10808
+ juniorConversations.conversationId
10809
+ )
10810
+ ).where(
10811
+ and(
10812
+ eq(juniorAttachments.provider, args.storage.provider),
10813
+ or(
10814
+ isNotNull(juniorAttachments.deleteRequestedAt),
10815
+ isNotNull(juniorConversations.transcriptPurgedAt)
10816
+ )
10817
+ )
10818
+ ).orderBy(asc(juniorAttachments.createdAt), asc(juniorAttachments.id)).limit(args.limit ?? ATTACHMENT_GC_BATCH_LIMIT);
10819
+ if (rows.length === 0) return { deleted: 0 };
10820
+ await args.storage.delete(rows.map((row) => row.storageKey));
10821
+ const stillEligible = await args.db.db().select({ id: juniorAttachments.id }).from(juniorAttachments).innerJoin(
10822
+ juniorConversations,
10823
+ eq(
10824
+ juniorAttachments.conversationId,
10825
+ juniorConversations.conversationId
10826
+ )
10827
+ ).where(
10828
+ and(
10829
+ inArray(
10830
+ juniorAttachments.id,
10831
+ rows.map((row) => row.id)
10832
+ ),
10833
+ or(
10834
+ isNotNull(juniorAttachments.deleteRequestedAt),
10835
+ isNotNull(juniorConversations.transcriptPurgedAt)
10836
+ )
10837
+ )
10838
+ );
10839
+ if (stillEligible.length === 0) return { deleted: 0 };
10840
+ const removed = await args.db.db().delete(juniorAttachments).where(
10841
+ inArray(
10842
+ juniorAttachments.id,
10843
+ stillEligible.map((row) => row.id)
10844
+ )
10845
+ ).returning({
10846
+ id: juniorAttachments.id
10847
+ });
10848
+ return { deleted: removed.length };
10849
+ }
10850
+
10851
+ // src/chat/slack/tools/send-files.ts
10544
10852
  import { z as z42 } from "zod";
10545
10853
  var sendFilesResultSchema = juniorToolOutputSchema.extend({
10546
- target: z42.string().min(1),
10547
- channel_id: z42.string().min(1),
10548
10854
  deduplicated: z42.boolean().optional(),
10549
- file_count: z42.number().int().nonnegative(),
10550
- file_ids: z42.array(z42.string().min(1)).optional(),
10551
- thread_ts: z42.string().min(1)
10855
+ attachment_refs: z42.array(
10856
+ z42.object({
10857
+ id: z42.string().min(1),
10858
+ name: z42.string().min(1)
10859
+ })
10860
+ )
10552
10861
  });
10553
10862
  function normalizeFiles(files) {
10554
10863
  return files.map((file) => ({
@@ -10558,7 +10867,7 @@ function normalizeFiles(files) {
10558
10867
  }));
10559
10868
  }
10560
10869
  function fileContentDigest(data) {
10561
- return createHash4("sha256").update(data).digest("hex");
10870
+ return createHash5("sha256").update(data).digest("hex");
10562
10871
  }
10563
10872
  function fileOperationInput(files) {
10564
10873
  return files.map((file) => ({
@@ -10569,7 +10878,7 @@ function fileOperationInput(files) {
10569
10878
  sha256: fileContentDigest(file.data)
10570
10879
  }));
10571
10880
  }
10572
- function createSendFilesTool(context, state, materializeFile) {
10881
+ function createSendFilesTool(context, state, materializeFile, attachments) {
10573
10882
  return zodTool({
10574
10883
  annotations: {
10575
10884
  destructiveHint: false,
@@ -10611,22 +10920,26 @@ function createSendFilesTool(context, state, materializeFile) {
10611
10920
  deduplicated: true
10612
10921
  });
10613
10922
  }
10923
+ const stored = attachments ? await storeAttachments({
10924
+ conversationId: attachments.conversationId,
10925
+ db: attachments.db,
10926
+ files: materializedFiles,
10927
+ storage: attachments.storage
10928
+ }) : [];
10614
10929
  const uploads = materializedFiles.map((file) => ({
10615
10930
  data: file.data,
10616
10931
  filename: file.filename
10617
10932
  }));
10618
- const uploaded = await uploadFilesToConversation({
10933
+ await uploadFilesToConversation({
10619
10934
  channelId: activeChannelId,
10620
10935
  files: uploads,
10621
10936
  threadTs
10622
10937
  });
10623
- const fileIds = uploaded?.files?.map((file) => file.id).filter((id) => Boolean(id));
10624
10938
  const response = {
10625
- target: `${activeChannelId}:${threadTs}`,
10626
- channel_id: activeChannelId,
10627
- thread_ts: threadTs,
10628
- file_count: uploads.length,
10629
- ...fileIds ? { file_ids: fileIds } : {}
10939
+ attachment_refs: stored.map((attachment, index) => ({
10940
+ id: attachment.id,
10941
+ name: materializedFiles[index].filename
10942
+ }))
10630
10943
  };
10631
10944
  state.setOperationResult(operationKey, response);
10632
10945
  return response;
@@ -12009,7 +12322,7 @@ function createSlackThreadReadTool(context) {
12009
12322
  }
12010
12323
 
12011
12324
  // src/chat/tools/user-lookup.ts
12012
- import { and, eq, or, sql } from "drizzle-orm";
12325
+ import { and as and2, eq as eq2, or as or2, sql } from "drizzle-orm";
12013
12326
  import { alias } from "drizzle-orm/pg-core";
12014
12327
  import { z as z54 } from "zod";
12015
12328
  var linkedSlackIdentity = alias(juniorIdentities, "user_lookup_slack_identity");
@@ -12075,12 +12388,12 @@ async function storedSlackUserByEmail(teamId, email) {
12075
12388
  const emailNormalized = normalizeIdentityEmail(email);
12076
12389
  if (!emailNormalized) return void 0;
12077
12390
  const identities = await getSqlExecutor().db().select().from(juniorIdentities).where(
12078
- and(
12079
- eq(juniorIdentities.provider, "slack"),
12080
- eq(juniorIdentities.providerTenantId, teamId),
12081
- eq(juniorIdentities.kind, "user"),
12082
- eq(juniorIdentities.emailVerified, true),
12083
- eq(juniorIdentities.emailNormalized, emailNormalized)
12391
+ and2(
12392
+ eq2(juniorIdentities.provider, "slack"),
12393
+ eq2(juniorIdentities.providerTenantId, teamId),
12394
+ eq2(juniorIdentities.kind, "user"),
12395
+ eq2(juniorIdentities.emailVerified, true),
12396
+ eq2(juniorIdentities.emailNormalized, emailNormalized)
12084
12397
  )
12085
12398
  ).limit(2);
12086
12399
  if (identities.length > 1) {
@@ -12132,18 +12445,18 @@ async function resolveSlack(teamId, query) {
12132
12445
  async function resolveStoredProvider(teamId, provider, query) {
12133
12446
  const rows = await getSqlExecutor().db().select({ slack: linkedSlackIdentity }).from(juniorIdentities).innerJoin(
12134
12447
  linkedSlackIdentity,
12135
- and(
12136
- eq(linkedSlackIdentity.userId, juniorIdentities.userId),
12137
- eq(linkedSlackIdentity.kind, "user"),
12138
- eq(linkedSlackIdentity.provider, "slack"),
12139
- eq(linkedSlackIdentity.providerTenantId, teamId)
12448
+ and2(
12449
+ eq2(linkedSlackIdentity.userId, juniorIdentities.userId),
12450
+ eq2(linkedSlackIdentity.kind, "user"),
12451
+ eq2(linkedSlackIdentity.provider, "slack"),
12452
+ eq2(linkedSlackIdentity.providerTenantId, teamId)
12140
12453
  )
12141
12454
  ).where(
12142
- and(
12143
- eq(juniorIdentities.kind, "user"),
12144
- eq(juniorIdentities.provider, provider),
12145
- or(
12146
- eq(juniorIdentities.providerSubjectId, query),
12455
+ and2(
12456
+ eq2(juniorIdentities.kind, "user"),
12457
+ eq2(juniorIdentities.provider, provider),
12458
+ or2(
12459
+ eq2(juniorIdentities.providerSubjectId, query),
12147
12460
  sql`lower(${juniorIdentities.handle}) = lower(${query})`
12148
12461
  )
12149
12462
  )
@@ -13323,7 +13636,12 @@ function createTools(availableSkills, hooks = {}, context, options = {}) {
13323
13636
  tools.sendFiles = createSendFilesTool(
13324
13637
  slackContext,
13325
13638
  state,
13326
- (input) => readSandboxFileUpload(context.workspace, input)
13639
+ (input) => readSandboxFileUpload(context.workspace, input),
13640
+ context.conversationId && context.attachmentStorage ? {
13641
+ conversationId: context.conversationId,
13642
+ db: getSqlExecutor(),
13643
+ storage: context.attachmentStorage
13644
+ } : void 0
13327
13645
  );
13328
13646
  }
13329
13647
  tools.slackChannelListMessages = createSlackChannelListMessagesTool(slackContext);
@@ -14672,7 +14990,7 @@ function createMcpAuthOrchestration(input) {
14672
14990
  }
14673
14991
 
14674
14992
  // src/chat/egress/plugin.ts
14675
- import { randomUUID as randomUUID3 } from "crypto";
14993
+ import { randomUUID as randomUUID4 } from "crypto";
14676
14994
 
14677
14995
  // src/chat/sandbox/egress/credentials.ts
14678
14996
  var HTTP_READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
@@ -15409,8 +15727,8 @@ async function executeCredentialedEgressRequest(input) {
15409
15727
  function credentialContextForPluginEgress(credentialContext) {
15410
15728
  return {
15411
15729
  credentials: credentialContext,
15412
- contextId: randomUUID3(),
15413
- egressId: `plugin-egress:${randomUUID3()}`,
15730
+ contextId: randomUUID4(),
15731
+ egressId: `plugin-egress:${randomUUID4()}`,
15414
15732
  expiresAtMs: Date.now() + 30 * 60 * 1e3
15415
15733
  };
15416
15734
  }
@@ -15945,7 +16263,8 @@ async function wireAgentTools(args) {
15945
16263
  });
15946
16264
  const toolRuntimeContext = {
15947
16265
  ...commonToolRuntimeContext,
15948
- ...toolRoute
16266
+ ...toolRoute,
16267
+ attachmentStorage: args.run.environment?.attachmentStorage
15949
16268
  };
15950
16269
  const actionReview = createToolActionReview({
15951
16270
  context: {
@@ -18120,33 +18439,6 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
18120
18439
  }
18121
18440
  }
18122
18441
 
18123
- // src/chat/runtime/agent-runner.ts
18124
- function createAgentRunner(execute, options) {
18125
- const streamFn = options?.streamFn;
18126
- const tracePropagation = options?.tracePropagation;
18127
- const bindSpawnAgent = options?.bindSpawnAgent;
18128
- const canBindSpawn = Boolean(bindSpawnAgent) && isExperimentalFeatureEnabled("subagents");
18129
- return {
18130
- run: async (run) => {
18131
- const spawnAgent = bindSpawnAgent && canBindSpawn && !isAgentRunFeatureDisabled(run.disabledFeatures, "subagents") ? bindSpawnAgent(run) : void 0;
18132
- const nextRun = {
18133
- ...run,
18134
- environment: {
18135
- ...run.environment,
18136
- sandboxTracePropagation: run.environment?.sandboxTracePropagation ?? tracePropagation
18137
- },
18138
- ...spawnAgent ? {
18139
- durability: {
18140
- ...run.durability,
18141
- spawnAgent
18142
- }
18143
- } : {}
18144
- };
18145
- return await execute(nextRun, streamFn);
18146
- }
18147
- };
18148
- }
18149
-
18150
18442
  export {
18151
18443
  setConfigDefaults,
18152
18444
  getConfigDefaults,
@@ -18166,11 +18458,12 @@ export {
18166
18458
  deliverResourceEventSubscription,
18167
18459
  sanitizeScheduledTaskPrincipal,
18168
18460
  listThreadReplies,
18461
+ requestAttachmentDeletion,
18462
+ collectAttachmentGarbage,
18169
18463
  renderBlockText,
18170
18464
  renderAttachmentText,
18171
18465
  latestReportedProgress,
18172
18466
  executeCredentialedEgressRequest,
18173
18467
  createContextCompactor,
18174
- executeAgentRun,
18175
- createAgentRunner
18468
+ executeAgentRun
18176
18469
  };