@sentry/junior 0.138.0 → 0.140.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/{agent-hooks-SCR74Q3E.js → agent-hooks-BFTDRFUO.js} +10 -10
  2. package/dist/api/schema/task.d.ts +104 -1
  3. package/dist/api/schema.d.ts +2 -2
  4. package/dist/api/schema.js +9 -1
  5. package/dist/api.js +198 -54
  6. package/dist/app.js +374 -607
  7. package/dist/{catalog-runtime-IU3QF4GG.js → catalog-runtime-RK664S5T.js} +6 -6
  8. package/dist/chat/agent/request.d.ts +12 -2
  9. package/dist/chat/credentials/context.d.ts +14 -0
  10. package/dist/chat/resource-events/actor.d.ts +23 -0
  11. package/dist/chat/scheduled-tasks/telemetry.d.ts +7 -0
  12. package/dist/chat/scheduled-tasks/tool-support.d.ts +10 -2
  13. package/dist/chat/scheduled-tasks/tools/create.d.ts +4 -2
  14. package/dist/chat/scheduled-tasks/tools/delete.d.ts +4 -2
  15. package/dist/chat/scheduled-tasks/tools/list.d.ts +4 -2
  16. package/dist/chat/scheduled-tasks/tools/run-now.d.ts +4 -2
  17. package/dist/chat/scheduled-tasks/tools/update.d.ts +4 -2
  18. package/dist/chat/scheduled-tasks/types.d.ts +1 -1
  19. package/dist/chat/slack/dashboard-link.d.ts +2 -0
  20. package/dist/chat/state/turn-session-keys.d.ts +2 -2
  21. package/dist/chat/state/turn-session.d.ts +16 -12
  22. package/dist/chat/tasks/execution-stats.d.ts +34 -3
  23. package/dist/chat/tasks/read.d.ts +3 -1
  24. package/dist/chunk-3B7O3GNU.js +207 -0
  25. package/dist/{chunk-N7WARMC6.js → chunk-3CUBAGA3.js} +7 -7
  26. package/dist/{chunk-5MXWHTCK.js → chunk-4PIT6O7A.js} +17 -8
  27. package/dist/{chunk-DCLLQGS6.js → chunk-557HLSNH.js} +8 -3
  28. package/dist/{chunk-SD3OFRWY.js → chunk-5KAZMR5U.js} +6 -6
  29. package/dist/{chunk-ZBSMCXKV.js → chunk-5YAWB2AX.js} +7 -6
  30. package/dist/{chunk-FEUNPTGV.js → chunk-A6BI25J3.js} +434 -12
  31. package/dist/{chunk-5LTJ4ITU.js → chunk-AA2EPMUF.js} +4 -4
  32. package/dist/{chunk-TUUDXEXU.js → chunk-BMFRFSTM.js} +68 -79
  33. package/dist/{chunk-C7ZVPO4Z.js → chunk-CPPTBHQN.js} +1 -1
  34. package/dist/{chunk-ZXNTJJIE.js → chunk-D3X2PI3D.js} +2 -2
  35. package/dist/{chunk-LPVUNO2W.js → chunk-DMDOKRPC.js} +7 -7
  36. package/dist/{chunk-TCS76X5D.js → chunk-HEFBGCCU.js} +2 -2
  37. package/dist/{chunk-EPBBYMNE.js → chunk-HUO3UTAQ.js} +1 -1
  38. package/dist/{chunk-KFRHRSCJ.js → chunk-I3OZR3HG.js} +2 -2
  39. package/dist/{chunk-BOLJOV5T.js → chunk-J5H65GY2.js} +1 -1
  40. package/dist/{chunk-RZIBSCYC.js → chunk-MYVCHWDG.js} +39 -23
  41. package/dist/{chunk-QS527AV5.js → chunk-N2I7W2VS.js} +1 -1
  42. package/dist/{chunk-JNNJZURV.js → chunk-NPL66ZWL.js} +1 -1
  43. package/dist/{chunk-A7WCHXZV.js → chunk-OGNL2QY7.js} +4 -4
  44. package/dist/{chunk-77VDLF3Z.js → chunk-RUIXF3YQ.js} +10 -0
  45. package/dist/{chunk-BYW4B4PR.js → chunk-S3FAOKQU.js} +2 -2
  46. package/dist/chunk-TGIKZJ3Y.js +115 -0
  47. package/dist/{chunk-YU5QUF67.js → chunk-TKTZ4L4Z.js} +1 -1
  48. package/dist/{chunk-VPZFVJND.js → chunk-UXMRN2ZX.js} +1 -1
  49. package/dist/{chunk-HIWJWWMT.js → chunk-VRDHAITB.js} +3 -3
  50. package/dist/{chunk-SIPKWSXK.js → chunk-WSWM6A3R.js} +3 -3
  51. package/dist/{chunk-FNLBTB4K.js → chunk-WTGLUX4K.js} +1 -1
  52. package/dist/{chunk-PB3CXNVL.js → chunk-XROWZSOY.js} +1 -1
  53. package/dist/{chunk-TDJCLNDH.js → chunk-XX2QNQN4.js} +147 -86
  54. package/dist/{chunk-KDCHVT25.js → chunk-YB363QGA.js} +9 -2
  55. package/dist/{chunk-HCIVH3JS.js → chunk-YWM6MJT4.js} +29 -1
  56. package/dist/{chunk-E5UYAIKJ.js → chunk-Z2A7BDHT.js} +17 -19
  57. package/dist/cli/chat.js +33 -33
  58. package/dist/cli/check.js +7 -7
  59. package/dist/cli/plugins.js +14 -14
  60. package/dist/cli/snapshot-create.js +10 -10
  61. package/dist/cli/upgrade.js +8 -8
  62. package/dist/db/schema/task-executions.d.ts +23 -2
  63. package/dist/db/schema.d.ts +20 -1
  64. package/dist/{db-UO2IWXGA.js → db-QOGYDDQM.js} +7 -7
  65. package/dist/instrumentation.js +4 -4
  66. package/dist/nitro.js +2 -2
  67. package/dist/{oauth-callback-server-MAQI43LX.js → oauth-callback-server-CT4RMGCP.js} +22 -21
  68. package/dist/{runner-PIUQEXKE.js → runner-PHMLHNLX.js} +18 -16
  69. package/dist/{sandbox-egress-signals-A5HWIQH4.js → sandbox-egress-signals-EKPAVWQL.js} +2 -2
  70. package/dist/{skills-4ARJADHQ.js → skills-AYRM4QGF.js} +7 -7
  71. package/dist/spawn-OGRQFUNK.js +38 -0
  72. package/dist/{validation-S3EE3HWL.js → validation-M5JNGRGL.js} +7 -7
  73. package/dist/version.js +1 -1
  74. package/dist/{work-UARRUAW3.js → work-LZWE6HK4.js} +18 -18
  75. package/dist/{worker-DOCIRVD3.js → worker-KOGPXFYK.js} +10 -10
  76. package/migrations/0022_task_execution_status.sql +3 -0
  77. package/migrations/meta/0022_snapshot.json +2186 -0
  78. package/migrations/meta/_journal.json +7 -0
  79. package/package.json +5 -5
  80. package/dist/chunk-ELKW5FIB.js +0 -233
  81. package/dist/spawn-G2AWPMCT.js +0 -38
package/dist/app.js CHANGED
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  processConversationWork
3
- } from "./chunk-HIWJWWMT.js";
3
+ } from "./chunk-VRDHAITB.js";
4
4
  import {
5
5
  bindSpawnAgent
6
- } from "./chunk-PB3CXNVL.js";
6
+ } from "./chunk-XROWZSOY.js";
7
7
  import {
8
8
  createAgentInvocationWorker,
9
9
  enqueueAgentInvocation,
10
10
  listPendingAgentInvocationMailboxAppends,
11
11
  routeAgentInvocationWork
12
- } from "./chunk-ZBSMCXKV.js";
12
+ } from "./chunk-5YAWB2AX.js";
13
13
  import {
14
14
  GET as GET2,
15
15
  GET2 as GET3,
@@ -39,7 +39,7 @@ import {
39
39
  splitSlackReplyText,
40
40
  startSlackProcessingReaction,
41
41
  stripLeadingBotMention
42
- } from "./chunk-E5UYAIKJ.js";
42
+ } from "./chunk-Z2A7BDHT.js";
43
43
  import "./chunk-FA6POGLH.js";
44
44
  import {
45
45
  CONVERSATION_WORK_STALE_ENQUEUE_MS,
@@ -51,32 +51,50 @@ import {
51
51
  isInvalidConversationRecordError,
52
52
  listActiveConversationIds,
53
53
  removeActiveConversation
54
- } from "./chunk-5LTJ4ITU.js";
54
+ } from "./chunk-AA2EPMUF.js";
55
55
  import {
56
56
  buildDeliveredTurnStatePatch,
57
+ claimDispatchMailboxAppend,
57
58
  completeAuthPauseTurn,
59
+ confirmDispatchMailboxAppend,
60
+ createOrGetDispatch,
58
61
  finalizeFailedTurnReplyWithEvent,
59
62
  getAgentTurnDiagnosticsAttributes,
63
+ getDispatchConversationId,
64
+ getDispatchInputMessageId,
65
+ getDispatchInputMessageIds,
66
+ getDispatchRecord,
67
+ getDispatchTurnId,
68
+ getPluginDispatchProjection,
60
69
  getTurnUserMessage,
61
70
  getTurnUserReplyAttachmentContext,
62
71
  getTurnUserSlackMessageTs,
72
+ isTerminalDispatchStatus,
73
+ listPendingDispatchMailboxAppends,
74
+ markDispatchAwaitingResume,
75
+ markDispatchBlocked,
76
+ markDispatchCompleted,
77
+ markDispatchFailed,
78
+ markDispatchRunning,
63
79
  persistAuthPauseTurnState,
64
80
  processPluginTask,
65
81
  requireTurnFailureEventId,
66
82
  resolveTurnSessionRouting,
67
83
  scheduleSessionCompletedPluginTasks
68
- } from "./chunk-FEUNPTGV.js";
84
+ } from "./chunk-A6BI25J3.js";
69
85
  import {
70
86
  ConversationTurnLifecycleService
71
87
  } from "./chunk-Q6QZTTLS.js";
