@sentry/junior 0.75.0 → 0.76.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 (100) 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 +317 -118
  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/state/turn-session.d.ts +8 -5
  38. package/dist/chat/tools/agent-tools.d.ts +8 -1
  39. package/dist/chat/tools/slack/context.d.ts +2 -2
  40. package/dist/chat/tools/types.d.ts +4 -4
  41. package/dist/chat/vercel-queue-client.d.ts +3 -0
  42. package/dist/{chunk-C3AM4Z4J.js → chunk-2ECJXSVQ.js} +5 -5
  43. package/dist/{chunk-OJODNL2P.js → chunk-4SCWV7TJ.js} +2 -2
  44. package/dist/chunk-4UO6FK4G.js +64 -0
  45. package/dist/{chunk-BNJIEFQC.js → chunk-56TBVRJG.js} +2 -2
  46. package/dist/{chunk-OK4KKR7B.js → chunk-EJN6G5A2.js} +28 -12
  47. package/dist/{chunk-TQ74BATR.js → chunk-HHDUKWVG.js} +428 -111
  48. package/dist/{chunk-XJHDZUGD.js → chunk-JBASI5VV.js} +4 -4
  49. package/dist/chunk-KNFROR7R.js +127 -0
  50. package/dist/{chunk-VNTLUFTY.js → chunk-KOIMO7S3.js} +126 -87
  51. package/dist/chunk-MLKGABMK.js +9 -0
  52. package/dist/{chunk-NPVUAXUE.js → chunk-NFTMTIP3.js} +303 -33
  53. package/dist/chunk-NYKJ3KON.js +1082 -0
  54. package/dist/{chunk-SJHUF3DP.js → chunk-OJ53FYVG.js} +2 -10
  55. package/dist/{chunk-62FUNJYS.js → chunk-Q6XFTRV5.js} +54 -3
  56. package/dist/{chunk-UJ7OTHPO.js → chunk-R6Z5XWY3.js} +12 -670
  57. package/dist/chunk-RV5RYIJW.js +56 -0
  58. package/dist/{chunk-EE6PJWY4.js → chunk-SG5WAA7H.js} +7 -5
  59. package/dist/chunk-ST6YNAXG.js +54 -0
  60. package/dist/{chunk-FCZO7LAR.js → chunk-T77LUIX3.js} +139 -153
  61. package/dist/{chunk-EIYL7I4S.js → chunk-VALUBQ7R.js} +22 -30
  62. package/dist/{chunk-OZSPLAQ4.js → chunk-XBBC6W45.js} +1 -1
  63. package/dist/{chunk-ZNNTSPNF.js → chunk-Y5OFBCBZ.js} +1 -1
  64. package/dist/{chunk-74HO27II.js → chunk-Z4CIQ3EB.js} +5 -1
  65. package/dist/{chunk-2RWFUS5F.js → chunk-ZLMBNBUG.js} +101 -44
  66. package/dist/{chunk-JEELK46E.js → chunk-ZQB37HUX.js} +11 -11
  67. package/dist/cli/chat.js +52 -23
  68. package/dist/cli/check.js +7 -7
  69. package/dist/cli/env.js +4 -53
  70. package/dist/cli/init.js +6 -1
  71. package/dist/cli/main.js +84 -0
  72. package/dist/cli/plugins.js +244 -0
  73. package/dist/cli/run.js +5 -52
  74. package/dist/cli/snapshot-warmup.js +9 -9
  75. package/dist/cli/upgrade.js +167 -48
  76. package/dist/db-7A7PFRGL.js +17 -0
  77. package/dist/deployment.d.ts +1 -0
  78. package/dist/instrumentation.js +14 -18
  79. package/dist/nitro.d.ts +1 -1
  80. package/dist/nitro.js +43 -22
  81. package/dist/plugins-PZMDS7AT.js +15 -0
  82. package/dist/plugins.d.ts +4 -2
  83. package/dist/{registry-NLZFIW23.js → registry-OIPAJU2O.js} +6 -6
  84. package/dist/reporting.js +34 -26
  85. package/dist/{runner-LUQZ5G67.js → runner-KPLNHDCV.js} +76 -23
  86. package/dist/sentry-4CP5NNQ5.js +31 -0
  87. package/dist/validation-SLA6IGF7.js +15 -0
  88. package/dist/vercel.js +1 -1
  89. package/package.json +7 -6
  90. package/dist/agent-hooks-2HEB4C3Q.js +0 -33
  91. package/dist/chat/conversations/configured.d.ts +0 -7
  92. package/dist/chat/conversations/state.d.ts +0 -4
  93. package/dist/chat/plugins/db.d.ts +0 -31
  94. package/dist/chunk-2KG3PWR4.js +0 -17
  95. package/dist/chunk-D7NFH5GD.js +0 -570
  96. package/dist/chunk-MCMROINU.js +0 -12
  97. package/dist/chunk-WBZ4M5N5.js +0 -59
  98. package/dist/db-A3ILH67H.js +0 -20
  99. package/dist/plugins-OMJKLRJ2.js +0 -13
  100. 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-HHDUKWVG.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";
