@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
@@ -3,38 +3,38 @@ import {
3
3
  } from "./chunk-RF6J3DIO.js";
4
4
  import {
5
5
  resolveBaseUrl
6
- } from "./chunk-KBT2I5ER.js";
6
+ } from "./chunk-VDAEXEBB.js";
7
7
  import {
8
8
  isExperimentalFeatureEnabled
9
9
  } from "./chunk-5IDFPCON.js";
10
10
  import {
11
11
  createPluginLogger,
12
12
  getPlugins
13
- } from "./chunk-V57LZDHN.js";
13
+ } from "./chunk-MGJW3SW4.js";
14
14
  import {
15
15
  getSlackClient,
16
16
  withSlackRetries
17
- } from "./chunk-TKJ7277U.js";
17
+ } from "./chunk-MDZQ62PB.js";
18
18
  import {
19
19
  getDb,
20
20
  parseDestination
21
- } from "./chunk-JJX77MB7.js";
21
+ } from "./chunk-G2FN3I66.js";
22
22
  import {
23
23
  sleep
24
24
  } from "./chunk-EM225ITJ.js";
25
25
  import {
26
26
  acquireActiveLock,
27
27
  getStateAdapter
28
- } from "./chunk-SDRJ2LE3.js";
28
+ } from "./chunk-I3OQVVMV.js";
29
29
  import {
30
30
  pluginCatalogRuntime
31
- } from "./chunk-TVXAN43I.js";
31
+ } from "./chunk-6MI43QZO.js";
32
32
  import {
33
33
  fetchWithBoundedOAuthErrorBodies
34
- } from "./chunk-RN3AWYEP.js";
34
+ } from "./chunk-77QB5FNI.js";
35
35
  import {
36
36
  isRecord
37
- } from "./chunk-7NYEB2TM.js";
37
+ } from "./chunk-MKA3T2R6.js";
38
38
 
39
39
  // src/chat/capabilities/router.ts
