@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
package/dist/app.js CHANGED
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  processConversationWork
3
- } from "./chunk-QGR6QTC4.js";
3
+ } from "./chunk-JUJR6WTL.js";
4
4
  import {
5
5
  bindSpawnAgent
6
- } from "./chunk-F2Q3ZHW2.js";
6
+ } from "./chunk-IZEBHYI4.js";
7
7
  import {
8
8
  createAgentInvocationWorker,
9
9
  enqueueAgentInvocation,
10
10
  listPendingAgentInvocationMailboxAppends,
11
11
  routeAgentInvocationWork
12
- } from "./chunk-HOGJ6TBT.js";
12
+ } from "./chunk-CN2XSVKF.js";
13
13
  import {
14
14
  GET as GET2,
15
15
  GET2 as GET3,
@@ -36,12 +36,12 @@ import {
36
36
  startSlackProcessingReaction,
37
37
  stripLeadingBotMention,
38
38
  wakePausedTurn
39
- } from "./chunk-UCH55IGZ.js";
39
+ } from "./chunk-OMQJB7YN.js";
40
40
  import "./chunk-FA6POGLH.js";
41
41
  import {
42
42
  getTurnRequestDeadline,
43
43
  runWithTurnRequestDeadline
44
- } from "./chunk-4R2XCKUH.js";
44
+ } from "./chunk-A6WPDFHQ.js";
45
45
  import {
46
46
  GET,
47
47
  conversationVisibilityFromSlackChannelType,
@@ -50,8 +50,8 @@ import {
50
50
  resolveSlackChannelTypeFromMessage,
51
51
  resolveSlackConversationContext,
52
52
  routeApiTurnWork
53
- } from "./chunk-2XNVS6RC.js";
54
- import "./chunk-65IAU2L2.js";
53
+ } from "./chunk-PMFGVLDU.js";
54
+ import "./chunk-SE3D225X.js";
55
55
  import {
56
56
  CONVERSATION_WORK_STALE_ENQUEUE_MS,
57
57
  appendAndEnqueueInboundMessage,
@@ -62,7 +62,7 @@ import {
62
62
  isInvalidConversationRecordError,
63
63
  listActiveConversationIds,
64
64
  removeActiveConversation
65
- } from "./chunk-F7QUK3YP.js";
65
+ } from "./chunk-6CINHXIL.js";
66
66
  import {
67
67
  buildDeliveredTurnStatePatch,
68
68
  claimDispatchMailboxAppend,
@@ -93,7 +93,7 @@ import {
93
93
  requireTurnFailureEventId,
94
94
  resolveTurnSessionRouting,
95
95
  scheduleSessionCompletedPluginTasks
96
- } from "./chunk-A6LWN4K6.js";
96
+ } from "./chunk-GSBPZHLQ.js";
97
97
  import {
98
98
  ConversationTurnLifecycleService,
99
99
  coerceThreadConversationState,
@@ -103,7 +103,7 @@ import {
103
103
  persistThreadRuntimeState,
104
104
  persistThreadState,
105
105
  persistThreadStateById
106
- } from "./chunk-7RFWIHPM.js";
106
+ } from "./chunk-TN4LVILT.js";
107
107
  import "./chunk-UTL3MHPC.js";
