@sentry/junior 0.75.0 → 0.76.1

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 (101) hide show
  1. package/README.md +1 -1
  2. package/bin/junior.mjs +4 -66
  3. package/dist/agent-hooks-ZOE7RIED.js +37 -0
  4. package/dist/api-reference.d.ts +2 -0
  5. package/dist/app.js +364 -135
  6. package/dist/build/virtual-config.d.ts +2 -2
  7. package/dist/chat/agent-dispatch/runner.d.ts +2 -0
  8. package/dist/chat/config.d.ts +1 -0
  9. package/dist/chat/credentials/state-adapter-token-store.d.ts +2 -0
  10. package/dist/chat/credentials/user-token-store.d.ts +17 -12
  11. package/dist/chat/db.d.ts +8 -0
  12. package/dist/chat/mcp/auth-store.d.ts +2 -1
  13. package/dist/chat/mcp/oauth.d.ts +2 -1
  14. package/dist/chat/oauth-flow.d.ts +3 -1
  15. package/dist/chat/pi/client.d.ts +15 -7
  16. package/dist/chat/plugins/agent-hooks.d.ts +7 -0
  17. package/dist/chat/plugins/auth/oauth-request.d.ts +11 -7
  18. package/dist/chat/plugins/model.d.ts +9 -0
  19. package/dist/chat/plugins/prompt.d.ts +5 -0
  20. package/dist/chat/plugins/task-callback.d.ts +5 -0
  21. package/dist/chat/plugins/task-message.d.ts +23 -0
  22. package/dist/chat/plugins/task-queue.d.ts +5 -0
  23. package/dist/chat/plugins/task-runner.d.ts +12 -0
  24. package/dist/chat/plugins/task-signing.d.ts +31 -0
  25. package/dist/chat/prompt.d.ts +4 -0
  26. package/dist/chat/requester.d.ts +6 -5
  27. package/dist/chat/respond-helpers.d.ts +2 -0
  28. package/dist/chat/respond.d.ts +4 -2
  29. package/dist/chat/runtime/agent-continue-runner.d.ts +4 -0
  30. package/dist/chat/runtime/reply-executor.d.ts +5 -1
  31. package/dist/chat/runtime/slack-resume.d.ts +10 -2
  32. package/dist/chat/sentry.d.ts +1 -0
  33. package/dist/chat/services/mcp-auth-orchestration.d.ts +2 -1
  34. package/dist/chat/services/plugin-auth-orchestration.d.ts +2 -1
  35. package/dist/chat/services/subscribed-decision.d.ts +2 -2
  36. package/dist/chat/services/turn-session-record.d.ts +11 -7
  37. package/dist/chat/slack/footer.d.ts +1 -1
  38. package/dist/chat/state/turn-session.d.ts +8 -5
  39. package/dist/chat/tools/agent-tools.d.ts +8 -1
  40. package/dist/chat/tools/slack/context.d.ts +2 -2
  41. package/dist/chat/tools/types.d.ts +4 -4
  42. package/dist/chat/vercel-queue-client.d.ts +3 -0
  43. package/dist/{chunk-C3AM4Z4J.js → chunk-2ECJXSVQ.js} +5 -5
  44. package/dist/{chunk-OJODNL2P.js → chunk-4SCWV7TJ.js} +2 -2
  45. package/dist/chunk-4UO6FK4G.js +64 -0
  46. package/dist/{chunk-BNJIEFQC.js → chunk-56TBVRJG.js} +2 -2
  47. package/dist/{chunk-OK4KKR7B.js → chunk-EJN6G5A2.js} +28 -12
  48. package/dist/{chunk-TQ74BATR.js → chunk-FFGXUXMD.js} +435 -111
  49. package/dist/{chunk-XJHDZUGD.js → chunk-JBASI5VV.js} +4 -4
  50. package/dist/chunk-KNFROR7R.js +127 -0
  51. package/dist/{chunk-VNTLUFTY.js → chunk-KOIMO7S3.js} +126 -87
  52. package/dist/chunk-MLKGABMK.js +9 -0
  53. package/dist/{chunk-NPVUAXUE.js → chunk-NFTMTIP3.js} +303 -33
  54. package/dist/chunk-NYKJ3KON.js +1082 -0
  55. package/dist/{chunk-SJHUF3DP.js → chunk-OJ53FYVG.js} +2 -10
  56. package/dist/{chunk-62FUNJYS.js → chunk-Q6XFTRV5.js} +54 -3
  57. package/dist/{chunk-UJ7OTHPO.js → chunk-R6Z5XWY3.js} +12 -670
  58. package/dist/chunk-RV5RYIJW.js +56 -0
  59. package/dist/{chunk-EE6PJWY4.js → chunk-SG5WAA7H.js} +7 -5
  60. package/dist/chunk-ST6YNAXG.js +54 -0
  61. package/dist/{chunk-FCZO7LAR.js → chunk-T77LUIX3.js} +139 -153
  62. package/dist/{chunk-EIYL7I4S.js → chunk-VALUBQ7R.js} +22 -30
  63. package/dist/{chunk-OZSPLAQ4.js → chunk-XBBC6W45.js} +1 -1
  64. package/dist/{chunk-ZNNTSPNF.js → chunk-Y5OFBCBZ.js} +1 -1
  65. package/dist/{chunk-74HO27II.js → chunk-Z4CIQ3EB.js} +5 -1
  66. package/dist/{chunk-2RWFUS5F.js → chunk-ZLMBNBUG.js} +101 -44
  67. package/dist/{chunk-JEELK46E.js → chunk-ZQB37HUX.js} +11 -11
  68. package/dist/cli/chat.js +52 -23
  69. package/dist/cli/check.js +7 -7
  70. package/dist/cli/env.js +4 -53
  71. package/dist/cli/init.js +6 -1
  72. package/dist/cli/main.js +84 -0
  73. package/dist/cli/plugins.js +244 -0
  74. package/dist/cli/run.js +5 -52
  75. package/dist/cli/snapshot-warmup.js +9 -9
  76. package/dist/cli/upgrade.js +167 -48
  77. package/dist/db-7A7PFRGL.js +17 -0
  78. package/dist/deployment.d.ts +1 -0
  79. package/dist/instrumentation.js +14 -18
  80. package/dist/nitro.d.ts +1 -1
  81. package/dist/nitro.js +43 -22
  82. package/dist/plugins-PZMDS7AT.js +15 -0
  83. package/dist/plugins.d.ts +4 -2
  84. package/dist/{registry-NLZFIW23.js → registry-OIPAJU2O.js} +6 -6
  85. package/dist/reporting.js +34 -26
  86. package/dist/{runner-LUQZ5G67.js → runner-7Z4D6AKV.js} +76 -23
  87. package/dist/sentry-4CP5NNQ5.js +31 -0
  88. package/dist/validation-SLA6IGF7.js +15 -0
  89. package/dist/vercel.js +1 -1
  90. package/package.json +8 -7
  91. package/dist/agent-hooks-2HEB4C3Q.js +0 -33
  92. package/dist/chat/conversations/configured.d.ts +0 -7
  93. package/dist/chat/conversations/state.d.ts +0 -4
  94. package/dist/chat/plugins/db.d.ts +0 -31
  95. package/dist/chunk-2KG3PWR4.js +0 -17
  96. package/dist/chunk-D7NFH5GD.js +0 -570
  97. package/dist/chunk-MCMROINU.js +0 -12
  98. package/dist/chunk-WBZ4M5N5.js +0 -59
  99. package/dist/db-A3ILH67H.js +0 -20
  100. package/dist/plugins-OMJKLRJ2.js +0 -13
  101. package/dist/validation-VMCPP3YO.js +0 -15