116
- import {
117
- discoverSkills
118
- } from "./chunk-OJODNL2P.js";
151
+ } from "./chunk-NFTMTIP3.js";
119
152
  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: {
@@ -2708,6 +2734,10 @@ function createResumeReplyContext(args, statusSession) {
2708
2734
  if (!replyContext) {
2709
2735
  throw new TypeError("Slack resume requires a reply context");
2710
2736
  }
2737
+ if (!replyContext.source) {
2738
+ throw new TypeError("Slack resume requires a reply context source");
2739
+ }
2740
+ const source = replyContext.source;
2711
2741
  if (replyContext.destination.platform !== "slack") {
2712
2742
  throw new TypeError("Slack resume requires a Slack destination");
2713
2743
  }
@@ -2716,6 +2746,7 @@ function createResumeReplyContext(args, statusSession) {
2716
2746
  const persistedChannelConfiguration = replyContext.channelConfiguration ?? (replyContext.configuration ? createReadOnlyConfigService(replyContext.configuration) : void 0);
2717
2747
  return {
2718
2748
  ...replyContext,
2749
+ source,
2719
2750
  turnDeadlineAtMs: replyContext.turnDeadlineAtMs ?? requestDeadline?.deadlineAtMs,
2720
2751
  correlation: {
2721
2752
  ...replyContext.correlation,
@@ -2833,6 +2864,27 @@ async function resumeSlackTurn(args) {
2833
2864
  });
2834
2865
  finalReplyDelivered = true;
2835
2866
  await runArgs.onSuccess?.(reply);
2867
+ if (reply.diagnostics.outcome === "success" && replyContext.correlation?.conversationId && replyContext.correlation.turnId) {
2868
+ try {
2869
+ const params = {
2870
+ conversationId: replyContext.correlation.conversationId,
2871
+ sessionId: replyContext.correlation.turnId
2872
+ };
2873
+ if (runArgs.scheduleSessionCompletedPluginTasks) {
2874
+ await runArgs.scheduleSessionCompletedPluginTasks(params);
2875
+ } else {
2876
+ await scheduleSessionCompletedPluginTasks(params);
2877
+ }
2878
+ } catch (scheduleError) {
2879
+ logException(
2880
+ scheduleError,
2881
+ "plugin_session_completed_task_schedule_failed",
2882
+ getResumeLogContext(runArgs, lockKey),
2883
+ {},
2884
+ "Plugin session.completed task scheduling failed"
2885
+ );
2886
+ }
2887
+ }
2836
2888
  } catch (error) {
2837
2889
  await status.stop();
2838
2890
  const onAuthPause = runArgs.onAuthPause;
@@ -3236,7 +3288,7 @@ async function resumeAuthorizedMcpTurn(args) {
3236
3288
  );
3237
3289
  let requester;
3238
3290
  try {
3239
- requester = createRequesterFromStoredSlackRequester({
3291
+ requester = createSlackResumeRequester({
3240
3292
  requester: lockedSessionRecord.requester,
3241
3293
  teamId: destination.teamId,
3242
3294
  userId: authSession.userId
@@ -3250,6 +3302,15 @@ async function resumeAuthorizedMcpTurn(args) {
3250
3302
  });
3251
3303
  return false;
3252
3304
  }
3305
+ if (!lockedSessionRecord.source) {
3306
+ await failAgentTurnSessionRecord({
3307
+ conversationId: authSession.conversationId,
3308
+ expectedVersion: lockedSessionRecord.version,
3309
+ sessionId: lockedSessionId,
3310
+ errorMessage: "Stored Slack source missing for MCP OAuth resume"
3311
+ });
3312
+ return false;
3313
+ }
3253
3314
  await recordAuthorizationCompleted({
3254
3315
  conversationId: authSession.conversationId,
3255
3316
  kind: "mcp",
@@ -3261,15 +3322,17 @@ async function resumeAuthorizedMcpTurn(args) {
3261
3322
  }),
3262
3323
  ttlMs: THREAD_STATE_TTL_MS
3263
3324
  });
3325
+ const lockedMessageTs = getTurnUserSlackMessageTs(lockedUserMessage);
3264
3326
  return {
3265
3327
  messageText: lockedUserMessage.text,
3266
- messageTs: getTurnUserSlackMessageTs(lockedUserMessage),
3328
+ messageTs: lockedMessageTs,
3267
3329
  replyContext: {
3268
3330
  credentialContext: {
3269
3331
  actor: { type: "user", userId: requester.userId }
3270
3332
  },
3271
3333
  requester,
3272
3334
  destination,
3335
+ source: lockedSessionRecord.source,
3273
3336
  correlation: {
3274
3337
  conversationId: authSession.conversationId,
3275
3338
  turnId: lockedSessionId,
@@ -3459,11 +3522,10 @@ function pluginFor(provider) {
3459
3522
  }
3460
3523
  function basePluginContext(plugin) {
3461
3524
  const pluginName = plugin.manifest.name;
3462
- const db = getPluginDbForRegistration(plugin);
3463
3525
  return {
3464
3526
  plugin: { name: pluginName },
3465
3527
  log: createPluginLogger(pluginName),
3466
- ...db ? { db } : {}
3528
+ db: getDb()
3467
3529
  };
3468
3530
  }
3469
3531
  function parseCredentialResult(value, pluginName) {
@@ -3572,7 +3634,12 @@ async function issuePluginCredential(input) {
3572
3634
  plugin.manifest.name,
3573
3635
  tokens
3574
3636
  );
3575
- }
3637
+ },
3638
+ withRefresh: async (callback) => await input.userTokenStore.withRefresh(
3639
+ currentUserId,
3640
+ plugin.manifest.name,
3641
+ callback
3642
+ )
3576
3643
  }
3577
3644
  } : {},
3578
3645
  ...credentialSubjectUserId ? {
@@ -3588,7 +3655,12 @@ async function issuePluginCredential(input) {
3588
3655
  plugin.manifest.name,
3589
3656
  tokens
3590
3657
  );
3591
- }
3658
+ },
3659
+ withRefresh: async (callback) => await input.userTokenStore.withRefresh(
3660
+ credentialSubjectUserId,
3661
+ plugin.manifest.name,
3662
+ callback
3663
+ )
3592
3664
  }
3593
3665
  } : {}
3594
3666
  }
@@ -3908,7 +3980,7 @@ async function persistFailedOAuthReplyState(args) {
3908
3980
  });
3909
3981
  }