40
40
  var ProviderCredentialRouter = class {
@@ -149,6 +149,7 @@ async function issueProviderCredentialLease(input) {
149
149
  // src/chat/runtime/agent-runner.ts
150
150
  var AGENT_ABORT_SETTLE_GRACE_MS = 5e3;
151
151
  function createAgentRunner(execute, options) {
152
+ const attachmentStorage = options?.attachmentStorage;
152
153
  const streamFn = options?.streamFn;
153
154
  const tracePropagation = options?.tracePropagation;
154
155
  const bindSpawnAgent = options?.bindSpawnAgent;
@@ -160,6 +161,7 @@ function createAgentRunner(execute, options) {
160
161
  ...run,
161
162
  environment: {
162
163
  ...run.environment,
164
+ attachmentStorage: run.environment?.attachmentStorage ?? attachmentStorage,
163
165
  sandboxTracePropagation: run.environment?.sandboxTracePropagation ?? tracePropagation
164
166
  },
165
167
  ...spawnAgent ? {
@@ -8,14 +8,14 @@ import {
8
8
  getConversationStore,
9
9
  parseDestination,
10
10
  sameDestination
11
- } from "./chunk-JJX77MB7.js";
11
+ } from "./chunk-G2FN3I66.js";
12
12
  import {
13
13
  getDefaultRedisStateAdapterFor,
14
14
  getStateAdapter
15
- } from "./chunk-SDRJ2LE3.js";
15
+ } from "./chunk-I3OQVVMV.js";
16
16
  import {
17
17
  getChatConfig
18
- } from "./chunk-XFZLQANQ.js";
18
+ } from "./chunk-UJYA75N5.js";
19
19
  import {
20
20
  parseStoredSlackActor
21
21
  } from "./chunk-V7YE4WCP.js";
@@ -23,7 +23,7 @@ import {
23
23
  isRecord,
24
24
  toOptionalNumber,
25
25
  toOptionalString
26
- } from "./chunk-7NYEB2TM.js";
26
+ } from "./chunk-MKA3T2R6.js";
27
27
 
28
28
  // src/chat/task-execution/state.ts
29
29
  import { randomUUID } from "crypto";
@@ -56,6 +56,10 @@ var conversationSurfaceSchema = z2.enum([
56
56
  ]);
57
57
  var conversationUsageSchema = usageSchema;
58
58
  var conversationParamsSchema = z2.object({ conversationId: z2.string().min(1) }).strict();
59
+ var conversationAttachmentParamsSchema = z2.object({
60
+ attachmentId: z2.string().min(1),
61
+ conversationId: z2.string().min(1)
62
+ }).strict();
59
63
  var conversationDetailQuerySchema = z2.object({
60
64
  limit: z2.coerce.number().int().min(1).max(1e3).default(500)
61
65
  }).strict();
@@ -175,13 +179,25 @@ var conversationSummaryReportSchema = z2.object({
175
179
  sentryTraceUrl: z2.string().optional(),
176
180
  sourceUrl: z2.string().url().optional(),
177
181
  traceId: z2.string().optional(),
182
+ assignedWork: z2.boolean().optional(),
183
+ finishedWorkAt: z2.string().datetime().optional(),
184
+ /**
185
+ * Dashboard Priority membership for this conversation summary.
186
+ * Present on the conversation feed. Clients must not recompute it.
187
+ *
188
+ * True only when:
189
+ * - unfinished work was last seen within 48 hours
190
+ * - finished assigned work has conversation activity after the finish time
191
+ * - there is no known work and the conversation was last seen within 3 hours
192
+ */
193
+ isPriority: z2.boolean().optional(),
178
194
  unfinishedWork: z2.boolean().optional()
179
195
  }).strict();
180
196
  var conversationReportMessageEventDataSchema = z2.object({
181
197
  type: z2.literal("message"),
182
198
  messageId: z2.string().min(1),
183
199
  role: z2.enum(["assistant", "system", "user"]),
184
- source: z2.literal("web").optional(),
200
+ source: z2.enum(["slack", "web"]).optional(),
185
201
  actorIdentity: actorIdentitySchema.optional(),
186
202
  eventType: z2.string().min(1).optional(),
187
203
  explicitMention: z2.boolean().optional(),
@@ -781,6 +797,7 @@ var statsReportSchema = z8.object({
781
797
  export {
782
798
  apiErrorSchema,
783
799
  conversationParamsSchema,
800
+ conversationAttachmentParamsSchema,
784
801
  conversationDetailQuerySchema,
785
802
  conversationEventsQuerySchema,
786
803
  conversationFeedQuerySchema,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  logWarn
3
- } from "./chunk-7NYEB2TM.js";
3
+ } from "./chunk-MKA3T2R6.js";
4
4
 
5
5
  // src/chat/conversation-privacy.ts
6
6
  import { AsyncLocalStorage } from "async_hooks";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  JUNIOR_VERSION
3
- } from "./chunk-JFZBGFNX.js";
3
+ } from "./chunk-46ADYIPE.js";
4
4
 
5
5
  // src/deployment.ts
6
6
  var JUNIOR_HEARTBEAT_ROUTE = "/api/internal/heartbeat";
@@ -6,12 +6,12 @@ import {
6
6
  juniorConversations,
7
7
  juniorIdentities,
8
8
  juniorUsers
9
- } from "./chunk-773XC7P4.js";
9
+ } from "./chunk-3GS7O66Z.js";
10
10
  import {
11
11
  TURN_REASONING_LEVELS,
12
12
  getChatConfig,
13
13
  modelProfileSchema
14
- } from "./chunk-XFZLQANQ.js";
14
+ } from "./chunk-UJYA75N5.js";
15
15
  import {
16
16
  juniorDestinations
17
17
  } from "./chunk-VFUK3X5B.js";
@@ -22,7 +22,7 @@ import {
22
22
  } from "./chunk-V7YE4WCP.js";
23
23
  import {
24
24
  normalizeIdentityEmail
25
- } from "./chunk-7NYEB2TM.js";
25
+ } from "./chunk-MKA3T2R6.js";
26
26
 
27
27
  // src/chat/conversations/sql/store.ts
28
28
  import { randomUUID as randomUUID2 } from "crypto";