package/dist/app.js CHANGED
@@ -53,10 +53,12 @@ import {
53
53
  persistThreadState,
54
54
  persistThreadStateById,
55
55
  postSlackMessage,
56
+ processPluginTask,
56
57
  removeReactionFromMessage,
57
58
  renderSlackLegacyAttachmentText,
58
59
  resolveBaseUrl,
59
60
  resolveSandboxEgressProviderForHost,
61
+ scheduleSessionCompletedPluginTasks,
60
62
  setConfigDefaults,
61
63
  setSandboxEgressAuthRequiredSignal,
62
64
  setSandboxEgressCredentialLease,
@@ -69,12 +71,41 @@ import {
69
71
  updateConversationStats,
70
72
  uploadFilesToThread,
71
73
  upsertConversationMessage
72
- } from "./chunk-TQ74BATR.js";
74
+ } from "./chunk-FFGXUXMD.js";
75
+ import {
76
+ CONVERSATION_WORK_CHECK_IN_INTERVAL_MS,
77
+ CONVERSATION_WORK_STALE_ENQUEUE_MS,
78
+ appendInboundMessage,
79
+ checkInConversationWork,
80
+ clearExpiredConversationLease,
81
+ completeConversationWork,
82
+ drainConversationMailbox,
83
+ getConversation,
84
+ getConversationWorkState,
85
+ listActiveConversationIds,
86
+ markConversationMessagesInjected,
87
+ markConversationWorkEnqueued,
88
+ releaseConversationWork,
89
+ removeActiveConversation,
90
+ requestConversationContinuation,
91
+ requestConversationWork,
92
+ startConversationWork
93
+ } from "./chunk-R6Z5XWY3.js";
94
+ import {
95
+ JUNIOR_THREAD_STATE_TTL_MS,
96
+ coerceThreadConversationState
97
+ } from "./chunk-Z4CIQ3EB.js";
98
+ import "./chunk-JBASI5VV.js";
73
99
  import {
74
100
  getVercelConversationWorkQueue,
75
101
  resolveConversationWorkQueueTopic,
76
102
  verifyConversationQueueMessage
77
- } from "./chunk-C3AM4Z4J.js";
103
+ } from "./chunk-2ECJXSVQ.js";
104
+ import {
105
+ PLUGIN_TASK_QUEUE_TOPIC,
106
+ createVercelQueueClient,
107
+ verifyPluginTaskQueueMessage
108
+ } from "./chunk-KNFROR7R.js";
78
109
  import {
79
110
  GET,
80
111
  buildSentryConversationUrl,
@@ -82,7 +113,7 @@ import {
82
113
  resolveSlackChannelTypeFromMessage,
83
114
  resolveSlackConversationContext,
84
115
  setConversationTitle
85
- } from "./chunk-JEELK46E.js";
116
+ } from "./chunk-ZQB37HUX.js";
86
117
  import {
87
118
  abandonAgentTurnSessionRecord,
88
119
  buildSlackOutputMessage,
@@ -90,7 +121,6 @@ import {
90
121
  escapeXml,
91
122
  failAgentTurnSessionRecord,
92
123
  getAgentTurnSessionRecord,
93
- getConfiguredConversationStore,
94
124
  getInterruptionMarker,
95
125
  listAgentTurnSessionSummariesForConversation,
96
126
  loadProjection,
@@ -99,7 +129,17 @@ import {
99
129
  recordAuthorizationCompleted,
100
130
  splitSlackReplyText,
101
131
  truncateStatusText
102
- } from "./chunk-VNTLUFTY.js";
132
+ } from "./chunk-KOIMO7S3.js";
133
+ import {
134
+ validatePluginEgressCredentialHooks,
135
+ validatePluginRegistrations
136
+ } from "./chunk-XBBC6W45.js";
137
+ import {
138
+ defineJuniorPlugins,
139
+ pluginCatalogConfigFromEnv,
140
+ pluginCatalogConfigFromPluginSet,
141
+ pluginRuntimeRegistrationsFromPluginSet
142
+ } from "./chunk-SG5WAA7H.js";
103
143
  import {
104
144
  bindSlackDirectCredentialSubject,
105
145
  getPluginRoutes,
@@ -108,71 +148,32 @@ import {
108
148
  setPlugins,
109
149
  validatePlugins,
110
150
  verifySlackDirectCredentialSubject
111
- } from "./chunk-NPVUAXUE.js";
112
- import {
113
- validatePluginEgressCredentialHooks,
114
- validatePluginRegistrations
115
- } from "./chunk-OZSPLAQ4.js";
151
+ } from "./chunk-NFTMTIP3.js";
116
152
  import {
117
- discoverSkills
118
- } from "./chunk-OJODNL2P.js";
119
- import {
120
- coerceThreadConversationState
121
- } from "./chunk-74HO27II.js";
153
+ createPluginLogger,
154
+ createPluginState
155
+ } from "./chunk-56TBVRJG.js";
122
156
  import {
123
- defineJuniorPlugins,
124
- pluginCatalogConfigFromEnv,
125
- pluginCatalogConfigFromPluginSet,
126
- pluginHookRegistrationsFromPluginSet
127
- } from "./chunk-EE6PJWY4.js";
157
+ getConversationStore,
158
+ getDb
159
+ } from "./chunk-NYKJ3KON.js";
160
+ import "./chunk-G3E7SCME.js";
128
161
  import {
129
- CONVERSATION_WORK_CHECK_IN_INTERVAL_MS,
130
- CONVERSATION_WORK_STALE_ENQUEUE_MS,
131
- JUNIOR_THREAD_STATE_TTL_MS,
132
- appendInboundMessage,
133
- checkInConversationWork,
134
- clearExpiredConversationLease,
135
- completeConversationWork,
136
- drainConversationMailbox,
137
- getConversation,
138
- getConversationWorkState,
139
- listActiveConversationIds,
140
- markConversationMessagesInjected,
141
- markConversationWorkEnqueued,
142
- releaseConversationWork,
143
- removeActiveConversation,
144
- requestConversationContinuation,
145
- requestConversationWork,
146
- startConversationWork
147
- } from "./chunk-UJ7OTHPO.js";
162
+ ACTIVE_LOCK_TTL_MS,
163
+ getStateAdapter
164
+ } from "./chunk-Y5OFBCBZ.js";
148
165
  import {
149
166
  createSlackDestination,
150
167
  destinationKey,
151
- parseDestination,
152
- requireSlackDestination,
153
- sameDestination
154
- } from "./chunk-WBZ4M5N5.js";
155
- import {
156
- createPluginLogger,
157
- createPluginState
158
- } from "./chunk-BNJIEFQC.js";
159
- import {
160
168
  downloadPrivateSlackFile,
161
169
  getHeaderString,
162
170
  getSlackClient,
163
171
  isDmChannel,
164
- normalizeSlackConversationId
165
- } from "./chunk-62FUNJYS.js";
166
- import {
167
- getPluginDbForRegistration,
168
- validatePluginDatabaseRequirements
169
- } from "./chunk-D7NFH5GD.js";
170
- import "./chunk-XJHDZUGD.js";
171
- import "./chunk-G3E7SCME.js";
172
- import {
173
- ACTIVE_LOCK_TTL_MS,
174
- getStateAdapter
175
- } from "./chunk-ZNNTSPNF.js";
172
+ normalizeSlackConversationId,
173
+ parseDestination,
174
+ requireSlackDestination,
175
+ sameDestination
176
+ } from "./chunk-Q6XFTRV5.js";
176
177
  import {
177
178
  FUNCTION_TIMEOUT_BUFFER_SECONDS,
178
179
  GEN_AI_PROVIDER_NAME,
@@ -194,7 +195,10 @@ import {
194
195
  setSlackReactionConfig,
195
196
  stripRuntimeTurnContext,
196
197
  trimTrailingAssistantMessages
197
- } from "./chunk-FCZO7LAR.js";
198
+ } from "./chunk-T77LUIX3.js";
199
+ import {
200
+ discoverSkills
201
+ } from "./chunk-4SCWV7TJ.js";
198
202
  import {
199
203
  CredentialUnavailableError,
200
204
  buildOAuthTokenRequest,
@@ -205,19 +209,18 @@ import {
205
209
  isPluginProvider,
206
210
  parseOAuthTokenResponse,
207
211
  setPluginCatalogConfig
208
- } from "./chunk-2RWFUS5F.js";
209
- import {
210
- homeDir
211
- } from "./chunk-Q3XNY442.js";
212
+ } from "./chunk-ZLMBNBUG.js";
212
213
  import {
213
214
  createRequester,
214
- createRequesterFromStoredSlackRequester,
215
215
  createSlackRequester,
216
+ createSlackResumeRequester,
216
217
  isActorUserId,
217
218
  parseActorUserId,
218
219
  toStoredSlackRequester
219
- } from "./chunk-EIYL7I4S.js";
220
- import "./chunk-MCMROINU.js";
220
+ } from "./chunk-VALUBQ7R.js";
221
+ import {
222
+ homeDir
223
+ } from "./chunk-Q3XNY442.js";
221
224
  import {
222
225
  buildTurnFailureResponse,
223
226
  createChatSdkLogger,
@@ -234,11 +237,14 @@ import {
234
237
  toOptionalString,
235
238
  withContext,
236
239
  withSpan
237
- } from "./chunk-OK4KKR7B.js";
240
+ } from "./chunk-EJN6G5A2.js";
238
241
  import {
239
- sentry_exports
240
- } from "./chunk-SJHUF3DP.js";
241
- import "./chunk-2KG3PWR4.js";
242
+ JUNIOR_PLUGIN_TASK_CALLBACK_ROUTE
243
+ } from "./chunk-OJ53FYVG.js";
244
+ import {
245
+ continueTrace
246
+ } from "./chunk-ST6YNAXG.js";
247
+ import "./chunk-MLKGABMK.js";
242
248
 