108
108
  import {
109
109
  getVercelConversationWorkQueue,
@@ -122,7 +122,7 @@ import {
122
122
  import {
123
123
  validatePluginEgressCredentialHooks,
124
124
  validatePluginRegistrations
125
- } from "./chunk-3ZR2R5EQ.js";
125
+ } from "./chunk-TNXGMFWV.js";
126
126
  import {
127
127
  defineJuniorPlugins,
128
128
  pluginCatalogConfigFromEnv,
@@ -134,9 +134,9 @@ import {
134
134
  buildSteeringPiMessage,
135
135
  cancelSubscriptions,
136
136
  clearSandboxEgressSignals,
137
+ collectAttachmentGarbage,
137
138
  consumeSandboxEgressAuthRequiredSignal,
138
139
  consumeSandboxEgressPermissionDeniedSignal,
139
- createAgentRunner,
140
140
  createContextCompactor,
141
141
  deliverResourceEventSubscription,
142
142
  executeAgentRun,
@@ -149,24 +149,26 @@ import {
149
149
  parseSandboxEgressCredentialToken,
150
150
  renderAttachmentText,
151
151
  renderBlockText,
152
+ requestAttachmentDeletion,
152
153
  resolveDestinationVisibility,
153
154
  resolveSandboxEgressProviderForHost,
154
155
  sanitizeScheduledTaskPrincipal,
155
156
  setConfigDefaults,
156
157
  shouldEmitDevAgentTrace,
157
158
  shouldPropagateSandboxEgressTrace
158
- } from "./chunk-DAJZZFU7.js";
159
+ } from "./chunk-53W3LZEW.js";
159
160
  import {
160
161
  AuthorizationFlowDisabledError,
161
162
  PluginCredentialFailureError
162
- } from "./chunk-FVY7AJ46.js";
163
+ } from "./chunk-ZNWRYNCA.js";
163
164
  import "./chunk-DIB4GX3G.js";
164
165
  import {
166
+ createAgentRunner,
165
167
  createUserTokenStore,
166
168
  deleteMcpAuthSessionsForUserProvider,
167
169
  deleteMcpServerSessionId,
168
170
  deleteMcpStoredOAuthCredentials
169
- } from "./chunk-PTJ75ZWR.js";
171
+ } from "./chunk-4ZZDTHFC.js";
170
172
  import {
171
173
  RetryableDeliveryError
172
174
  } from "./chunk-RF6J3DIO.js";
@@ -174,8 +176,8 @@ import {
174
176
  formatProviderLabel,
175
177
  postSlackMessage,
176
178
  startOAuthFlow
177
- } from "./chunk-O4AQDOKA.js";
178
- import "./chunk-HUO3UTAQ.js";
179
+ } from "./chunk-7RAETIYO.js";
180
+ import "./chunk-7MA4HV4W.js";
179
181
  import {
180
182
  getExperimentalFeatures,
181
183
  setExperimentalFeatures
@@ -183,16 +185,16 @@ import {
183
185
  import {
184
186
  getSandboxResourceConfig,
185
187
  setSandboxResourceConfig
186
- } from "./chunk-6VWMX7NS.js";
188
+ } from "./chunk-6JUU7J2B.js";
187
189
  import {
188
190
  createSchedulerSqlStore,
189
191
  findMatchingEventTasks,
190
192
  logScheduledTaskRunSkipped,
191
193
  scheduledTaskRunAttributes
192
- } from "./chunk-DK2FSQHS.js";
194
+ } from "./chunk-KCA5MKHU.js";
193
195
  import {
194
196
  parseSlackThreadId
195
- } from "./chunk-NUG3VYHL.js";
197
+ } from "./chunk-CK2H7CFO.js";
196
198
  import {
197
199
  ConversationTurnBoundaryError,
198
200
  CooperativeTurnYieldError,
@@ -242,7 +244,7 @@ import {
242
244
  trimTrailingAssistantMessages,
243
245
  turnHasReply,
244
246
  upsertConversationMessage
245
- } from "./chunk-4U4MNYGP.js";
247
+ } from "./chunk-3L2PMOSR.js";
246
248
  import "./chunk-3Y47VMHV.js";
247
249
  import "./chunk-PDO5BLNM.js";
248
250
  import "./chunk-UIE3R5XU.js";
@@ -262,7 +264,7 @@ import {
262
264
  verifyEventTaskCredentialSubject,
263
265
  verifyScheduledTaskCredentialSubject,
264
266
  verifySlackDirectCredentialSubject
265
- } from "./chunk-XYMCZGH6.js";
267
+ } from "./chunk-3FFUC32L.js";
266
268
  import {
267
269
  SlackActionError,
268
270
  downloadPrivateSlackFile,
@@ -272,7 +274,7 @@ import {
272
274
  normalizeSlackConversationId,
273
275
  runWithSlackInstallationToken,
274
276
  runWithWorkspaceTeamId
275
- } from "./chunk-7CPN5JTA.js";
277
+ } from "./chunk-NTNU6T2D.js";
276
278
  import {
277
279
  contextProvenance,
278
280
  createSlackDestination,
@@ -285,19 +287,19 @@ import {
285
287
  requireSlackDestination,
286
288
  withConversationEventLock,
287
289
  withConversationMutationLock
288
- } from "./chunk-Y654CTW2.js";
290
+ } from "./chunk-IIO5PFZX.js";
289
291
  import {
290
292
  juniorAgentBindings,
291
293
  juniorAgentInvocations,
292
294
  juniorConversationEvents,
293
295
  juniorConversations
294
- } from "./chunk-CZD6M57V.js";
296
+ } from "./chunk-AIJRG227.js";
295
297
  import "./chunk-EM225ITJ.js";
