@sentry/junior 0.181.1 → 0.183.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 (107) hide show
  1. package/dist/{agent-hooks-KO56HJ74.js → agent-hooks-SNRBZYAU.js} +11 -11
  2. package/dist/api/code/overview.d.ts +35 -0
  3. package/dist/api/code/routes.d.ts +4 -0
  4. package/dist/api/schema/code.d.ts +75 -0
  5. package/dist/api/schema.d.ts +2 -0
  6. package/dist/api/schema.js +9 -1
  7. package/dist/api.js +347 -149
  8. package/dist/app-adapter.d.ts +85 -0
  9. package/dist/app.d.ts +6 -1
  10. package/dist/app.js +380 -501
  11. package/dist/{catalog-runtime-CJTFLCK3.js → catalog-runtime-3I3JOIIH.js} +4 -4
  12. package/dist/chat/agent-invocations/store.d.ts +4 -2
  13. package/dist/chat/agent-invocations/work.d.ts +1 -3
  14. package/dist/chat/api-turns/adapter-conversations.d.ts +15 -0
  15. package/dist/chat/api-turns/stop.d.ts +12 -0
  16. package/dist/chat/api-turns/work.d.ts +9 -1
  17. package/dist/chat/app/conversation-work.d.ts +1 -1
  18. package/dist/chat/app/factory.d.ts +3 -3
  19. package/dist/chat/app/services.d.ts +3 -3
  20. package/dist/chat/code/publisher.d.ts +3 -0
  21. package/dist/chat/code/store.d.ts +9 -0
  22. package/dist/chat/event-tasks/store.d.ts +10 -0
  23. package/dist/chat/event-tasks/tool-support.d.ts +7 -1
  24. package/dist/chat/event-tasks/types.d.ts +1 -0
  25. package/dist/chat/experimental.d.ts +1 -1
  26. package/dist/chat/ingress/slack-webhook.d.ts +1 -1
  27. package/dist/chat/plugins/agent-hooks.d.ts +5 -0
  28. package/dist/chat/{runtime/slack-runtime.d.ts → providers/slack/runtime.d.ts} +2 -2
  29. package/dist/chat/{runtime/reply-executor.d.ts → providers/slack/turn.d.ts} +10 -9
  30. package/dist/chat/resource-events/catalog.d.ts +11 -1
  31. package/dist/chat/resource-events/store.d.ts +17 -1
  32. package/dist/chat/runtime/turn-execution.d.ts +30 -0
  33. package/dist/chat/services/subscribed-decision.d.ts +2 -1
  34. package/dist/chat/slack/dispatch-turn.d.ts +12 -14
  35. package/dist/chat/task-execution/slack-work.d.ts +1 -1
  36. package/dist/chat/tools/create-event-task.d.ts +3 -0
  37. package/dist/chat/tools/delete-event-task.d.ts +2 -0
  38. package/dist/chat/tools/list-event-tasks.d.ts +2 -0
  39. package/dist/chat/tools/list-resource-event-subscriptions.d.ts +2 -0
  40. package/dist/chat/tools/search-resource-event-types.d.ts +10 -0
  41. package/dist/chat/tools/update-event-task.d.ts +3 -0
  42. package/dist/chat/tools/watch-resource-events.d.ts +3 -0
  43. package/dist/{chunk-DTRCZOHE.js → chunk-24OJZ5OU.js} +6 -6
  44. package/dist/{chunk-Z2UG4FBT.js → chunk-2ADFGSUS.js} +1 -1
  45. package/dist/{chunk-RULGB5NX.js → chunk-2EPYYVJC.js} +9 -9
  46. package/dist/{chunk-VRS6WDK5.js → chunk-2LYHWMR4.js} +1 -1
  47. package/dist/{chunk-XQ56HNH5.js → chunk-35PM4YY5.js} +6 -6
  48. package/dist/{chunk-6TB7JN63.js → chunk-37IQ6DGU.js} +3 -3
  49. package/dist/{chunk-MR36BRPE.js → chunk-3HJ4F4L7.js} +4 -4
  50. package/dist/{chunk-4WYHL4EH.js → chunk-3L7CWDQQ.js} +1 -1
  51. package/dist/{chunk-CJRUBOJI.js → chunk-4WGFWHWP.js} +1 -1
  52. package/dist/{chunk-VHSBLFNT.js → chunk-56XONJPF.js} +1 -1
  53. package/dist/{chunk-ULM7P4EQ.js → chunk-6UXDTPEN.js} +233 -173
  54. package/dist/{chunk-WDNA4LML.js → chunk-AHMWPFIW.js} +188 -11
  55. package/dist/{chunk-YCW6RZGX.js → chunk-AX3UDIMA.js} +3 -3
  56. package/dist/{chunk-HG6HNOTQ.js → chunk-AYW7J6K4.js} +130 -200
  57. package/dist/{chunk-DXK3VTVP.js → chunk-E3OBGRHN.js} +1 -1
  58. package/dist/{chunk-7UF37LNL.js → chunk-EENQGEIP.js} +1 -1
  59. package/dist/{chunk-HRTW7FFX.js → chunk-FJMTMGBC.js} +1 -1
  60. package/dist/{chunk-H6MPFFBE.js → chunk-G2B2XZQW.js} +1 -1
  61. package/dist/{chunk-4GOZBB22.js → chunk-HUK5XIZL.js} +63 -7
  62. package/dist/{chunk-DINF4LMR.js → chunk-IO6KFXYE.js} +160 -97
  63. package/dist/{chunk-KH2SL2HT.js → chunk-KTXOGLNE.js} +2 -2
  64. package/dist/{chunk-S63LNPIV.js → chunk-LMNMMESF.js} +2 -2
  65. package/dist/{chunk-2CZ63QCL.js → chunk-LZBPGX5A.js} +1 -1
  66. package/dist/{chunk-PJ7EGFME.js → chunk-PLZT45VC.js} +5 -5
  67. package/dist/{chunk-ZQQWJMHE.js → chunk-QI5JW5AG.js} +6 -6
  68. package/dist/{chunk-IWRPJ3J5.js → chunk-QZV76CM4.js} +58 -32
  69. package/dist/{chunk-YFJDRORU.js → chunk-R767Y7QV.js} +202 -90
  70. package/dist/{chunk-4OSVTPYQ.js → chunk-SQHFVIMZ.js} +1 -1
  71. package/dist/{chunk-J4ID4ZI6.js → chunk-T5MTGX2E.js} +19 -19
  72. package/dist/{chunk-NRF23QZ4.js → chunk-V64JCZUV.js} +3 -3
  73. package/dist/{chunk-GITIZ6TZ.js → chunk-VOV3SCM3.js} +1 -1
  74. package/dist/{chunk-S5X2VQOD.js → chunk-W46NIB5H.js} +3 -2
  75. package/dist/{chunk-XL3C4NAR.js → chunk-W5JMPEBO.js} +9 -9
  76. package/dist/{chunk-HPFHC7YR.js → chunk-WOKJRLM2.js} +5 -5
  77. package/dist/chunk-WVIA7V3M.js +975 -0
  78. package/dist/{chunk-QA6RE6IB.js → chunk-X52ESOBP.js} +3 -3
  79. package/dist/cli/chat.js +35 -37
  80. package/dist/cli/check.js +5 -5
  81. package/dist/cli/plugins.js +12 -12
  82. package/dist/cli/snapshot-create.js +9 -9
  83. package/dist/cli/upgrade.js +6 -6
  84. package/dist/db/schema/code.d.ts +367 -0
  85. package/dist/db/schema.d.ts +367 -1
  86. package/dist/{db-4PXTD3OH.js → db-K2VMNLHQ.js} +6 -6
  87. package/dist/{experimental-SIJSRDJD.js → experimental-P5FZM4CU.js} +1 -1
  88. package/dist/instrumentation.js +3 -3
  89. package/dist/nitro.js +6 -6
  90. package/dist/{oauth-callback-server-TQWNEVR6.js → oauth-callback-server-DZZMTKSS.js} +26 -26
  91. package/dist/{runner-G5HLQA43.js → runner-IIAKWDXI.js} +18 -18
  92. package/dist/{skills-BNNNRYNT.js → skills-EXIQ7BP5.js} +5 -5
  93. package/dist/spawn-ZR6OOEAN.js +37 -0
  94. package/dist/task-runner-C52HYCVD.js +32 -0
  95. package/dist/{validation-IJSBB6JR.js → validation-XUGIWBJF.js} +5 -5
  96. package/dist/version.js +1 -1
  97. package/dist/{work-5FQI4P2A.js → work-G4RLJ634.js} +18 -18
  98. package/dist/worker-R2JEHK67.js +22 -0
  99. package/migrations/0036_complex_rage.sql +32 -0
  100. package/migrations/meta/0036_snapshot.json +3186 -0
  101. package/migrations/meta/_journal.json +8 -1
  102. package/package.json +6 -5
  103. package/dist/api/acp/route.d.ts +0 -13
  104. package/dist/chunk-CZYNWPWD.js +0 -930
  105. package/dist/spawn-U2RKOFP2.js +0 -37
  106. package/dist/task-runner-CDZJBEXP.js +0 -32
  107. package/dist/worker-KJ362GVB.js +0 -22