243
249
  // src/app.ts
244
250
  import { Hono } from "hono";
@@ -738,11 +744,7 @@ function buildDispatchId(plugin, idempotencyKey) {
738
744
  return `dispatch_${digest}`;
739
745
  }
740
746
  function parseDispatchRecord(value) {
741
- const candidate = value && typeof value === "object" && !Array.isArray(value) && !("source" in value) && "destination" in value ? {
742
- ...value,
743
- source: value.destination
744
- } : value;
745
- const parsed = dispatchRecordSchema.safeParse(candidate);
747
+ const parsed = dispatchRecordSchema.safeParse(value);
746
748
  return parsed.success ? parsed.data : void 0;
747
749
  }
748
750
  function getDispatchDestinationLockId(destination) {
@@ -962,6 +964,7 @@ function canClaimDispatch(record, nowMs) {
962
964
  async function runAgentDispatchSlice(callback, deps = {}) {
963
965
  const generateAssistantReply2 = deps.generateAssistantReply ?? generateAssistantReply;
964
966
  const scheduleCallback = deps.scheduleCallback ?? scheduleDispatchCallback;
967
+ const scheduleCompletedTasks = deps.scheduleSessionCompletedPluginTasks ?? scheduleSessionCompletedPluginTasks;
965
968
  const nowMs = Date.now();
966
969
  const claimedDispatch = await withDispatchLock(callback.id, async (state) => {
967
970
  const current = parseDispatchRecord(
@@ -1159,6 +1162,30 @@ async function runAgentDispatchSlice(callback, deps = {}) {
1159
1162
  ...failure ? { errorMessage: failure } : {},
1160
1163
  resultMessageTs
1161
1164
  });
1165
+ if (!failure) {
1166
+ try {
1167
+ await scheduleCompletedTasks({
1168
+ conversationId,
1169
+ sessionId: getDispatchTurnId(dispatch.id)
1170
+ });
1171
+ } catch (error) {
1172
+ logException(
1173
+ error,
1174
+ "plugin_session_completed_task_schedule_failed",
1175
+ {
1176
+ conversationId,
1177
+ slackThreadId: conversationId,
1178
+ slackChannelId: dispatch.destination.channelId,
1179
+ runId: dispatch.id,
1180
+ actorType: dispatch.actor.type,
1181
+ actorId: dispatch.actor.id,
1182
+ assistantUserName: botConfig.userName
1183
+ },
1184
+ {},
1185
+ "Plugin session.completed task scheduling failed"
1186
+ );
1187
+ }
1188
+ }
1162
1189
  } catch (error) {
1163
1190
  if (error instanceof AuthorizationFlowDisabledError) {
1164
1191
  await markDispatch({
@@ -1251,7 +1278,7 @@ import { timingSafeEqual as timingSafeEqual2 } from "crypto";
1251
1278
 
1252
1279
  // src/chat/task-execution/store.ts
1253
1280
  function metadataStore(options) {
1254
- return options.conversationStore ?? getConfiguredConversationStore();
1281
+ return options.conversationStore ?? getConversationStore();
1255
1282
  }
1256
1283
  function duplicateInboundNudgeIdempotencyKey(message, nowMs) {
1257
1284
  return `duplicate:${message.conversationId}:${message.inboundMessageId}:${nowMs}`;
@@ -1695,12 +1722,11 @@ function bindDispatchCredentialSubject(options) {
1695
1722
  }
1696
1723
  function createHeartbeatContext(args) {
1697
1724
  const pluginName = typeof args.plugin === "string" ? args.plugin : args.plugin.manifest.name;
1698
- const db = typeof args.plugin === "string" ? void 0 : getPluginDbForRegistration(args.plugin);
1699
1725
  let dispatchCount = 0;
1700
1726
  return {
1701
1727
  plugin: { name: pluginName },
1702
1728
  nowMs: args.nowMs,
1703
- ...db ? { db } : {},
1729
+ db: getDb(),
1704
1730
  state: createPluginState(pluginName),
1705
1731
  log: createPluginLogger(pluginName),
1706
1732
  agent: {
@@ -2608,13 +2634,23 @@ function resolveReplyTimeoutMs(explicitTimeoutMs) {
2608
2634
  const parsed = Number.parseInt(raw, 10);
2609
2635
  return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
2610
2636
  }
2611
- async function postSlackMessageBestEffort(channelId, threadTs, text) {
2637
+ async function postSlackMessageBestEffort(channelId, threadTs, text, footer) {
2612
2638
  try {
2613
- await postSlackMessage({
2614
- channelId,
2615
- threadTs,
2616
- text
2617
- });
2639
+ if (footer) {
2640
+ await postSlackApiReplyPosts({
2641
+ channelId,
2642
+ threadTs,
2643
+ posts: [
2644
+ {
2645
+ text,
2646
+ stage: "thread_reply"
2647
+ }
2648
+ ],
2649
+ footer
2650
+ });
2651
+ return;
2652
+ }
2653
+ await postSlackMessage({ channelId, threadTs, text });
2618
2654
  } catch {
2619
2655
  }
2620
2656
  }
@@ -2665,6 +2701,9 @@ function getResumeLogContext(args, lockKey) {
2665
2701
  modelId: botConfig.modelId
2666
2702
  };
2667
2703
  }
2704
+ function getResumeConversationId(args, lockKey) {
2705
+ return args.replyContext?.correlation?.conversationId ?? lockKey;
2706
+ }
2668
2707
  async function postResumeFailureReply(args) {
2669
2708
  try {
2670
2709
  await postSlackMessage({
@@ -2708,6 +2747,10 @@ function createResumeReplyContext(args, statusSession) {
2708
2747
  if (!replyContext) {
2709
2748
  throw new TypeError("Slack resume requires a reply context");
2710
2749
  }
2750
+ if (!replyContext.source) {
2751
+ throw new TypeError("Slack resume requires a reply context source");
2752
+ }
2753
+ const source = replyContext.source;
2711
2754
  if (replyContext.destination.platform !== "slack") {
2712
2755
  throw new TypeError("Slack resume requires a Slack destination");
2713
2756
  }
@@ -2716,6 +2759,7 @@ function createResumeReplyContext(args, statusSession) {
2716
2759
  const persistedChannelConfiguration = replyContext.channelConfiguration ?? (replyContext.configuration ? createReadOnlyConfigService(replyContext.configuration) : void 0);
2717
2760
  return {
2718
2761
  ...replyContext,
2762
+ source,
2719
2763
  turnDeadlineAtMs: replyContext.turnDeadlineAtMs ?? requestDeadline?.deadlineAtMs,
2720
2764
  correlation: {
2721
2765
  ...replyContext.correlation,
@@ -2822,7 +2866,7 @@ async function resumeSlackTurn(args) {
2822
2866
  });
2823
2867
  await status.stop();
2824
2868
  const footer = buildSlackReplyFooter({
2825
- conversationId: runArgs.replyContext?.correlation?.conversationId ?? lockKey
2869
+ conversationId: getResumeConversationId(runArgs, lockKey)
2826
2870
  });
2827
2871
  await postSlackApiReplyPosts({
2828
2872
  channelId: runArgs.channelId,
@@ -2833,6 +2877,27 @@ async function resumeSlackTurn(args) {
2833
2877
  });
2834
2878
  finalReplyDelivered = true;
2835
2879
  await runArgs.onSuccess?.(reply);
2880
+ if (reply.diagnostics.outcome === "success" && replyContext.correlation?.conversationId && replyContext.correlation.turnId) {
2881
+ try {
2882
+ const params = {
2883
+ conversationId: replyContext.correlation.conversationId,
2884
+ sessionId: replyContext.correlation.turnId
2885
+ };
2886
+ if (runArgs.scheduleSessionCompletedPluginTasks) {
2887
+ await runArgs.scheduleSessionCompletedPluginTasks(params);
2888
+ } else {
2889
+ await scheduleSessionCompletedPluginTasks(params);
2890
+ }
2891
+ } catch (scheduleError) {
2892
+ logException(
2893
+ scheduleError,
2894
+ "plugin_session_completed_task_schedule_failed",
2895
+ getResumeLogContext(runArgs, lockKey),
2896
+ {},
2897
+ "Plugin session.completed task scheduling failed"
2898
+ );
2899
+ }
2900
+ }
2836
2901
  } catch (error) {
2837
2902
  await status.stop();
2838
2903
  const onAuthPause = runArgs.onAuthPause;
@@ -2898,13 +2963,17 @@ async function resumeSlackTurn(args) {
2898
2963
  try {
2899
2964
  await deferredPauseHandler();
2900
2965
  if (deferredPauseKind === "auth" && deferredAuthInfo) {
2966
+ const footer = buildSlackReplyFooter({
2967
+ conversationId: getResumeConversationId(runArgs, lockKey)
2968
+ });
2901
2969
  await postSlackMessageBestEffort(
2902
2970
  runArgs.channelId,
2903
2971
  runArgs.threadTs,
2904
2972
  buildAuthPauseResponse(
2905
2973
  deferredAuthInfo.requesterId,
2906
2974
  deferredAuthInfo.providerDisplayName
2907
- )
2975
+ ),
2976
+ footer
2908
2977
  );
2909
2978
  }
2910
2979
  return true;
@@ -3236,7 +3305,7 @@ async function resumeAuthorizedMcpTurn(args) {
3236
3305
  );
3237
3306
  let requester;
3238
3307
  try {
3239
- requester = createRequesterFromStoredSlackRequester({
3308
+ requester = createSlackResumeRequester({
3240
3309
  requester: lockedSessionRecord.requester,
3241
3310
  teamId: destination.teamId,
3242
3311
  userId: authSession.userId
@@ -3250,6 +3319,15 @@ async function resumeAuthorizedMcpTurn(args) {
3250
3319
  });
3251
3320
  return false;
3252
3321
  }
3322
+ if (!lockedSessionRecord.source) {
3323
+ await failAgentTurnSessionRecord({
3324
+ conversationId: authSession.conversationId,
3325
+ expectedVersion: lockedSessionRecord.version,
3326
+ sessionId: lockedSessionId,
3327
+ errorMessage: "Stored Slack source missing for MCP OAuth resume"
3328
+ });
3329
+ return false;
3330
+ }
3253
3331
  await recordAuthorizationCompleted({
3254
3332
  conversationId: authSession.conversationId,
3255
3333
  kind: "mcp",
@@ -3261,15 +3339,17 @@ async function resumeAuthorizedMcpTurn(args) {
3261
3339
  }),
3262
3340
  ttlMs: THREAD_STATE_TTL_MS
3263
3341
  });
3342
+ const lockedMessageTs = getTurnUserSlackMessageTs(lockedUserMessage);
3264
3343
  return {
3265
3344
  messageText: lockedUserMessage.text,
3266
- messageTs: getTurnUserSlackMessageTs(lockedUserMessage),
3345
+ messageTs: lockedMessageTs,
3267
3346
  replyContext: {
3268
3347
  credentialContext: {
3269
3348
  actor: { type: "user", userId: requester.userId }
3270
3349
  },
3271
3350
  requester,
3272
3351
  destination,
3352
+ source: lockedSessionRecord.source,
3273
3353
  correlation: {
3274
3354
  conversationId: authSession.conversationId,
3275
3355
  turnId: lockedSessionId,
@@ -3459,11 +3539,10 @@ function pluginFor(provider) {
3459
3539
  }
3460
3540
  function basePluginContext(plugin) {
3461
3541
  const pluginName = plugin.manifest.name;
3462
- const db = getPluginDbForRegistration(plugin);
3463
3542
  return {
3464
3543
  plugin: { name: pluginName },
3465
3544
  log: createPluginLogger(pluginName),
3466
- ...db ? { db } : {}
3545
+ db: getDb()
3467
3546
  };
3468
3547
  }
3469
3548
  function parseCredentialResult(value, pluginName) {
@@ -3572,7 +3651,12 @@ async function issuePluginCredential(input) {
3572
3651
  plugin.manifest.name,
3573
3652
  tokens
3574
3653
  );
3575
- }
3654
+ },
3655
+ withRefresh: async (callback) => await input.userTokenStore.withRefresh(
3656
+ currentUserId,
3657
+ plugin.manifest.name,
3658
+ callback
3659
+ )
3576
3660
  }
3577
3661
  } : {},
3578
3662
  ...credentialSubjectUserId ? {
@@ -3588,7 +3672,12 @@ async function issuePluginCredential(input) {
3588
3672
  plugin.manifest.name,
3589
3673
  tokens
3590
3674
  );
3591
- }
3675
+ },
3676
+ withRefresh: async (callback) => await input.userTokenStore.withRefresh(
3677
+ credentialSubjectUserId,
3678
+ plugin.manifest.name,
3679
+ callback
3680
+ )
3592
3681
  }
3593
3682
  } : {}
3594
3683
  }
@@ -3908,7 +3997,7 @@ async function persistFailedOAuthReplyState(args) {
3908
3997
  });
3909
3998
  }
3910
3999
  async function resumeOAuthSessionRecordTurn(stored, options) {
3911
- if (!stored.resumeConversationId || !stored.resumeSessionId || !stored.channelId || !stored.destination || !stored.threadTs) {
4000
+ if (!stored.resumeConversationId || !stored.resumeSessionId || !stored.channelId || !stored.destination || !stored.source || !stored.threadTs) {
3912
4001
  return false;
3913
4002
  }
3914
4003
  const destination = requireSlackDestination(
@@ -4027,7 +4116,7 @@ async function resumeOAuthSessionRecordTurn(stored, options) {
4027
4116
  );
4028
4117
  let requester;
4029
4118
  try {
4030
- requester = createRequesterFromStoredSlackRequester({
4119
+ requester = createSlackResumeRequester({
4031
4120
  requester: lockedSessionRecord.requester,
4032
4121
  teamId: destination.teamId,
4033
4122
  userId: lockedUserMessage.author.userId
@@ -4041,6 +4130,15 @@ async function resumeOAuthSessionRecordTurn(stored, options) {
4041
4130
  });
4042
4131
  return false;
4043
4132
  }
4133
+ if (!lockedSessionRecord.source) {
4134
+ await failAgentTurnSessionRecord({
4135
+ conversationId: stored.resumeConversationId,
4136
+ expectedVersion: lockedSessionRecord.version,
4137
+ sessionId: lockedSessionId,
4138
+ errorMessage: "Stored Slack source missing for OAuth resume"
4139
+ });
4140
+ return false;
4141
+ }
4044
4142
  await recordAuthorizationCompleted({
4045
4143
  conversationId: stored.resumeConversationId,
4046
4144
  kind: "plugin",
@@ -4052,9 +4150,10 @@ async function resumeOAuthSessionRecordTurn(stored, options) {
4052
4150
  }),
4053
4151
  ttlMs: THREAD_STATE_TTL_MS2
4054
4152
  });
4153
+ const lockedMessageTs = getTurnUserSlackMessageTs(lockedUserMessage);
4055
4154
  return {
4056
4155
  messageText: lockedPendingAuth ? lockedUserMessage.text : stored.pendingMessage ?? lockedUserMessage.text,
4057
- messageTs: getTurnUserSlackMessageTs(lockedUserMessage),
4156
+ messageTs: lockedMessageTs,
4058
4157
  replyContext: {
4059
4158
  credentialContext: {
4060
4159
  actor: {
@@ -4064,6 +4163,7 @@ async function resumeOAuthSessionRecordTurn(stored, options) {
4064
4163
  },
4065
4164
  requester,
4066
4165
  destination,
4166
+ source: lockedSessionRecord.source,
4067
4167
  correlation: {
4068
4168
  conversationId: stored.resumeConversationId,
4069
4169
  turnId: lockedSessionId,
@@ -4151,7 +4251,8 @@ async function resumeOAuthSessionRecordTurn(stored, options) {
4151
4251
  return true;
4152
4252
  }
4153
4253
  async function resumePendingOAuthMessage(stored, options) {
4154
- if (!stored.pendingMessage || !stored.channelId || !stored.destination || !stored.threadTs) {
4254
+ const source = stored.source;
4255
+ if (!stored.pendingMessage || !stored.channelId || !stored.destination || !source || !stored.threadTs) {
4155
4256
  return;
4156
4257
  }
4157
4258
  const threadId = `slack:${stored.channelId}:${stored.threadTs}`;
@@ -4170,11 +4271,12 @@ async function resumePendingOAuthMessage(stored, options) {
4170
4271
  destination.teamId,
4171
4272
  stored.userId
4172
4273
  );
4274
+ const messageTs = getTurnUserSlackMessageTs(latestUserMessage);
4173
4275
  await resumeAuthorizedRequest({
4174
4276
  messageText: stored.pendingMessage,
4175
4277
  channelId: stored.channelId,
4176
4278
  threadTs: stored.threadTs,
4177
- messageTs: getTurnUserSlackMessageTs(latestUserMessage),
4279
+ messageTs,
4178
4280
  connectedText: "",
4179
4281
  generateReply: options.generateReply,
4180
4282
  replyContext: {
@@ -4183,6 +4285,7 @@ async function resumePendingOAuthMessage(stored, options) {
4183
4285
  },
4184
4286
  requester,
4185
4287
  destination: stored.destination,
4288
+ source,
4186
4289
  correlation: {
4187
4290
  conversationId: threadId,
4188
4291
  channelId: stored.channelId,
@@ -5005,7 +5108,7 @@ function continueSandboxEgressTrace(request, upstreamHost, tracePropagation, cal
5005
5108
  if (!shouldPropagateSandboxEgressTrace(upstreamHost, tracePropagation) || !sentryTrace && !baggage) {
5006
5109
  return run();
5007
5110
  }
5008
- return sentry_exports.continueTrace({ sentryTrace, baggage }, run);
5111
+ return continueTrace({ sentryTrace, baggage }, run);
5009
5112
  }
5010
5113
  function isSandboxEgressForwardedRequest(request) {
5011
5114
  return Boolean(
@@ -6645,7 +6748,6 @@ async function POST2(request, waitUntil, services) {
6645
6748
  // src/chat/task-execution/vercel-callback.ts
6646
6749
  import {
6647
6750
  handleCallback,
6648
- QueueClient,
6649
6751
  registerDevConsumer
6650
6752
  } from "@vercel/queue";
6651
6753
 
@@ -7113,7 +7215,7 @@ function registerVercelConversationWorkDevConsumer(options) {
7113
7215
  return void 0;
7114
7216
  }
7115
7217
  return registerDevConsumer({
7116
- client: new QueueClient(),
7218
+ client: createVercelQueueClient(),
7117
7219
  consumerGroup: CONVERSATION_WORK_DEV_CONSUMER_GROUP,
7118
7220
  handler: (message, metadata) => handleConversationQueueMessage(message, metadata, options),
7119
7221
  retry: handleConversationQueueRetry,
@@ -7122,16 +7224,79 @@ function registerVercelConversationWorkDevConsumer(options) {
7122
7224
  });
7123
7225
  }
7124
7226
 
7227
+ // src/chat/plugins/task-callback.ts
7228
+ import {
7229
+ handleCallback as handleCallback2,
7230
+ registerDevConsumer as registerDevConsumer2
7231
+ } from "@vercel/queue";
7232
+ var PLUGIN_TASK_DEV_CONSUMER_GROUP = "junior_plugin_tasks_dev";
7233
+ var PLUGIN_TASK_MAX_DELIVERIES = 5;
7234
+ function logPluginTaskQueueMessageRejected(reason, metadata) {
7235
+ logWarn(
7236
+ "plugin_task_queue_message_rejected",
7237
+ {},
7238
+ {
7239
+ "app.queue.consumer_group": metadata.consumerGroup,
7240
+ "app.queue.delivery_count": metadata.deliveryCount,
7241
+ "app.queue.message_id": metadata.messageId,
7242
+ "app.queue.reject_reason": reason,
7243
+ "app.queue.topic_name": metadata.topicName
7244
+ },
7245
+ "Plugin task queue message rejected without retry"
7246
+ );
7247
+ }
7248
+ async function handlePluginTaskQueueMessage(message, metadata) {
7249
+ const verification = verifyPluginTaskQueueMessage(message);
7250
+ if (verification.status === "rejected") {
7251
+ logPluginTaskQueueMessageRejected(verification.reason, metadata);
7252
+ return;
7253
+ }
7254
+ if (verification.status === "unavailable") {
7255
+ throw new Error(
7256
+ `Plugin task queue message verification unavailable: ${verification.reason}`
7257
+ );
7258
+ }
7259
+ await runWithTurnRequestDeadline(
7260
+ () => processPluginTask(verification.message)
7261
+ );
7262
+ }
7263
+ function handlePluginTaskQueueRetry(_error, metadata) {
7264
+ if (metadata.deliveryCount >= PLUGIN_TASK_MAX_DELIVERIES) {
7265
+ return { acknowledge: true };
7266
+ }
7267
+ return void 0;
7268
+ }
7269
+ function createVercelPluginTaskCallback() {
7270
+ return handleCallback2(
7271
+ (message, metadata) => handlePluginTaskQueueMessage(message, metadata),
7272
+ {
7273
+ retry: handlePluginTaskQueueRetry
7274
+ }
7275
+ );
7276
+ }
7277
+ function registerVercelPluginTaskDevConsumer() {
7278
+ if (process.env.NODE_ENV !== "development") {
7279
+ return void 0;
7280
+ }
7281
+ return registerDevConsumer2({
7282
+ client: createVercelQueueClient(),
7283
+ consumerGroup: PLUGIN_TASK_DEV_CONSUMER_GROUP,
7284
+ handler: (message, metadata) => handlePluginTaskQueueMessage(message, metadata),
7285
+ retry: handlePluginTaskQueueRetry,
7286
+ topic: PLUGIN_TASK_QUEUE_TOPIC
7287
+ });
7288
+ }
7289
+
7125
7290
  // src/chat/services/subscribed-decision.ts
7126
7291
  import { z as z2 } from "zod";
7127
7292
  var replyDecisionSchema = z2.object({
7128
7293
  should_reply: z2.boolean().describe("Whether Junior should respond to this thread message."),
7129
- should_unsubscribe: z2.boolean().optional().describe(
7294
+ should_unsubscribe: z2.boolean().describe(
7130
7295
  "Whether Junior should unsubscribe from this thread because the user clearly asked it to stop participating."
7131
7296
  ),
7132
7297
  confidence: z2.number().min(0).max(1).describe("Classifier confidence from 0 to 1."),
7133
7298
  reason: z2.string().optional().describe("Short reason for the decision.")
7134
- });
7299
+ }).strict();
7135
7300
  var ROUTER_CONFIDENCE_THRESHOLD = 0.8;
7136
7301
  var ROUTER_CLASSIFIER_MAX_TOKENS = 240;
7137
7302
  var LEADING_SLACK_MENTION_RE = /^\s*<@([A-Z0-9]+)(?:\|([^>]+))?>[\s,:-]*/i;
@@ -9059,6 +9224,9 @@ function createJuniorRuntimeServices(overrides = {}) {
9059
9224
  getAwaitingAgentContinueRequest: overrides.replyExecutor?.getAwaitingAgentContinueRequest ?? getAwaitingAgentContinueRequest,
9060
9225
  lookupSlackUser: overrides.replyExecutor?.lookupSlackUser ?? lookupSlackUser,
9061
9226
  scheduleAgentContinue: overrides.replyExecutor?.scheduleAgentContinue ?? scheduleAgentContinue,
9227
+ scheduleSessionCompletedPluginTasks: overrides.replyExecutor?.scheduleSessionCompletedPluginTasks ?? (async (params) => {
9228
+ await scheduleSessionCompletedPluginTasks(params);
9229
+ }),
9062
9230
  generateThreadTitle: conversationMemory.generateThreadTitle
9063
9231
  },
9064
9232
  subscribedReplyPolicy: createSubscribedReplyPolicy({
@@ -9068,6 +9236,9 @@ function createJuniorRuntimeServices(overrides = {}) {
9068
9236
  };
9069
9237
  }
9070
9238
 
9239
+ // src/chat/runtime/reply-executor.ts
9240
+ import { createSlackSource } from "@sentry/junior-plugin-api";
9241
+
9071
9242
  // src/chat/slack/message.ts
9072
9243
  function isSlackMessageTs(value) {
9073
9244
  return /^\d+(?:\.\d+)?$/.test(value.trim());
@@ -9277,10 +9448,11 @@ function createReplyToThread(deps) {
9277
9448
  const threadId = getThreadId(thread, message);
9278
9449
  const channelId = getChannelId(thread, message);
9279
9450
  const channelName = channelId ? await resolveChannelName(thread) : void 0;
9451
+ const slackChannelType = resolveSlackChannelTypeFromMessage(message);
9280
9452
  const slackConversation = resolveSlackConversationContext({
9281
9453
  channelId,
9282
9454
  channelName,
9283
- channelType: resolveSlackChannelTypeFromMessage(message)
9455
+ channelType: slackChannelType
9284
9456
  });
9285
9457
  const threadTs = getThreadTs(threadId);
9286
9458
  const assistantThreadContext = getAssistantThreadContext(message);
@@ -9290,6 +9462,12 @@ function createReplyToThread(deps) {
9290
9462
  "Slack reply execution"
9291
9463
  );
9292
9464
  const teamId = destination.teamId;
9465
+ const source = createSlackSource({
9466
+ channelId: channelId ?? destination.channelId,
9467
+ messageTs,
9468
+ teamId,
9469
+ threadTs
9470
+ });
9293
9471
  const runId = getRunId(thread, message);
9294
9472
  const conversationId = threadId ?? runId;
9295
9473
  await withSpan(
@@ -9329,12 +9507,12 @@ function createReplyToThread(deps) {
9329
9507
  )
9330
9508
  )
9331
9509
  );
9332
- const requesterIdentity = await ensureSlackMessageActorIdentity(
9510
+ const requester = await ensureSlackMessageActorIdentity(
9333
9511
  message,
9334
9512
  teamId,
9335
9513
  deps.services.lookupSlackUser
9336
9514
  );
9337
- const requester = turnRequester(requesterIdentity);
9515
+ const storedRequester = turnRequester(requester);
9338
9516
  const preparedState = options.preparedState ?? await deps.prepareTurnState({
9339
9517
  thread,
9340
9518
  message,
@@ -9370,16 +9548,29 @@ function createReplyToThread(deps) {
9370
9548
  await options.beforeFirstResponsePost?.();
9371
9549
  };
9372
9550
  const postAuthPauseNotice = async (providerDisplayName) => {
9551
+ const text = buildAuthPauseResponse(
9552
+ message.author.userId,
9553
+ providerDisplayName
9554
+ );
9555
+ const footer = buildSlackReplyFooter({ conversationId });
9373
9556
  try {
9374
- await beforeFirstResponsePost();
9375
- await thread.post(
9376
- buildSlackOutputMessage(
9377
- buildAuthPauseResponse(
9378
- message.author.userId,
9379
- providerDisplayName
9380
- )
9381
- )
9382
- );
9557
+ if (channelId && threadTs) {
9558
+ await postSlackApiReplyPosts({
9559
+ beforePost: beforeFirstResponsePost,
9560
+ channelId,
9561
+ threadTs,
9562
+ posts: [
9563
+ {
9564
+ text,
9565
+ stage: "thread_reply"
9566
+ }
9567
+ ],
9568
+ footer
9569
+ });
9570
+ } else {
9571
+ await beforeFirstResponsePost();
9572
+ await thread.post(buildSlackOutputMessage(text));
9573
+ }
9383
9574
  } catch (error) {
9384
9575
  logException(
9385
9576
  error,
@@ -9515,6 +9706,7 @@ function createReplyToThread(deps) {
9515
9706
  surface: "slack",
9516
9707
  requester,
9517
9708
  destination,
9709
+ source,
9518
9710
  traceId: getActiveTraceId()
9519
9711
  }).catch((error) => {
9520
9712
  logException(
@@ -9528,7 +9720,7 @@ function createReplyToThread(deps) {
9528
9720
  void initConversationContext(conversationId, {
9529
9721
  channelName,
9530
9722
  originSurface: "slack",
9531
- originRequester: requester,
9723
+ originRequester: storedRequester,
9532
9724
  startedAtMs: turnStartedAtMs
9533
9725
  }).catch((error) => {
9534
9726
  logException(
@@ -9578,12 +9770,12 @@ function createReplyToThread(deps) {
9578
9770
  if (message.author.userId) {
9579
9771
  setSentryUser({
9580
9772
  id: message.author.userId,
9581
- ...requesterIdentity.userName ? { username: requesterIdentity.userName } : {},
9582
- ...requesterIdentity.email ? { email: requesterIdentity.email } : {}
9773
+ ...requester.userName ? { username: requester.userName } : {},
9774
+ ...requester.email ? { email: requester.email } : {}
9583
9775
  });
9584
9776
  }
9585
- if (requesterIdentity.userName) {
9586
- setTags({ slackUserName: requesterIdentity.userName });
9777
+ if (requester.userName) {
9778
+ setTags({ slackUserName: requester.userName });
9587
9779
  }
9588
9780
  const turnAttachments = collectTurnAttachments(
9589
9781
  message,
@@ -9763,7 +9955,7 @@ function createReplyToThread(deps) {
9763
9955
  credentialContext: {
9764
9956
  actor: { type: "user", userId: message.author.userId }
9765
9957
  },
9766
- requester: requesterIdentity,
9958
+ requester,
9767
9959
  conversationContext: preparedState.conversationContext,
9768
9960
  artifactState: preparedState.artifacts,
9769
9961
  piMessages,
@@ -9774,6 +9966,7 @@ function createReplyToThread(deps) {
9774
9966
  omittedImageAttachmentCount,
9775
9967
  userAttachments,
9776
9968
  slackConversation,
9969
+ source,
9777
9970
  destination,
9778
9971
  surface: "slack",
9779
9972
  turnDeadlineAtMs: getTurnRequestDeadline()?.deadlineAtMs,
@@ -9940,6 +10133,7 @@ function createReplyToThread(deps) {
9940
10133
  state: "completed",
9941
10134
  requester,
9942
10135
  destination,
10136
+ source,
9943
10137
  traceId: getActiveTraceId()
9944
10138
  });
9945
10139
  }
@@ -9958,6 +10152,22 @@ function createReplyToThread(deps) {
9958
10152
  );
9959
10153
  }
9960
10154
  await options.onTurnCompleted?.();
10155
+ if (reply.diagnostics.outcome === "success" && conversationId) {
10156
+ try {
10157
+ await deps.services.scheduleSessionCompletedPluginTasks({
10158
+ conversationId,
10159
+ sessionId: turnId
10160
+ });
10161
+ } catch (error) {
10162
+ logException(
10163
+ error,
10164
+ "plugin_session_completed_task_schedule_failed",
10165
+ turnTraceContext,
10166
+ {},
10167
+ "Plugin session.completed task scheduling failed"
10168
+ );
10169
+ }
10170
+ }
9961
10171
  } catch (error) {
9962
10172
  if (isCooperativeTurnYieldError(error)) {
9963
10173
  shouldPersistFailureState = false;
@@ -10093,6 +10303,7 @@ function createReplyToThread(deps) {
10093
10303
  state: "failed",
10094
10304
  requester,
10095
10305
  destination,
10306
+ source,
10096
10307
  traceId: getActiveTraceId()
10097
10308
  });
10098
10309
  const sessionRecord = await getAgentTurnSessionRecord(
@@ -10642,6 +10853,7 @@ async function continueSlackAgentRun(payload, options = {}) {
10642
10853
  threadTs: thread.threadTs,
10643
10854
  lockKey: payload.conversationId,
10644
10855
  generateReply: options.generateReply,
10856
+ scheduleSessionCompletedPluginTasks: options.scheduleSessionCompletedPluginTasks,
10645
10857
  beforeStart: async () => {
10646
10858
  let sessionRecord;
10647
10859
  try {
@@ -10678,11 +10890,20 @@ async function continueSlackAgentRun(payload, options = {}) {
10678
10890
  payload.destination,
10679
10891
  "Slack continuation"
10680
10892
  );
10681
- const requester = createRequesterFromStoredSlackRequester({
10893
+ const requester = createSlackResumeRequester({
10682
10894
  requester: activeSessionRecord.requester,
10683
10895
  teamId: destination.teamId,
10684
10896
  userId: userMessage2.author.userId
10685
10897
  });
10898
+ if (!activeSessionRecord.source) {
10899
+ await failAgentTurnSessionRecord({
10900
+ conversationId: payload.conversationId,
10901
+ expectedVersion: activeSessionRecord.version,
10902
+ sessionId: payload.sessionId,
10903
+ errorMessage: "Stored Slack source missing for continuation"
10904
+ });
10905
+ return false;
10906
+ }
10686
10907
  return {
10687
10908
  messageText: userMessage2.text,
10688
10909
  messageTs: getTurnUserSlackMessageTs(userMessage2),
@@ -10695,6 +10916,7 @@ async function continueSlackAgentRun(payload, options = {}) {
10695
10916
  },
10696
10917
  requester,
10697
10918
  destination: payload.destination,
10919
+ source: activeSessionRecord.source,
10698
10920
  correlation: {
10699
10921
  conversationId: payload.conversationId,
10700
10922
  turnId: payload.sessionId,
@@ -10878,7 +11100,7 @@ function getProductionSlackAdapter() {
10878
11100
  return productionSlackAdapter;
10879
11101
  }
10880
11102
  function getProductionConversationStore() {
10881
- return getConfiguredConversationStore();
11103
+ return getConversationStore();
10882
11104
  }
10883
11105
  function createProductionSlackWebhookServices(options) {
10884
11106
  const conversationStore = getProductionConversationStore();
@@ -10910,7 +11132,8 @@ function createProductionConversationWorkOptions(options) {
10910
11132
  generateReply: withSandboxTracePropagation(
10911
11133
  generateAssistantReply,
10912
11134
  options?.services?.sandbox?.tracePropagation
10913
- )
11135
+ ),
11136
+ scheduleSessionCompletedPluginTasks: options?.services?.replyExecutor?.scheduleSessionCompletedPluginTasks
10914
11137
  }),
10915
11138
  runtime
10916
11139
  })
@@ -10938,7 +11161,7 @@ async function resolveVirtualConfig() {
10938
11161
  return {
10939
11162
  pluginSet: mod.pluginSet,
10940
11163
  plugins: mod.plugins,
10941
- pluginHookRegistrations: mod.pluginHookRegistrations ?? []
11164
+ pluginRuntimeRegistrations: mod.pluginRuntimeRegistrations ?? []
10942
11165
  };
10943
11166
  } catch (error) {
10944
11167
  if (!isMissingVirtualConfig(error)) {
@@ -10980,22 +11203,22 @@ function validateBuildIncludesPluginPackages(pluginConfig, virtualConfig) {
10980
11203
  `createApp() registered plugin package(s) not bundled by juniorNitro(): ${missing.join(", ")}. Point juniorNitro({ plugins: "./plugins" }) at the runtime plugin module or pass the same defineJuniorPlugins(...) set to juniorNitro({ plugins }) and createApp({ plugins }).`
10981
11204
  );
10982
11205
  }
10983
- function validateBuildIncludesPluginHookRegistrations(hookRegistrations, virtualConfig) {
10984
- const bundledHookRegistrations = virtualConfig?.pluginHookRegistrations ?? [];
10985
- if (bundledHookRegistrations.length === 0) {
11206
+ function validateBuildIncludesPluginRuntimeRegistrations(runtimeRegistrations, virtualConfig) {
11207
+ const bundledRuntimeRegistrations = virtualConfig?.pluginRuntimeRegistrations ?? [];
11208
+ if (bundledRuntimeRegistrations.length === 0) {
10986
11209
  return;
10987
11210
  }
10988
11211
  const registered = new Set(
10989
- hookRegistrations.map((plugin) => plugin.manifest.name)
11212
+ runtimeRegistrations.map((plugin) => plugin.manifest.name)
10990
11213
  );
10991
- const missing = bundledHookRegistrations.filter(
11214
+ const missing = bundledRuntimeRegistrations.filter(
10992
11215
  (pluginName) => !registered.has(pluginName)
10993
11216
  );
10994
11217
  if (missing.length === 0) {
10995
11218
  return;
10996
11219
  }
10997
11220
  throw new Error(
10998
- `createApp() is missing plugin registration(s) with runtime hooks bundled by juniorNitro(): ${missing.join(", ")}. Pass a runtime-safe plugin module to juniorNitro({ plugins: "./plugins" }) or pass the same defineJuniorPlugins(...) set to createApp({ plugins }).`
11221
+ `createApp() is missing plugin registration(s) with runtime code bundled by juniorNitro(): ${missing.join(", ")}. Pass a runtime-safe plugin module to juniorNitro({ plugins: "./plugins" }) or pass the same defineJuniorPlugins(...) set to createApp({ plugins }).`
10999
11222
  );
11000
11223
  }
11001
11224
  function mountPluginRoutes(app, routes) {
@@ -11015,14 +11238,14 @@ function mountPluginRoutes(app, routes) {
11015
11238
  async function createApp(options) {
11016
11239
  const virtualConfig = await resolveVirtualConfig();
11017
11240
  const configuredPlugins = options?.plugins ?? virtualConfig?.pluginSet;
11018
- const plugins = pluginHookRegistrationsFromPluginSet(configuredPlugins);
11241
+ const plugins = pluginRuntimeRegistrationsFromPluginSet(configuredPlugins);
11019
11242
  const pluginConfig = configuredPlugins ? pluginCatalogConfigFromPluginSet(configuredPlugins) : virtualConfig?.plugins ?? pluginCatalogConfigFromEnv();
11020
11243
  if (configuredPlugins) {
11021
11244
  validateBuildIncludesPluginPackages(pluginConfig, virtualConfig);
11022
11245
  }
11023
- validateBuildIncludesPluginHookRegistrations(plugins, virtualConfig);
11246
+ validateBuildIncludesPluginRuntimeRegistrations(plugins, virtualConfig);
11024
11247
  validatePlugins(plugins);
11025
- validatePluginDatabaseRequirements(plugins);
11248
+ getDb();
11026
11249
  const shouldValidatePluginCatalog = hasConfiguredPluginCatalog(pluginConfig) || Boolean(configuredPlugins?.registrations.length) || Boolean(Object.keys(options?.configDefaults ?? {}).length);
11027
11250
  const previousPluginCatalogConfig = setPluginCatalogConfig(pluginConfig);
11028
11251
  const previousPlugins = setPlugins(plugins);
@@ -11097,6 +11320,7 @@ async function createApp(options) {
11097
11320
  });
11098
11321
  });
11099
11322
  let agentContinuePOST;
11323
+ let pluginTaskPOST;
11100
11324
  let conversationWorkOptions;
11101
11325
  const getConversationWorkOptions = () => {
11102
11326
  conversationWorkOptions ??= options?.conversationWork ?? createProductionConversationWorkOptions({
@@ -11106,6 +11330,7 @@ async function createApp(options) {
11106
11330
  };
11107
11331
  if (process.env.NODE_ENV === "development") {
11108
11332
  registerVercelConversationWorkDevConsumer(getConversationWorkOptions());
11333
+ registerVercelPluginTaskDevConsumer();
11109
11334
  }
11110
11335
  app.post("/api/internal/agent/continue", (c) => {
11111
11336
  agentContinuePOST ??= createVercelConversationWorkCallback(
@@ -11113,6 +11338,10 @@ async function createApp(options) {
11113
11338
  );
11114
11339
  return agentContinuePOST(c.req.raw);
11115
11340
  });
11341
+ app.post(JUNIOR_PLUGIN_TASK_CALLBACK_ROUTE, (c) => {
11342
+ pluginTaskPOST ??= createVercelPluginTaskCallback();
11343
+ return pluginTaskPOST(c.req.raw);
11344
+ });
11116
11345
  app.get("/api/internal/heartbeat", (c) => {
11117
11346
  return GET2(c.req.raw, waitUntil);
11118
11347
  });