@sentry/junior 0.137.0 → 0.138.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 (87) hide show
  1. package/dist/{agent-hooks-MZOKEP3J.js → agent-hooks-SCR74Q3E.js} +11 -9
  2. package/dist/api.js +21 -18
  3. package/dist/app.d.ts +7 -0
  4. package/dist/app.js +248 -689
  5. package/dist/{catalog-runtime-MD66YLFV.js → catalog-runtime-IU3QF4GG.js} +5 -5
  6. package/dist/chat/agent/request.d.ts +32 -2
  7. package/dist/chat/agent-dispatch/heartbeat.d.ts +5 -0
  8. package/dist/chat/agent-dispatch/types.d.ts +1 -1
  9. package/dist/chat/agent-invocations/errors.d.ts +8 -0
  10. package/dist/chat/agent-invocations/spawn.d.ts +12 -0
  11. package/dist/chat/agent-invocations/store.d.ts +44 -0
  12. package/dist/chat/agent-invocations/types.d.ts +449 -0
  13. package/dist/chat/agent-invocations/work.d.ts +31 -0
  14. package/dist/chat/config.d.ts +1 -0
  15. package/dist/chat/conversations/sql/store.d.ts +8 -0
  16. package/dist/chat/conversations/store.d.ts +7 -0
  17. package/dist/chat/experimental.d.ts +16 -0
  18. package/dist/chat/runtime/agent-runner.d.ts +2 -1
  19. package/dist/chat/services/auth-pause.d.ts +0 -1
  20. package/dist/chat/services/mcp-auth-orchestration.d.ts +3 -2
  21. package/dist/chat/services/plugin-auth-orchestration.d.ts +3 -2
  22. package/dist/chat/state/turn-session.d.ts +0 -2
  23. package/dist/chat/task-execution/state.d.ts +23 -11
  24. package/dist/chat/task-execution/store.d.ts +20 -11
  25. package/dist/chat/task-execution/worker.d.ts +2 -2
  26. package/dist/chat/tools/runtime/spawn-agent.d.ts +31 -0
  27. package/dist/chat/tools/types.d.ts +2 -0
  28. package/dist/chunk-3Y47VMHV.js +6 -0
  29. package/dist/chunk-5IDFPCON.js +46 -0
  30. package/dist/chunk-5LTJ4ITU.js +1523 -0
  31. package/dist/{chunk-WPPXIAFB.js → chunk-5MXWHTCK.js} +17 -434
  32. package/dist/{chunk-UOXATPSI.js → chunk-A7WCHXZV.js} +7 -5
  33. package/dist/{chunk-5G34IEK2.js → chunk-BOLJOV5T.js} +2 -56
  34. package/dist/{chunk-UG6ZYPBI.js → chunk-BYW4B4PR.js} +73 -60
  35. package/dist/{chunk-XZ7NPXAB.js → chunk-C7ZVPO4Z.js} +1 -1
  36. package/dist/{chunk-RB4NJKEX.js → chunk-DCLLQGS6.js} +119 -50
  37. package/dist/{chunk-DQXAAVTR.js → chunk-E5UYAIKJ.js} +44 -1467
  38. package/dist/{chunk-6QW2RRQT.js → chunk-ELKW5FIB.js} +5 -5
  39. package/dist/chunk-EM225ITJ.js +56 -0
  40. package/dist/{chunk-SYCX4DOK.js → chunk-FEUNPTGV.js} +7 -60
  41. package/dist/{chunk-YRPC6MJY.js → chunk-FNLBTB4K.js} +1 -1
  42. package/dist/chunk-HIWJWWMT.js +668 -0
  43. package/dist/chunk-IUFM5JWV.js +24 -0
  44. package/dist/{chunk-T6Z5S6PQ.js → chunk-JNNJZURV.js} +1 -1
  45. package/dist/{chunk-6ZK6FZIV.js → chunk-KCBZSJRU.js} +6 -27
  46. package/dist/{chunk-5PNJMOY5.js → chunk-KDCHVT25.js} +1 -1
  47. package/dist/{chunk-BZAMASJP.js → chunk-KFRHRSCJ.js} +1 -1
  48. package/dist/chunk-LPVUNO2W.js +320 -0
  49. package/dist/{chunk-EWDSB24B.js → chunk-N7WARMC6.js} +16 -776
  50. package/dist/chunk-PB3CXNVL.js +41 -0
  51. package/dist/chunk-Q6QZTTLS.js +55 -0
  52. package/dist/{chunk-36EG34YI.js → chunk-QS527AV5.js} +1 -1
  53. package/dist/chunk-R776HKPK.js +81 -0
  54. package/dist/{chunk-KJ4HGRHN.js → chunk-RZIBSCYC.js} +507 -596
  55. package/dist/chunk-SD3OFRWY.js +714 -0
  56. package/dist/{chunk-ZFS67WIR.js → chunk-SIPKWSXK.js} +57 -6
  57. package/dist/chunk-TCS76X5D.js +441 -0
  58. package/dist/{chunk-K2NCQJKH.js → chunk-TDJCLNDH.js} +3 -3
  59. package/dist/{chunk-POBSY47Q.js → chunk-TUUDXEXU.js} +336 -302
  60. package/dist/{chunk-YNFERGKP.js → chunk-VPZFVJND.js} +1 -1
  61. package/dist/{chunk-UZQ4VY3M.js → chunk-YU5QUF67.js} +1 -1
  62. package/dist/chunk-ZBSMCXKV.js +710 -0
  63. package/dist/{chunk-3Q342EJJ.js → chunk-ZXNTJJIE.js} +2 -2
  64. package/dist/cli/chat.js +65 -26
  65. package/dist/cli/check.js +6 -6
  66. package/dist/cli/plugins.js +14 -12
  67. package/dist/cli/snapshot-create.js +10 -9
  68. package/dist/cli/upgrade.js +7 -7
  69. package/dist/conversation-work-KATFHLG3.js +56 -0
  70. package/dist/db/schema/agent-invocations.d.ts +544 -0
  71. package/dist/db/schema.d.ts +543 -1
  72. package/dist/{db-66E7VDNC.js → db-UO2IWXGA.js} +7 -7
  73. package/dist/experimental-HH6OM6UH.js +13 -0
  74. package/dist/instrumentation.js +4 -4
  75. package/dist/nitro.js +4 -3
  76. package/dist/{oauth-callback-server-7QPD4Q7P.js → oauth-callback-server-MAQI43LX.js} +26 -18
  77. package/dist/{runner-L2BKJKOD.js → runner-PIUQEXKE.js} +26 -14
  78. package/dist/{skills-HOQ2NSPW.js → skills-4ARJADHQ.js} +6 -6
  79. package/dist/spawn-G2AWPMCT.js +38 -0
  80. package/dist/{validation-ENHI7S2C.js → validation-S3EE3HWL.js} +6 -6
  81. package/dist/version.js +1 -1
  82. package/dist/work-UARRUAW3.js +46 -0
  83. package/dist/worker-DOCIRVD3.js +24 -0
  84. package/migrations/0021_first_warhawk.sql +35 -0
  85. package/migrations/meta/0021_snapshot.json +2176 -0
  86. package/migrations/meta/_journal.json +8 -1
  87. package/package.json +5 -5