package/dist/app.js CHANGED
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  processConversationWork
3
- } from "./chunk-MR36BRPE.js";
3
+ } from "./chunk-3HJ4F4L7.js";
4
4
  import {
5
5
  bindSpawnAgent
6
- } from "./chunk-GITIZ6TZ.js";
6
+ } from "./chunk-VOV3SCM3.js";
7
7
  import {
8
8
  createAgentInvocationWorker,
9
9
  enqueueAgentInvocation,
10
10
  listPendingAgentInvocationMailboxAppends,
11
11
  routeAgentInvocationWork
12
- } from "./chunk-DINF4LMR.js";
12
+ } from "./chunk-IO6KFXYE.js";
13
13
  import {
14
14
  GET as GET2,
15
15
  GET2 as GET3,
@@ -36,25 +36,25 @@ import {
36
36
  startSlackProcessingReaction,
37
37
  stripLeadingBotMention,
38
38
  wakePausedTurn
39
- } from "./chunk-J4ID4ZI6.js";
39
+ } from "./chunk-T5MTGX2E.js";
40
40
  import "./chunk-FA6POGLH.js";
41
41
  import {
42
42
  GET,
43
43
  apiTurnIdForMessage,
44
44
  appendAndEnqueueApiConversationMessage,
45
- authenticatePersonalToken,
46
45
  conversationVisibilityFromSlackChannelType,
47
46
  createApiTurnCancellation,
48
47
  createApiTurnWorker,
49
48
  createVercelAttachmentStorage,
49
+ getAuthPausedApiTurnId,
50
50
  readConversationAccessFromSql,
51
51
  recordApiConversationActivity,
52
52
  resolveSlackChannelTypeFromMessage,
53
53
  resolveSlackConversationContext,
54
54
  routeApiTurnWork,
55
55
  webActorFromEmail
56
- } from "./chunk-HG6HNOTQ.js";
57
- import "./chunk-CJRUBOJI.js";
56
+ } from "./chunk-AYW7J6K4.js";
57
+ import "./chunk-4WGFWHWP.js";
58
58
  import {
59
59
  buildDeliveredTurnStatePatch,
60
60
  completeAuthPauseTurn,
@@ -65,7 +65,7 @@ import {
65
65
  getTurnUserSlackMessageTs,
66
66
  persistAuthPauseTurnState,
67
67
  requireTurnFailureEventId
68
- } from "./chunk-HPFHC7YR.js";
68
+ } from "./chunk-WOKJRLM2.js";
69
69
  import {
70
70
  claimDispatchMailboxAppend,
71
71
  confirmDispatchMailboxAppend,
@@ -85,7 +85,7 @@ import {
85
85
  recordTaskExecution,
86
86
  resolveTurnSessionRouting,
87
87
  scheduleSessionCompletedPluginTasks
88
- } from "./chunk-XL3C4NAR.js";
88
+ } from "./chunk-W5JMPEBO.js";
89
89
  import {
90
90
  ConversationTurnLifecycleService
91
91
  } from "./chunk-M3QYTBQ6.js";
@@ -97,17 +97,17 @@ import {
97
97
  persistThreadRuntimeState,
98
98
  persistThreadState,
99
99
  persistThreadStateById
100
- } from "./chunk-PJ7EGFME.js";
100
+ } from "./chunk-PLZT45VC.js";
101
101
  import "./chunk-UTL3MHPC.js";
102
102
  import {
103
103
  createVercelPluginTaskCallback,
104
104
  queueCallback,
105
105
  registerVercelPluginTaskDevConsumer
106
- } from "./chunk-NRF23QZ4.js";
106
+ } from "./chunk-V64JCZUV.js";
107
107
  import {
108
108
  validatePluginEgressCredentialHooks,
109
109
  validatePluginRegistrations
110
- } from "./chunk-4WYHL4EH.js";
110
+ } from "./chunk-3L7CWDQQ.js";
111
111
  import {
112
112
  defineJuniorPlugins,
113
113
  pluginCatalogConfigFromEnv,
@@ -155,11 +155,11 @@ import {
155
155
  unpublishedArtifactHeaders,
156
156
  verifySlackSignature,
157
157
  withNormalizedThreadId
158
- } from "./chunk-IWRPJ3J5.js";
158
+ } from "./chunk-QZV76CM4.js";
159
159
  import {
160
160
  AuthorizationFlowDisabledError,
161
161
  PluginCredentialFailureError
162
- } from "./chunk-XQ56HNH5.js";
162
+ } from "./chunk-35PM4YY5.js";
163
163
  import "./chunk-6Y5B5PEX.js";
164
164
  import "./chunk-DIB4GX3G.js";
165
165
  import {
@@ -168,7 +168,7 @@ import {
168
168
  deleteMcpAuthSessionsForUserProvider,
169
169
  deleteMcpServerSessionId,
170
170
  deleteMcpStoredOAuthCredentials
171
- } from "./chunk-RULGB5NX.js";
171
+ } from "./chunk-2EPYYVJC.js";
172
172
  import {
173
173
  RetryableDeliveryError
174
174
  } from "./chunk-RF6J3DIO.js";
@@ -176,14 +176,15 @@ import {
176
176
  formatProviderLabel,
177
177
  postSlackMessage,
178
178
  startOAuthFlow
179
- } from "./chunk-ZQQWJMHE.js";
179
+ } from "./chunk-QI5JW5AG.js";
180
180
  import {
181
181
  getExperimentalFeatures,
182
182
  isExperimentalFeatureEnabled,
183
183
  setExperimentalFeatures
184
- } from "./chunk-VRS6WDK5.js";
184
+ } from "./chunk-2LYHWMR4.js";
185
185
  import {
186
186
  collectAttachmentGarbage,
187
+ collectEventTaskMatchKeys,
187
188
  findMatchingEventTasks,
188
189
  getResourceEventCatalog,
189
190
  listScheduledTasks,
@@ -196,21 +197,25 @@ import {
196
197
  scheduledRunId,
197
198
  scheduledRunSchema,
198
199
  withScheduledTaskLock
199
- } from "./chunk-YFJDRORU.js";
200
+ } from "./chunk-R767Y7QV.js";
200
201
  import {
201
202
  parseSlackThreadId
202
- } from "./chunk-4OSVTPYQ.js";
203
+ } from "./chunk-SQHFVIMZ.js";
203
204
  import {
204
205
  CONVERSATION_WORK_STALE_ENQUEUE_MS,
205
206
  appendAndEnqueueInboundMessage,
206
207
  clearExpiredConversationLease,
207
208
  ensureConversationWake,
209
+ getConversation,
208
210
  getConversationWorkState,
209
211
  hasRunnableConversationWork,
212
+ hasRunnableConversationWork2,
210
213
  isInvalidConversationRecordError,
211
214
  listActiveConversationIds,
212
- removeActiveConversation
213
- } from "./chunk-4GOZBB22.js";
215
+ removeActiveConversation,
216
+ requestConversationWork,
217
+ stopConversationWork
218
+ } from "./chunk-HUK5XIZL.js";
214
219
  import {
215
220
  ConversationTurnBoundaryError,
216
221
  CooperativeTurnYieldError,
@@ -228,7 +233,7 @@ import {
228
233
  markTurnCompleted,
229
234
  markTurnFailed,
230
235
  startActiveTurn
231
- } from "./chunk-VHSBLFNT.js";
236
+ } from "./chunk-56XONJPF.js";
232
237
  import {
233
238
  abandonTurnRecord,
234
239
  appendThreadContextMessages,
@@ -264,11 +269,11 @@ import {
264
269
  trimTrailingAssistantMessages,
265
270
  turnHasReply,
266
271
  upsertConversationMessage
267
- } from "./chunk-DTRCZOHE.js";
272
+ } from "./chunk-24OJZ5OU.js";
268
273
  import {
269
274
  getSandboxResourceConfig,
270
275
  setSandboxResourceConfig
271
- } from "./chunk-6TB7JN63.js";
276
+ } from "./chunk-37IQ6DGU.js";
272
277
  import "./chunk-PDO5BLNM.js";