3910
3982
  async function resumeOAuthSessionRecordTurn(stored, options) {
3911
- if (!stored.resumeConversationId || !stored.resumeSessionId || !stored.channelId || !stored.destination || !stored.threadTs) {
3983
+ if (!stored.resumeConversationId || !stored.resumeSessionId || !stored.channelId || !stored.destination || !stored.source || !stored.threadTs) {
3912
3984
  return false;
3913
3985
  }
3914
3986
  const destination = requireSlackDestination(
@@ -4027,7 +4099,7 @@ async function resumeOAuthSessionRecordTurn(stored, options) {
4027
4099
  );
4028
4100
  let requester;
4029
4101
  try {
4030
- requester = createRequesterFromStoredSlackRequester({
4102
+ requester = createSlackResumeRequester({
4031
4103
  requester: lockedSessionRecord.requester,
4032
4104
  teamId: destination.teamId,
4033
4105
  userId: lockedUserMessage.author.userId
@@ -4041,6 +4113,15 @@ async function resumeOAuthSessionRecordTurn(stored, options) {
4041
4113
  });
4042
4114
  return false;
4043
4115
  }
4116
+ if (!lockedSessionRecord.source) {
4117
+ await failAgentTurnSessionRecord({
4118
+ conversationId: stored.resumeConversationId,
4119
+ expectedVersion: lockedSessionRecord.version,
4120
+ sessionId: lockedSessionId,
4121
+ errorMessage: "Stored Slack source missing for OAuth resume"
4122
+ });
4123
+ return false;
4124
+ }
4044
4125
  await recordAuthorizationCompleted({
4045
4126
  conversationId: stored.resumeConversationId,
4046
4127
  kind: "plugin",
@@ -4052,9 +4133,10 @@ async function resumeOAuthSessionRecordTurn(stored, options) {
4052
4133
  }),
4053
4134
  ttlMs: THREAD_STATE_TTL_MS2
4054
4135
  });
4136
+ const lockedMessageTs = getTurnUserSlackMessageTs(lockedUserMessage);
4055
4137
  return {
4056
4138
  messageText: lockedPendingAuth ? lockedUserMessage.text : stored.pendingMessage ?? lockedUserMessage.text,
4057
- messageTs: getTurnUserSlackMessageTs(lockedUserMessage),
4139
+ messageTs: lockedMessageTs,
4058
4140
  replyContext: {
4059
4141
  credentialContext: {
4060
4142
  actor: {
@@ -4064,6 +4146,7 @@ async function resumeOAuthSessionRecordTurn(stored, options) {
4064
4146
  },
4065
4147
  requester,
4066
4148
  destination,
4149
+ source: lockedSessionRecord.source,
4067
4150
  correlation: {
4068
4151
  conversationId: stored.resumeConversationId,
4069
4152
  turnId: lockedSessionId,
@@ -4151,7 +4234,8 @@ async function resumeOAuthSessionRecordTurn(stored, options) {
4151
4234
  return true;
4152
4235
  }
4153
4236
  async function resumePendingOAuthMessage(stored, options) {
4154
- if (!stored.pendingMessage || !stored.channelId || !stored.destination || !stored.threadTs) {
4237
+ const source = stored.source;
4238
+ if (!stored.pendingMessage || !stored.channelId || !stored.destination || !source || !stored.threadTs) {
4155
4239
  return;
4156
4240
  }
4157
4241
  const threadId = `slack:${stored.channelId}:${stored.threadTs}`;
@@ -4170,11 +4254,12 @@ async function resumePendingOAuthMessage(stored, options) {
4170
4254
  destination.teamId,
4171
4255
  stored.userId
4172
4256
  );
4257
+ const messageTs = getTurnUserSlackMessageTs(latestUserMessage);
4173
4258
  await resumeAuthorizedRequest({
4174
4259
  messageText: stored.pendingMessage,
4175
4260
  channelId: stored.channelId,
4176
4261
  threadTs: stored.threadTs,
4177
- messageTs: getTurnUserSlackMessageTs(latestUserMessage),
4262
+ messageTs,
4178
4263
  connectedText: "",
4179
4264
  generateReply: options.generateReply,
4180
4265
  replyContext: {
@@ -4183,6 +4268,7 @@ async function resumePendingOAuthMessage(stored, options) {
4183
4268
  },
4184
4269
  requester,
4185
4270
  destination: stored.destination,
4271
+ source,
4186
4272
  correlation: {
4187
4273
  conversationId: threadId,
4188
4274
  channelId: stored.channelId,
@@ -5005,7 +5091,7 @@ function continueSandboxEgressTrace(request, upstreamHost, tracePropagation, cal
5005
5091
  if (!shouldPropagateSandboxEgressTrace(upstreamHost, tracePropagation) || !sentryTrace && !baggage) {
5006
5092
  return run();
5007
5093
  }
5008
- return sentry_exports.continueTrace({ sentryTrace, baggage }, run);
5094
+ return continueTrace({ sentryTrace, baggage }, run);
5009
5095
  }
5010
5096
  function isSandboxEgressForwardedRequest(request) {
5011
5097
  return Boolean(
@@ -6645,7 +6731,6 @@ async function POST2(request, waitUntil, services) {
6645
6731
  // src/chat/task-execution/vercel-callback.ts
6646
6732
  import {
6647
6733
  handleCallback,
6648
- QueueClient,
6649
6734
  registerDevConsumer
6650
6735
  } from "@vercel/queue";
6651
6736
 
@@ -7113,7 +7198,7 @@ function registerVercelConversationWorkDevConsumer(options) {
7113
7198
  return void 0;
7114
7199
  }
7115
7200
  return registerDevConsumer({
7116
- client: new QueueClient(),
7201
+ client: createVercelQueueClient(),
7117
7202
  consumerGroup: CONVERSATION_WORK_DEV_CONSUMER_GROUP,
7118
7203
  handler: (message, metadata) => handleConversationQueueMessage(message, metadata, options),
7119
7204
  retry: handleConversationQueueRetry,
@@ -7122,16 +7207,79 @@ function registerVercelConversationWorkDevConsumer(options) {
7122
7207
  });
7123
7208
  }
7124
7209
 
7210
+ // src/chat/plugins/task-callback.ts
7211
+ import {
7212
+ handleCallback as handleCallback2,
7213
+ registerDevConsumer as registerDevConsumer2
7214
+ } from "@vercel/queue";
7215
+ var PLUGIN_TASK_DEV_CONSUMER_GROUP = "junior_plugin_tasks_dev";
7216
+ var PLUGIN_TASK_MAX_DELIVERIES = 5;
7217
+ function logPluginTaskQueueMessageRejected(reason, metadata) {
7218
+ logWarn(
7219
+ "plugin_task_queue_message_rejected",
7220
+ {},
7221
+ {
7222
+ "app.queue.consumer_group": metadata.consumerGroup,
7223
+ "app.queue.delivery_count": metadata.deliveryCount,
7224
+ "app.queue.message_id": metadata.messageId,
7225
+ "app.queue.reject_reason": reason,
7226
+ "app.queue.topic_name": metadata.topicName
7227
+ },
7228
+ "Plugin task queue message rejected without retry"
7229
+ );
7230
+ }
7231
+ async function handlePluginTaskQueueMessage(message, metadata) {
7232
+ const verification = verifyPluginTaskQueueMessage(message);
7233
+ if (verification.status === "rejected") {
7234
+ logPluginTaskQueueMessageRejected(verification.reason, metadata);
7235
+ return;
7236
+ }
7237
+ if (verification.status === "unavailable") {
7238
+ throw new Error(
7239
+ `Plugin task queue message verification unavailable: ${verification.reason}`
7240
+ );
7241
+ }
7242
+ await runWithTurnRequestDeadline(
7243
+ () => processPluginTask(verification.message)
7244
+ );
7245
+ }
7246
+ function handlePluginTaskQueueRetry(_error, metadata) {
7247
+ if (metadata.deliveryCount >= PLUGIN_TASK_MAX_DELIVERIES) {
7248
+ return { acknowledge: true };
7249
+ }
7250
+ return void 0;
7251
+ }
7252
+ function createVercelPluginTaskCallback() {
7253
+ return handleCallback2(
7254
+ (message, metadata) => handlePluginTaskQueueMessage(message, metadata),
7255
+ {
7256
+ retry: handlePluginTaskQueueRetry
7257
+ }
7258
+ );
7259
+ }
7260
+ function registerVercelPluginTaskDevConsumer() {
7261
+ if (process.env.NODE_ENV !== "development") {
7262
+ return void 0;
7263
+ }
7264
+ return registerDevConsumer2({
7265
+ client: createVercelQueueClient(),
7266
+ consumerGroup: PLUGIN_TASK_DEV_CONSUMER_GROUP,
7267
+ handler: (message, metadata) => handlePluginTaskQueueMessage(message, metadata),
7268
+ retry: handlePluginTaskQueueRetry,
7269
+ topic: PLUGIN_TASK_QUEUE_TOPIC
7270
+ });
7271
+ }
7272
+
7125
7273
  // src/chat/services/subscribed-decision.ts
7126
7274
  import { z as z2 } from "zod";
7127
7275
  var replyDecisionSchema = z2.object({
7128
7276
  should_reply: z2.boolean().describe("Whether Junior should respond to this thread message."),
7129
- should_unsubscribe: z2.boolean().optional().describe(
7277
+ should_unsubscribe: z2.boolean().describe(
7130
7278
  "Whether Junior should unsubscribe from this thread because the user clearly asked it to stop participating."
7131
7279
  ),
7132
7280
  confidence: z2.number().min(0).max(1).describe("Classifier confidence from 0 to 1."),
7133
7281
  reason: z2.string().optional().describe("Short reason for the decision.")
7134
- });
7282
+ }).strict();
7135
7283
  var ROUTER_CONFIDENCE_THRESHOLD = 0.8;
7136
7284
  var ROUTER_CLASSIFIER_MAX_TOKENS = 240;
7137
7285
  var LEADING_SLACK_MENTION_RE = /^\s*<@([A-Z0-9]+)(?:\|([^>]+))?>[\s,:-]*/i;
@@ -9059,6 +9207,9 @@ function createJuniorRuntimeServices(overrides = {}) {
9059
9207
  getAwaitingAgentContinueRequest: overrides.replyExecutor?.getAwaitingAgentContinueRequest ?? getAwaitingAgentContinueRequest,
9060
9208
  lookupSlackUser: overrides.replyExecutor?.lookupSlackUser ?? lookupSlackUser,
9061
9209
  scheduleAgentContinue: overrides.replyExecutor?.scheduleAgentContinue ?? scheduleAgentContinue,
9210
+ scheduleSessionCompletedPluginTasks: overrides.replyExecutor?.scheduleSessionCompletedPluginTasks ?? (async (params) => {
9211
+ await scheduleSessionCompletedPluginTasks(params);
9212
+ }),
9062
9213
  generateThreadTitle: conversationMemory.generateThreadTitle
9063
9214
  },
9064
9215
  subscribedReplyPolicy: createSubscribedReplyPolicy({
@@ -9068,6 +9219,9 @@ function createJuniorRuntimeServices(overrides = {}) {
9068
9219
  };
9069
9220
  }
9070
9221
 
9222
+ // src/chat/runtime/reply-executor.ts
9223
+ import { createSlackSource } from "@sentry/junior-plugin-api";
9224
+
9071
9225
  // src/chat/slack/message.ts
9072
9226
  function isSlackMessageTs(value) {
9073
9227
  return /^\d+(?:\.\d+)?$/.test(value.trim());
@@ -9277,10 +9431,11 @@ function createReplyToThread(deps) {
9277
9431
  const threadId = getThreadId(thread, message);
9278
9432
  const channelId = getChannelId(thread, message);
9279
9433
  const channelName = channelId ? await resolveChannelName(thread) : void 0;
9434
+ const slackChannelType = resolveSlackChannelTypeFromMessage(message);
9280
9435
  const slackConversation = resolveSlackConversationContext({
9281
9436
  channelId,
9282
9437
  channelName,
9283
- channelType: resolveSlackChannelTypeFromMessage(message)
9438
+ channelType: slackChannelType
9284
9439
  });
9285
9440
  const threadTs = getThreadTs(threadId);
9286
9441
  const assistantThreadContext = getAssistantThreadContext(message);
@@ -9290,6 +9445,12 @@ function createReplyToThread(deps) {
9290
9445
  "Slack reply execution"
9291
9446
  );
9292
9447
  const teamId = destination.teamId;
9448
+ const source = createSlackSource({
9449
+ channelId: channelId ?? destination.channelId,
9450
+ messageTs,
9451
+ teamId,
9452
+ threadTs
9453
+ });
9293
9454
  const runId = getRunId(thread, message);
9294
9455
  const conversationId = threadId ?? runId;
9295
9456
  await withSpan(
@@ -9329,12 +9490,12 @@ function createReplyToThread(deps) {
9329
9490
  )
9330
9491
  )
9331
9492
  );
9332
- const requesterIdentity = await ensureSlackMessageActorIdentity(
9493
+ const requester = await ensureSlackMessageActorIdentity(
9333
9494
  message,
9334
9495
  teamId,
9335
9496
  deps.services.lookupSlackUser
9336
9497
  );
9337
- const requester = turnRequester(requesterIdentity);
9498
+ const storedRequester = turnRequester(requester);
9338
9499
  const preparedState = options.preparedState ?? await deps.prepareTurnState({
9339
9500
  thread,
9340
9501
  message,
@@ -9515,6 +9676,7 @@ function createReplyToThread(deps) {
9515
9676
  surface: "slack",
9516
9677
  requester,
9517
9678
  destination,
9679
+ source,
9518
9680
  traceId: getActiveTraceId()
9519
9681
  }).catch((error) => {
9520
9682
  logException(
@@ -9528,7 +9690,7 @@ function createReplyToThread(deps) {
9528
9690
  void initConversationContext(conversationId, {
9529
9691
  channelName,
9530
9692
  originSurface: "slack",
9531
- originRequester: requester,
9693
+ originRequester: storedRequester,
9532
9694
  startedAtMs: turnStartedAtMs
9533
9695
  }).catch((error) => {
9534
9696
  logException(
@@ -9578,12 +9740,12 @@ function createReplyToThread(deps) {
9578
9740
  if (message.author.userId) {
9579
9741
  setSentryUser({
9580
9742
  id: message.author.userId,
9581
- ...requesterIdentity.userName ? { username: requesterIdentity.userName } : {},
9582
- ...requesterIdentity.email ? { email: requesterIdentity.email } : {}
9743
+ ...requester.userName ? { username: requester.userName } : {},
9744
+ ...requester.email ? { email: requester.email } : {}
9583
9745
  });
9584
9746
  }
9585
- if (requesterIdentity.userName) {
9586
- setTags({ slackUserName: requesterIdentity.userName });
9747
+ if (requester.userName) {
9748
+ setTags({ slackUserName: requester.userName });
9587
9749
  }
9588
9750
  const turnAttachments = collectTurnAttachments(
9589
9751
  message,
@@ -9763,7 +9925,7 @@ function createReplyToThread(deps) {
9763
9925
  credentialContext: {
9764
9926
  actor: { type: "user", userId: message.author.userId }
9765
9927
  },
9766
- requester: requesterIdentity,
9928
+ requester,
9767
9929
  conversationContext: preparedState.conversationContext,
9768
9930
  artifactState: preparedState.artifacts,
9769
9931
  piMessages,
@@ -9774,6 +9936,7 @@ function createReplyToThread(deps) {
9774
9936
  omittedImageAttachmentCount,
9775
9937
  userAttachments,
9776
9938
  slackConversation,
9939
+ source,
9777
9940
  destination,
9778
9941
  surface: "slack",
9779
9942
  turnDeadlineAtMs: getTurnRequestDeadline()?.deadlineAtMs,
@@ -9940,6 +10103,7 @@ function createReplyToThread(deps) {
9940
10103
  state: "completed",
9941
10104
  requester,
9942
10105
  destination,
10106
+ source,
9943
10107
  traceId: getActiveTraceId()
9944
10108
  });
9945
10109
  }
@@ -9958,6 +10122,22 @@ function createReplyToThread(deps) {
9958
10122
  );
9959
10123
  }
9960
10124
  await options.onTurnCompleted?.();
10125
+ if (reply.diagnostics.outcome === "success" && conversationId) {
10126
+ try {
10127
+ await deps.services.scheduleSessionCompletedPluginTasks({
10128
+ conversationId,
10129
+ sessionId: turnId
10130
+ });
10131
+ } catch (error) {
10132
+ logException(
10133
+ error,
10134
+ "plugin_session_completed_task_schedule_failed",
10135
+ turnTraceContext,
10136
+ {},
10137
+ "Plugin session.completed task scheduling failed"
10138
+ );
10139
+ }
10140
+ }
9961
10141
  } catch (error) {
9962
10142
  if (isCooperativeTurnYieldError(error)) {
9963
10143
  shouldPersistFailureState = false;
@@ -10093,6 +10273,7 @@ function createReplyToThread(deps) {
10093
10273
  state: "failed",
10094
10274
  requester,
10095
10275
  destination,
10276
+ source,
10096
10277
  traceId: getActiveTraceId()
10097
10278
  });
10098
10279
  const sessionRecord = await getAgentTurnSessionRecord(
@@ -10642,6 +10823,7 @@ async function continueSlackAgentRun(payload, options = {}) {
10642
10823
  threadTs: thread.threadTs,
10643
10824
  lockKey: payload.conversationId,
10644
10825
  generateReply: options.generateReply,
10826
+ scheduleSessionCompletedPluginTasks: options.scheduleSessionCompletedPluginTasks,
10645
10827
  beforeStart: async () => {
10646
10828
  let sessionRecord;
10647
10829
  try {
@@ -10678,11 +10860,20 @@ async function continueSlackAgentRun(payload, options = {}) {
10678
10860
  payload.destination,
10679
10861
  "Slack continuation"
10680
10862
  );
10681
- const requester = createRequesterFromStoredSlackRequester({
10863
+ const requester = createSlackResumeRequester({
10682
10864
  requester: activeSessionRecord.requester,
10683
10865
  teamId: destination.teamId,
10684
10866
  userId: userMessage2.author.userId
10685
10867
  });
10868
+ if (!activeSessionRecord.source) {
10869
+ await failAgentTurnSessionRecord({
10870
+ conversationId: payload.conversationId,
10871
+ expectedVersion: activeSessionRecord.version,
10872
+ sessionId: payload.sessionId,
10873
+ errorMessage: "Stored Slack source missing for continuation"
10874
+ });
10875
+ return false;
10876
+ }
10686
10877
  return {
10687
10878
  messageText: userMessage2.text,
10688
10879
  messageTs: getTurnUserSlackMessageTs(userMessage2),
@@ -10695,6 +10886,7 @@ async function continueSlackAgentRun(payload, options = {}) {
10695
10886
  },
10696
10887
  requester,
10697
10888
  destination: payload.destination,
10889
+ source: activeSessionRecord.source,
10698
10890
  correlation: {
10699
10891
  conversationId: payload.conversationId,
10700
10892
  turnId: payload.sessionId,
@@ -10878,7 +11070,7 @@ function getProductionSlackAdapter() {
10878
11070
  return productionSlackAdapter;
10879
11071
  }
10880
11072
  function getProductionConversationStore() {
10881
- return getConfiguredConversationStore();
11073
+ return getConversationStore();
10882
11074
  }
10883
11075
  function createProductionSlackWebhookServices(options) {
10884
11076
  const conversationStore = getProductionConversationStore();
@@ -10910,7 +11102,8 @@ function createProductionConversationWorkOptions(options) {
10910
11102
  generateReply: withSandboxTracePropagation(
10911
11103
  generateAssistantReply,
10912
11104
  options?.services?.sandbox?.tracePropagation
10913
- )
11105
+ ),
11106
+ scheduleSessionCompletedPluginTasks: options?.services?.replyExecutor?.scheduleSessionCompletedPluginTasks
10914
11107
  }),
10915
11108
  runtime
10916
11109
  })
@@ -10938,7 +11131,7 @@ async function resolveVirtualConfig() {
10938
11131
  return {
10939
11132
  pluginSet: mod.pluginSet,
10940
11133
  plugins: mod.plugins,
10941
- pluginHookRegistrations: mod.pluginHookRegistrations ?? []
11134
+ pluginRuntimeRegistrations: mod.pluginRuntimeRegistrations ?? []
10942
11135
  };
10943
11136
  } catch (error) {
10944
11137
  if (!isMissingVirtualConfig(error)) {
@@ -10980,22 +11173,22 @@ function validateBuildIncludesPluginPackages(pluginConfig, virtualConfig) {
10980
11173
  `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
11174
  );
10982
11175
  }
10983
- function validateBuildIncludesPluginHookRegistrations(hookRegistrations, virtualConfig) {
10984
- const bundledHookRegistrations = virtualConfig?.pluginHookRegistrations ?? [];
10985
- if (bundledHookRegistrations.length === 0) {
11176
+ function validateBuildIncludesPluginRuntimeRegistrations(runtimeRegistrations, virtualConfig) {
11177
+ const bundledRuntimeRegistrations = virtualConfig?.pluginRuntimeRegistrations ?? [];
11178
+ if (bundledRuntimeRegistrations.length === 0) {
10986
11179
  return;
10987
11180
  }
10988
11181
  const registered = new Set(
10989
- hookRegistrations.map((plugin) => plugin.manifest.name)
11182
+ runtimeRegistrations.map((plugin) => plugin.manifest.name)
10990
11183
  );
10991
- const missing = bundledHookRegistrations.filter(
11184
+ const missing = bundledRuntimeRegistrations.filter(
10992
11185
  (pluginName) => !registered.has(pluginName)
10993
11186
  );
10994
11187
  if (missing.length === 0) {
10995
11188
  return;
10996
11189
  }
10997
11190
  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 }).`
11191
+ `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
11192
  );
11000
11193
  }
11001
11194
  function mountPluginRoutes(app, routes) {
@@ -11015,14 +11208,14 @@ function mountPluginRoutes(app, routes) {
11015
11208
  async function createApp(options) {
11016
11209
  const virtualConfig = await resolveVirtualConfig();
11017
11210
  const configuredPlugins = options?.plugins ?? virtualConfig?.pluginSet;
11018
- const plugins = pluginHookRegistrationsFromPluginSet(configuredPlugins);
11211
+ const plugins = pluginRuntimeRegistrationsFromPluginSet(configuredPlugins);
11019
11212
  const pluginConfig = configuredPlugins ? pluginCatalogConfigFromPluginSet(configuredPlugins) : virtualConfig?.plugins ?? pluginCatalogConfigFromEnv();
11020
11213
  if (configuredPlugins) {
11021
11214
  validateBuildIncludesPluginPackages(pluginConfig, virtualConfig);
11022
11215
  }
11023
- validateBuildIncludesPluginHookRegistrations(plugins, virtualConfig);
11216
+ validateBuildIncludesPluginRuntimeRegistrations(plugins, virtualConfig);
11024
11217
  validatePlugins(plugins);
11025
- validatePluginDatabaseRequirements(plugins);
11218
+ getDb();
11026
11219
  const shouldValidatePluginCatalog = hasConfiguredPluginCatalog(pluginConfig) || Boolean(configuredPlugins?.registrations.length) || Boolean(Object.keys(options?.configDefaults ?? {}).length);
11027
11220
  const previousPluginCatalogConfig = setPluginCatalogConfig(pluginConfig);
11028
11221
  const previousPlugins = setPlugins(plugins);
@@ -11097,6 +11290,7 @@ async function createApp(options) {
11097
11290
  });
11098
11291
  });
11099
11292
  let agentContinuePOST;
11293
+ let pluginTaskPOST;
11100
11294
  let conversationWorkOptions;
11101
11295
  const getConversationWorkOptions = () => {
11102
11296
  conversationWorkOptions ??= options?.conversationWork ?? createProductionConversationWorkOptions({
@@ -11106,6 +11300,7 @@ async function createApp(options) {
11106
11300
  };
11107
11301
  if (process.env.NODE_ENV === "development") {
11108
11302
  registerVercelConversationWorkDevConsumer(getConversationWorkOptions());
11303
+ registerVercelPluginTaskDevConsumer();
11109
11304
  }
11110
11305
  app.post("/api/internal/agent/continue", (c) => {
11111
11306
  agentContinuePOST ??= createVercelConversationWorkCallback(
@@ -11113,6 +11308,10 @@ async function createApp(options) {
11113
11308
  );
11114
11309
  return agentContinuePOST(c.req.raw);
11115
11310
  });
11311
+ app.post(JUNIOR_PLUGIN_TASK_CALLBACK_ROUTE, (c) => {
11312
+ pluginTaskPOST ??= createVercelPluginTaskCallback();
11313
+ return pluginTaskPOST(c.req.raw);
11314
+ });
11116
11315
  app.get("/api/internal/heartbeat", (c) => {
11117
11316
  return GET2(c.req.raw, waitUntil);
11118
11317
  });