@@ -1,39 +1,30 @@
1
1
  import {
2
- SlackActionError,
2
+ resolveBaseUrl
3
+ } from "./chunk-SD3OFRWY.js";
4
+ import {
3
5
  createPluginLogger,
4
- getPlugins,
5
- getSlackClient,
6
- isDmChannel,
7
- normalizeSlackConversationId,
8
- withSlackRetries
9
- } from "./chunk-WPPXIAFB.js";
6
+ getPlugins
7
+ } from "./chunk-5MXWHTCK.js";
10
8
  import {
11
9
  getDb,
12
10
  parseDestination
13
- } from "./chunk-ZFS67WIR.js";
11
+ } from "./chunk-SIPKWSXK.js";
14
12
  import {
15
- acquireActiveLock,
16
- getStateAdapter,
17
13
  sleep
18
- } from "./chunk-5G34IEK2.js";
14
+ } from "./chunk-EM225ITJ.js";
19
15
  import {
20
- normalizeSlackEmojiName
21
- } from "./chunk-UG6ZYPBI.js";
16
+ acquireActiveLock,
17
+ getStateAdapter
18
+ } from "./chunk-BOLJOV5T.js";
22
19
  import {
23
20
  pluginCatalogRuntime
24
- } from "./chunk-36EG34YI.js";
21
+ } from "./chunk-QS527AV5.js";
25
22
  import {
26
23
  fetchWithBoundedOAuthErrorBodies
27
- } from "./chunk-BZAMASJP.js";
28
- import {
29
- parseSlackMessageTs,
30
- parseSlackUserId
31
- } from "./chunk-F3B2YJ76.js";
24
+ } from "./chunk-KFRHRSCJ.js";
32
25
  import {
33
- isRecord,
34
- logInfo,
35
- logWarn
36
- } from "./chunk-T6Z5S6PQ.js";
26
+ isRecord
27
+ } from "./chunk-JNNJZURV.js";
37
28
 
38
29
  // src/chat/capabilities/router.ts