72
88
  import {
73
89
  GET,
74
90
  conversationVisibilityFromSlackChannelType,
75
- recordTaskExecution,
76
91
  resolveSlackChannelTypeFromMessage,
77
92
  resolveSlackConversationContext
78
- } from "./chunk-ELKW5FIB.js";
93
+ } from "./chunk-TGIKZJ3Y.js";
79
94
  import "./chunk-4CODA7P5.js";
95
+ import {
96
+ recordTaskExecution
97
+ } from "./chunk-3B7O3GNU.js";
80
98
  import "./chunk-UTL3MHPC.js";
81
99
  import {
82
100
  getVercelConversationWorkQueue,
@@ -95,7 +113,7 @@ import {
95
113
  import {
96
114
  validatePluginEgressCredentialHooks,
97
115
  validatePluginRegistrations
98
- } from "./chunk-YU5QUF67.js";
116
+ } from "./chunk-TKTZ4L4Z.js";
99
117
  import {
100
118
  defineJuniorPlugins,
101
119
  pluginCatalogConfigFromEnv,
@@ -128,18 +146,18 @@ import {
128
146
  setConfigDefaults,
129
147
  shouldEmitDevAgentTrace,
130
148
  shouldPropagateSandboxEgressTrace
131
- } from "./chunk-RZIBSCYC.js";
149
+ } from "./chunk-MYVCHWDG.js";
132
150
  import {
133
151
  AuthorizationFlowDisabledError,
134
152
  PluginCredentialFailureError
135
- } from "./chunk-LPVUNO2W.js";
153
+ } from "./chunk-DMDOKRPC.js";
136
154
  import "./chunk-DIB4GX3G.js";
137
155
  import {
138
156
  createUserTokenStore,
139
157
  deleteMcpAuthSessionsForUserProvider,
140
158
  deleteMcpServerSessionId,
141
159
  deleteMcpStoredOAuthCredentials
142
- } from "./chunk-N7WARMC6.js";
160
+ } from "./chunk-3CUBAGA3.js";
143
161
  import {
144
162
  RetryableDeliveryError
145
163
  } from "./chunk-R776HKPK.js";
@@ -147,7 +165,7 @@ import {
147
165
  formatProviderLabel,
148
166
  postSlackMessage,
149
167
  startOAuthFlow
150
- } from "./chunk-SD3OFRWY.js";
168
+ } from "./chunk-5KAZMR5U.js";
151
169
  import {
152
170
  ConversationTurnBoundaryError,
153
171
  CooperativeTurnYieldError,
@@ -203,11 +221,9 @@ import {
203
221
  trimTrailingAssistantMessages,
204
222
  updateConversationStats,
205
223
  upsertConversationMessage
206
- } from "./chunk-TUUDXEXU.js";
207
- import {
208
- JUNIOR_THREAD_STATE_TTL_MS
209
- } from "./chunk-3Y47VMHV.js";
210
- import "./chunk-EPBBYMNE.js";
224
+ } from "./chunk-BMFRFSTM.js";
225
+ import "./chunk-3Y47VMHV.js";
226
+ import "./chunk-HUO3UTAQ.js";
211
227
  import {
212
228
  getExperimentalFeatures,
213
229
  setExperimentalFeatures
@@ -215,14 +231,16 @@ import {
215
231
  import {
216
232
  getSandboxResourceConfig,
217
233
  setSandboxResourceConfig
218
- } from "./chunk-A7WCHXZV.js";
234
+ } from "./chunk-OGNL2QY7.js";
219
235
  import {
220
236
  createSchedulerSqlStore,
221
- findMatchingEventTasks
222
- } from "./chunk-TDJCLNDH.js";
237
+ findMatchingEventTasks,
238
+ logScheduledTaskRunSkipped,
239
+ scheduledTaskRunAttributes
240
+ } from "./chunk-XX2QNQN4.js";
223
241
  import {
224
242
  parseSlackThreadId
225
- } from "./chunk-FNLBTB4K.js";
243
+ } from "./chunk-WTGLUX4K.js";
226
244
  import "./chunk-CDQ4YIZM.js";
227
245
  import "./chunk-PDO5BLNM.js";
228
246
  import "./chunk-UIE3R5XU.js";
@@ -242,7 +260,7 @@ import {
242
260
  verifyEventTaskCredentialSubject,
243
261
  verifyScheduledTaskCredentialSubject,
244
262
  verifySlackDirectCredentialSubject
245
- } from "./chunk-5MXWHTCK.js";
263
+ } from "./chunk-4PIT6O7A.js";
246
264
  import {
247
265
  SlackActionError,
248
266
  downloadPrivateSlackFile,
@@ -252,7 +270,7 @@ import {
252
270
  normalizeSlackConversationId,
253
271
  runWithSlackInstallationToken,
254
272
  runWithWorkspaceTeamId
255
- } from "./chunk-TCS76X5D.js";
273
+ } from "./chunk-HEFBGCCU.js";
256
274
  import {
257
275
  contextProvenance,
258
276
  createSlackDestination,
@@ -264,20 +282,20 @@ import {
264
282
  requireSlackDestination,
265
283
  withConversationEventLock,
266
284
  withConversationMutationLock
267
- } from "./chunk-SIPKWSXK.js";
285
+ } from "./chunk-WSWM6A3R.js";
268
286
  import {
269
287
  juniorAgentBindings,
270
288
  juniorAgentInvocations,
271
289
  juniorConversationEvents,
272
290
  juniorConversations
273
- } from "./chunk-DCLLQGS6.js";
291
+ } from "./chunk-557HLSNH.js";
274
292
  import {
275
293
  sleep
276
294
  } from "./chunk-EM225ITJ.js";
277
295
  import {
278
296
  acquireActiveLock,
279
297
  getStateAdapter
280
- } from "./chunk-BOLJOV5T.js";
298
+ } from "./chunk-J5H65GY2.js";
281
299
  import {
282
300
  STANDARD_MODEL_PROFILE,
283
301
  botConfig,
@@ -295,19 +313,19 @@ import {
295
313
  modelIdForProfile,
296
314
  setSlackReactionConfig,
297
315
  standardModelId
298
- } from "./chunk-BYW4B4PR.js";
316
+ } from "./chunk-S3FAOKQU.js";
299
317
  import {
300
318
  juniorDestinations
301
319
  } from "./chunk-VFUK3X5B.js";
302
- import "./chunk-VPZFVJND.js";
303
- import "./chunk-ZXNTJJIE.js";
320
+ import "./chunk-UXMRN2ZX.js";
321
+ import "./chunk-D3X2PI3D.js";
304
322
  import {
305
323
  pluginCatalogRuntime
306
- } from "./chunk-QS527AV5.js";
307
- import "./chunk-KFRHRSCJ.js";
324
+ } from "./chunk-N2I7W2VS.js";
325
+ import "./chunk-I3OZR3HG.js";
308
326
  import {
309
- credentialSubjectSchema
310
- } from "./chunk-77VDLF3Z.js";
327
+ credentialContextForActor
328
+ } from "./chunk-RUIXF3YQ.js";
311
329
  import {
312
330
  createActor,
313
331
  createSlackActor,
@@ -336,7 +354,7 @@ import {
336
354
  withContext,
337
355
  withLogContext,
338
356
  withSpan
339
- } from "./chunk-JNNJZURV.js";
357
+ } from "./chunk-NPL66ZWL.js";
340
358
  import {
341
359
  continueTrace
342
360
  } from "./chunk-6GWA276C.js";
@@ -344,10 +362,10 @@ import "./chunk-OOUNJNMR.js";
344
362
  import "./chunk-H4HBKISO.js";
345
363
  import {
346
364
  JUNIOR_PLUGIN_TASK_CALLBACK_ROUTE
347
- } from "./chunk-KDCHVT25.js";
365
+ } from "./chunk-YB363QGA.js";
348
366
  import {
349
367
  JUNIOR_VERSION
350
- } from "./chunk-C7ZVPO4Z.js";
368
+ } from "./chunk-CPPTBHQN.js";
351
369
  import "./chunk-MLKGABMK.js";
352
370
 
353
371
  // src/app.ts
@@ -409,400 +427,20 @@ async function verifyDispatchCallbackRequest(request) {
409
427
  }
410
428
  }
411
429
 