296
298
  import {
297
299
  acquireActiveLock,
298
300
  getStateAdapter,
299
301
  withActiveLock
300
- } from "./chunk-NNN2WYAD.js";
302
+ } from "./chunk-MC57E5U5.js";
301
303
  import {
302
304
  STANDARD_MODEL_PROFILE,
303
305
  botConfig,
@@ -315,30 +317,28 @@ import {
315
317
  modelIdForProfile,
316
318
  setSlackReactionConfig,
317
319
  standardModelId
318
- } from "./chunk-6PJXR4BC.js";
320
+ } from "./chunk-GKC43TLS.js";
319
321
  import {
320
322
  juniorDestinations
321
323
  } from "./chunk-VFUK3X5B.js";
322
- import "./chunk-6AF5GOZA.js";
323
- import "./chunk-CCAU73CE.js";
324
+ import "./chunk-MJE7XKWP.js";
325
+ import "./chunk-SRJA6QLM.js";
324
326
  import {
325
327
  pluginCatalogRuntime
326
- } from "./chunk-3567VGCN.js";
327
- import "./chunk-MSOZLASC.js";
328
+ } from "./chunk-EDJ64P2P.js";
329
+ import "./chunk-WD7SEUZO.js";
328
330
  import {
329
331
  credentialContextForActor
330
- } from "./chunk-RUIXF3YQ.js";
332
+ } from "./chunk-BDUENQD3.js";
331
333
  import {
332
334
  createActor,
333
335
  createSlackActor,
334
336
  createSlackResumeActor,
335
337
  isActorUserId,
336
- parseActorUserId
337
- } from "./chunk-HZF5P5UN.js";
338
- import {
338
+ parseActorUserId,
339
339
  parseSlackChannelId,
340
340
  parseSlackMessageTs
341
- } from "./chunk-F3B2YJ76.js";
341
+ } from "./chunk-V7YE4WCP.js";
342
342
  import {
343
343
  buildTurnFailureResponse,
344
344
  createChatSdkLogger,
@@ -358,7 +358,7 @@ import {
358
358
  withContext,
359
359
  withLogContext,
360
360
  withSpan
361
- } from "./chunk-3HX67UPN.js";
361
+ } from "./chunk-X7W45B5G.js";
362
362
  import {
363
363
  continueTrace
364
364
  } from "./chunk-6GWA276C.js";
@@ -366,10 +366,10 @@ import "./chunk-OOUNJNMR.js";
366
366
  import "./chunk-H4HBKISO.js";
367
367
  import {
368
368
  JUNIOR_PLUGIN_TASK_CALLBACK_ROUTE
369
- } from "./chunk-NG4IAOVD.js";
369
+ } from "./chunk-OGZPRMZA.js";
370
370
  import {
371
371
  JUNIOR_VERSION
372
- } from "./chunk-ZQT4RHUO.js";
372
+ } from "./chunk-KBGMYLSV.js";
373
373
  import "./chunk-MLKGABMK.js";
374
374
 
375
375
  // src/app.ts
@@ -1940,6 +1940,7 @@ async function purgeConversationTree(executor, args) {
1940
1940
  inArray(juniorAgentBindings.childConversationId, ids)
1941
1941
  )
1942
1942
  );
1943
+ await requestAttachmentDeletion(executor, ids, args.nowMs);
1943
1944
  await executor.db().update(juniorConversations).set({
1944
1945
  transcriptPurgedAt: new Date(args.nowMs),
1945
1946
  ...(args.retention ? !isPublic : resolvedScrubMetadata) ? { title: null, channelName: null, actor: null } : {}
@@ -1991,14 +1992,46 @@ async function runRetentionPurge(executor, args) {
1991
1992
  }
1992
1993
  });