39
30
  var ProviderCredentialRouter = class {
@@ -145,737 +136,6 @@ async function issueProviderCredentialLease(input) {
145
136
  return await getSandboxEgressRouter().issue(input);
146
137
  }
147
138
 
148
- // src/chat/agent/request.ts
149
- var RetryableDeliveryError = class extends Error {
150
- constructor(cause) {
151
- super("Assistant delivery was transient or ambiguous", { cause });
152
- this.name = "RetryableDeliveryError";
153
- }
154
- };
155
- function actorFromRouting(routing) {
156
- if (routing.dispatch?.actor) {
157
- return routing.dispatch.actor;
158
- }
159
- if (routing.actor) {
160
- return routing.actor;
161
- }
162
- if (routing.credentialContext && !("type" in routing.credentialContext.actor)) {
163
- return routing.credentialContext.actor;
164
- }
165
- return void 0;
166
- }
167
- function assertRunRoutingConsistency(request) {
168
- const { destination, source } = request.routing;
169
- if (source.platform !== destination.platform) {
170
- throw new TypeError("Run source and destination platforms do not match");
171
- }
172
- if (source.platform === "slack" && destination.platform === "slack") {
173
- if (source.teamId !== destination.teamId) {
174
- throw new TypeError("Slack source and destination teams do not match");
175
- }
176
- } else if (source.platform === "local" && destination.platform === "local" && (source.conversationId !== request.conversationId || destination.conversationId !== request.conversationId)) {
177
- throw new TypeError(
178
- "Local source, destination, and run conversation IDs do not match"
179
- );
180
- }
181
- const actor = request.routing.dispatch?.actor ?? request.routing.actor;
182
- if (!actor || actor.platform === "system") {
183
- return;
184
- }
185
- if (actor.platform !== destination.platform) {
186
- throw new TypeError(
187
- `Actor platform "${actor.platform}" does not match destination platform "${destination.platform}"`
188
- );
189
- }
190
- if (actor.platform === "slack" && destination.platform === "slack" && actor.teamId !== destination.teamId) {
191
- throw new TypeError("Slack actor team does not match destination team");
192
- }
193
- }
194
- function toolInvocationDestination(routing) {
195
- if (routing.destination.platform !== "slack" || !routing.toolChannelId) {
196
- return routing.destination;
197
- }
198
- return {
199
- platform: "slack",
200
- teamId: routing.destination.teamId,
201
- channelId: routing.toolChannelId
202
- };
203
- }
204
- function surfaceFromRouting(routing) {
205
- if (routing.surface) {
206
- return routing.surface;
207
- }
208
- return routing.source.platform === "slack" ? "slack" : "internal";
209
- }
210
-
211
- // src/chat/slack/outbound.ts
212
- var MAX_SLACK_MESSAGE_TEXT_CHARS = 4e4;
213
- function requireSlackConversationId(channelId, action) {
214
- const normalized = normalizeSlackConversationId(channelId);
215
- if (!normalized) {
216
- throw new Error(`${action} requires a valid channel ID`);
217
- }
218
- return normalized;
219
- }
220
- function requireSlackThreadTimestamp(threadTs, action) {
221
- const normalized = threadTs.trim();
222
- if (!normalized) {
223
- throw new Error(`${action} requires a thread timestamp`);
224
- }
225
- return normalized;
226
- }
227
- function requireSlackMessageTimestamp(timestamp, action) {
228
- const normalized = parseSlackMessageTs(timestamp);
229
- if (!normalized) {
230
- throw new Error(`${action} requires a target message timestamp`);
231
- }
232
- return normalized;
233
- }
234
- function requireSlackMessageText(text, action) {
235
- if (text.trim().length === 0) {
236
- throw new Error(`${action} requires non-empty text`);
237
- }
238
- if (text.length > MAX_SLACK_MESSAGE_TEXT_CHARS) {
239
- throw new Error(
240
- `${action} text exceeds Slack's 40000 character truncation limit`
241
- );
242
- }
243
- return text;
244
- }
245
- async function getSlackMessagePermalink(args) {
246
- try {
247
- const response = await withSlackRetries(
248
- () => getSlackClient().chat.getPermalink({
249
- channel: args.channelId,
250
- message_ts: args.messageTs
251
- }),
252
- 3,
253
- {
254
- action: "chat.getPermalink",
255
- idempotent: true,
256
- spanAttributes: {
257
- "app.slack.channel_id": args.channelId,
258
- "app.slack.message_ts": args.messageTs
259
- }
260
- }
261
- );
262
- return response.permalink;
263
- } catch {
264
- return void 0;
265
- }
266
- }
267
- async function postSlackMessage(input) {
268
- const channelId = requireSlackConversationId(
269
- input.channelId,
270
- "Slack message posting"
271
- );
272
- const text = requireSlackMessageText(input.text, "Slack message posting");
273
- const threadTs = input.threadTs ? requireSlackThreadTimestamp(
274
- input.threadTs,
275
- "Slack thread message posting"
276
- ) : void 0;
277
- const response = await withSlackRetries(
278
- () => getSlackClient().chat.postMessage({
279
- channel: channelId,
280
- text,
281
- unfurl_links: false,
282
- unfurl_media: false,
283
- ...input.blocks?.length ? {
284
- blocks: input.blocks
285
- } : {},
286
- ...threadTs ? { thread_ts: threadTs } : {}
287
- }),
288
- 3,
289
- {
290
- action: "chat.postMessage",
291
- spanAttributes: {
292
- "app.slack.channel_id": channelId,
293
- ...threadTs ? { "app.slack.thread_ts": threadTs } : {}
294
- }
295
- }
296
- );
297
- const messageTs = parseSlackMessageTs(response.ts);
298
- if (!messageTs) {
299
- throw new Error("Slack message posted without ts");
300
- }
301
- return {
302
- ts: messageTs,
303
- ...input.includePermalink ? {
304
- permalink: await getSlackMessagePermalink({
305
- channelId,
306
- messageTs
307
- })
308
- } : {}
309
- };
310
- }
311
- async function postSlackEphemeralMessage(input) {
312
- const channelId = requireSlackConversationId(
313
- input.channelId,
314
- "Slack ephemeral message posting"
315
- );
316
- const userId = parseSlackUserId(input.userId);
317
- if (!userId) {
318
- throw new Error("Slack ephemeral message posting requires a user ID");
319
- }
320
- const text = requireSlackMessageText(
321
- input.text,
322
- "Slack ephemeral message posting"
323
- );
324
- const threadTs = input.threadTs ? requireSlackThreadTimestamp(
325
- input.threadTs,
326
- "Slack ephemeral thread message posting"
327
- ) : void 0;
328
- const response = await withSlackRetries(
329
- () => getSlackClient().chat.postEphemeral({
330
- channel: channelId,
331
- user: userId,
332
- text,
333
- ...threadTs ? { thread_ts: threadTs } : {}
334
- }),
335
- 3,
336
- {
337
- action: "chat.postEphemeral",
338
- spanAttributes: {
339
- "app.slack.channel_id": channelId,
340
- "app.slack.user_id": userId,
341
- ...threadTs ? { "app.slack.thread_ts": threadTs } : {}
342
- }
343
- }
344
- );
345
- return {
346
- messageTs: response.message_ts
347
- };
348
- }
349
- async function uploadFilesToConversation(input) {
350
- const channelId = requireSlackConversationId(
351
- input.channelId,
352
- "Slack file upload"
353
- );
354
- const threadTs = input.threadTs ? requireSlackThreadTimestamp(input.threadTs, "Slack file upload") : void 0;
355
- if (input.files.length === 0) {
356
- throw new Error("Slack file upload requires at least one file");
357
- }
358
- const fileUploads = input.files.map((file) => {
359
- const filename = file.filename.trim();
360
- if (!filename) {
361
- throw new Error(
362
- "Slack file upload requires every file to have a filename"
363
- );
364
- }
365
- return {
366
- file: file.data,
367
- filename
368
- };
369
- });
370
- const response = await withSlackRetries(
371
- () => getSlackClient().filesUploadV2({
372
- channel_id: channelId,
373
- ...threadTs ? { thread_ts: threadTs } : {},
374
- file_uploads: fileUploads
375
- }),
376
- 3,
377
- {
378
- action: "filesUploadV2",
379
- spanAttributes: {
380
- "app.slack.channel_id": channelId,
381
- ...threadTs ? { "app.slack.thread_ts": threadTs } : {}
382
- }
383
- }
384
- );
385
- return {
386
- files: (response.files ?? []).flatMap(
387
- (completion) => (completion.files ?? []).map((file) => file.id ? { id: file.id } : {})
388
- )
389
- };
390
- }
391
- async function addReactionToMessage(input) {
392
- const channelId = requireSlackConversationId(
393
- input.channelId,
394
- "Slack reaction"
395
- );
396
- const timestamp = requireSlackMessageTimestamp(
397
- input.timestamp,
398
- "Slack reaction"
399
- );
400
- const emoji = normalizeSlackEmojiName(input.emoji);
401
- if (!emoji) {
402
- throw new Error("Slack reaction requires a valid emoji alias name");
403
- }
404
- try {
405
- await withSlackRetries(
406
- () => getSlackClient().reactions.add({
407
- channel: channelId,
408
- timestamp,
409
- name: emoji
410
- }),
411
- 3,
412
- {
413
- action: "reactions.add",
414
- idempotent: true,
415
- spanAttributes: {
416
- "app.slack.channel_id": channelId,
417
- "app.slack.message_ts": timestamp,
418
- "app.slack.reaction": emoji
419
- }
420
- }
421
- );
422
- } catch (error) {
423
- if (error instanceof SlackActionError && error.code === "already_reacted") {
424
- return { ok: true };
425
- }
426
- throw error;
427
- }
428
- return { ok: true };
429
- }
430
- async function removeReactionFromMessage(input) {
431
- const channelId = requireSlackConversationId(
432
- input.channelId,
433
- "Slack reaction removal"
434
- );
435
- const timestamp = requireSlackMessageTimestamp(
436
- input.timestamp,
437
- "Slack reaction removal"
438
- );
439
- const emoji = normalizeSlackEmojiName(input.emoji);
440
- if (!emoji) {
441
- throw new Error("Slack reaction removal requires a valid emoji alias name");
442
- }
443
- try {
444
- await withSlackRetries(
445
- () => getSlackClient().reactions.remove({
446
- channel: channelId,
447
- timestamp,
448
- name: emoji
449
- }),
450
- 3,
451
- {
452
- action: "reactions.remove",
453
- idempotent: true,
454
- spanAttributes: {
455
- "app.slack.channel_id": channelId,
456
- "app.slack.message_ts": timestamp,
457
- "app.slack.reaction": emoji
458
- }
459
- }
460
- );
461
- } catch (error) {
462
- if (error instanceof SlackActionError && error.code === "no_reaction") {
463
- return { ok: true };
464
- }
465
- throw error;
466
- }
467
- return { ok: true };
468
- }
469
-
470
- // src/chat/oauth-flow.ts
471
- import { randomBytes } from "crypto";
472
- import {
473
- sourceSchema
474
- } from "@sentry/junior-plugin-api";
475
-
476
- // src/chat/slack/status-format.ts
477
- var SLACK_STATUS_MAX_LENGTH = 50;
478
- function truncateStatusText(text) {
479
- const trimmed = text.trim();
480
- if (!trimmed) {
481
- return "";
482
- }
483
- if (trimmed.length <= SLACK_STATUS_MAX_LENGTH) {
484
- return trimmed;
485
- }
486
- return `${trimmed.slice(0, SLACK_STATUS_MAX_LENGTH - 3).trimEnd()}...`;
487
- }
488
-
489
- // src/chat/slack/mrkdwn.ts
490
- function escapeSlackMrkdwnText(text) {
491
- return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
492
- }
493
- function escapeSlackLinkUrl(url) {
494
- return url.replaceAll("&", "&amp;").replaceAll("<", "%3C").replaceAll(">", "%3E");
495
- }
496
- function formatSlackLink(url, label) {
497
- return `<${escapeSlackLinkUrl(url)}|${escapeSlackMrkdwnText(label)}>`;
498
- }
499
- function readInlineCodeSpan(line, start) {
500
- if (line[start] !== "`") {
501
- return void 0;
502
- }
503
- let n = 1;
504
- while (line[start + n] === "`") {
505
- n++;
506
- }
507
- const marker = "`".repeat(n);
508
- let search = start + n;
509
- while (search < line.length) {
510
- const close = line.indexOf(marker, search);
511
- if (close === -1) {
512
- return void 0;
513
- }
514
- const after = close + n;
515
- if (line[after] !== "`") {
516
- return { text: line.slice(start, after), end: after };
517
- }
518
- search = after + 1;
519
- }
520
- return void 0;
521
- }
522
- function readExistingSlackAngleToken(line, start) {
523
- if (line[start] !== "<") {
524
- return void 0;
525
- }
526
- const close = line.indexOf(">", start + 1);
527
- if (close === -1) {
528
- return void 0;
529
- }
530
- const body = line.slice(start + 1, close);
531
- if (/^(?:https?:\/\/|@|#|!)/.test(body)) {
532
- return { text: line.slice(start, close + 1), end: close + 1 };
533
- }
534
- return void 0;
535
- }
536
- function readMarkdownLink(line, start) {
537
- if (line[start] !== "[") {
538
- return void 0;
539
- }
540
- const labelEnd = line.indexOf("](", start + 1);
541
- if (labelEnd === -1) {
542
- return void 0;
543
- }
544
- const destStart = labelEnd + 2;
545
- if (!line.startsWith("http://", destStart) && !line.startsWith("https://", destStart)) {
546
- return void 0;
547
- }
548
- const closeParens = line.indexOf(")", destStart);
549
- if (closeParens === -1) {
550
- return void 0;
551
- }
552
- return { text: line.slice(start, closeParens + 1), end: closeParens + 1 };
553
- }
554
- function hasUnmatchedClosingParen(text) {
555
- let balance = 0;
556
- for (const ch of text) {
557
- if (ch === "(") balance++;
558
- else if (ch === ")") balance--;
559
- }
560
- return balance < 0;
561
- }
562
- function readBareUrl(line, start) {
563
- let end = start;
564
- while (end < line.length) {
565
- const ch = line[end];
566
- if (/\s/.test(ch) || ch === "<" || ch === ">" || ch === '"' || ch === "`" || ch === "|" || ch === "*") {
567
- break;
568
- }
569
- end++;
570
- }
571
- if (end === start) {
572
- return void 0;
573
- }
574
- let raw = line.slice(start, end);
575
- let suffix = "";
576
- const peel = () => {
577
- suffix = raw.slice(-1) + suffix;
578
- raw = raw.slice(0, -1);
579
- };
580
- const shouldPeel = () => raw.endsWith("_") || /[.,!?;:]$/.test(raw) || raw.endsWith(")") && hasUnmatchedClosingParen(raw);
581
- while (raw.length > 0 && shouldPeel()) {
582
- peel();
583
- }
584
- if (!/^https?:\/\/.+/.test(raw)) {
585
- return void 0;
586
- }
587
- return { url: raw, suffix, end };
588
- }
589
- function wrapBareUrlsOnLine(line) {
590
- let result = "";
591
- let i = 0;
592
- while (i < line.length) {
593
- const codeSpan = readInlineCodeSpan(line, i);
594
- if (codeSpan) {
595
- result += codeSpan.text;
596
- i = codeSpan.end;
597
- continue;
598
- }
599
- const angleToken = readExistingSlackAngleToken(line, i);
600
- if (angleToken) {
601
- result += angleToken.text;
602
- i = angleToken.end;
603
- continue;
604
- }
605
- const mdLink = readMarkdownLink(line, i);
606
- if (mdLink) {
607
- result += mdLink.text;
608
- i = mdLink.end;
609
- continue;
610
- }
611
- if (line.startsWith("https://", i) || line.startsWith("http://", i)) {
612
- const parsed = readBareUrl(line, i);
613
- if (parsed) {
614
- result += `<${parsed.url}>${parsed.suffix}`;
615
- i = parsed.end;
616
- continue;
617
- }
618
- }
619
- result += line[i];
620
- i++;
621
- }
622
- return result;
623
- }
624
- function wrapBareUrls(text) {
625
- const lines = text.split("\n");
626
- const out = [];
627
- let inCodeBlock = false;
628
- for (const line of lines) {
629
- if (line.trimStart().startsWith("```")) {
630
- inCodeBlock = !inCodeBlock;
631
- out.push(line);
632
- continue;
633
- }
634
- out.push(inCodeBlock ? line : wrapBareUrlsOnLine(line));
635
- }
636
- return out.join("\n");
637
- }
638
- function ensureBlockSpacing(text) {
639
- const codeBlockPattern = /^```/;
640
- const listItemPattern = /^[-*•]\s|^\d+\.\s/;
641
- const lines = text.split("\n");
642
- const result = [];
643
- let inCodeBlock = false;
644
- for (let i = 0; i < lines.length; i++) {
645
- const line = lines[i];
646
- const isCodeFence = codeBlockPattern.test(line.trimStart());
647
- if (isCodeFence) {
648
- if (!inCodeBlock) {
649
- const prev2 = result.length > 0 ? result[result.length - 1] : void 0;
650
- if (prev2 !== void 0 && prev2.trim() !== "") {
651
- result.push("");
652
- }
653
- }
654
- inCodeBlock = !inCodeBlock;
655
- result.push(line);
656
- continue;
657
- }
658
- if (inCodeBlock) {
659
- result.push(line);
660
- continue;
661
- }
662
- const prev = result.length > 0 ? result[result.length - 1] : void 0;
663
- if (prev !== void 0 && prev.trim() !== "" && line.trim() !== "" && !(listItemPattern.test(prev.trimStart()) && listItemPattern.test(line.trimStart()))) {
664
- result.push("");
665
- }
666
- result.push(line);
667
- }
668
- return result.join("\n");
669
- }
670
- function normalizeSlackReplyMarkdown(text) {
671
- let normalized = text.replace(/\r\n?/g, "\n").replace(/[ \t]+$/gm, "");
672
- normalized = wrapBareUrls(normalized);
673
- normalized = ensureBlockSpacing(normalized);
674
- return normalized.replace(/\n{3,}/g, "\n\n").trim();
675
- }
676
- function normalizeSlackStatusText(text) {
677
- const trimmed = text.trim();
678
- if (!trimmed) {
679
- return "";
680
- }
681
- return truncateStatusText(trimmed.replace(/(?:\.\s*)+$/, "").trim());
682
- }
683
-
684
- // src/chat/slack/oauth-authorization-message.ts
685
- function formatOAuthAuthorizationMessage(args) {
686
- return `${formatSlackLink(args.authorizationUrl, args.label)}. ${args.completionText}`;
687
- }
688
-
689
- // src/chat/oauth-flow.ts
690
- var OAUTH_STATE_TTL_MS = 10 * 60 * 1e3;
691
- function optionalString(value) {
692
- return typeof value === "string" ? value : void 0;
693
- }
694
- function parseOAuthStatePayload(value) {
695
- if (!isRecord(value)) {
696
- return void 0;
697
- }
698
- if (typeof value.userId !== "string" || typeof value.provider !== "string") {
699
- return void 0;
700
- }
701
- const destination = parseDestination(value.destination);
702
- if (value.destination !== void 0 && !destination) {
703
- return void 0;
704
- }
705
- const source = value.source === void 0 ? void 0 : sourceSchema.safeParse(value.source);
706
- if (value.source !== void 0 && (!source || !source.success)) {
707
- return void 0;
708
- }
709
- return {
710
- userId: value.userId,
711
- provider: value.provider,
712
- ...optionalString(value.channelId) ? { channelId: optionalString(value.channelId) } : {},
713
- ...destination ? { destination } : {},
714
- ...source?.success ? { source: source.data } : {},
715
- ...optionalString(value.threadTs) ? { threadTs: optionalString(value.threadTs) } : {},
716
- ...optionalString(value.resumeConversationId) ? { resumeConversationId: optionalString(value.resumeConversationId) } : {},
717
- ...optionalString(value.resumeSessionId) ? { resumeSessionId: optionalString(value.resumeSessionId) } : {},
718
- ...optionalString(value.scope) ? { scope: optionalString(value.scope) } : {}
719
- };
720
- }
721
- function formatProviderLabel(provider) {
722
- const displayName = pluginCatalogRuntime.getDisplayName(provider);
723
- if (!displayName) {
724
- throw new Error(`Unknown plugin provider display name: "${provider}"`);
725
- }
726
- return displayName;
727
- }
728
- function resolveBaseUrl() {
729
- const explicit = process.env.JUNIOR_BASE_URL?.trim();
730
- if (explicit) return explicit;
731
- const vercelProd = process.env.VERCEL_PROJECT_PRODUCTION_URL?.trim();
732
- if (vercelProd) return `https://${vercelProd}`;
733
- const vercelUrl = process.env.VERCEL_URL?.trim();
734
- if (vercelUrl) return `https://${vercelUrl}`;
735
- return void 0;
736
- }
737
- async function deliverPrivateMessage(input) {
738
- let client;
739
- try {
740
- client = getSlackClient();
741
- } catch {
742
- logWarn("oauth.private_delivery.skipped", {
743
- "app.reason": "missing_bot_token"
744
- });
745
- return false;
746
- }
747
- if (input.channelId) {
748
- try {
749
- if (isDmChannel(input.channelId)) {
750
- await postSlackMessage({
751
- channelId: input.channelId,
752
- text: input.text,
753
- threadTs: input.threadTs
754
- });
755
- } else {
756
- await postSlackEphemeralMessage({
757
- channelId: input.channelId,
758
- userId: input.userId,
759
- text: input.text,
760
- threadTs: input.threadTs
761
- });
762
- }
763
- return "in_context";
764
- } catch (error) {
765
- logWarn("oauth.private_delivery.failed", {
766
- "app.slack.error": error instanceof Error ? error.message : String(error),
767
- "app.slack.channel": input.channelId
768
- });
769
- }
770
- }
771
- try {
772
- const dmChannelId = (await withSlackRetries(
773
- () => client.conversations.open({ users: input.userId }),
774
- 3,
775
- {
776
- action: "conversations.open",
777
- spanAttributes: { "app.slack.user_id": input.userId }
778
- }
779
- )).channel?.id;
780
- if (!dmChannelId) {
781
- logWarn("oauth.dm.fallback.failed", { "app.reason": "no_dm_channel_id" });
782
- return false;
783
- }
784
- await postSlackMessage({ channelId: dmChannelId, text: input.text });
785
- return "fallback_dm";
786
- } catch (error) {
787
- logWarn("oauth.dm.fallback.failed", {
788
- "app.slack.error": error instanceof Error ? error.message : String(error)
789
- });
790
- return false;
791
- }
792
- }
793
- async function deliverOAuthAuthorization(request, input) {
794
- if (input.authorization) {
795
- await input.authorization.deliver(request);
796
- return "in_context";
797
- }
798
- return await deliverPrivateMessage({
799
- channelId: input.channelId,
800
- threadTs: input.threadTs,
801
- userId: input.userId,
802
- text: formatOAuthAuthorizationMessage(request)
803
- });
804
- }
805
- async function startOAuthFlow(provider, input) {
806
- const providerConfig = pluginCatalogRuntime.getOAuthConfig(provider);
807
- if (!providerConfig) {
808
- return {
809
- ok: false,
810
- error: `Provider "${provider}" does not support OAuth authorization`
811
- };
812
- }
813
- const clientId = process.env[providerConfig.clientIdEnv]?.trim();
814
- if (!clientId) {
815
- return {
816
- ok: false,
817
- error: `Missing ${providerConfig.clientIdEnv} environment variable`
818
- };
819
- }
820
- const baseUrl = resolveBaseUrl();
821
- if (!baseUrl) {
822
- return {
823
- ok: false,
824
- error: "Cannot determine base URL (set JUNIOR_BASE_URL or deploy to Vercel)"
825
- };
826
- }
827
- const state = input.authorization ? await input.authorization.createState() : randomBytes(32).toString("hex");
828
- const requestedScope = input.scope ?? providerConfig.scope;
829
- await getStateAdapter().set(
830
- `oauth-state:${state}`,
831
- {
832
- userId: input.actorId,
833
- provider,
834
- ...input.channelId ? { channelId: input.channelId } : {},
835
- ...input.destination ? { destination: input.destination } : {},
836
- ...input.source ? { source: input.source } : {},
837
- ...input.threadTs ? { threadTs: input.threadTs } : {},
838
- ...input.resumeConversationId ? { resumeConversationId: input.resumeConversationId } : {},
839
- ...input.resumeSessionId ? { resumeSessionId: input.resumeSessionId } : {},
840
- ...requestedScope ? { scope: requestedScope } : {}
841
- },
842
- OAUTH_STATE_TTL_MS
843
- );
844
- const authorizeParams = new URLSearchParams({
845
- client_id: clientId,
846
- state,
847
- redirect_uri: `${baseUrl}${providerConfig.callbackPath}`,
848
- response_type: "code"
849
- });
850
- if (requestedScope) {
851
- authorizeParams.set("scope", requestedScope);
852
- }
853
- for (const [key, value] of Object.entries(
854
- providerConfig.authorizeParams ?? {}
855
- )) {
856
- authorizeParams.set(key, value);
857
- }
858
- logInfo("jr_rpc.oauth.started", {
859
- "app.credential.provider": provider,
860
- ...input.activeSkillName ? { "app.skill.name": input.activeSkillName } : {}
861
- });
862
- const authorizationUrl = `${providerConfig.authorizeEndpoint}?${authorizeParams.toString()}`;
863
- const authorizationRequest = {
864
- authorizationUrl,
865
- label: `Click here to link your ${formatProviderLabel(provider)} account`,
866
- completionText: input.resumeSessionId ? "Once you've authorized, Junior will continue automatically." : "Once you've authorized, you'll see a confirmation in Slack."
867
- };
868
- return {
869
- ok: true,
870
- delivery: await deliverOAuthAuthorization(authorizationRequest, {
871
- authorization: input.authorization,
872
- channelId: input.channelId,
873
- threadTs: input.threadTs,
874
- userId: input.actorId
875
- })
876
- };
877
- }
878
-
879
139
  // src/chat/mcp/errors.ts
880
140
  var McpProviderError = class extends Error {
881
141
  phase;
@@ -951,7 +211,7 @@ function getMcpAwareTelemetryMessage(error, _privacy) {
951
211
 
952
212
  // src/chat/mcp/auth-store.ts
953
213
  import {
954
- sourceSchema as sourceSchema2
214
+ sourceSchema
955
215
  } from "@sentry/junior-plugin-api";
956
216
  var MCP_AUTH_SESSION_PREFIX = "junior:mcp_oauth_attempt:v2";
957
217
  var MCP_AUTH_CREDENTIALS_PREFIX = "junior:mcp_auth_credentials";
@@ -1004,7 +264,7 @@ function parseMcpAuthSession(value) {
1004
264
  if (parsed.destination !== void 0 && !destination) {
1005
265
  return void 0;
1006
266
  }
1007
- const source = parsed.source === void 0 ? void 0 : sourceSchema2.safeParse(parsed.source);
267
+ const source = parsed.source === void 0 ? void 0 : sourceSchema.safeParse(parsed.source);
1008
268
  if (parsed.source !== void 0 && (!source || !source.success)) {
1009
269
  return void 0;
1010
270
  }
@@ -1689,28 +949,8 @@ async function issuePluginCredential(input) {
1689
949
  }
1690
950
 
1691
951
  export {
1692
- RetryableDeliveryError,
1693
- actorFromRouting,
1694
- assertRunRoutingConsistency,
1695
- toolInvocationDestination,
1696
- surfaceFromRouting,
1697
952
  createUserTokenStore,
1698
953
  issueProviderCredentialLease,
1699
- getSlackMessagePermalink,
1700
- postSlackMessage,
1701
- uploadFilesToConversation,
1702
- addReactionToMessage,
1703
- removeReactionFromMessage,
1704
- truncateStatusText,
1705
- escapeSlackMrkdwnText,
1706
- formatSlackLink,
1707
- normalizeSlackReplyMarkdown,
1708
- normalizeSlackStatusText,
1709
- parseOAuthStatePayload,
1710
- formatProviderLabel,
1711
- resolveBaseUrl,
1712
- deliverOAuthAuthorization,
1713
- startOAuthFlow,
1714
954
  McpProviderError,
1715
955
  toMcpProviderError,
1716
956
  getMcpProviderErrorAttributes,