273
278
  import "./chunk-UIE3R5XU.js";
274
279
  import {
@@ -287,13 +292,14 @@ import {
287
292
  import "./chunk-7H2VZTWN.js";
288
293
  import {
289
294
  getTurnRequestDeadline
290
- } from "./chunk-HRTW7FFX.js";
295
+ } from "./chunk-FJMTMGBC.js";
291
296
  import {
292
297
  SlackActionError,
293
298
  bindEventTaskCredentialSubject,
294
299
  bindScheduledTaskCredentialSubject,
295
300
  bindSlackDirectCredentialSubject,
296
301
  cancelSubscriptions,
302
+ collectResourceEventMatchKeys,
297
303
  createPluginLogger,
298
304
  createPluginState,
299
305
  createResourceEventTeamIdResolver,
@@ -305,7 +311,6 @@ import {
305
311
  getWorkspaceTeamId,
306
312
  isDmChannel,
307
313
  normalizeSlackConversationId,
308
- resolveViewerUser,
309
314
  runWithWorkspaceTeamId,
310
315
  setDashboardConversationLinkOptions,
311
316
  setPlugins,
@@ -313,7 +318,7 @@ import {
313
318
  verifyEventTaskCredentialSubject,
314
319
  verifyScheduledTaskCredentialSubject,
315
320
  verifySlackDirectCredentialSubject
316
- } from "./chunk-WDNA4LML.js";
321
+ } from "./chunk-AHMWPFIW.js";
317
322
  import "./chunk-3Y47VMHV.js";
318
323
  import {
319
324
  contextProvenance,
@@ -326,7 +331,7 @@ import {
326
331
  requireSlackDestination,
327
332
  withConversationEventLock,
328
333
  withConversationMutationLock
329
- } from "./chunk-YCW6RZGX.js";
334
+ } from "./chunk-AX3UDIMA.js";
330
335
  import {
331
336
  juniorAgentBindings,
332
337
  juniorAgentInvocations,
@@ -334,15 +339,13 @@ import {
334
339
  juniorConversations,
335
340
  juniorSchedulerRuns,
336
341
  juniorSchedulerTasks
337
- } from "./chunk-ULM7P4EQ.js";
338
- import {
339
- sleep
340
- } from "./chunk-QA6RE6IB.js";
342
+ } from "./chunk-6UXDTPEN.js";
343
+ import "./chunk-X52ESOBP.js";
341
344
  import {
342
345
  acquireActiveLock,
343
346
  getStateAdapter,
344
347
  withActiveLock
345
- } from "./chunk-H6MPFFBE.js";
348
+ } from "./chunk-G2B2XZQW.js";
346
349
  import {
347
350
  juniorDestinations
348
351
  } from "./chunk-VFUK3X5B.js";
@@ -363,13 +366,13 @@ import {
363
366
  modelIdForProfile,
364
367
  setProfiles,
365
368
  setSlackReactionConfig
366
- } from "./chunk-S63LNPIV.js";
367
- import "./chunk-Z2UG4FBT.js";
368
- import "./chunk-KH2SL2HT.js";
369
+ } from "./chunk-LMNMMESF.js";
370
+ import "./chunk-2ADFGSUS.js";
371
+ import "./chunk-KTXOGLNE.js";
369
372
  import {
370
373
  pluginCatalogRuntime
371
- } from "./chunk-DXK3VTVP.js";
372
- import "./chunk-7UF37LNL.js";
374
+ } from "./chunk-E3OBGRHN.js";
375
+ import "./chunk-EENQGEIP.js";
373
376
  import {
374
377
  credentialContextForActor
375
378
  } from "./chunk-CEMMLV5L.js";
@@ -404,13 +407,13 @@ import {
404
407
  withContext,
405
408
  withLogContext,
406
409
  withSpan
407
- } from "./chunk-2CZ63QCL.js";
410
+ } from "./chunk-LZBPGX5A.js";
408
411
  import {
409
412
  continueTrace
410
413
  } from "./chunk-PEQXYSSE.js";
411
414
  import {
412
415
  JUNIOR_VERSION
413
- } from "./chunk-S5X2VQOD.js";
416
+ } from "./chunk-W46NIB5H.js";
414
417
 
415
418
  // src/app.ts
416
419
  import { createRequire } from "module";
@@ -514,7 +517,7 @@ async function recoverConversationWork(args) {
514
517
  }
515
518
  return;
516
519
  }