1993
1994
  }
1995
+ const attachmentResult = args.attachmentStorage ? await collectAttachmentGarbage({
1996
+ db: executor,
1997
+ nowMs: args.nowMs,
1998
+ storage: args.attachmentStorage
1999
+ }) : { deleted: 0 };
1994
2000
  logInfo("retention.purge.completed", {
2001
+ "app.retention.attachments": attachmentResult.deleted,
1995
2002
  "app.retention.scanned": roots.length,
1996
2003
  "app.retention.purged": purged,
1997
2004
  "app.retention.failed": failed,
1998
2005
  "app.retention.conversations": conversations,
1999
2006
  "app.retention.duration_ms": Date.now() - startedAtMs
2000
2007
  });
2001
- return { scanned: roots.length, purged, failed, conversations };
2008
+ return {
2009
+ scanned: roots.length,
2010
+ purged,
2011
+ failed,
2012
+ conversations,
2013
+ attachments: attachmentResult.deleted
2014
+ };
2015
+ }
2016
+
2017
+ // src/chat/attachments/vercel.ts
2018
+ import { del, put } from "@vercel/blob";
2019
+ function createVercelAttachmentStorage() {
2020
+ return {
2021
+ provider: "vercel-blob",
2022
+ async put(input) {
2023
+ await put(input.key, input.body, {
2024
+ access: "private",
2025
+ addRandomSuffix: false,
2026
+ allowOverwrite: true,
2027
+ contentType: input.contentType
2028
+ });
2029
+ },
2030
+ async delete(keys) {
2031
+ if (keys.length === 0) return;
2032
+ await del(keys);
2033
+ }
2034
+ };
2002
2035
  }
2003
2036
 
2004
2037
  // src/handlers/retention.ts
@@ -2018,12 +2051,13 @@ function verifyRetentionRequest(request) {
2018
2051
  const expected = Buffer.from(secret);
2019
2052
  return actual.length === expected.length && timingSafeEqual3(actual, expected);
2020
2053
  }