@@ -13,7 +13,7 @@ import {
13
13
  instructionProvenanceFor,
14
14
  sanitizePostgresJson,
15
15
  withConversationEventLock
16
- } from "./chunk-JJX77MB7.js";
16
+ } from "./chunk-G2FN3I66.js";
17
17
  import {
18
18
  SANDBOX_WORKSPACE_ROOT,
19
19
  sleep
@@ -23,14 +23,14 @@ import {
23
23
  fenceLock,
24
24
  getStateAdapter,
25
25
  withLock
26
- } from "./chunk-SDRJ2LE3.js";
26
+ } from "./chunk-I3OQVVMV.js";
27
27
  import {
28
28
  botConfig,
29
29
  completeText,
30
30
  escapeXml,
31
31
  resolveGatewayModel,
32
32
  unwrapCurrentInstruction
33
- } from "./chunk-XFZLQANQ.js";
33
+ } from "./chunk-UJYA75N5.js";
34
34
  import {
35
35
  parseSlackMessageTs,
36
36
  toStoredSlackActor
@@ -40,7 +40,7 @@ import {
40
40
  logException,
41
41
  logWarn,
42
42
  setSpanAttributes
43
- } from "./chunk-7NYEB2TM.js";
43
+ } from "./chunk-MKA3T2R6.js";
44
44
 
45
45
  // src/chat/turn-context-tag.ts
46
46
  var TURN_CONTEXT_TAG = "runtime-turn-context";
@@ -4,18 +4,18 @@ import {
4
4
  } from "./chunk-EM225ITJ.js";
5
5
  import {
6
6
  getStateAdapter
7
- } from "./chunk-SDRJ2LE3.js";
7
+ } from "./chunk-I3OQVVMV.js";
8
8
  import {
9
9
  toOptionalTrimmed
10
- } from "./chunk-XFZLQANQ.js";
10
+ } from "./chunk-UJYA75N5.js";
11
11
  import {
12
12
  pluginCatalogRuntime
13
- } from "./chunk-TVXAN43I.js";
13
+ } from "./chunk-6MI43QZO.js";
14
14
  import {
15
15
  setSpanAttributes,
16
16
  setSpanStatus,
17
17
  withSpan
18
- } from "./chunk-7NYEB2TM.js";
18
+ } from "./chunk-MKA3T2R6.js";
19
19
 
20
20
  // src/chat/sandbox/resources.ts
21
21
  var configuredResources;
@@ -1,25 +1,27 @@
1
1
  import {
2
2
  ToolInputError
3
- } from "./chunk-J2SYI36O.js";
3
+ } from "./chunk-GEY6IFXH.js";
4
4
  import {
5
5
  canRouteResourceEvents,
6
6
  getPlugins
7
- } from "./chunk-V57LZDHN.js";
7
+ } from "./chunk-MGJW3SW4.js";
8
8
  import {
9
9
  getDb
10
- } from "./chunk-JJX77MB7.js";
10
+ } from "./chunk-G2FN3I66.js";
11
11
  import {
12
+ juniorAttachments,
13
+ juniorConversations,
12
14
  juniorEventTasks,
13
15
  juniorSchedulerRuns,
14
16
  juniorSchedulerTasks,
15
17
  juniorStats
16
- } from "./chunk-773XC7P4.js";
18
+ } from "./chunk-3GS7O66Z.js";
17
19
  import {
18
20
  juniorDestinations
19
21
  } from "./chunk-VFUK3X5B.js";
20
22
  import {
21
23
  logInfo
22
- } from "./chunk-7NYEB2TM.js";
24
+ } from "./chunk-MKA3T2R6.js";
23
25
 
24
26
  // src/chat/event-tasks/store.ts
25
27
  import { and, asc, desc, eq, inArray, or, sql } from "drizzle-orm";
@@ -1553,6 +1555,346 @@ function createSchedulerSqlStore(db) {
1553
1555
  return new SqlSchedulerStore(db);
1554
1556
  }
1555
1557
 