412
- // src/chat/agent-dispatch/store.ts
413
- import { createHash } from "crypto";
414
- import {
415
- destinationSchema,
416
- destinationVisibilitySchema,
417
- isSlackDestination,
418
- replyAttributionSchema,
419
- sourceSchema
420
- } from "@sentry/junior-plugin-api";
421
- import { z as z2 } from "zod";
422
- var DISPATCH_PREFIX = "junior:agent_dispatch";
423
- var DISPATCH_LOCK_TTL_MS = 10 * 60 * 1e3;
424
- var DISPATCH_MAILBOX_APPEND_INDEX_LOCK_TTL_MS = 1e4;
425
- var DISPATCH_MAILBOX_APPEND_INDEX_MAX_LENGTH = 1e4;
426
- var nonEmptyExactStringSchema = z2.string().min(1).refine(
427
- (value) => value === value.trim() && value.toLowerCase() !== "unknown"
428
- );
429
- var pendingDispatchMailboxAppendIndexSchema = z2.array(
430
- nonEmptyExactStringSchema
431
- );
432
- var dispatchStatusSchema = z2.enum([
433
- "pending",
434
- "running",
435
- "awaiting_resume",
436
- "completed",
437
- "failed",
438
- "blocked"
439
- ]);
440
- var dispatchActorSchema = z2.object({
441
- platform: z2.literal("system"),
442
- name: nonEmptyExactStringSchema
443
- }).strict();
444
- var dispatchRecordSchema = z2.object({
445
- actor: dispatchActorSchema,
446
- createdAtMs: z2.number().finite(),
447
- credentialSubject: credentialSubjectSchema.optional(),
448
- destination: destinationSchema,
449
- destinationVisibility: destinationVisibilitySchema,
450
- errorMessage: z2.string().optional(),
451
- id: nonEmptyExactStringSchema,
452
- idempotencyKey: z2.string().min(1),
453
- input: z2.string().min(1),
454
- metadata: z2.record(z2.string(), z2.string()).optional(),
455
- plugin: nonEmptyExactStringSchema,
456
- replyAttribution: replyAttributionSchema.optional(),
457
- resultMessageTs: z2.string().optional(),
458
- source: sourceSchema,
459
- status: dispatchStatusSchema,
460
- updatedAtMs: z2.number().finite()
461
- }).strict().superRefine((record, ctx) => {
462
- if (!isSlackDestination(record.destination)) {
463
- ctx.addIssue({
464
- code: z2.ZodIssueCode.custom,
465
- message: "Dispatch destination platform must be slack",
466
- path: ["destination"]
467
- });
468
- return;
469
- }
470
- const subject = record.credentialSubject;
471
- if (!subject) {
472
- return;
473
- }
474
- if (subject.allowedWhen === "private-direct-conversation") {
475
- if (!record.destination.channelId.startsWith("D")) {
476
- ctx.addIssue({
477
- code: z2.ZodIssueCode.custom,
478
- message: "Dispatch credentialSubject requires a private direct Slack destination",
479
- path: ["credentialSubject"]
480
- });
481
- return;
482
- }
483
- if (subject.binding.type !== "slack-direct-conversation" || subject.binding.teamId !== record.destination.teamId || subject.binding.channelId !== record.destination.channelId) {
484
- ctx.addIssue({
485
- code: z2.ZodIssueCode.custom,
486
- message: "Dispatch credentialSubject binding must match destination",
487
- path: ["credentialSubject", "binding"]
488
- });
489
- }
490
- return;
491
- }
492
- if (subject.binding.type !== subject.allowedWhen || subject.binding.plugin !== record.plugin || subject.binding.taskId !== subject.taskId) {
493
- ctx.addIssue({
494
- code: z2.ZodIssueCode.custom,
495
- message: "Dispatch credentialSubject binding must match task",
496
- path: ["credentialSubject", "binding"]
497
- });
498
- }
499
- });
500
- var storedDispatchRecordSchema = dispatchRecordSchema.extend({
501
- attempt: z2.number().int().nonnegative().optional(),
502
- lastCallbackAtMs: z2.number().finite().optional(),
503
- leaseExpiresAtMs: z2.number().finite().optional(),
504
- maxAttempts: z2.number().int().positive().optional(),
505
- version: z2.number().int().positive().optional()
506
- });
507
- function getDispatchStorageKey(id) {
508
- return `${DISPATCH_PREFIX}:record:${id}`;
509
- }
510
- function dispatchLockKey(id) {
511
- return `${DISPATCH_PREFIX}:lock:${id}`;
512
- }
513
- function pendingDispatchMailboxAppendIndexKey() {
514
- return `${DISPATCH_PREFIX}:incomplete`;
515
- }
516
- function pendingDispatchMailboxAppendIndexLockKey() {
517
- return `${DISPATCH_PREFIX}:incomplete:lock`;
518
- }
519
- function normalizeMetadata(metadata) {
520
- if (!metadata) {
521
- return void 0;
522
- }
523
- const entries = Object.entries(metadata).filter(
524
- (entry) => typeof entry[0] === "string" && typeof entry[1] === "string"
525
- );
526
- return entries.length > 0 ? Object.fromEntries(entries) : void 0;
527
- }
528
- function buildDispatchId(plugin, idempotencyKey) {
529
- const digest = createHash("sha256").update(plugin).update("\0").update(idempotencyKey).digest("hex").slice(0, 32);
530
- return `dispatch_${digest}`;
531
- }
532
- function parseDispatchRecord(value) {
533
- const parsed = parseStoredDispatchRecord(value);
534
- return parsed?.record;
535
- }
536
- function parseStoredDispatchRecord(value) {
537
- const parsed = storedDispatchRecordSchema.safeParse(value);
538
- if (!parsed.success) {
539
- return void 0;
540
- }
541
- const {
542
- attempt: _attempt,
543
- lastCallbackAtMs: _lastCallbackAtMs,
544
- leaseExpiresAtMs: _leaseExpiresAtMs,
545
- maxAttempts: _maxAttempts,
546
- version: _version,
547
- ...record
548
- } = parsed.data;
549
- return {
550
- ...typeof parsed.data.leaseExpiresAtMs === "number" ? { legacyLeaseExpiresAtMs: parsed.data.leaseExpiresAtMs } : {},
551
- record
552
- };
553
- }
554
- function getDispatchConversationId(dispatch2) {
555
- return `agent-dispatch:${dispatch2.id}`;
556
- }
557
- function getDispatchInputMessageId(dispatchId) {
558
- return `agent-dispatch:${dispatchId}`;
559
- }
560
- function getLegacyDispatchInputMessageId(dispatchId) {
561
- return `dispatch:${dispatchId}:user`;
562
- }
563
- function getDispatchInputMessageIds(dispatchId) {
564
- return [
565
- getDispatchInputMessageId(dispatchId),
566
- getLegacyDispatchInputMessageId(dispatchId)
567
- ];
568
- }
569
- function getDispatchTurnId(dispatchId) {
570
- return `dispatch:${dispatchId}`;
571
- }
572
- function toDispatchProjection(record) {
573
- return {
574
- id: record.id,
575
- status: record.status,
576
- ...record.resultMessageTs ? { resultMessageTs: record.resultMessageTs } : {},
577
- ...record.errorMessage ? { errorMessage: record.errorMessage } : {}
578
- };
579
- }
580
- function isTerminalDispatchStatus(status) {
581
- return status === "completed" || status === "failed" || status === "blocked";
582
- }
583
- async function withDispatchLock(dispatchId, task) {
584
- const state = getStateAdapter();
585
- await state.connect();
586
- const lock = await state.acquireLock(
587
- dispatchLockKey(dispatchId),
588
- DISPATCH_LOCK_TTL_MS
589
- );
590
- if (!lock) {
591
- throw new Error(`Could not acquire dispatch lock for ${dispatchId}`);
592
- }
593
- try {
594
- return await task(state);
595
- } finally {
596
- await state.releaseLock(lock);
597
- }
598
- }
599
- async function putRecord(state, record) {
600
- const parsed = dispatchRecordSchema.safeParse(record);
601
- if (!parsed.success) {
602
- throw new Error(
603
- `Dispatch record is invalid: ${parsed.error.issues.map((issue) => `${issue.path.join(".") || "<root>"} ${issue.message}`).join("; ")}`
604
- );
605
- }
606
- const next = parsed.data;
607
- await state.set(
608
- getDispatchStorageKey(next.id),
609
- next,
610
- JUNIOR_THREAD_STATE_TTL_MS
611
- );
612
- }
613
- async function updatePendingDispatchMailboxAppendIndex(state, update) {
614
- const lock = await state.acquireLock(
615
- pendingDispatchMailboxAppendIndexLockKey(),
616
- DISPATCH_MAILBOX_APPEND_INDEX_LOCK_TTL_MS
617
- );
618
- if (!lock) {
619
- throw new Error("Could not acquire dispatch mailbox append index lock");
620
- }
621
- try {
622
- const stored = await state.get(pendingDispatchMailboxAppendIndexKey());
623
- const ids = stored === void 0 || stored === null ? [] : pendingDispatchMailboxAppendIndexSchema.parse(stored);
624
- const next = [...new Set(update([...new Set(ids)]))].slice(
625
- -DISPATCH_MAILBOX_APPEND_INDEX_MAX_LENGTH
626
- );
627
- if (next.length === ids.length && next.every((id, index) => id === ids[index])) {
628
- return;
629
- }
630
- await state.set(
631
- pendingDispatchMailboxAppendIndexKey(),
632
- next,
633
- JUNIOR_THREAD_STATE_TTL_MS
634
- );
635
- } finally {
636
- await state.releaseLock(lock);
637
- }
638
- }
639
- async function getDispatchRecord(id) {
640
- const state = getStateAdapter();
641
- await state.connect();
642
- return parseDispatchRecord(await state.get(getDispatchStorageKey(id)));
643
- }
644
- async function createOrGetDispatch(args) {
645
- const id = buildDispatchId(args.plugin, args.options.idempotencyKey);
646
- return await withDispatchLock(id, async (state) => {
647
- const existing = parseDispatchRecord(
648
- await state.get(getDispatchStorageKey(id))
649
- );
650
- if (existing) {
651
- return { record: existing, status: "already_exists" };
652
- }
653
- const metadata = normalizeMetadata(args.options.metadata);
654
- const record = {
655
- actor: { platform: "system", name: args.plugin },
656
- createdAtMs: args.nowMs,
657
- ...args.options.credentialSubject ? { credentialSubject: args.options.credentialSubject } : {},
658
- destination: args.options.destination,
659
- destinationVisibility: args.options.destinationVisibility,
660
- id,
661
- idempotencyKey: args.options.idempotencyKey,
662
- input: args.options.input,
663
- ...metadata ? { metadata } : {},
664
- plugin: args.plugin,
665
- ...args.options.replyAttribution ? { replyAttribution: args.options.replyAttribution } : {},
666
- status: "pending",
667
- source: args.options.source,
668
- updatedAtMs: args.nowMs
669
- };
670
- await updatePendingDispatchMailboxAppendIndex(
671
- state,
672
- (ids) => ids.includes(id) ? ids : [...ids, id]
673
- );
674
- await putRecord(state, record);
675
- return { record, status: "created" };
676
- });
677
- }
678
- async function transitionDispatch(id, transition) {
679
- return await withDispatchLock(id, async (state) => {
680
- const current = parseDispatchRecord(
681
- await state.get(getDispatchStorageKey(id))
682
- );
683
- if (!current) {
684
- return void 0;
685
- }
686
- const transitioned = transition(current);
687
- if (transitioned === current) {
688
- return current;
689
- }
690
- const next = {
691
- ...transitioned,
692
- updatedAtMs: Date.now()
693
- };
694
- await putRecord(state, next);
695
- return next;
696
- });
697
- }
698
- async function markDispatchRunning(id) {
699
- return await transitionDispatch(
700
- id,
701
- (record) => isTerminalDispatchStatus(record.status) ? record : { ...record, status: "running", errorMessage: void 0 }
702
- );
703
- }
704
- async function markDispatchAwaitingResume(id) {
705
- return await transitionDispatch(
706
- id,
707
- (record) => isTerminalDispatchStatus(record.status) ? record : {
708
- ...record,
709
- status: "awaiting_resume"
710
- }
711
- );
712
- }
713
- async function markDispatchBlocked(id, errorMessage, resultMessageTs) {
714
- return await transitionDispatch(
715
- id,
716
- (record) => isTerminalDispatchStatus(record.status) ? record : {
717
- ...record,
718
- errorMessage,
719
- ...resultMessageTs ? { resultMessageTs } : {},
720
- status: "blocked"
721
- }
722
- );
723
- }
724
- async function markDispatchCompleted(id, resultMessageTs) {
725
- const previous = await getDispatchRecord(id);
726
- const next = await transitionDispatch(
727
- id,
728
- (record) => isTerminalDispatchStatus(record.status) ? record : {
729
- ...record,
730
- errorMessage: void 0,
731
- ...resultMessageTs ? { resultMessageTs } : {},
732
- status: "completed"
733
- }
734
- );
735
- if (next?.status === "completed" && previous?.status !== "completed" && next.plugin === "junior") {
736
- const eventTaskId = next.metadata?.eventTaskId;
737
- if (eventTaskId) {
738
- await recordTaskExecution("event", eventTaskId, {
739
- conversationId: getDispatchConversationId(next),
740
- executionId: next.id,
741
- nowMs: next.updatedAtMs
742
- });
743
- }
744
- }
745
- return next;
746
- }
747
- async function markDispatchFailed(id, errorMessage, resultMessageTs) {
748
- return await transitionDispatch(
749
- id,
750
- (record) => isTerminalDispatchStatus(record.status) ? record : {
751
- ...record,
752
- errorMessage,
753
- ...resultMessageTs ? { resultMessageTs } : {},
754
- status: "failed"
755
- }
756
- );
757
- }
758
- async function confirmDispatchMailboxAppend(id) {
759
- const state = getStateAdapter();
760
- await state.connect();
761
- await updatePendingDispatchMailboxAppendIndex(
762
- state,
763
- (ids) => ids.filter((candidate) => candidate !== id)
764
- );
765
- }
766
- async function listPendingDispatchMailboxAppends() {
767
- const state = getStateAdapter();
768
- await state.connect();
769
- const stored = await state.get(pendingDispatchMailboxAppendIndexKey());
770
- return stored === void 0 || stored === null ? [] : [...new Set(pendingDispatchMailboxAppendIndexSchema.parse(stored))];
771
- }
772
- async function claimDispatchMailboxAppend(id, nowMs) {
773
- return await withDispatchLock(id, async (state) => {
774
- const stored = parseStoredDispatchRecord(
775
- await state.get(getDispatchStorageKey(id))
776
- );
777
- if (!stored || stored.legacyLeaseExpiresAtMs && stored.legacyLeaseExpiresAtMs > nowMs) {
778
- return void 0;
779
- }
780
- await putRecord(state, stored.record);
781
- return stored.record;
782
- });
783
- }
784
- async function getPluginDispatchProjection(args) {
785
- const record = await getDispatchRecord(args.id);
786
- if (!record || record.plugin !== args.plugin) {
787
- return void 0;
788
- }
789
- return toDispatchProjection(record);
790
- }
791
-
792
430
  // src/chat/agent-dispatch/work.ts