2021
- async function GET5(request) {
2054
+ async function GET5(request, options = {}) {
2022
2055
  if (!verifyRetentionRequest(request)) {
2023
2056
  return new Response("Unauthorized", { status: 401 });
2024
2057
  }
2025
2058
  try {
2026
2059
  const result = await runRetentionPurge(getSqlExecutor(), {
2060
+ attachmentStorage: options.attachmentStorage ?? createVercelAttachmentStorage(),
2027
2061
  nowMs: Date.now()
2028
2062
  });
2029
2063
  return Response.json(result, { status: 200 });
@@ -3211,6 +3245,71 @@ function buildSlackInboundMessage(args) {
3211
3245
  };
3212
3246
  }
3213
3247
 
3248
+ // src/chat/ingress/bot-mention.ts
3249
+ function escapeRegExp(value) {
3250
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3251
+ }
3252
+ function stripInlineCode(line) {
3253
+ return line.replace(/(`+)(?:(?!\1)[^\n])*\1/g, " ");
3254
+ }
3255
+ function fenceMarkerLength(trimmed) {
3256
+ let n = 0;
3257
+ while (trimmed[n] === "`") {
3258
+ n += 1;
3259
+ }
3260
+ return n;
3261
+ }
3262
+ function singleLineFenceSuffix(trimmed) {
3263
+ const openLen = fenceMarkerLength(trimmed);
3264
+ if (openLen < 3) {
3265
+ return void 0;
3266
+ }
3267
+ const afterOpen = trimmed.slice(openLen);
3268
+ const closeRel = afterOpen.indexOf("`".repeat(openLen));
3269
+ if (closeRel === -1) {
3270
+ return void 0;
3271
+ }
3272
+ return afterOpen.slice(closeRel + openLen);
3273
+ }
3274
+ function textOutsideCode(text) {
3275
+ const lines = text.split("\n");
3276
+ const out = [];
3277
+ let inFence = false;
3278
+ for (const line of lines) {
3279
+ const trimmed = line.trimStart();
3280
+ if (trimmed.startsWith("```")) {
3281
+ if (inFence) {
3282
+ inFence = false;
3283
+ const suffix2 = trimmed.slice(fenceMarkerLength(trimmed));
3284
+ if (suffix2.length > 0) {
3285
+ out.push(stripInlineCode(suffix2));
3286
+ }
3287
+ continue;
3288
+ }
3289
+ const suffix = singleLineFenceSuffix(trimmed);
3290
+ if (suffix !== void 0) {
3291
+ out.push(stripInlineCode(suffix));
3292
+ continue;
3293
+ }
3294
+ inFence = true;
3295
+ continue;
3296
+ }
3297
+ if (inFence) {
3298
+ continue;
3299
+ }
3300
+ out.push(stripInlineCode(line));
3301
+ }
3302
+ return out.join("\n");
3303
+ }
3304
+ function textMentionsBot(text, botUserId) {
3305
+ if (!botUserId || !text) {
3306
+ return false;
3307
+ }
3308
+ const outside = textOutsideCode(text);
3309
+ const id = escapeRegExp(botUserId);
3310
+ return new RegExp(`<@${id}(?:\\|[^>]+)?>`).test(outside);
3311
+ }
3312
+
3214
3313
  // src/chat/ingress/message-changed.ts
3215
3314
  import { Message as Message2 } from "chat";
3216
3315
  function getEditedMentionMessageId(messageTs) {
@@ -3224,9 +3323,6 @@ function isMessageChangedEnvelope(value) {
3224
3323
  if (!event || typeof event !== "object") return false;
3225
3324
  return event.type === "message" && event.subtype === "message_changed" && typeof event.channel === "string" && typeof event.message === "object" && event.message !== null && typeof event.previous_message === "object" && event.previous_message !== null;
3226
3325
  }
3227
- function textMentionsBot(text, botUserId) {
3228
- return text.includes(`<@${botUserId}>`);
3229
- }
3230
3326
  function getAttachmentType(mimeType) {
3231
3327
  if (mimeType?.startsWith("image/")) {
3232
3328
  return "image";
@@ -3509,9 +3605,6 @@ function installationFromEnvelope(body) {
3509
3605
  function isDmEvent(event) {
3510
3606
  return event.channel_type === "im" || event.channel?.startsWith("D") === true;
3511
3607
  }
3512
- function textMentionsBot2(event, botUserId) {
3513
- return Boolean(botUserId && event.text?.includes(`<@${botUserId}>`));
3514
- }
3515
3608
  function shouldIgnoreMessageSubtype(event) {
3516
3609
  return Boolean(event.subtype && IGNORED_MESSAGE_SUBTYPES.has(event.subtype));
3517
3610
  }
@@ -3593,7 +3686,10 @@ async function routeParsedMessage(args) {
3593
3686
  conversationStore: args.conversationStore,
3594
3687
  installation: args.installation
3595
3688
  });
3596
- const isMention = args.event.type === "app_mention" || textMentionsBot2(args.event, args.adapter.botUserId);
3689
+ const botUserId = args.adapter.botUserId;
3690
+ const isMention = Boolean(
3691
+ botUserId && textMentionsBot(args.event.text ?? "", botUserId)
3692
+ );
3597
3693
  if (isMention) {
3598
3694
  args.message.isMention = true;
3599
3695
  }
@@ -4283,11 +4379,11 @@ var THREAD_OPTOUT_PATTERNS = [
4283
4379
  var ACKNOWLEDGMENT_ONLY_RE = /^(?:thanks(?: you)?|thank you|thx|ty|got it|sounds good|sgtm|lgtm|ok(?:ay)?|cool|nice|perfect|awesome|great|makes sense|understood|roger|yep|yup|kk|on it|will do)(?:[.!]+)?$/i;
4284
4380
  var DIRECTED_FOLLOW_UP_CUE_RE = /\b(?:you said|you just said|your last response|your last answer|what did you just say|what do you mean|what did you mean|explain(?: that| this| it| more)?|clarify(?: that| this| it)?|expand(?: on)?(?: that| this| it)?|elaborate(?: on)?(?: that| this| it)?|say more)\b/i;
4285
4381
  var TERSE_CLARIFICATION_RE = /^(?:which one|which ones|why|how so|what do you mean|what did you mean|say more|explain that|clarify that|expand on that|elaborate on that)\??$/i;
4286
- function escapeRegExp(value) {
4382
+ function escapeRegExp2(value) {
4287
4383
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4288
4384
  }
4289
4385
  function containsAssistantInvocation(text, botUserName) {
4290
- const escapedUserName = escapeRegExp(botUserName);
4386
+ const escapedUserName = escapeRegExp2(botUserName);
4291
4387
  const plainNameMentionRe = new RegExp(`(^|\\s)@${escapedUserName}\\b`, "i");
4292
4388
  const labeledEntityMentionRe = new RegExp(
4293
4389
  `<@[^>|]+\\|${escapedUserName}>`,
@@ -6531,7 +6627,8 @@ function createReplyToThread(deps) {
6531
6627
  isBot: true
6532
6628
  },
6533
6629
  meta: {
6534
- replied: true
6630
+ replied: true,
6631
+ source: "slack"
6535
6632
  }
6536
6633
  });
6537
6634
  await persistThreadState(thread, {
@@ -6719,6 +6816,7 @@ function createReplyToThread(deps) {
6719
6816
  const recordedMessageId = recordDeliveredAssistantMessage({
6720
6817
  conversation: preparedState.conversation,
6721
6818
  sessionId: turnId,
6819
+ source: "slack",
6722
6820
  text,
6723
6821
  userMessageId: preparedState.userMessageId
6724
6822
  });
@@ -7374,6 +7472,7 @@ function toConversationMessage(args) {
7374
7472
  explicitMention: args.explicitMention,
7375
7473
  imageAttachmentCount: imageAttachmentCount > 0 ? imageAttachmentCount : void 0,
7376
7474
  imagesHydrated: !messageHasPotentialImageAttachment,
7475
+ source: "slack",
7377
7476
  ...slackTs ? { slackTs } : {}
7378
7477
  }
7379
7478
  };
@@ -8849,6 +8948,8 @@ function dashboardHostRoutePaths(dashboard) {
8849
8948
  "/favicon.ico",
8850
8949
  "/_junior/dashboard/avatar.png",
8851
8950
  "/_junior/dashboard/client.js",
8951
+ "/_junior/dashboard/icon-512.png",
8952
+ "/_junior/dashboard/manifest.webmanifest",
8852
8953
  loginPath,
8853
8954
  "/api/health",
8854
8955
  "/api/runtime",
@@ -9060,7 +9161,9 @@ async function createApp(options) {
9060
9161
  const waitUntil = options?.waitUntil ?? await defaultWaitUntil();
9061
9162
  const tracePropagation = { domains: sandboxEgressTracePropagationDomains };
9062
9163
  const conversationWorkQueue = getVercelConversationWorkQueue();
9164
+ const attachmentStorage = createVercelAttachmentStorage();
9063
9165
  const agentRunner = createAgentRunner(executeAgentRun, {
9166
+ attachmentStorage,
9064
9167
  bindSpawnAgent: (request) => bindSpawnAgent(request, { queue: conversationWorkQueue }),
9065
9168
  tracePropagation
9066
9169
  });
@@ -0,0 +1,16 @@
1
+ import {
2
+ pluginCatalogRuntime
3
+ } from "./chunk-EDJ64P2P.js";
4
+ import "./chunk-WD7SEUZO.js";
5
+ import "./chunk-BDUENQD3.js";
6
+ import "./chunk-V7YE4WCP.js";
7
+ import "./chunk-X7W45B5G.js";
8
+ import "./chunk-6GWA276C.js";
9
+ import "./chunk-OOUNJNMR.js";
10
+ import "./chunk-H4HBKISO.js";
11
+ import "./chunk-OGZPRMZA.js";
12
+ import "./chunk-KBGMYLSV.js";
13
+ import "./chunk-MLKGABMK.js";
14
+ export {
15
+ pluginCatalogRuntime
16
+ };
@@ -24,6 +24,7 @@ import type { SlackActionToken } from "@/chat/slack/action-token";
24
24
  import type { TurnReasoningLevel } from "@/chat/reasoning-level";
25
25
  import type { ImageGenerateToolDeps, ViewImageToolDeps, WebFetchToolDeps, WebSearchToolDeps } from "@/chat/tools/types";
26
26
  import type { AssistantMessage } from "@earendil-works/pi-ai";
27
+ import type { AttachmentStorage } from "@/chat/attachments/storage";
27
28
  /** One attachment the model may see for the current instruction. */
28
29
  export type AgentAttachment = {
29
30
  data?: Buffer;
@@ -136,6 +137,7 @@ export type AgentEvent = {
136
137
  };
137
138
  /** Resolved environment and optional per-run tool test overrides. */
138
139
  export type AgentEnvironment = {
140
+ attachmentStorage?: AttachmentStorage;
139
141
  configuration?: Record<string, unknown>;
140
142
  locationConfiguration?: LocationConfigurationService;
141
143
  skillDirs?: string[];
@@ -0,0 +1,12 @@
1
+ /** Bytes and metadata written to attachment object storage. */
2
+ export interface AttachmentWrite {
3
+ body: Buffer;
4
+ contentType: string;
5
+ key: string;
6
+ }
7
+ /** Small object-storage capability used by conversation attachments. */
8
+ export interface AttachmentStorage {
9
+ readonly provider: string;
10
+ put(input: AttachmentWrite): Promise<void>;
11
+ delete(keys: string[]): Promise<void>;
12
+ }
@@ -0,0 +1,52 @@
1
+ import type { JuniorSqlDatabase } from "@/db/db";
2
+ import type { SandboxFileUpload } from "@/chat/tools/sandbox/file-uploads";
3
+ import type { AttachmentStorage } from "./storage";
4
+ export interface StoredAttachment {
5
+ id: string;
6
+ }
7
+ export interface AttachmentGarbageCollectionResult {
8
+ deleted: number;
9
+ }
10
+ /**
11
+ * Persist one conversation-owned file.
12
+ *
13
+ * Identity is content-stable under the conversation. Object storage is written
14
+ * first under a unique key; the SQL row is created only after that write
15
+ * succeeds. A row means the attachment is durable. Retries reuse an existing
16
+ * live row. A purge-marked row is revived by writing a new object and pointing
17
+ * the row at it, unless the conversation is still purged. If the SQL write
18
+ * fails after object storage accepts the bytes, this path deletes that unique
19
+ * object. A process crash between those steps can leave an unreferenced unique
20
+ * object with no SQL row.
21
+ */
22
+ export declare function storeAttachment(args: {
23
+ conversationId: string;
24
+ db: JuniorSqlDatabase;
25
+ file: SandboxFileUpload;
26
+ nowMs?: number;
27
+ storage: AttachmentStorage;
28
+ }): Promise<StoredAttachment>;
29
+ /** Persist many conversation-owned files. */
30
+ export declare function storeAttachments(args: {
31
+ conversationId: string;
32
+ db: JuniorSqlDatabase;
33
+ files: SandboxFileUpload[];
34
+ nowMs?: number;
35
+ storage: AttachmentStorage;
36
+ }): Promise<StoredAttachment[]>;
37
+ /** Mark attachments unavailable when their conversation content is purged. */
38
+ export declare function requestAttachmentDeletion(db: JuniorSqlDatabase, conversationIds: string[], nowMs: number): Promise<void>;
39
+ /**
40
+ * Delete purged attachments in one bounded batch.
41
+ *
42
+ * Eligible rows are explicitly purge-marked, or owned by a conversation whose
43
+ * transcript is already purged. That second path covers a concurrent store that
44
+ * inserted after purge marked existing rows. Delete object keys first, then
45
+ * remove rows that are still eligible so a failed blob delete remains retryable.
46
+ */
47
+ export declare function collectAttachmentGarbage(args: {
48
+ db: JuniorSqlDatabase;
49
+ nowMs: number;
50
+ storage: AttachmentStorage;
51
+ limit?: number;
52
+ }): Promise<AttachmentGarbageCollectionResult>;
@@ -0,0 +1,3 @@
1
+ import type { AttachmentStorage } from "./storage";
2
+ /** Store private conversation attachments in Vercel Blob. */
3
+ export declare function createVercelAttachmentStorage(): AttachmentStorage;
@@ -1,3 +1,4 @@
1
+ import type { AttachmentStorage } from "@/chat/attachments/storage";
1
2
  import type { JuniorSqlDatabase } from "@/db/db";
2
3
  /** Content retention windows keyed by resolved conversation visibility. */
3
4
  export declare const CONTENT_RETENTION_MS: {
@@ -6,6 +7,8 @@ export declare const CONTENT_RETENTION_MS: {
6
7
  };
7
8
  /** Aggregate outcome of one bounded retention purge batch. */
8
9
  export interface RetentionPurgeResult {
10
+ /** Attachment blobs removed after conversation content was purged. */
11
+ attachments: number;
9
12
  /** Root conversations examined and attempted this run. */
10
13
  scanned: number;
11
14
  /** Root trees successfully purged. */
@@ -22,6 +25,7 @@ export interface RetentionPurgeResult {
22
25
  * remainder of an over-limit backlog is left for the next run.
23
26
  */
24
27
  export declare function runRetentionPurge(executor: JuniorSqlDatabase, args: {
28
+ attachmentStorage?: AttachmentStorage;
25
29
  nowMs: number;
26
30
  limit?: number;
27
31
  }): Promise<RetentionPurgeResult>;
@@ -0,0 +1,14 @@
1
+ import type { JuniorSqlDatabase } from "@/db/db";
2
+ import type { ConversationEventData } from "../history";
3
+ /**
4
+ * Resolve the durable actor identity for one event payload.
5
+ * Human user messages get an identity when author data is enough; other events
6
+ * keep a null actor.
7
+ */
8
+ export declare function resolveEventActorIdentityId(executor: JuniorSqlDatabase, args: {
9
+ conversationId: string;
10
+ data: ConversationEventData;
11
+ nowMs: number;
12
+ }): Promise<string | undefined>;
13
+ /** Drop payload-only authorIdentityId after lifting it onto the event column. */
14
+ export declare function stripPayloadAuthorIdentityId(data: ConversationEventData): ConversationEventData;
@@ -1,3 +1,3 @@
1
1
  import type { JuniorSqlDatabase } from "@/db/db";
2
- /** Serialize all event sequence allocation and visible projection writes. */
2
+ /** Serialize event sequence allocation with other conversation writes. */
3
3
  export declare function withConversationEventLock<T>(executor: JuniorSqlDatabase, conversationId: string, callback: () => Promise<T>): Promise<T>;
@@ -0,0 +1,17 @@
1
+ import type { JuniorSqlDatabase } from "@/db/db";
2
+ /** Resolve the root conversation id that owns personal-feed membership. */
3
+ export declare function resolveRootConversationId(executor: JuniorSqlDatabase, conversationId: string): Promise<string>;
4
+ /**
5
+ * Upsert one linked user as a participant on a root conversation.
6
+ * No-ops when the identity is missing, unlinked, or not a user.
7
+ */
8
+ export declare function recordConversationParticipant(executor: JuniorSqlDatabase, args: {
9
+ actorIdentityId: string;
10
+ conversationId: string;
11
+ atMs: number;
12
+ }): Promise<void>;
13
+ /** Find which of the supplied root conversations include the linked user. */
14
+ export declare function rootConversationIdsForParticipant(executor: JuniorSqlDatabase, args: {
15
+ rootConversationIds: readonly string[];
16
+ userId: string;
17
+ }): Promise<Set<string>>;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Detect Slack bot @mentions that should activate Junior.
3
+ *
4
+ * Mentions inside inline code or fenced code blocks are display-only
5
+ * references. Slack can still deliver `app_mention` for those tokens, so
6
+ * both `message` and `app_mention` paths must use this check.
7
+ */
8
+ /**
9
+ * Return true when `text` contains a Slack bot mention token outside code.
10
+ *
11
+ * Slack encodes user mentions as `<@UXXXXXXXX>` or `<@UXXXXXXXX|label>`.
12
+ */
13
+ export declare function textMentionsBot(text: string, botUserId: string): boolean;
@@ -0,0 +1,8 @@
1
+ export type ConversationWork = {
2
+ assignedIds: string[];
3
+ unfinishedIds: string[];
4
+ };
5
+ /** Return assigned and unfinished plugin work for the candidate conversations. */
6
+ export declare function listConversationWork(conversationIds: string[]): Promise<ConversationWork>;
7
+ /** Return candidate conversations that have unfinished plugin work. */
8
+ export declare function listUnfinishedWork(conversationIds: string[]): Promise<string[]>;