1558
+ // src/chat/attachments/store.ts
1559
+ import { createHash, randomUUID } from "crypto";
1560
+ import { and as and4, asc as asc4, eq as eq3, inArray as inArray3, isNotNull as isNotNull2, isNull, or as or3 } from "drizzle-orm";
1561
+ var ATTACHMENT_GC_BATCH_LIMIT = 200;
1562
+ function attachmentDigest(file) {
1563
+ return createHash("sha256").update(file.data).digest("hex");
1564
+ }
1565
+ function attachmentId(args) {
1566
+ return createHash("sha256").update(
1567
+ [
1568
+ args.conversationId,
1569
+ args.provider,
1570
+ args.sha256,
1571
+ args.filename,
1572
+ args.contentType
1573
+ ].join("\0")
1574
+ ).digest("hex");
1575
+ }
1576
+ function attachmentKey(args) {
1577
+ return `conversations/${args.conversationId}/attachments/${args.attachmentId}/${args.writeId}/${args.filename}`;
1578
+ }
1579
+ async function bestEffortDelete(storage, key) {
1580
+ try {
1581
+ await storage.delete([key]);
1582
+ } catch {
1583
+ }
1584
+ }
1585
+ async function isConversationPurged(db, conversationId) {
1586
+ const [row] = await db.db().select({
1587
+ transcriptPurgedAt: juniorConversations.transcriptPurgedAt
1588
+ }).from(juniorConversations).where(eq3(juniorConversations.conversationId, conversationId));
1589
+ return row?.transcriptPurgedAt != null;
1590
+ }
1591
+ function purgedConversationError(conversationId) {
1592
+ return new Error(
1593
+ `Cannot store attachment for purged conversation ${conversationId}`
1594
+ );
1595
+ }
1596
+ async function assertConversationNotPurged(db, conversationId) {
1597
+ if (await isConversationPurged(db, conversationId)) {
1598
+ throw purgedConversationError(conversationId);
1599
+ }
1600
+ }
1601
+ async function rejectPurgedAttachmentWrite(args) {
1602
+ await args.db.db().update(juniorAttachments).set({ deleteRequestedAt: args.now }).where(eq3(juniorAttachments.id, args.id));
1603
+ await bestEffortDelete(args.storage, args.storageKey);
1604
+ throw purgedConversationError(args.conversationId);
1605
+ }
1606
+ async function reuseLiveAttachment(args) {
1607
+ await bestEffortDelete(args.storage, args.storageKey);
1608
+ await assertConversationNotPurged(args.db, args.conversationId);
1609
+ return { id: args.id };
1610
+ }
1611
+ async function storeAttachment(args) {
1612
+ const now = new Date(args.nowMs ?? Date.now());
1613
+ const sha256 = attachmentDigest(args.file);
1614
+ const id = attachmentId({
1615
+ conversationId: args.conversationId,
1616
+ contentType: args.file.mimeType,
1617
+ filename: args.file.filename,
1618
+ provider: args.storage.provider,
1619
+ sha256
1620
+ });
1621
+ const [existing] = await args.db.db().select({
1622
+ contentType: juniorAttachments.contentType,
1623
+ conversationId: juniorAttachments.conversationId,
1624
+ deleteRequestedAt: juniorAttachments.deleteRequestedAt,
1625
+ filename: juniorAttachments.filename,
1626
+ id: juniorAttachments.id,
1627
+ provider: juniorAttachments.provider,
1628
+ sha256: juniorAttachments.sha256,
1629
+ storageKey: juniorAttachments.storageKey
1630
+ }).from(juniorAttachments).where(eq3(juniorAttachments.id, id));
1631
+ if (existing) {
1632
+ if (existing.contentType !== args.file.mimeType || existing.conversationId !== args.conversationId || existing.filename !== args.file.filename || existing.provider !== args.storage.provider || existing.sha256 !== sha256) {
1633
+ throw new Error(`Attachment write conflicts with ${existing.id}`);
1634
+ }
1635
+ if (existing.deleteRequestedAt === null) {
1636
+ await assertConversationNotPurged(args.db, args.conversationId);
1637
+ return { id: existing.id };
1638
+ }
1639
+ }
1640
+ await assertConversationNotPurged(args.db, args.conversationId);
1641
+ const storageKey = attachmentKey({
1642
+ attachmentId: id,
1643
+ conversationId: args.conversationId,
1644
+ filename: args.file.filename,
1645
+ writeId: randomUUID()
1646
+ });
1647
+ await args.storage.put({
1648
+ body: args.file.data,
1649
+ contentType: args.file.mimeType,
1650
+ key: storageKey
1651
+ });
1652
+ if (existing?.deleteRequestedAt != null) {
1653
+ if (await isConversationPurged(args.db, args.conversationId)) {
1654
+ await bestEffortDelete(args.storage, storageKey);
1655
+ throw purgedConversationError(args.conversationId);
1656
+ }
1657
+ const oldStorageKey = existing.storageKey;
1658
+ const revived2 = await args.db.db().update(juniorAttachments).set({
1659
+ deleteRequestedAt: null,
1660
+ storageKey
1661
+ }).where(
1662
+ and4(
1663
+ eq3(juniorAttachments.id, id),
1664
+ isNotNull2(juniorAttachments.deleteRequestedAt)
1665
+ )
1666
+ ).returning({ id: juniorAttachments.id });
1667
+ if (revived2.length > 0) {
1668
+ if (await isConversationPurged(args.db, args.conversationId)) {
1669
+ await rejectPurgedAttachmentWrite({
1670
+ conversationId: args.conversationId,
1671
+ db: args.db,
1672
+ id,
1673
+ now,
1674
+ storage: args.storage,
1675
+ storageKey
1676
+ });
1677
+ }
1678
+ if (oldStorageKey !== storageKey) {
1679
+ await bestEffortDelete(args.storage, oldStorageKey);
1680
+ }
1681
+ return { id };
1682
+ }
1683
+ const [afterRace2] = await args.db.db().select({
1684
+ deleteRequestedAt: juniorAttachments.deleteRequestedAt,
1685
+ id: juniorAttachments.id,
1686
+ storageKey: juniorAttachments.storageKey
1687
+ }).from(juniorAttachments).where(eq3(juniorAttachments.id, id));
1688
+ if (afterRace2?.deleteRequestedAt === null) {
1689
+ return reuseLiveAttachment({
1690
+ conversationId: args.conversationId,
1691
+ db: args.db,
1692
+ id: afterRace2.id,
1693
+ storage: args.storage,
1694
+ storageKey
1695
+ });
1696
+ }
1697
+ }
1698
+ let inserted = [];
1699
+ try {
1700
+ inserted = await args.db.db().insert(juniorAttachments).values({
1701
+ id,
1702
+ conversationId: args.conversationId,
1703
+ provider: args.storage.provider,
1704
+ storageKey,
1705
+ filename: args.file.filename,
1706
+ contentType: args.file.mimeType,
1707
+ bytes: args.file.bytes,
1708
+ sha256,
1709
+ createdAt: now
1710
+ }).onConflictDoNothing({ target: juniorAttachments.id }).returning({ id: juniorAttachments.id });
1711
+ } catch (error) {
1712
+ await bestEffortDelete(args.storage, storageKey);
1713
+ throw error;
1714
+ }
1715
+ if (inserted.length > 0) {
1716
+ if (await isConversationPurged(args.db, args.conversationId)) {
1717
+ await rejectPurgedAttachmentWrite({
1718
+ conversationId: args.conversationId,
1719
+ db: args.db,
1720
+ id,
1721
+ now,
1722
+ storage: args.storage,
1723
+ storageKey
1724
+ });
1725
+ }
1726
+ return { id };
1727
+ }
1728
+ const [row] = await args.db.db().select({
1729
+ deleteRequestedAt: juniorAttachments.deleteRequestedAt,
1730
+ id: juniorAttachments.id,
1731
+ storageKey: juniorAttachments.storageKey
1732
+ }).from(juniorAttachments).where(eq3(juniorAttachments.id, id));
1733
+ if (!row) {
1734
+ await bestEffortDelete(args.storage, storageKey);
1735
+ throw new Error(`Attachment row missing after write for ${id}`);
1736
+ }
1737
+ if (row.deleteRequestedAt === null) {
1738
+ return reuseLiveAttachment({
1739
+ conversationId: args.conversationId,
1740
+ db: args.db,
1741
+ id: row.id,
1742
+ storage: args.storage,
1743
+ storageKey
1744
+ });
1745
+ }
1746
+ if (await isConversationPurged(args.db, args.conversationId)) {
1747
+ await bestEffortDelete(args.storage, storageKey);
1748
+ throw purgedConversationError(args.conversationId);
1749
+ }
1750
+ const revived = await args.db.db().update(juniorAttachments).set({
1751
+ deleteRequestedAt: null,
1752
+ storageKey
1753
+ }).where(
1754
+ and4(
1755
+ eq3(juniorAttachments.id, id),
1756
+ isNotNull2(juniorAttachments.deleteRequestedAt)
1757
+ )
1758
+ ).returning({ id: juniorAttachments.id });
1759
+ if (revived.length > 0) {
1760
+ if (await isConversationPurged(args.db, args.conversationId)) {
1761
+ await rejectPurgedAttachmentWrite({
1762
+ conversationId: args.conversationId,
1763
+ db: args.db,
1764
+ id,
1765
+ now,
1766
+ storage: args.storage,
1767
+ storageKey
1768
+ });
1769
+ }
1770
+ if (row.storageKey !== storageKey) {
1771
+ await bestEffortDelete(args.storage, row.storageKey);
1772
+ }
1773
+ return { id };
1774
+ }
1775
+ const [afterRace] = await args.db.db().select({
1776
+ deleteRequestedAt: juniorAttachments.deleteRequestedAt,
1777
+ id: juniorAttachments.id
1778
+ }).from(juniorAttachments).where(eq3(juniorAttachments.id, id));
1779
+ if (afterRace?.deleteRequestedAt === null) {
1780
+ return reuseLiveAttachment({
1781
+ conversationId: args.conversationId,
1782
+ db: args.db,
1783
+ id: afterRace.id,
1784
+ storage: args.storage,
1785
+ storageKey
1786
+ });
1787
+ }
1788
+ await bestEffortDelete(args.storage, storageKey);
1789
+ throw new Error(`Attachment ${id} disappeared while reviving`);
1790
+ }
1791
+ async function readLiveAttachment(args) {
1792
+ const [row] = await args.db.db().select({
1793
+ bytes: juniorAttachments.bytes,
1794
+ contentType: juniorAttachments.contentType,
1795
+ conversationId: juniorAttachments.conversationId,
1796
+ deleteRequestedAt: juniorAttachments.deleteRequestedAt,
1797
+ filename: juniorAttachments.filename,
1798
+ id: juniorAttachments.id,
1799
+ provider: juniorAttachments.provider,
1800
+ storageKey: juniorAttachments.storageKey
1801
+ }).from(juniorAttachments).where(
1802
+ and4(
1803
+ eq3(juniorAttachments.id, args.attachmentId),
1804
+ eq3(juniorAttachments.conversationId, args.conversationId),
1805
+ isNull(juniorAttachments.deleteRequestedAt)
1806
+ )
1807
+ );
1808
+ if (!row) return null;
1809
+ if (await isConversationPurged(args.db, args.conversationId)) {
1810
+ return null;
1811
+ }
1812
+ return {
1813
+ bytes: row.bytes,
1814
+ contentType: row.contentType,
1815
+ conversationId: row.conversationId,
1816
+ filename: row.filename,
1817
+ id: row.id,
1818
+ provider: row.provider,
1819
+ storageKey: row.storageKey
1820
+ };
1821
+ }
1822
+ async function storeAttachments(args) {
1823
+ const stored = [];
1824
+ for (const file of args.files) {
1825
+ stored.push(
1826
+ await storeAttachment({
1827
+ conversationId: args.conversationId,
1828
+ db: args.db,
1829
+ file,
1830
+ ...args.nowMs !== void 0 ? { nowMs: args.nowMs } : {},
1831
+ storage: args.storage
1832
+ })
1833
+ );
1834
+ }
1835
+ return stored;
1836
+ }
1837
+ async function requestAttachmentDeletion(db, conversationIds, nowMs) {
1838
+ if (conversationIds.length === 0) return;
1839
+ await db.db().update(juniorAttachments).set({ deleteRequestedAt: new Date(nowMs) }).where(
1840
+ and4(
1841
+ inArray3(juniorAttachments.conversationId, conversationIds),
1842
+ isNull(juniorAttachments.deleteRequestedAt)
1843
+ )
1844
+ );
1845
+ }
1846
+ async function collectAttachmentGarbage(args) {
1847
+ void args.nowMs;
1848
+ const rows = await args.db.db().select({
1849
+ id: juniorAttachments.id,
1850
+ storageKey: juniorAttachments.storageKey
1851
+ }).from(juniorAttachments).innerJoin(
1852
+ juniorConversations,
1853
+ eq3(
1854
+ juniorAttachments.conversationId,
1855
+ juniorConversations.conversationId
1856
+ )
1857
+ ).where(
1858
+ and4(
1859
+ eq3(juniorAttachments.provider, args.storage.provider),
1860
+ or3(
1861
+ isNotNull2(juniorAttachments.deleteRequestedAt),
1862
+ isNotNull2(juniorConversations.transcriptPurgedAt)
1863
+ )
1864
+ )
1865
+ ).orderBy(asc4(juniorAttachments.createdAt), asc4(juniorAttachments.id)).limit(args.limit ?? ATTACHMENT_GC_BATCH_LIMIT);
1866
+ if (rows.length === 0) return { deleted: 0 };
1867
+ await args.storage.delete(rows.map((row) => row.storageKey));
1868
+ const stillEligible = await args.db.db().select({ id: juniorAttachments.id }).from(juniorAttachments).innerJoin(
1869
+ juniorConversations,
1870
+ eq3(
1871
+ juniorAttachments.conversationId,
1872
+ juniorConversations.conversationId
1873
+ )
1874
+ ).where(
1875
+ and4(
1876
+ inArray3(
1877
+ juniorAttachments.id,
1878
+ rows.map((row) => row.id)
1879
+ ),
1880
+ or3(
1881
+ isNotNull2(juniorAttachments.deleteRequestedAt),
1882
+ isNotNull2(juniorConversations.transcriptPurgedAt)
1883
+ )
1884
+ )
1885
+ );
1886
+ if (stillEligible.length === 0) return { deleted: 0 };
1887
+ const removed = await args.db.db().delete(juniorAttachments).where(
1888
+ inArray3(
1889
+ juniorAttachments.id,
1890
+ stillEligible.map((row) => row.id)
1891
+ )
1892
+ ).returning({
1893
+ id: juniorAttachments.id
1894
+ });
1895
+ return { deleted: removed.length };
1896
+ }
1897
+
1556
1898
  export {
1557
1899
  juniorToolOutputSchema,
1558
1900
  injectContinuationToolName,
@@ -1591,6 +1933,10 @@ export {
1591
1933
  SCHEDULED_TASK_SYSTEM_ACTOR,
1592
1934
  listPublicScheduledTasksForTeams,
1593
1935
  createSchedulerSqlStore,
1936
+ readLiveAttachment,
1937
+ storeAttachments,
1938
+ requestAttachmentDeletion,
1939
+ collectAttachmentGarbage,
1594
1940
  incrementStat,
1595
1941
  readStats
1596
1942
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getChatConfig
3
- } from "./chunk-XFZLQANQ.js";
3
+ } from "./chunk-UJYA75N5.js";
4
4
 
5
5
  // src/chat/state/adapter.ts
6
6
  import { createMemoryState } from "@chat-adapter/state-memory";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-EM225ITJ.js";
4
4
  import {
5
5
  getSlackBotToken
6
- } from "./chunk-XFZLQANQ.js";
6
+ } from "./chunk-UJYA75N5.js";
7
7
  import {
8
8
  parseSlackChannelReferenceId
9
9
  } from "./chunk-V7YE4WCP.js";
@@ -12,7 +12,7 @@ import {
12
12
  setSpanAttributes,
13
13
  setSpanStatus,
14
14
  withSpan
15
- } from "./chunk-7NYEB2TM.js";
15
+ } from "./chunk-MKA3T2R6.js";
16
16
 
17
17
  // src/chat/slack/client.ts
18
18
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";