793
- import { z as z3 } from "zod";
794
- var agentDispatchMailboxMetadataSchema = z3.object({
795
- dispatchId: z3.string().min(1),
796
- kind: z3.literal("agent_dispatch")
431
+ import { z as z2 } from "zod";
432
+ var agentDispatchMailboxMetadataSchema = z2.object({
433
+ dispatchId: z2.string().min(1),
434
+ kind: z2.literal("agent_dispatch")
797
435
  }).strict();
798
436
  var AGENT_DISPATCH_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
799
437
  function buildDispatchRoutingContext(dispatch2) {
800
438
  return {
801
439
  actor: dispatch2.actor,
802
- credentialContext: {
803
- actor: dispatch2.actor,
804
- ...dispatch2.credentialSubject ? { subject: dispatch2.credentialSubject } : {}
805
- },
440
+ credentialContext: credentialContextForActor(
441
+ dispatch2.actor,
442
+ dispatch2.credentialSubject
443
+ ),
806
444
  destinationVisibility: dispatch2.destinationVisibility,
807
445
  dispatch: {
808
446
  actor: dispatch2.actor,
@@ -1301,7 +939,7 @@ import {
1301
939
  // src/chat/agent-dispatch/validation.ts
1302
940
  import {
1303
941
  dispatchOptionsSchema,
1304
- isSlackDestination as isSlackDestination2
942
+ isSlackDestination
1305
943
  } from "@sentry/junior-plugin-api";
1306
944
  function hasIssueAtPath(issues, path) {
1307
945
  return issues.some(
@@ -1398,7 +1036,7 @@ function validateDispatchOptions(options) {
1398
1036
  }
1399
1037
  const candidate = parsed.data;
1400
1038
  const { credentialSubject, destination } = candidate;
1401
- if (!isSlackDestination2(destination)) {
1039
+ if (!isSlackDestination(destination)) {
1402
1040
  throw new Error("Dispatch destination platform must be slack");
1403
1041
  }
1404
1042
  if (credentialSubject !== void 0) {
@@ -1623,6 +1261,37 @@ function shouldSkipRun(task, run) {
1623
1261
  }
1624
1262
  return void 0;
1625
1263
  }
1264
+ async function recordScheduledExecution(args) {
1265
+ const dispatchId = args.run.dispatchId;
1266
+ await recordTaskExecution("scheduled", args.run.taskId, {
1267
+ ...dispatchId ? { conversationId: getDispatchConversationId({ id: dispatchId }) } : {},
1268
+ executionId: args.run.id,
1269
+ nowMs: args.nowMs,
1270
+ status: args.status
1271
+ });
1272
+ }
1273
+ async function logRunOutcome(args) {
1274
+ const task = args.task ?? await args.store.getTask(args.run.taskId) ?? void 0;
1275
+ if (!task) {
1276
+ logInfo(args.eventName, {
1277
+ "app.task.id": args.run.taskId,
1278
+ "app.task.type": "scheduled",
1279
+ "app.task.run.id": args.run.id,
1280
+ "app.task.run.status": args.run.status,
1281
+ "app.task.run.scheduled_for": new Date(
1282
+ args.run.scheduledForMs
1283
+ ).toISOString(),
1284
+ ...args.run.dispatchId ? { "app.dispatch.id": args.run.dispatchId } : {},
1285
+ ...args.run.resultMessageTs ? { "app.task.result_message_ts": args.run.resultMessageTs } : {},
1286
+ ...args.extras
1287
+ });
1288
+ return;
1289
+ }
1290
+ logInfo(
1291
+ args.eventName,
1292
+ scheduledTaskRunAttributes(task, args.run, args.extras)
1293
+ );
1294
+ }
1626
1295
  async function applyDispatchResult(args) {
1627
1296
  if (args.dispatch.status === "completed") {
1628
1297
  const completed = await args.store.markRunCompleted({
@@ -1637,14 +1306,16 @@ async function applyDispatchResult(args) {
1637
1306
  run: args.run,
1638
1307
  status: "completed"
1639
1308
  });
1640
- const dispatchId = args.run.dispatchId;
1641
- if (dispatchId) {
1642
- await recordTaskExecution("scheduled", args.run.taskId, {
1643
- conversationId: getDispatchConversationId({ id: dispatchId }),
1644
- executionId: args.run.id,
1645
- nowMs: args.nowMs
1646
- });
1647
- }
1309
+ await recordScheduledExecution({
1310
+ nowMs: args.nowMs,
1311
+ run: args.run,
1312
+ status: "completed"
1313
+ });
1314
+ await logRunOutcome({
1315
+ eventName: "scheduled_task.run.completed",
1316
+ run: completed,
1317
+ store: args.store
1318
+ });
1648
1319
  return true;
1649
1320
  }
1650
1321
  if (args.dispatch.status === "blocked") {
@@ -1661,6 +1332,17 @@ async function applyDispatchResult(args) {
1661
1332
  run: args.run,
1662
1333
  status: "blocked"
1663
1334
  });
1335
+ await recordScheduledExecution({
1336
+ nowMs: args.nowMs,
1337
+ run: args.run,
1338
+ status: "blocked"
1339
+ });
1340
+ await logRunOutcome({
1341
+ eventName: "scheduled_task.run.blocked",
1342
+ run: blocked,
1343
+ store: args.store,
1344
+ extras: blocked.errorMessage ? { "app.task.run.error": blocked.errorMessage } : void 0
1345
+ });
1664
1346
  return true;
1665
1347
  }
1666
1348
  if (args.dispatch.status === "failed") {
@@ -1677,6 +1359,17 @@ async function applyDispatchResult(args) {
1677
1359
  run: args.run,
1678
1360
  status: "failed"
1679
1361
  });
1362
+ await recordScheduledExecution({
1363
+ nowMs: args.nowMs,
1364
+ run: args.run,
1365
+ status: "failed"
1366
+ });
1367
+ await logRunOutcome({
1368
+ eventName: "scheduled_task.run.failed",
1369
+ run: failed,
1370
+ store: args.store,
1371
+ extras: failed.errorMessage ? { "app.task.run.error": failed.errorMessage } : void 0
1372
+ });
1680
1373
  return true;
1681
1374
  }
1682
1375
  return false;
@@ -1699,6 +1392,17 @@ async function finishClaimedRun(args) {
1699
1392
  run: args.run,
1700
1393
  status: args.status
1701
1394
  });
1395
+ await recordScheduledExecution({
1396
+ nowMs: args.nowMs,
1397
+ run: args.run,
1398
+ status: args.status
1399
+ });
1400
+ await logRunOutcome({
1401
+ eventName: args.status === "blocked" ? "scheduled_task.run.blocked" : "scheduled_task.run.failed",
1402
+ run: finished,
1403
+ store: args.store,
1404
+ extras: { "app.task.run.error": args.errorMessage }
1405
+ });
1702
1406
  }
1703
1407
  async function runScheduledTaskHeartbeat(args) {
1704
1408
  const store = createSchedulerSqlStore(getDb());
@@ -1717,7 +1421,12 @@ async function runScheduledTaskHeartbeat(args) {
1717
1421
  });
1718
1422
  continue;
1719
1423
  }
1720
- if (await applyDispatchResult({ dispatch: dispatch2, nowMs: args.nowMs, run, store })) {
1424
+ if (await applyDispatchResult({
1425
+ dispatch: dispatch2,
1426
+ nowMs: args.nowMs,
1427
+ run,
1428
+ store
1429
+ })) {
1721
1430
  processedCount += 1;
1722
1431
  }
1723
1432
  }
@@ -1726,20 +1435,39 @@ async function runScheduledTaskHeartbeat(args) {
1726
1435
  if (!run) break;
1727
1436
  const task = await store.getTask(run.taskId);
1728
1437
  if (!task) {
1729
- await store.markRunFailed({
1438
+ const failed = await store.markRunFailed({
1730
1439
  completedAtMs: args.nowMs,
1731
1440
  errorMessage: `Scheduled task ${run.taskId} was not found`,
1732
1441
  runId: run.id
1733
1442
  });
1443
+ if (failed) {
1444
+ await logRunOutcome({
1445
+ eventName: "scheduled_task.run.failed",
1446
+ run: failed,
1447
+ store,
1448
+ extras: {
1449
+ "app.task.run.error": `Scheduled task ${run.taskId} was not found`
1450
+ }
1451
+ });
1452
+ }
1734
1453
  continue;
1735
1454
  }
1455
+ logInfo(
1456
+ "scheduled_task.run.claimed",
1457
+ scheduledTaskRunAttributes(task, run)
1458
+ );
1736
1459
  const skippedReason = shouldSkipRun(task, run);
1737
1460
  if (skippedReason) {
1738
- await store.markRunSkipped({
1461
+ const skipped = await store.markRunSkipped({
1739
1462
  completedAtMs: args.nowMs,
1740
1463
  errorMessage: skippedReason,
1741
1464
  runId: run.id
1742
1465
  });
1466
+ if (skipped) {
1467
+ logScheduledTaskRunSkipped(task, skipped, {
1468
+ "app.task.run.error": skippedReason
1469
+ });
1470
+ }
1743
1471
  continue;
1744
1472
  }
1745
1473
  let metadata;
@@ -1788,12 +1516,22 @@ async function runScheduledTaskHeartbeat(args) {
1788
1516
  });
1789
1517
  continue;
1790
1518
  }
1791
- await store.markRunDispatched({
1519
+ const dispatched = await store.markRunDispatched({
1792
1520
  claimedAtMs: run.claimedAtMs,
1793
1521
  dispatchId: dispatch2.id,
1794
1522
  nowMs: args.nowMs,
1795
1523
  runId: run.id
1796
1524
  });
1525
+ logInfo(
1526
+ "scheduled_task.run.dispatched",
1527
+ scheduledTaskRunAttributes(
1528
+ task,
1529
+ dispatched ?? { ...run, dispatchId: dispatch2.id },
1530
+ {
1531
+ "app.dispatch.id": dispatch2.id
1532
+ }
1533
+ )
1534
+ );
1797
1535
  dispatchCount += 1;
1798
1536
  }
1799
1537
  return dispatchCount;
@@ -2658,7 +2396,7 @@ import {
2658
2396
  Message,
2659
2397
  ThreadImpl
2660
2398
  } from "chat";
2661
- import { z as z5 } from "zod";
2399
+ import { z as z4 } from "zod";
2662
2400
 
2663
2401
  // src/chat/ingress/message-router.ts
2664
2402
  function normalizeIncomingSlackThreadId(threadId, message) {
@@ -2829,14 +2567,14 @@ async function ensureSlackMessageActorIdentity(message, teamId, lookupSlackUser2
2829
2567
  }
2830
2568
 
2831
2569
  // src/chat/slack/user.ts
2832
- import { z as z4 } from "zod";
2833
- var slackUserInfoSchema = z4.object({
2834
- ok: z4.literal(true),
2835
- user: z4.object({
2836
- name: z4.string().nullish(),
2837
- profile: z4.object({
2838
- email: z4.string().nullish(),
2839
- real_name: z4.string().nullish()
2570
+ import { z as z3 } from "zod";
2571
+ var slackUserInfoSchema = z3.object({
2572
+ ok: z3.literal(true),
2573
+ user: z3.object({
2574
+ name: z3.string().nullish(),
2575
+ profile: z3.object({
2576
+ email: z3.string().nullish(),
2577
+ real_name: z3.string().nullish()
2840
2578
  }).optional()
2841
2579
  })
2842
2580
  });
@@ -2913,6 +2651,20 @@ async function lookupSlackUser(teamId, userId) {
2913
2651
  }
2914
2652
  }
2915
2653
 
2654
+ // src/chat/resource-events/actor.ts
2655
+ var RESOURCE_EVENT_SLACK_AUTHOR_ID = "UJRNEVENT";
2656
+ var RESOURCE_EVENT_SYSTEM_ACTOR = {
2657
+ platform: "system",
2658
+ name: "resource-event"
2659
+ };
2660
+ function isResourceEventConversationMessage(message) {
2661
+ return Boolean(message.meta?.eventType) || message.author?.userId === RESOURCE_EVENT_SLACK_AUTHOR_ID;
2662
+ }
2663
+ function isResourceEventSlackMessage(message) {
2664
+ const raw = message.raw && typeof message.raw === "object" ? message.raw : void 0;
2665
+ return raw?.event_type === "resource_event";
2666
+ }
2667
+
2916
2668
  // src/chat/slack/message-control.ts
2917
2669
  var LEADING_MENTION_BEFORE_STEERING_RE = /^(\s*(?:<@[^>]+>|@[A-Za-z0-9._-]+\b)[\s,:-]*)!!\s*/;
2918
2670
  function stripLeadingSteeringOverride(text) {
@@ -2920,17 +2672,17 @@ function stripLeadingSteeringOverride(text) {
2920
2672
  }
2921
2673
 
2922
2674
  // src/chat/task-execution/slack-work.ts
2923
- var slackConversationRouteSchema = z5.enum(["mention", "subscribed"]);
2675
+ var slackConversationRouteSchema = z4.enum(["mention", "subscribed"]);
2924
2676
  function hasSteeringOverride(text) {
2925
2677
  return stripLeadingSteeringOverride(text) !== text;
2926
2678
  }
2927
- var serializedDateSchema = z5.iso.datetime();
2928
- var mdastPointSchema = z5.object({
2929
- column: z5.number().int().positive(),
2930
- line: z5.number().int().positive(),
2931
- offset: z5.number().int().nonnegative().optional()
2679
+ var serializedDateSchema = z4.iso.datetime();
2680
+ var mdastPointSchema = z4.object({
2681
+ column: z4.number().int().positive(),
2682
+ line: z4.number().int().positive(),
2683
+ offset: z4.number().int().nonnegative().optional()
2932
2684
  }).strict();
2933
- var mdastPositionSchema = z5.object({
2685
+ var mdastPositionSchema = z4.object({
2934
2686
  end: mdastPointSchema,
2935
2687
  start: mdastPointSchema
2936
2688
  }).strict();
@@ -3042,7 +2794,7 @@ function isFormattedContentNode(value) {
3042
2794
  return false;
3043
2795
  }
3044
2796
  }
3045
- var formattedContentSchema = z5.custom(
2797
+ var formattedContentSchema = z4.custom(
3046
2798
  (value) => {
3047
2799
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
3048
2800
  return false;
@@ -3052,79 +2804,79 @@ var formattedContentSchema = z5.custom(
3052
2804
  },
3053
2805
  "must be a valid formatted-content root"
3054
2806
  );
3055
- var serializedMessageSchema = z5.object({
3056
- _type: z5.literal("chat:Message"),
3057
- attachments: z5.array(
3058
- z5.object({
3059
- type: z5.enum(["image", "file", "video", "audio"]),
3060
- url: z5.string().optional(),
3061
- name: z5.string().optional(),
3062
- mimeType: z5.string().optional(),
3063
- size: z5.number().finite().optional(),
3064
- width: z5.number().finite().optional(),
3065
- height: z5.number().finite().optional(),
3066
- fetchMetadata: z5.record(z5.string(), z5.string()).optional()
2807
+ var serializedMessageSchema = z4.object({
2808
+ _type: z4.literal("chat:Message"),
2809
+ attachments: z4.array(
2810
+ z4.object({
2811
+ type: z4.enum(["image", "file", "video", "audio"]),
2812
+ url: z4.string().optional(),
2813
+ name: z4.string().optional(),
2814
+ mimeType: z4.string().optional(),
2815
+ size: z4.number().finite().optional(),
2816
+ width: z4.number().finite().optional(),
2817
+ height: z4.number().finite().optional(),
2818
+ fetchMetadata: z4.record(z4.string(), z4.string()).optional()
3067
2819
  }).strict()
3068
2820
  ),
3069
- author: z5.object({
3070
- userId: z5.string(),
3071
- userName: z5.string(),
3072
- fullName: z5.string(),
3073
- isBot: z5.union([z5.boolean(), z5.literal("unknown")]),
3074
- isMe: z5.boolean()
2821
+ author: z4.object({
2822
+ userId: z4.string(),
2823
+ userName: z4.string(),
2824
+ fullName: z4.string(),
2825
+ isBot: z4.union([z4.boolean(), z4.literal("unknown")]),
2826
+ isMe: z4.boolean()
3075
2827
  }).strict(),
3076
2828
  formatted: formattedContentSchema,
3077
- id: z5.string().min(1),
3078
- isMention: z5.boolean().optional(),
3079
- links: z5.array(
3080
- z5.object({
3081
- url: z5.string(),
3082
- title: z5.string().optional(),
3083
- description: z5.string().optional(),
3084
- imageUrl: z5.string().optional(),
3085
- siteName: z5.string().optional()
2829
+ id: z4.string().min(1),
2830
+ isMention: z4.boolean().optional(),
2831
+ links: z4.array(
2832
+ z4.object({
2833
+ url: z4.string(),
2834
+ title: z4.string().optional(),
2835
+ description: z4.string().optional(),
2836
+ imageUrl: z4.string().optional(),
2837
+ siteName: z4.string().optional()
3086
2838
  }).strict()
3087
2839
  ).optional(),
3088
- metadata: z5.object({
2840
+ metadata: z4.object({
3089
2841
  dateSent: serializedDateSchema,
3090
- edited: z5.boolean(),
2842
+ edited: z4.boolean(),
3091
2843
  editedAt: serializedDateSchema.optional()
3092
2844
  }).strict(),
3093
- raw: z5.unknown(),
3094
- text: z5.string(),
3095
- threadId: z5.string().min(1)
2845
+ raw: z4.unknown(),
2846
+ text: z4.string(),
2847
+ threadId: z4.string().min(1)
3096
2848
  }).strict();
3097
- var serializedThreadSchema = z5.object({
3098
- _type: z5.literal("chat:Thread"),
3099
- adapterName: z5.literal("slack"),
3100
- channelId: z5.string().min(1),
3101
- channelVisibility: z5.enum(["private", "workspace", "external", "unknown"]).optional(),
2849
+ var serializedThreadSchema = z4.object({
2850
+ _type: z4.literal("chat:Thread"),
2851
+ adapterName: z4.literal("slack"),
2852
+ channelId: z4.string().min(1),
2853
+ channelVisibility: z4.enum(["private", "workspace", "external", "unknown"]).optional(),
3102
2854
  currentMessage: serializedMessageSchema.optional(),
3103
- id: z5.string().min(1),
3104
- isDM: z5.boolean()
2855
+ id: z4.string().min(1),
2856
+ isDM: z4.boolean()
3105
2857
  }).strict();
3106
- var slackInstallationSchema = z5.object({
3107
- enterpriseId: z5.string().optional(),
3108
- isEnterpriseInstall: z5.boolean().optional(),
3109
- teamId: z5.string().optional()
2858
+ var slackInstallationSchema = z4.object({
2859
+ enterpriseId: z4.string().optional(),
2860
+ isEnterpriseInstall: z4.boolean().optional(),
2861
+ teamId: z4.string().optional()
3110
2862
  }).strict();
3111
- var slackConversationMessageMetadataBaseSchema = z5.object({
2863
+ var slackConversationMessageMetadataBaseSchema = z4.object({
3112
2864
  installation: slackInstallationSchema.optional(),
3113
2865
  message: serializedMessageSchema,
3114
- platform: z5.literal("slack"),
2866
+ platform: z4.literal("slack"),
3115
2867
  route: slackConversationRouteSchema,
3116
2868
  thread: serializedThreadSchema
3117
2869
  });
3118
- var slackConversationMessageMetadataSchema = z5.union([
2870
+ var slackConversationMessageMetadataSchema = z4.union([
3119
2871
  slackConversationMessageMetadataBaseSchema.strict(),
3120
2872
  slackConversationMessageMetadataBaseSchema.extend({
3121
- kind: z5.literal("resource_event"),
3122
- resourceEvent: z5.object({
3123
- eventKey: z5.string(),
3124
- eventType: z5.string(),
3125
- namespace: z5.string(),
3126
- identifier: z5.string(),
3127
- subscriptionId: z5.string()
2873
+ kind: z4.literal("resource_event"),
2874
+ resourceEvent: z4.object({
2875
+ eventKey: z4.string(),
2876
+ eventType: z4.string(),
2877
+ namespace: z4.string(),
2878
+ identifier: z4.string(),
2879
+ subscriptionId: z4.string()
3128
2880
  }).strict()
3129
2881
  }).strict()
3130
2882
  ]);
@@ -3157,7 +2909,7 @@ function slackSerializedResourceEventMessage(input) {
3157
2909
  _type: "chat:Message",
3158
2910
  attachments: [],
3159
2911
  author: {
3160
- userId: "UJRNEVENT",
2912
+ userId: RESOURCE_EVENT_SLACK_AUTHOR_ID,
3161
2913
  userName: "junior-event",
3162
2914
  fullName: "Junior event",
3163
2915
  isBot: true,
@@ -3175,7 +2927,7 @@ function slackSerializedResourceEventMessage(input) {
3175
2927
  resource_event_type: input.eventType,
3176
2928
  thread_ts: input.threadTs,
3177
2929
  type: "message",
3178
- user: "UJRNEVENT"
2930
+ user: RESOURCE_EVENT_SLACK_AUTHOR_ID
3179
2931
  },
3180
2932
  text: input.text,
3181
2933
  threadId: `slack:${input.channelId}:${input.threadTs}`
@@ -3219,7 +2971,7 @@ function createSlackResourceEventInboundMessage(input) {
3219
2971
  receivedAtMs: Date.now(),
3220
2972
  input: {
3221
2973
  text: input.text,
3222
- authorId: "UJRNEVENT",
2974
+ authorId: RESOURCE_EVENT_SLACK_AUTHOR_ID,
3223
2975
  metadata: {
3224
2976
  kind: "resource_event",
3225
2977
  installation: {
@@ -3259,10 +3011,6 @@ function routeForRecords(records) {
3259
3011
  return metadata.route === "mention";
3260
3012
  }) ? "mention" : "subscribed";
3261
3013
  }
3262
- function isResourceEventNotificationMessage(message) {
3263
- const raw = message.raw && typeof message.raw === "object" ? message.raw : void 0;
3264
- return raw?.event_type === "resource_event";
3265
- }
3266
3014
  function restoreMessage(args) {
3267
3015
  const metadata = parseSlackMetadata(args.record.input.metadata);
3268
3016
  if (!metadata) {
@@ -3278,7 +3026,7 @@ function restoreMessage(args) {
3278
3026
  async function bindSlackActorIdentities(args) {
3279
3027
  const byAuthorId = /* @__PURE__ */ new Map();
3280
3028
  for (const message of args.messages) {
3281
- if (isResourceEventNotificationMessage(message)) {
3029
+ if (isResourceEventSlackMessage(message)) {
3282
3030
  continue;
3283
3031
  }
3284
3032
  const authorId = requireSlackAuthorId(message);
@@ -4497,7 +4245,7 @@ function registerVercelPluginTaskDevConsumer() {
4497
4245
  }
4498
4246
 
4499
4247
  // src/chat/services/subscribed-decision.ts
4500
- import { z as z6 } from "zod";
4248
+ import { z as z5 } from "zod";
4501
4249
 
4502
4250
  // src/chat/services/subscribed-reply-router-policy.ts
4503
4251
  function buildSubscribedReplyRouterPolicy(assistantName) {
@@ -4542,13 +4290,13 @@ Assess conversation floor ownership and whether the latest message continues wor
4542
4290
  }
4543
4291
 
4544
4292
  // src/chat/services/subscribed-decision.ts
4545
- var replyDecisionSchema = z6.object({
4546
- should_reply: z6.boolean().describe("Whether Junior should respond to this thread message."),
4547
- should_unsubscribe: z6.boolean().describe(
4293
+ var replyDecisionSchema = z5.object({
4294
+ should_reply: z5.boolean().describe("Whether Junior should respond to this thread message."),
4295
+ should_unsubscribe: z5.boolean().describe(
4548
4296
  "Whether Junior should unsubscribe from this thread because the user clearly asked it to stop participating."
4549
4297
  ),
4550
- confidence: z6.number().min(0).max(1).describe("Classifier confidence from 0 to 1."),
4551
- reason: z6.string().describe("Short reason for the decision; use an empty string if none.")
4298
+ confidence: z5.number().min(0).max(1).describe("Classifier confidence from 0 to 1."),
4299
+ reason: z5.string().describe("Short reason for the decision; use an empty string if none.")
4552
4300
  }).strict();
4553
4301
  var ROUTER_CONFIDENCE_THRESHOLD = 0.7;
4554
4302
  var ROUTER_CLASSIFIER_MAX_TOKENS = 400;
@@ -5003,10 +4751,6 @@ function buildLogContext(deps, args) {
5003
4751
  function actorUserName(message) {
5004
4752
  return getMessageActorIdentity(message)?.userName;
5005
4753
  }
5006
- function isResourceEventNotificationMessage2(message) {
5007
- const raw = message.raw && typeof message.raw === "object" ? message.raw : void 0;
5008
- return raw?.event_type === "resource_event";
5009
- }
5010
4754
  function createSlackTurnRuntime(deps) {
5011
4755
  const logContext = (args) => buildLogContext(deps, args);
5012
4756
  const maybeHandleThreadOptOutDecision = async (args) => {
@@ -5427,7 +5171,7 @@ function createSlackTurnRuntime(deps) {
5427
5171
  const threadId = deps.getThreadId(thread, message);
5428
5172
  const channelId = deps.getChannelId(thread, message);
5429
5173
  const runId = deps.getRunId(thread, message);
5430
- const isResourceEventNotification = isResourceEventNotificationMessage2(message);
5174
+ const isResourceEventNotification = isResourceEventSlackMessage(message);
5431
5175
  const actorId = isResourceEventNotification ? void 0 : message.author.userId;
5432
5176
  const turnContext = logContext({
5433
5177
  threadId,
@@ -6225,11 +5969,11 @@ function createJuniorRuntimeServices(overrides = {}) {
6225
5969
  import { createSlackSource as createSlackSource2 } from "@sentry/junior-plugin-api";
6226
5970
 
6227
5971
  // src/chat/slack/action-token.ts
6228
- import { z as z7 } from "zod";
6229
- var slackActionTokenSchema = z7.string().trim().min(1).brand();
6230
- var slackMessageEnvelopeSchema = z7.object({
6231
- raw: z7.object({
6232
- action_token: z7.unknown().optional()
5972
+ import { z as z6 } from "zod";
5973
+ var slackActionTokenSchema = z6.string().trim().min(1).brand();
5974
+ var slackMessageEnvelopeSchema = z6.object({
5975
+ raw: z6.object({
5976
+ action_token: z6.unknown().optional()
6233
5977
  }).optional()
6234
5978
  });
6235
5979
  function readSlackActionToken(message) {
@@ -6393,7 +6137,7 @@ function queuedInstructionActor(queued) {
6393
6137
  };
6394
6138
  }
6395
6139
  function queuedInstructionProvenance(queued, teamId) {
6396
- if (isResourceEventMessage(queued.message)) {
6140
+ if (isResourceEventSlackMessage(queued.message)) {
6397
6141
  return contextProvenance;
6398
6142
  }
6399
6143
  const identity = getMessageActorIdentity(queued.message);
@@ -6407,13 +6151,6 @@ function queuedInstructionProvenance(queued, teamId) {
6407
6151
  );
6408
6152
  return instructionProvenanceFor(author);
6409
6153
  }
6410
- function isResourceEventMessage(message) {
6411
- const raw = message.raw && typeof message.raw === "object" ? message.raw : void 0;
6412
- return raw?.event_type === "resource_event";
6413
- }
6414
- function resourceEventCredentialContext(message) {
6415
- return isResourceEventMessage(message) ? { actor: { platform: "system", name: "resource-event" } } : void 0;
6416
- }
6417
6154
  async function resolveChannelName(thread) {
6418
6155
  const existingName = thread.channel.name?.trim();
6419
6156
  if (existingName) {
@@ -6542,7 +6279,7 @@ function createReplyToThread(deps) {
6542
6279
  )
6543
6280
  };
6544
6281
  await Promise.all(
6545
- (options.queuedMessages ?? []).filter((queued) => !isResourceEventMessage(queued.message)).map(
6282
+ (options.queuedMessages ?? []).filter((queued) => !isResourceEventSlackMessage(queued.message)).map(
6546
6283
  (queued) => ensureSlackMessageActorIdentity(
6547
6284
  queued.message,
6548
6285
  teamId,
@@ -6551,15 +6288,28 @@ function createReplyToThread(deps) {
6551
6288
  )
6552
6289
  );
6553
6290
  const effectiveUserText = currentText.userText;
6554
- const credentialContext = options.execution?.credentialContext ?? resourceEventCredentialContext(message) ?? {
6555
- actor: { type: "user", userId: message.author.userId }
6556
- };
6557
- const actor = "type" in credentialContext.actor ? await ensureSlackMessageActorIdentity(
6558
- message,
6559
- teamId,
6560
- deps.services.lookupSlackUser
6561
- ) : void 0;
6562
- const executionActor = "type" in credentialContext.actor ? actor : credentialContext.actor;
6291
+ let executionActor;
6292
+ let credentialContext;
6293
+ if (options.execution) {
6294
+ executionActor = options.execution.dispatch.actor;
6295
+ credentialContext = options.execution.credentialContext;
6296
+ } else if (isResourceEventSlackMessage(message)) {
6297
+ executionActor = RESOURCE_EVENT_SYSTEM_ACTOR;
6298
+ credentialContext = credentialContextForActor(executionActor);
6299
+ } else {
6300
+ executionActor = await ensureSlackMessageActorIdentity(
6301
+ message,
6302
+ teamId,
6303
+ deps.services.lookupSlackUser
6304
+ );
6305
+ if (executionActor) {
6306
+ credentialContext = credentialContextForActor(executionActor);
6307
+ }
6308
+ }
6309
+ if (!executionActor || !credentialContext) {
6310
+ throw new Error("Slack reply execution requires an actor");
6311
+ }
6312
+ const actor = "userId" in executionActor ? executionActor : void 0;
6563
6313
  const slackActorId = actor?.userId;
6564
6314
  const preparedState = options.preparedState ?? await deps.prepareTurnState({
6565
6315
  thread,
@@ -6633,7 +6383,7 @@ function createReplyToThread(deps) {
6633
6383
  attachments,
6634
6384
  {
6635
6385
  threadId,
6636
- actorId: isResourceEventMessage(queued.message) ? void 0 : queued.message.author.userId,
6386
+ actorId: isResourceEventSlackMessage(queued.message) ? void 0 : queued.message.author.userId,
6637
6387
  channelId,
6638
6388
  runId,
6639
6389
  conversation: preparedState.conversation,
@@ -7208,7 +6958,9 @@ function createReplyToThread(deps) {
7208
6958
  },
7209
6959
  routing: {
7210
6960
  credentialContext,
7211
- actor: options.execution ? executionActor : actor,
6961
+ // Always set the execution actor when known. Resource-event turns
6962
+ // carry the system principal; interactive turns carry the Slack user.
6963
+ actor: executionActor,
7212
6964
  slackConversation,
7213
6965
  source,
7214
6966
  destination,
@@ -8236,27 +7988,57 @@ async function failContinuationStartup(args) {
8236
7988
  );
8237
7989
  }
8238
7990
  }
8239
- async function resolveContinuationActor(args) {
8240
- const stored = args.sessionRecordActor;
8241
- if (stored?.platform === "slack" && stored.teamId === args.teamId && stored.userId === args.userId) {
7991
+ async function resolveSlackResumeUserActor(args) {
7992
+ try {
7993
+ const work = await getConversationWorkState({
7994
+ conversationId: args.conversationId
7995
+ });
7996
+ const workActor = work?.actor;
7997
+ if (workActor?.teamId === args.teamId && workActor.slackUserId === args.userId) {
7998
+ try {
7999
+ return createSlackActor(args.teamId, args.userId, {
8000
+ email: workActor.email,
8001
+ fullName: workActor.fullName,
8002
+ userName: workActor.slackUserName
8003
+ });
8004
+ } catch {
8005
+ }
8006
+ }
8007
+ } catch {
8008
+ }
8009
+ try {
8242
8010
  return createSlackResumeActor({
8243
- actor: stored,
8244
8011
  teamId: args.teamId,
8245
8012
  userId: args.userId
8246
8013
  });
8014
+ } catch {
8015
+ return void 0;
8247
8016
  }
8248
- const work = await getConversationWorkState({
8249
- conversationId: args.conversationId
8250
- });
8251
- const workActor = work?.actor;
8252
- if (workActor && workActor.teamId === args.teamId && workActor.slackUserId === args.userId) {
8253
- return createSlackActor(args.teamId, args.userId, {
8254
- email: workActor.email,
8255
- fullName: workActor.fullName,
8256
- userName: workActor.slackUserName
8017
+ }
8018
+ async function resolveResumeExecutionIdentity(args) {
8019
+ const routing = args.routingContext;
8020
+ if (routing?.credentialContext) {
8021
+ const actor2 = routing.dispatch?.actor ?? routing.actor ?? (!("type" in routing.credentialContext.actor) ? routing.credentialContext.actor : void 0);
8022
+ return actor2 ? { actor: actor2, credentialContext: routing.credentialContext } : void 0;
8023
+ }
8024
+ let actor = routing?.actor;
8025
+ if (!actor && isResourceEventConversationMessage(args.userMessage)) {
8026
+ actor = RESOURCE_EVENT_SYSTEM_ACTOR;
8027
+ }
8028
+ if (!actor && args.userMessage.author?.userId) {
8029
+ actor = await resolveSlackResumeUserActor({
8030
+ conversationId: args.conversationId,
8031
+ teamId: args.teamId,
8032
+ userId: args.userMessage.author.userId
8257
8033
  });
8258
8034
  }
8259
- return void 0;
8035
+ if (!actor) {
8036
+ return void 0;
8037
+ }
8038
+ return {
8039
+ actor,
8040
+ credentialContext: credentialContextForActor(actor)
8041
+ };
8260
8042
  }
8261
8043
  function isContinuationResume(summary) {
8262
8044
  return summary.state === "awaiting_resume" && (summary.resumeReason === "timeout" || summary.resumeReason === "yield" || summary.resumeReason === "retry");
@@ -8274,7 +8056,6 @@ async function failUnresumableContinuation(args) {
8274
8056
  conversationId: args.conversationId
8275
8057
  });
8276
8058
  await recordAgentTurnSessionSummary({
8277
- actor: args.summary.actor,
8278
8059
  conversationId: args.conversationId,
8279
8060
  destination: routing.destination,
8280
8061
  dispatchId: args.summary.dispatchId,
@@ -8341,16 +8122,32 @@ async function continueSlackAgentRunInContext(payload, options, runOptions) {
8341
8122
  (message) => message.role === "user" && options.inputMessageIds?.includes(message.id)
8342
8123
  ) : void 0;
8343
8124
  const userMessage = getTurnUserMessage(conversation, payload.sessionId) ?? dispatchUserMessage;
8344
- const systemActor = activeSessionRecord.actor?.platform === "system" ? activeSessionRecord.actor : void 0;
8345
- const userActorId = userMessage?.author?.userId;
8346
- if (!userMessage || !systemActor && !userActorId) {
8347
- throw new Error(
8348
- `Unable to locate the persisted user message for agent continuation session "${payload.sessionId}"`
8349
- );
8125
+ if (!userMessage) {
8126
+ await failStrandedSessionWithFallback({
8127
+ conversationId: payload.conversationId,
8128
+ errorMessage: `Unable to locate the persisted user message for agent continuation session "${payload.sessionId}"`,
8129
+ sessionRecord: activeSessionRecord
8130
+ });
8131
+ return false;
8350
8132
  }
8351
8133
  if (conversation.processing.activeTurnId !== payload.sessionId) {
8352
8134
  return false;
8353
8135
  }
8136
+ const identity = await resolveResumeExecutionIdentity({
8137
+ conversationId: payload.conversationId,
8138
+ routingContext: options.routingContext,
8139
+ teamId: destination.teamId,
8140
+ userMessage
8141
+ });
8142
+ if (!identity) {
8143
+ await failStrandedSessionWithFallback({
8144
+ conversationId: payload.conversationId,
8145
+ errorMessage: "Unable to rebuild Slack actor for continuation",
8146
+ sessionRecord: activeSessionRecord
8147
+ });
8148
+ return false;
8149
+ }
8150
+ const { actor, credentialContext } = identity;
8354
8151
  const channelConfiguration = getChannelConfigurationServiceById(
8355
8152
  destination.channelId
8356
8153
  );
@@ -8358,34 +8155,6 @@ async function continueSlackAgentRunInContext(payload, options, runOptions) {
8358
8155
  excludeMessageId: userMessage.id
8359
8156
  });
8360
8157
  const sandboxRef = getPersistedSandboxState(currentState);
8361
- let actor;
8362
- let credentialContext;
8363
- if (options.routingContext?.credentialContext) {
8364
- credentialContext = options.routingContext.credentialContext;
8365
- } else if (systemActor) {
8366
- credentialContext = { actor: systemActor };
8367
- } else {
8368
- actor = await resolveContinuationActor({
8369
- conversationId: payload.conversationId,
8370
- sessionRecordActor: activeSessionRecord.actor,
8371
- teamId: destination.teamId,
8372
- userId: userActorId
8373
- });
8374
- if (!actor) {
8375
- await failStrandedSessionWithFallback({
8376
- conversationId: payload.conversationId,
8377
- errorMessage: "Stored Slack actor missing for continuation",
8378
- sessionRecord: activeSessionRecord
8379
- });
8380
- return false;
8381
- }
8382
- credentialContext = {
8383
- actor: {
8384
- type: "user",
8385
- userId: actor.userId
8386
- }
8387
- };
8388
- }
8389
8158
  const routing = await resolveTurnSessionRouting({
8390
8159
  conversationId: payload.conversationId
8391
8160
  });
@@ -8433,7 +8202,7 @@ async function continueSlackAgentRunInContext(payload, options, runOptions) {
8433
8202
  routing: {
8434
8203
  ...options.routingContext,
8435
8204
  credentialContext,
8436
- ...options.routingContext?.actor ?? actor ? { actor: options.routingContext?.actor ?? actor } : {},
8205
+ actor,
8437
8206
  destination: routingDestination,
8438
8207
  source,
8439
8208
  toolChannelId: artifacts.assistantContextChannelId ?? destination.channelId
@@ -8554,7 +8323,6 @@ async function failStrandedSessionWithFallback(args) {
8554
8323
  }
8555
8324
  if (args.sessionRecord.dispatchId) {
8556
8325
  await recordAgentTurnSessionSummary({
8557
- actor: args.sessionRecord.actor,
8558
8326
  conversationId: args.conversationId,
8559
8327
  destination: routing.destination,
8560
8328
  dispatchId: args.sessionRecord.dispatchId,
@@ -8619,7 +8387,6 @@ async function recoverStrandedRunningSession(args) {
8619
8387
  messages: sessionRecord.piMessages,
8620
8388
  errorMessage: "Recovered running session after hard worker death",
8621
8389
  modelId,
8622
- actor: sessionRecord.actor,
8623
8390
  surface: sessionRecord.surface
8624
8391
  });
8625
8392
  if (!parked) {
@@ -8949,7 +8716,7 @@ async function ingestResourceEvent(input, options) {
8949
8716
  }
8950
8717
 
8951
8718
  // src/chat/event-tasks/ingest.ts
8952
- import { createHash as createHash2 } from "crypto";
8719
+ import { createHash } from "crypto";
8953
8720
  import {
8954
8721
  createSlackSource as createSlackSource3,
8955
8722
  RESOURCE_EVENT_SUMMARY_MAX_LENGTH,
@@ -8957,7 +8724,7 @@ import {
8957
8724
  resourceEventSchema as resourceEventSchema2
8958
8725
  } from "@sentry/junior-plugin-api";
8959
8726
  function eventTaskDispatchKey(taskId, namespace, eventKey) {
8960
- return `event-task:${createHash2("sha256").update(`${taskId}\0${namespace}\0${eventKey}`).digest("hex").slice(0, 32)}`;
8727
+ return `event-task:${createHash("sha256").update(`${taskId}\0${namespace}\0${eventKey}`).digest("hex").slice(0, 32)}`;
8961
8728
  }
8962
8729
  function oneLine(value) {
8963
8730
  return value.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim().slice(0, 512);