517
- if (work.execution.lease || !hasRunnableConversationWork(work)) {
520
+ if (work.execution.lease || !hasRunnableConversationWork2(work)) {
518
521
  return;
519
522
  }
520
523
  const wake = await ensureConversationWake({
@@ -4044,17 +4047,9 @@ async function decideSubscribedThreadReply(args) {
4044
4047
  if (preflightDecision) {
4045
4048
  return preflightDecision;
4046
4049
  }
4047
- const evidence = buildRouterEvidence(args.input);
4048
4050
  if (!text && !args.input.hasAttachments) {
4049
4051
  return { shouldReply: false, reason: "empty_message" /* EmptyMessage */ };
4050
4052
  }
4051
- if (!args.input.isExplicitMention && !args.input.hasAttachments && isAcknowledgmentOnly(text)) {
4052
- return {
4053
- shouldReply: false,
4054
- reason: "side_conversation" /* SideConversation */,
4055
- reasonDetail: "acknowledgment"
4056
- };
4057
- }
4058
4053
  if (args.input.isExplicitMention) {
4059
4054
  if (isThreadOptOutInstruction(rawText, text)) {
4060
4055
  return {
@@ -4069,6 +4064,21 @@ async function decideSubscribedThreadReply(args) {
4069
4064
  reason: "explicit_mention" /* ExplicitMention */
4070
4065
  };
4071
4066
  }
4067
+ if (!isExperimentalFeatureEnabled("passive-routing")) {
4068
+ return {
4069
+ shouldReply: false,
4070
+ reason: "passive_disabled" /* PassiveDisabled */,
4071
+ reasonDetail: "passive-routing"
4072
+ };
4073
+ }
4074
+ if (!args.input.hasAttachments && isAcknowledgmentOnly(text)) {
4075
+ return {
4076
+ shouldReply: false,
4077
+ reason: "side_conversation" /* SideConversation */,
4078
+ reasonDetail: "acknowledgment"
4079
+ };
4080
+ }
4081
+ const evidence = buildRouterEvidence(args.input);
4072
4082
  if (evidence.assistantWasLastSpeaker && evidence.humanMessagesSinceLastAssistant === 0 && !evidence.currentMessageHasAttachments && (evidence.currentMessageHasDirectedFollowUpCue || evidence.currentMessageIsTerseClarification)) {
4073
4083
  return {
4074
4084
  shouldReply: true,
@@ -4200,7 +4210,7 @@ function combineTurnText(queuedMessages, latestText) {
4200
4210
  };
4201
4211
  }
4202
4212
 
4203
- // src/chat/runtime/slack-runtime.ts
4213
+ // src/chat/providers/slack/runtime.ts
4204
4214
  var THREAD_OPTOUT_ACK = "Understood. I'll stay out of this thread unless someone @mentions me again.";
4205
4215
  function shouldRethrowTurnControlError(error) {
4206
4216
  return isCooperativeTurnYieldError(error) || isTurnInputCommitLostError(error) || isTurnInputDeferredError(error) || isProviderRetryError(error);
@@ -4579,7 +4589,7 @@ function createSlackTurnRuntime(deps) {
4579
4589
  }),
4580
4590
  stripLeadingBotMention: deps.stripLeadingBotMention
4581
4591
  });
4582
- await deps.replyToThread(thread, message, {
4592
+ await deps.executeSlackTurn(thread, message, {
4583
4593
  explicitMention: true,
4584
4594
  beforeFirstResponsePost: hooks.beforeFirstResponsePost,
4585
4595
  conversationId: hooks.conversationId,
@@ -4745,6 +4755,31 @@ function createSlackTurnRuntime(deps) {
4745
4755
  });
4746
4756
  return;
4747
4757
  }
4758
+ if (!isResourceEventNotification && !turnIsExplicitMention && !isExperimentalFeatureEnabled("passive-routing")) {
4759
+ const decision2 = await deps.decideSubscribedReply({
4760
+ rawText: combinedText.rawText,
4761
+ text: combinedText.userText,
4762
+ hasAttachments: content.hasAttachments || queuedMessages.some(
4763
+ (queued) => queued.message.attachments.length > 0
4764
+ ),
4765
+ isExplicitMention: false,
4766
+ context: threadContext
4767
+ });
4768
+ await maybeHandleThreadOptOutDecision({
4769
+ thread,
4770
+ decision: decision2,
4771
+ beforeFirstResponsePost: hooks.beforeFirstResponsePost
4772
+ });
4773
+ await skipSubscribedMessage({
4774
+ thread,
4775
+ message,
4776
+ decision: decision2,
4777
+ context: threadContext,
4778
+ ack: hooks.ack,
4779
+ text: combinedText
4780
+ });
4781
+ return;
4782
+ }
4748
4783
  const preparedState = await deps.prepareTurnState({
4749
4784
  thread,
4750
4785
  message,
@@ -4844,7 +4879,7 @@ function createSlackTurnRuntime(deps) {
4844
4879
  processingReaction,
4845
4880
  hooks
4846
4881
  );
4847
- await deps.replyToThread(thread, message, {
4882
+ await deps.executeSlackTurn(thread, message, {
4848
4883
  explicitMention: Boolean(message.isMention),
4849
4884
  conversationId: hooks.conversationId,
4850
4885
  destination: hooks.destination,
@@ -5483,7 +5518,7 @@ function createJuniorRuntimeServices(overrides = {}) {
5483
5518
  };
5484
5519
  }
5485
5520
 
5486
- // src/chat/runtime/reply-executor.ts
5521
+ // src/chat/providers/slack/turn.ts
5487
5522
  import { createSlackSource as createSlackSource2 } from "@sentry/junior-plugin-api";
5488
5523
 
5489
5524
  // src/chat/slack/message/identity.ts
@@ -5581,7 +5616,7 @@ async function maybeApplyProviderDefaultConfigRequest(args) {
5581
5616
  };
5582
5617
  }
5583
5618
 
5584
- // src/chat/runtime/reply-executor.ts
5619
+ // src/chat/providers/slack/turn.ts
5585
5620
  async function persistThreadRuntimeStateWithRetry(thread, patch) {
5586
5621
  await persistWithRetry(() => persistThreadRuntimeState(thread, patch));
5587
5622
  }
@@ -5706,8 +5741,8 @@ async function loadPiMessagesForTurn(args) {
5706
5741
  function shouldPublishExternally2(publishExternally) {
5707
5742
  return publishExternally !== false;
5708
5743
  }
5709
- function createReplyToThread(deps) {
5710
- return async function replyToThread(thread, message, options) {
5744
+ function createSlackTurn(deps) {
5745
+ return async function executeSlackTurn(thread, message, options) {
5711
5746
  if (message.author.isMe) {
5712
5747
  return;
5713
5748
  }
@@ -7115,7 +7150,7 @@ function createSlackDispatchTurnRunner(options) {
7115
7150
  let errorMessage;
7116
7151
  let resultMessageTs;
7117
7152
  const routing = buildDispatchRoutingContext(dispatch2);
7118
- await options.replyToThread(thread, message, {
7153
+ await options.executeSlackTurn(thread, message, {
7119
7154
  ack: hooks.ack,
7120
7155
  conversationId,
7121
7156
  destination: dispatch2.destination,
@@ -7195,7 +7230,7 @@ function createSlackRuntime(options) {
7195
7230
  );
7196
7231
  }
7197
7232
  });
7198
- const replyToThread = createReplyToThread({
7233
+ const executeSlackTurn = createSlackTurn({
7199
7234
  getSlackAdapter: options.getSlackAdapter,
7200
7235
  prepareTurnState,
7201
7236
  resolveUserAttachments: services.visionContext.resolveUserAttachments,
@@ -7294,7 +7329,7 @@ function createSlackRuntime(options) {
7294
7329
  conversation: preparedState.conversation
7295
7330
  });
7296
7331
  },
7297
- replyToThread,
7332
+ executeSlackTurn,
7298
7333
  initializeAssistantThread: async ({
7299
7334
  channelId,
7300
7335
  threadTs,
@@ -7325,7 +7360,7 @@ function createSlackRuntime(options) {
7325
7360
  runDispatchTurn: createSlackDispatchTurnRunner({
7326
7361
  getLocationConfiguration: getLocationConfigurationService,
7327
7362
  getSlackAdapter: options.getSlackAdapter,
7328
- replyToThread
7363
+ executeSlackTurn
7329
7364
  })
7330
7365
  };
7331
7366
  }
@@ -7918,9 +7953,7 @@ function createConversationWork(options) {
7918
7953
  turnLifecycle: services.replyExecutor?.turnLifecycle
7919
7954
  }),
7920
7955
  fallbackWorker: routeAgentInvocationWork({
7921
- invocationWorker: createAgentInvocationWorker({
7922
- agentRunner: options.agentRunner
7923
- }),
7956
+ invocationWorker: createAgentInvocationWorker(options.agentRunner),
7924
7957
  fallbackWorker: providerWorker
7925
7958
  })
7926
7959
  }),
@@ -8118,416 +8151,203 @@ async function ingestEventTasks(input, options) {
8118
8151
  return { dispatched };
8119
8152
  }
8120
8153
 
8121
- // src/api/acp/route.ts
8122
- import { randomUUID } from "crypto";
8123
- import * as acp from "@agentclientprotocol/sdk";
8124
- import { AcpServer } from "@agentclientprotocol/sdk/experimental/server";
8125
- var ACP_CONNECTION_ID_HEADER = "Acp-Connection-Id";
8126
- var ACP_CONVERSATION_PREFIX = "local:acp:";
8127
- var EVENT_PAGE_SIZE = 50;
8128
- var EVENT_POLL_INTERVAL_MS = 25;
8129
- var MAX_PROMPT_TEXT_LENGTH = 32e3;
8130
- function bearerToken(request) {
8131
- const authorization = request.headers.get("Authorization");
8132
- const match = authorization?.match(/^Bearer ([^\s]+)$/);
8133
- return match?.[1];
8134
- }
8135
- async function authenticateRequest(request) {
8136
- const token = bearerToken(request);
8137
- if (!token) return void 0;
8138
- const email = await authenticatePersonalToken(token);
8139
- if (!email) return void 0;
8140
- const user = await resolveViewerUser(email);
8141
- if (!user) return void 0;
8142
- return {
8143
- actor: webActorFromEmail(
8144
- user.email ?? email,
8145
- user.displayName ? { fullName: user.displayName } : void 0
8146
- ),
8147
- user
8148
- };
8149
- }
8150
- function rejectUnsupportedMcpServers(mcpServers) {
8151
- if (mcpServers.length > 0) {
8152
- throw acp.RequestError.invalidParams(
8153
- { field: "mcpServers" },
8154
- "Junior does not accept client MCP servers"
8155
- );
8156
- }
8157
- }
8158
- async function requireOwnedSession(sessionId, user) {
8159
- if (!sessionId.startsWith(ACP_CONVERSATION_PREFIX)) {
8160
- throw acp.RequestError.resourceNotFound(sessionId);
8154
+ // src/chat/api-turns/stop.ts
8155
+ async function stopApiConversationTurn(args) {
8156
+ const nowMs = args.nowMs ?? Date.now();
8157
+ const stop = async () => await stopConversationWork({
8158
+ conversationId: args.conversationId,
8159
+ conversationStore: args.conversationStore,
8160
+ nowMs,
8161
+ state: args.state
8162
+ });
8163
+ let result = await stop();
8164
+ if (result.status === "no_work" && await getAuthPausedApiTurnId(args.conversationId)) {
8165
+ const conversation = await getConversation({
8166
+ conversationId: args.conversationId,
8167
+ state: args.state
8168
+ });
8169
+ await requestConversationWork({
8170
+ conversationId: args.conversationId,
8171
+ conversationStore: args.conversationStore,
8172
+ destination: conversation?.destination,
8173
+ nowMs,
8174
+ state: args.state
8175
+ });
8176
+ result = await stop();
8161
8177
  }
8162
- const access = (await readConversationAccessFromSql(getDb(), [sessionId], user)).get(sessionId);
8163
- if (!access?.isParticipant) {
8164
- throw acp.RequestError.resourceNotFound(sessionId);
8178
+ if (result.status === "requested") {
8179
+ await ensureConversationWake({
8180
+ conversationId: args.conversationId,
8181
+ conversationStore: args.conversationStore,
8182
+ idempotencyKey: `api-stop:${args.conversationId}:${result.runId}`,
8183
+ nowMs,
8184
+ queue: args.queue,
8185
+ replaceExistingWake: true,
8186
+ state: args.state
8187
+ });
8165
8188
  }
8189
+ return result;
8166
8190
  }
8167
- function promptText(prompt) {
8168
- if (prompt.length === 0) {
8169
- throw acp.RequestError.invalidParams(
8170
- { field: "prompt" },
8171
- "Junior accepts one or more text blocks only"
8172
- );
8173
- }
8174
- const blocks = [];
8175
- for (const block of prompt) {
8176
- if (block.type !== "text") {
8177
- throw acp.RequestError.invalidParams(
8178
- { field: "prompt" },
8179
- "Junior accepts one or more text blocks only"
8180
- );
8181
- }
8182
- if (!block.text.trim()) {
8183
- throw acp.RequestError.invalidParams(
8184
- { field: "prompt" },
8185
- "Junior accepts non-empty text blocks only"
8186
- );
8187
- }
8188
- blocks.push(block.text);
8191
+
8192
+ // src/chat/api-turns/adapter-conversations.ts
8193
+ var EVENT_PAGE_SIZE = 50;
8194
+ function actorFromUser(user) {
8195
+ return webActorFromEmail(
8196
+ user.email,
8197
+ user.displayName ? { fullName: user.displayName } : void 0
8198
+ );
8199
+ }
8200
+ async function hasConversationAccess(conversationId, user) {
8201
+ const access = (await readConversationAccessFromSql(getDb(), [conversationId], user)).get(conversationId);
8202
+ return Boolean(access?.isParticipant);
8203
+ }
8204
+ async function turnTerminalIsReady(args) {
8205
+ const conversation = await getConversation({
8206
+ conversationId: args.conversationId,
8207
+ state: args.state
8208
+ });
8209
+ if (conversation?.execution.pendingMessages.some(
8210
+ (message) => message.inboundMessageId === args.messageId
8211
+ )) {
8212
+ return false;
8189
8213
  }
8190
- const text = blocks.join("\n");
8191
- if (text.length > MAX_PROMPT_TEXT_LENGTH) {
8192
- throw acp.RequestError.invalidParams(
8193
- { field: "prompt" },
8194
- `Junior accepts at most ${MAX_PROMPT_TEXT_LENGTH} prompt characters`
8195
- );
8214
+ if (!conversation || !hasRunnableConversationWork(conversation)) {
8215
+ return true;
8196
8216
  }
8197
- return text;
8198
- }
8199
- function requestIdKey(requestId) {
8200
- if (requestId === null) return "null";
8201
- return `${typeof requestId}:${requestId}`;
8217
+ const later = await args.eventStore.query(args.conversationId, {
8218
+ afterSeq: args.terminalSeq,
8219
+ limit: 1,
8220
+ types: ["turn_started"]
8221
+ });
8222
+ return later.events.some(
8223
+ (event) => event.data.type === "turn_started" && event.data.turnId !== args.turnId
8224
+ );
8202
8225
  }
8203
- async function latestEventSeq(eventStore, conversationId) {
8226
+ async function latestEventCursor(eventStore, conversationId) {
8204
8227
  const page = await eventStore.query(conversationId, { limit: 1 });
8205
8228
  return page.events.at(-1)?.seq ?? 0;
8206
8229
  }
8207
- async function replaySession(eventStore, sessionId, client) {
8208
- const history = await eventStore.loadMessageHistory(sessionId);
8209
- for (const message of projectConversationMessages(history)) {
8210
- if (message.role === "system") continue;
8211
- await client.notify(acp.methods.client.session.update, {
8212
- sessionId,
8213
- update: {
8214
- sessionUpdate: message.role === "user" ? "user_message_chunk" : "agent_message_chunk",
8215
- content: { type: "text", text: message.text },
8216
- messageId: message.id
8217
- }
8218
- });
8219
- }
8220
- }
8221
- async function waitForTurn(args) {
8222
- let cursor = args.afterSeq;
8223
- const assistantPrefix = `${args.turnId}:assistant:`;
8224
- const sentMessageIds = /* @__PURE__ */ new Set();
8225
- while (true) {
8226
- if (args.signal.aborted) {
8227
- throw acp.RequestError.requestCancelled();
8228
- }
8229
- const page = await args.eventStore.query(args.sessionId, {
8230
- afterSeq: cursor,
8231
- limit: EVENT_PAGE_SIZE,
8232
- types: ["message", "turn_completed", "turn_failed"]
8233
- });
8234
- if (page.events.length === 0) {
8235
- try {
8236
- await sleep(EVENT_POLL_INTERVAL_MS, args.signal);
8237
- } catch (error) {
8238
- if (args.signal.aborted) {
8239
- throw acp.RequestError.requestCancelled();
8240
- }
8241
- throw error;
8242
- }
8243
- continue;
8244
- }
8245
- for (const event of page.events) {
8246
- cursor = event.seq;
8247
- const data = event.data;
8248
- if (data.type === "message" && data.role === "assistant" && data.messageId.startsWith(assistantPrefix) && !sentMessageIds.has(data.messageId)) {
8249
- sentMessageIds.add(data.messageId);
8250
- await args.client.notify(acp.methods.client.session.update, {
8251
- sessionId: args.sessionId,
8252
- update: {
8253
- sessionUpdate: "agent_message_chunk",
8254
- content: { type: "text", text: data.text },
8255
- messageId: data.messageId
8256
- }
8257
- });
8258
- continue;
8259
- }
8260
- if (data.type === "turn_completed" && data.turnId === args.turnId) {
8261
- args.cancellation.finish(args.sessionId, args.cancellationSignal);
8262
- return {
8263
- stopReason: data.outcome === "cancelled" ? "cancelled" : "end_turn"
8264
- };
8265
- }
8266
- if (data.type === "turn_failed" && data.turnId === args.turnId) {
8267
- args.cancellation.finish(args.sessionId, args.cancellationSignal);
8268
- throw acp.RequestError.internalError(
8269
- { failureCode: data.failureCode },
8270
- "Junior Turn failed"
8271
- );
8230
+ function createAdapterConversations(options) {
8231
+ return {
8232
+ async cancel({ conversationId, user }) {
8233
+ if (!await hasConversationAccess(conversationId, user)) {
8234
+ return "not_found";
8272
8235
  }
8273
- }
8274
- }
8275
- }
8276
- async function runAcpOperation(authenticated, operation, conversationId, callback) {
8277
- const name = `acp.${operation}`;
8278
- return await withSpan(
8279
- name,
8280
- name,
8281
- {
8282
- actorId: authenticated.actor.userId,
8283
- conversationId,
8284
- platform: "acp",
8285
- userId: authenticated.actor.userId
8236
+ await stopApiConversationTurn({
8237
+ conversationId,
8238
+ conversationStore: options.conversationStore,
8239
+ queue: options.queue,
8240
+ state: options.state
8241
+ });
8242
+ return "cancelled";
8286
8243
  },
8287
- async () => {
8288
- try {
8289
- return await callback();
8290
- } catch (error) {
8291
- if (!(error instanceof acp.RequestError)) {
8292
- logException(error, `${name}.failed`);
8293
- }
8294
- throw error;
8295
- }
8296
- }
8297
- );
8298
- }
8299
- function createActorAgent(authenticated, connectionNonce, options) {
8300
- const conversationStore = options.conversationStore ?? getConversationStore();
8301
- const eventStore = getConversationEventStore();
8302
- return acp.agent({ name: "junior" }).onRequest(
8303
- acp.methods.agent.initialize,
8304
- () => runAcpOperation(authenticated, "initialize", void 0, () => ({
8305
- protocolVersion: acp.PROTOCOL_VERSION,
8306
- agentCapabilities: {
8307
- loadSession: true,
8308
- promptCapabilities: {
8309
- image: false,
8310
- audio: false,
8311
- embeddedContext: false
8312
- }
8313
- },
8314
- authMethods: [],
8315
- agentInfo: { name: "junior", version: JUNIOR_VERSION }
8316
- }))
8317
- ).onRequest(acp.methods.agent.session.new, async (context) => {
8318
- return await runAcpOperation(
8319
- authenticated,
8320
- "session_new",
8321
- void 0,
8322
- async () => {
8323
- rejectUnsupportedMcpServers(context.params.mcpServers);
8324
- const sessionId = `${ACP_CONVERSATION_PREFIX}${randomUUID()}`;
8325
- await recordApiConversationActivity({
8326
- actor: authenticated.actor,
8327
- conversationId: sessionId,
8328
- conversationStore,
8329
- nowMs: Date.now(),
8330
- rootVisibility: "private"
8331
- });
8332
- return { sessionId };
8333
- }
8334
- );
8335
- }).onRequest(acp.methods.agent.session.load, async (context) => {
8336
- return await runAcpOperation(
8337
- authenticated,
8338
- "session_load",
8339
- context.params.sessionId,
8340
- async () => {
8341
- rejectUnsupportedMcpServers(context.params.mcpServers);
8342
- await requireOwnedSession(
8343
- context.params.sessionId,
8344
- authenticated.user
8345
- );
8346
- await replaySession(
8347
- eventStore,
8348
- context.params.sessionId,
8349
- context.client
8350
- );
8351
- return {};
8244
+ async create({ conversationId, user }) {
8245
+ await recordApiConversationActivity({
8246
+ actor: actorFromUser(user),
8247
+ conversationId,
8248
+ conversationStore: options.conversationStore,
8249
+ nowMs: Date.now(),
8250
+ rootVisibility: "private"
8251
+ });
8252
+ },
8253
+ async hasAccess({ conversationId, user }) {
8254
+ return await hasConversationAccess(conversationId, user);
8255
+ },
8256
+ async prompt({ conversationId, idempotencyKey, text, user }) {
8257
+ if (!await hasConversationAccess(conversationId, user)) {
8258
+ return { status: "not_found" };
8352
8259
  }
8353
- );
8354
- }).onRequest(acp.methods.agent.session.prompt, async (context) => {
8355
- return await runAcpOperation(
8356
- authenticated,
8357
- "session_prompt",
8358
- context.params.sessionId,
8359
- async () => {
8360
- await requireOwnedSession(
8361
- context.params.sessionId,
8362
- authenticated.user
8363
- );
8364
- const text = promptText(context.params.prompt);
8365
- const currentSeq = await latestEventSeq(
8366
- eventStore,
8367
- context.params.sessionId
8368
- );
8369
- const cancellationSignal = options.cancellation.begin(
8370
- context.params.sessionId
8371
- );
8372
- if (!cancellationSignal) {
8373
- throw acp.RequestError.invalidParams(
8374
- { field: "sessionId" },
8375
- "This ACP session already has an active prompt"
8376
- );
8377
- }
8378
- const recordDisconnect = () => options.cancellation.disconnect(
8379
- context.params.sessionId,
8380
- cancellationSignal
8381
- );
8382
- if (context.signal.aborted) {
8383
- recordDisconnect();
8384
- } else {
8385
- context.signal.addEventListener("abort", recordDisconnect, {
8386
- once: true
8387
- });
8388
- }
8389
- let accepted;
8390
- try {
8391
- accepted = await appendAndEnqueueApiConversationMessage(
8392
- {
8393
- actor: authenticated.actor,
8394
- conversationId: context.params.sessionId,
8395
- idempotencyKey: `${connectionNonce}:${requestIdKey(context.requestId)}`,
8396
- message: text
8397
- },
8398
- {
8399
- conversationStore,
8400
- queue: options.queue,
8401
- state: options.state
8402
- }
8403
- );
8404
- } catch (error) {
8405
- options.cancellation.finish(
8406
- context.params.sessionId,
8407
- cancellationSignal
8408
- );
8409
- throw error;
8410
- }
8411
- const afterSeq = accepted.status === "duplicate" ? 0 : currentSeq;
8412
- return await waitForTurn({
8413
- afterSeq,
8414
- cancellation: options.cancellation,
8415
- cancellationSignal,
8416
- client: context.client,
8417
- eventStore,
8418
- sessionId: context.params.sessionId,
8419
- signal: context.signal,
8420
- turnId: apiTurnIdForMessage(accepted.messageId)
8421
- });
8260
+ const currentCursor = await latestEventCursor(
8261
+ options.eventStore,
8262
+ conversationId
8263
+ );
8264
+ if (await getAuthPausedApiTurnId(conversationId)) {
8265
+ return { status: "active" };
8422
8266
  }
8423
- );
8424
- }).onNotification(acp.methods.agent.session.cancel, async (context) => {
8425
- await runAcpOperation(
8426
- authenticated,
8427
- "session_cancel",
8428
- context.params.sessionId,
8429
- async () => {
8430
- await requireOwnedSession(
8431
- context.params.sessionId,
8432
- authenticated.user
8433
- );
8434
- if (!options.cancellation.cancel(context.params.sessionId)) {
8435
- return;
8436
- }
8437
- const nowMs = Date.now();
8438
- await ensureConversationWake({
8439
- conversationId: context.params.sessionId,
8440
- conversationStore,
8441
- idempotencyKey: `acp-cancel:${context.params.sessionId}:${nowMs}`,
8442
- nowMs,
8267
+ const admission = await appendAndEnqueueApiConversationMessage(
8268
+ {
8269
+ actor: actorFromUser(user),
8270
+ conversationId,
8271
+ idempotencyKey,
8272
+ message: text
8273
+ },
8274
+ {
8275
+ conversationStore: options.conversationStore,
8276
+ exclusive: true,
8443
8277
  queue: options.queue,
8444
- replaceExistingWake: true,
8445
8278
  state: options.state
8446
- });
8447
- }
8448
- );
8449
- });
8450
- }
8451
- function isJsonRpcId(value) {
8452
- return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
8453
- }
8454
- async function hasSafeAcpEnvelope(request) {
8455
- if (request.method !== "POST") return true;
8456
- let value;
8457
- try {
8458
- value = await request.clone().json();
8459
- } catch {
8460
- return true;
8461
- }
8462
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
8463
- return true;
8464
- }
8465
- if (!("jsonrpc" in value) || value.jsonrpc !== "2.0") {
8466
- return false;
8467
- }
8468
- if (!("method" in value) || typeof value.method !== "string") {
8469
- return false;
8470
- }
8471
- return !("id" in value) || isJsonRpcId(value.id);
8472
- }
8473
- async function initializationSucceeded(response) {
8474
- try {
8475
- const value = await response.clone().json();
8476
- return typeof value === "object" && value !== null && "result" in value;
8477
- } catch {
8478
- return false;
8479
- }
8480
- }
8481
- function createAcpHttpHandler(options) {
8482
- const server = new AcpServer({ agent: acp.agent({ name: "junior" }) });
8483
- const connectionActors = /* @__PURE__ */ new Map();
8484
- return async (request) => {
8485
- const authenticated = await authenticateRequest(request);
8486
- if (!authenticated) {
8487
- return new Response("Unauthorized", { status: 401 });
8488
- }
8489
- const connectionId = request.headers.get(ACP_CONNECTION_ID_HEADER);
8490
- const connectionActorId = connectionId ? connectionActors.get(connectionId) : void 0;
8491
- if (connectionId && connectionActorId === void 0) {
8492
- return new Response("Unknown Acp-Connection-Id", { status: 404 });
8493
- }
8494
- if (connectionActorId !== void 0 && connectionActorId !== authenticated.actor.userId) {
8495
- return new Response("Unknown Acp-Connection-Id", { status: 404 });
8496
- }
8497
- if (!await hasSafeAcpEnvelope(request)) {
8498
- return new Response("Invalid JSON-RPC message", { status: 400 });
8499
- }
8500
- const response = await server.handleRequest(request, {
8501
- agent: createActorAgent(authenticated, randomUUID(), options)
8502
- });
8503
- const responseConnectionId = response.headers.get(ACP_CONNECTION_ID_HEADER);
8504
- if (response.ok && responseConnectionId && !connectionId) {
8505
- if (await initializationSucceeded(response)) {
8506
- connectionActors.set(responseConnectionId, authenticated.actor.userId);
8507
- } else {
8508
- await server.handleRequest(
8509
- new Request(request.url, {
8510
- method: "DELETE",
8511
- headers: { [ACP_CONNECTION_ID_HEADER]: responseConnectionId }
8512
- })
8513
- );
8514
- const headers = new Headers(response.headers);
8515
- headers.delete(ACP_CONNECTION_ID_HEADER);
8516
- return new Response(response.body, {
8517
- headers,
8518
- status: response.status,
8519
- statusText: response.statusText
8520
- });
8279
+ }
8280
+ );
8281
+ if (admission.status === "active") return { status: "active" };
8282
+ return {
8283
+ afterCursor: admission.status === "duplicate" ? 0 : currentCursor,
8284
+ messageId: admission.messageId,
8285
+ status: "accepted",
8286
+ turnId: apiTurnIdForMessage(admission.messageId)
8287
+ };
8288
+ },
8289
+ async readMessages(conversationId) {
8290
+ const history = await options.eventStore.loadMessageHistory(conversationId);
8291
+ return projectConversationMessages(history).filter(
8292
+ (message) => message.role !== "system"
8293
+ ).map(({ id, role, text }) => ({ id, role, text }));
8294
+ },
8295
+ async readTurn({ afterCursor, conversationId, messageId, turnId }) {
8296
+ const page = await options.eventStore.query(conversationId, {
8297
+ afterSeq: afterCursor,
8298
+ limit: EVENT_PAGE_SIZE,
8299
+ types: ["message", "turn_completed", "turn_failed"]
8300
+ });
8301
+ const cursor = page.events.at(-1)?.seq ?? afterCursor;
8302
+ const assistantPrefix = `${turnId}:assistant:`;
8303
+ const messages = page.events.flatMap((event) => {
8304
+ const data = event.data;
8305
+ return data.type === "message" && data.role === "assistant" && data.messageId.startsWith(assistantPrefix) ? [
8306
+ {
8307
+ id: data.messageId,
8308
+ role: "assistant",
8309
+ text: data.text
8310
+ }
8311
+ ] : [];
8312
+ });
8313
+ let terminal;
8314
+ const terminalEvent = await options.eventStore.loadByIdempotencyKey(
8315
+ conversationId,
8316
+ `turn:${turnId}:terminal`
8317
+ );
8318
+ if (terminalEvent && terminalEvent.seq <= cursor && await turnTerminalIsReady({
8319
+ conversationId,
8320
+ eventStore: options.eventStore,
8321
+ messageId,
8322
+ state: options.state,
8323
+ terminalSeq: terminalEvent.seq,
8324
+ turnId
8325
+ })) {
8326
+ const data = terminalEvent.data;
8327
+ if (data.type === "turn_completed" && data.turnId === turnId) {
8328
+ terminal = {
8329
+ outcome: data.outcome === "cancelled" ? "cancelled" : "completed",
8330
+ status: "completed"
8331
+ };
8332
+ } else if (data.type === "turn_failed" && data.turnId === turnId) {
8333
+ terminal = { failureCode: data.failureCode, status: "failed" };
8334
+ }
8521
8335
  }
8336
+ const turnPage = { cursor, messages };
8337
+ if (terminal) turnPage.terminal = terminal;
8338
+ return turnPage;
8522
8339
  }
8523
- if (request.method === "DELETE" && response.ok && connectionId) {
8524
- connectionActors.delete(connectionId);
8525
- }
8526
- return response;
8527
8340
  };
8528
8341
  }
8529
8342
 
8530
8343
  // src/app.ts
8344
+ function resolveAdapterBaseURL(dashboard) {
8345
+ const configured = dashboard?.baseURL?.trim() || process.env.JUNIOR_BASE_URL?.trim();
8346
+ if (configured) return configured;
8347
+ const vercelURL = process.env.VERCEL_PROJECT_PRODUCTION_URL?.trim() || process.env.VERCEL_URL?.trim();
8348
+ if (!vercelURL) return void 0;
8349
+ return /^https?:\/\//.test(vercelURL) ? vercelURL : `https://${vercelURL}`;
8350
+ }
8531
8351
  var DASHBOARD_PACKAGE_NAME = "@sentry/junior-dashboard";
8532
8352
  async function defaultWaitUntil() {
8533
8353
  try {
@@ -8625,6 +8445,7 @@ async function createDashboardRouteRegistrations(args) {
8625
8445
  }
8626
8446
  const createDashboardApp = args.createDashboardApp ?? await loadDashboardAppFactory();
8627
8447
  return dashboardRouteRegistrations({
8448
+ authenticatedRoutes: args.authenticatedRoutes,
8628
8449
  dashboard: args.dashboard,
8629
8450
  createDashboardApp,
8630
8451
  pluginRoutes: args.pluginRoutes
@@ -8672,7 +8493,7 @@ function normalizeDashboardPath(path, fallback) {
8672
8493
  const withSlash = value.startsWith("/") ? value : `/${value}`;
8673
8494
  return stripTrailingSlashes(withSlash);
8674
8495
  }
8675
- function dashboardHostRoutePaths(dashboard) {
8496
+ function dashboardHostRoutePaths(dashboard, authenticatedRoutes = []) {
8676
8497
  const basePath = normalizeDashboardPath(dashboard.basePath, "/");
8677
8498
  const authPath = normalizeDashboardPath(dashboard.authPath, "/api/auth");
8678
8499
  const pagePath = (suffix) => basePath === "/" ? `/${suffix}` : `${basePath}/${suffix}`;
@@ -8680,6 +8501,7 @@ function dashboardHostRoutePaths(dashboard) {
8680
8501
  const peoplePath = pagePath("people");
8681
8502
  const pagePaths = [
8682
8503
  basePath,
8504
+ pagePath("code"),
8683
8505
  conversationsPath,
8684
8506
  `${conversationsPath}/*`,
8685
8507
  pagePath("locations"),
@@ -8719,6 +8541,7 @@ function dashboardHostRoutePaths(dashboard) {
8719
8541
  "/api/tasks",
8720
8542
  "/api/tasks/*",
8721
8543
  "/api/skills",
8544
+ "/api/code",
8722
8545
  "/api/stats",
8723
8546
  "/api/conversations",
8724
8547
  "/api/conversations/*",
@@ -8732,6 +8555,7 @@ function dashboardHostRoutePaths(dashboard) {
8732
8555
  "/api/workspaces/*",
8733
8556
  "/api/config",
8734
8557
  "/api/me",
8558
+ ...authenticatedRoutes.map((route) => route.path),
8735
8559
  authPath,
8736
8560
  `${authPath}/*`
8737
8561
  ];
@@ -8782,35 +8606,43 @@ function routePatternOverlaps(ownedPath, routePath) {
8782
8606
  (example) => routePatternMatchesConcretePath(routePath, example)
8783
8607
  );
8784
8608
  }
8785
- function dashboardOwnedRoutePath(routePath, dashboard) {
8786
- return dashboardHostRoutePaths(dashboard).some(
8609
+ function dashboardOwnedRoutePath(routePath, dashboard, authenticatedRoutes = []) {
8610
+ return dashboardHostRoutePaths(dashboard, authenticatedRoutes).some(
8787
8611
  (path) => routePatternOverlaps(path, routePath)
8788
8612
  );
8789
8613
  }
8790
8614
  function dashboardRouteRegistrations(args) {
8791
8615
  let app;
8792
8616
  const fetch2 = (request) => {
8793
- app ??= args.createDashboardApp({
8617
+ const dashboardOptions = {
8794
8618
  ...args.dashboard,
8795
8619
  agentName: botConfig.userName,
8620
+ authenticatedRoutes: args.authenticatedRoutes,
8796
8621
  pluginRoutes: args.pluginRoutes
8797
- });
8622
+ };
8623
+ app ??= args.createDashboardApp(dashboardOptions);
8798
8624
  if (!app || typeof app.fetch !== "function") {
8799
8625
  throw new Error("createDashboardApp() must return an app with fetch()");
8800
8626
  }
8801
8627
  return app.fetch(request);
8802
8628
  };
8803
- return dashboardHostRoutePaths(args.dashboard).map((path) => ({
8804
- handler: fetch2,
8805
- path
8806
- }));
8629
+ return dashboardHostRoutePaths(args.dashboard, args.authenticatedRoutes).map(
8630
+ (path) => ({
8631
+ handler: fetch2,
8632
+ path
8633
+ })
8634
+ );
8807
8635
  }
8808
8636
  function validateDashboardRouteOwnership(args) {
8809
8637
  if (!args.dashboard || args.dashboard.disabled) {
8810
8638
  return;
8811
8639
  }
8812
8640
  for (const route of args.routes) {
8813
- if (dashboardOwnedRoutePath(route.path, args.dashboard)) {
8641
+ if (dashboardOwnedRoutePath(
8642
+ route.path,
8643
+ args.dashboard,
8644
+ args.authenticatedRoutes
8645
+ )) {
8814
8646
  throw new Error(
8815
8647
  `Plugin "${route.pluginName}" route "${route.path}" conflicts with core dashboard routes`
8816
8648
  );
@@ -8820,11 +8652,11 @@ function validateDashboardRouteOwnership(args) {
8820
8652
  function mountRoutes(app, routes) {
8821
8653
  for (const route of routes) {
8822
8654
  const handler = (c) => route.handler(c.req.raw);
8823
- const methods2 = Array.isArray(route.method) ? route.method : [route.method ?? "ALL"];
8824
- const explicitMethods = methods2.filter(
8655
+ const methods = typeof route.method === "string" ? [route.method] : route.method ?? ["ALL"];
8656
+ const explicitMethods = methods.filter(
8825
8657
  (method) => method !== "ALL"
8826
8658
  );
8827
- if (methods2.includes("ALL")) {
8659
+ if (methods.includes("ALL")) {
8828
8660
  app.all(route.path, handler);
8829
8661
  } else if (explicitMethods.length > 0) {
8830
8662
  app.on(explicitMethods, route.path, handler);
@@ -8858,10 +8690,42 @@ async function createApp(options) {
8858
8690
  const previousSandboxResources = getSandboxResourceConfig();
8859
8691
  const previousExperimentalFeatures = getExperimentalFeatures();
8860
8692
  const previousDashboardLinkOptions = setDashboardConversationLinkOptions(dashboard);
8693
+ const restoreRuntimeConfig = () => {
8694
+ pluginCatalogRuntime.setConfig(previousPluginCatalogConfig);
8695
+ setPlugins(previousPlugins);
8696
+ setConfigDefaults(previousConfigDefaults);
8697
+ botConfig.defaultProfile = previousDefaultProfile;
8698
+ botConfig.profiles = previousModelProfiles;
8699
+ setSlackReactionConfig(previousSlackReactionConfig);
8700
+ setSandboxResourceConfig(previousSandboxResources);
8701
+ setExperimentalFeatures(previousExperimentalFeatures);
8702
+ setDashboardConversationLinkOptions(previousDashboardLinkOptions);
8703
+ };
8861
8704
  let pluginRoutes = [];
8862
8705
  let pluginApiRoutes = [];
8863
8706
  const resolveResourceEventTeamId = createResourceEventTeamIdResolver();
8864
8707
  const resourceEvents = {
8708
+ async neededMatchKeys(input) {
8709
+ const teamId = await resolveResourceEventTeamId();
8710
+ if (!teamId) return [];
8711
+ const conversationWork = getConversationWorkOptions();
8712
+ const [watchKeys, taskKeys] = await Promise.all([
8713
+ collectResourceEventMatchKeys({
8714
+ eventTypes: input.eventTypes,
8715
+ identifiers: input.identifiers,
8716
+ namespace: input.namespace,
8717
+ state: conversationWork.state,
8718
+ teamId
8719
+ }),
8720
+ collectEventTaskMatchKeys(getDb(), {
8721
+ eventTypes: input.eventTypes,
8722
+ identifiers: input.identifiers,
8723
+ namespace: input.namespace,
8724
+ teamId
8725
+ })
8726
+ ]);
8727
+ return [.../* @__PURE__ */ new Set([...watchKeys, ...taskKeys])].sort();
8728
+ },
8865
8729
  async publish(event) {
8866
8730
  const teamId = await resolveResourceEventTeamId();
8867
8731
  if (!teamId) {
@@ -8909,20 +8773,11 @@ async function createApp(options) {
8909
8773
  );
8910
8774
  }
8911
8775
  pluginRoutes = getPluginRoutes({ resourceEvents });
8912
- validateDashboardRouteOwnership({ dashboard, routes: pluginRoutes });
8913
8776
  if (dashboard && !dashboard.disabled) {
8914
8777
  pluginApiRoutes = getPluginApiRoutes();
8915
8778
  }
8916
8779
  } catch (error) {
8917
- pluginCatalogRuntime.setConfig(previousPluginCatalogConfig);
8918
- setPlugins(previousPlugins);
8919
- setConfigDefaults(previousConfigDefaults);
8920
- botConfig.defaultProfile = previousDefaultProfile;
8921
- botConfig.profiles = previousModelProfiles;
8922
- setSlackReactionConfig(previousSlackReactionConfig);
8923
- setSandboxResourceConfig(previousSandboxResources);
8924
- setExperimentalFeatures(previousExperimentalFeatures);
8925
- setDashboardConversationLinkOptions(previousDashboardLinkOptions);
8780
+ restoreRuntimeConfig();
8926
8781
  throw error;
8927
8782
  }
8928
8783
  const waitUntil = options?.waitUntil ?? await defaultWaitUntil();
@@ -8938,6 +8793,54 @@ async function createApp(options) {
8938
8793
  replyExecutor: { agentRunner },
8939
8794
  sandbox: { tracePropagation }
8940
8795
  };
8796
+ let conversationWorkOptions;
8797
+ const getConversationWorkOptions = () => {
8798
+ conversationWorkOptions ??= options?.conversationWork ?? createProductionConversationWorkOptions({
8799
+ agentRunner,
8800
+ services: runtimeServiceOverrides
8801
+ });
8802
+ return conversationWorkOptions;
8803
+ };
8804
+ let adapterRoutes = [];
8805
+ let authenticatedRoutes = [];
8806
+ try {
8807
+ const adapterOutputs = await Promise.all(
8808
+ (options?.adapters ?? []).map(async (adapter) => {
8809
+ const work = getConversationWorkOptions();
8810
+ const state = work.state ?? getStateAdapter();
8811
+ return await adapter({
8812
+ agentName: botConfig.userName,
8813
+ baseURL: resolveAdapterBaseURL(dashboard),
8814
+ conversations: createAdapterConversations({
8815
+ conversationStore: work.conversationStore,
8816
+ eventStore: getConversationEventStore(),
8817
+ queue: work.queue ?? getVercelConversationWorkQueue(),
8818
+ state
8819
+ }),
8820
+ reportError: (error, event, attributes) => void logException(error, event, attributes),
8821
+ state,
8822
+ version: JUNIOR_VERSION
8823
+ });
8824
+ })
8825
+ );
8826
+ adapterRoutes = adapterOutputs.flatMap((output) => output.routes ?? []);
8827
+ authenticatedRoutes = adapterOutputs.flatMap(
8828
+ (output) => output.authenticatedRoutes ?? []
8829
+ );
8830
+ if (authenticatedRoutes.length > 0 && (!dashboard || dashboard.disabled)) {
8831
+ throw new Error(
8832
+ "createApp() adapters with authenticated routes require an enabled dashboard"
8833
+ );
8834
+ }
8835
+ validateDashboardRouteOwnership({
8836
+ authenticatedRoutes,
8837
+ dashboard,
8838
+ routes: pluginRoutes
8839
+ });
8840
+ } catch (error) {
8841
+ restoreRuntimeConfig();
8842
+ throw error;
8843
+ }
8941
8844
  const slackWebhookServices = createProductionSlackWebhookServices({
8942
8845
  services: runtimeServiceOverrides
8943
8846
  });
@@ -8954,9 +8857,11 @@ async function createApp(options) {
8954
8857
  );
8955
8858
  });
8956
8859
  mountRoutes(app, pluginRoutes);
8860
+ mountRoutes(app, adapterRoutes);
8957
8861
  mountRoutes(
8958
8862
  app,
8959
8863
  await createDashboardRouteRegistrations({
8864
+ authenticatedRoutes,
8960
8865
  dashboard,
8961
8866
  createDashboardApp: virtualConfig?.createDashboardApp,
8962
8867
  pluginRoutes: pluginApiRoutes
@@ -8994,32 +8899,6 @@ async function createApp(options) {
8994
8899
  });
8995
8900
  let agentContinuePOST;
8996
8901
  let pluginTaskPOST;
8997
- let conversationWorkOptions;
8998
- const getConversationWorkOptions = () => {
8999
- conversationWorkOptions ??= options?.conversationWork ?? createProductionConversationWorkOptions({
9000
- agentRunner,
9001
- services: runtimeServiceOverrides
9002
- });
9003
- return conversationWorkOptions;
9004
- };
9005
- if (isExperimentalFeatureEnabled("acp")) {
9006
- const work = getConversationWorkOptions();
9007
- const cancellation = work.apiTurnCancellation;
9008
- if (!cancellation) {
9009
- throw new Error("Experimental ACP requires API Turn cancellation wiring");
9010
- }
9011
- const handleAcpRequest = createAcpHttpHandler({
9012
- cancellation,
9013
- conversationStore: work.conversationStore,
9014
- queue: work.queue ?? getVercelConversationWorkQueue(),
9015
- state: work.state
9016
- });
9017
- app.on(
9018
- ["GET", "POST", "DELETE"],
9019
- "/api/acp",
9020
- (c) => handleAcpRequest(c.req.raw)
9021
- );
9022
- }
9023
8902
  if (process.env.NODE_ENV === "development") {
9024
8903
  registerVercelConversationWorkDevConsumer(getConversationWorkOptions());
9025
8904
  registerVercelPluginTaskDevConsumer();