@sentry/junior 0.84.1 → 0.86.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 (51) hide show
  1. package/dist/{agent-hooks-TFPY6V3T.js → agent-hooks-NWOUB3UR.js} +7 -7
  2. package/dist/app.js +916 -363
  3. package/dist/chat/conversation-privacy.d.ts +12 -2
  4. package/dist/chat/conversations/sql/migrations.d.ts +1 -1
  5. package/dist/chat/conversations/sql/store.d.ts +9 -1
  6. package/dist/chat/conversations/store.d.ts +13 -0
  7. package/dist/chat/current-instruction.d.ts +6 -0
  8. package/dist/chat/ingress/message-changed.d.ts +2 -0
  9. package/dist/chat/respond-helpers.d.ts +1 -2
  10. package/dist/chat/respond.d.ts +7 -1
  11. package/dist/chat/runtime/reply-executor.d.ts +2 -10
  12. package/dist/chat/runtime/slack-runtime.d.ts +5 -4
  13. package/dist/chat/runtime/turn.d.ts +7 -0
  14. package/dist/chat/services/persist-retry.d.ts +2 -0
  15. package/dist/chat/services/turn-session-record.d.ts +31 -2
  16. package/dist/chat/slack/client.d.ts +33 -2
  17. package/dist/chat/slack/conversation-context.d.ts +15 -0
  18. package/dist/chat/state/turn-session.d.ts +9 -0
  19. package/dist/chat/task-execution/queue.d.ts +1 -1
  20. package/dist/chat/task-execution/slack-work.d.ts +10 -2
  21. package/dist/chat/task-execution/state.d.ts +44 -4
  22. package/dist/chat/task-execution/store.d.ts +21 -4
  23. package/dist/chat/task-execution/worker.d.ts +11 -4
  24. package/dist/chat/tools/slack/channel-access.d.ts +29 -0
  25. package/dist/chat/tools/slack/thread-read.d.ts +4 -1
  26. package/dist/{chunk-AL6ZFV7U.js → chunk-2NFV5FMB.js} +4 -4
  27. package/dist/{chunk-JK7376UT.js → chunk-37B2R2QJ.js} +17 -17
  28. package/dist/{chunk-QWKB6NJV.js → chunk-6I6HBOQM.js} +80 -15
  29. package/dist/{chunk-SBYMRDH7.js → chunk-6O5UI3RG.js} +1 -1
  30. package/dist/{chunk-CWMMGUWM.js → chunk-BRSQQRG6.js} +1 -1
  31. package/dist/{chunk-FPN7NYTE.js → chunk-ENPSU7L7.js} +165 -27
  32. package/dist/{chunk-T2YRJZ5A.js → chunk-FPHA6GCQ.js} +905 -711
  33. package/dist/{chunk-MJ4E2PK7.js → chunk-GB5DFM4D.js} +1 -1
  34. package/dist/{chunk-BAPRSWNW.js → chunk-GGD6WK6V.js} +124 -25
  35. package/dist/{chunk-SVFA43LT.js → chunk-JRXCSSSU.js} +19 -4
  36. package/dist/{chunk-DR75T7J3.js → chunk-L7OHKDOX.js} +9 -5
  37. package/dist/{chunk-LSG6PQF4.js → chunk-RIB3M6YA.js} +2 -2
  38. package/dist/{chunk-ALL7GG6U.js → chunk-ZU2ALUVQ.js} +45 -6
  39. package/dist/cli/chat.js +4 -4
  40. package/dist/cli/plugins.js +7 -7
  41. package/dist/cli/snapshot-warmup.js +4 -4
  42. package/dist/cli/upgrade.js +10 -7
  43. package/dist/{db-ELRCNB4A.js → db-SZVUU7RB.js} +4 -4
  44. package/dist/handlers/github-webhook/issue-comment.d.ts +3 -0
  45. package/dist/handlers/github-webhook/pull-request-review-comment.d.ts +3 -0
  46. package/dist/instrumentation.js +1 -1
  47. package/dist/nitro.js +4 -4
  48. package/dist/reporting/conversations.d.ts +2 -1
  49. package/dist/reporting.js +110 -34
  50. package/dist/{runner-2XU6QQND.js → runner-HEBRPNN2.js} +27 -13
  51. package/package.json +5 -5
@@ -0,0 +1,29 @@
1
+ import type { ConversationPrivacy } from "@/chat/conversation-privacy";
2
+ /** Minimal persisted-visibility port for cross-conversation read gates. */
3
+ export interface DestinationVisibilityReader {
4
+ getDestinationVisibility(args: {
5
+ provider: string;
6
+ providerDestinationId: string;
7
+ providerTenantId?: string;
8
+ }): Promise<ConversationPrivacy | undefined>;
9
+ }
10
+ export type SlackChannelReadAccess = {
11
+ allowed: true;
12
+ } | {
13
+ allowed: false;
14
+ error: string;
15
+ };
16
+ /**
17
+ * Decide whether the model may read Slack content from a target channel.
18
+ *
19
+ * The current conversation is always readable. Any other channel requires
20
+ * persisted `public` visibility in the same workspace: channel-id prefixes
21
+ * cannot prove a channel public (modern Slack private channels also use `C`
22
+ * ids), so missing or private destinations fail closed.
23
+ */
24
+ export declare function checkSlackChannelReadAccess(args: {
25
+ currentChannelIds: Array<string | undefined>;
26
+ store?: DestinationVisibilityReader;
27
+ targetChannelId: string;
28
+ teamId: string;
29
+ }): Promise<SlackChannelReadAccess>;
@@ -1,6 +1,9 @@
1
+ import { type DestinationVisibilityReader } from "@/chat/tools/slack/channel-access";
1
2
  import type { SlackToolContext } from "@/chat/tools/slack/context";
2
3
  /** Create a tool that reads a Slack thread from a shared message URL or explicit coordinates. */
3
- export declare function createSlackThreadReadTool(context: SlackToolContext): import("@/chat/tools/definition").ToolDefinition<import("@sinclair/typebox").TObject<{
4
+ export declare function createSlackThreadReadTool(context: SlackToolContext, deps?: {
5
+ visibilityStore?: DestinationVisibilityReader;
6
+ }): import("@/chat/tools/definition").ToolDefinition<import("@sinclair/typebox").TObject<{
4
7
  url: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
5
8
  channel_id: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
6
9
  ts: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  createPluginLogger,
3
3
  createPluginState
4
- } from "./chunk-CWMMGUWM.js";
4
+ } from "./chunk-BRSQQRG6.js";
5
5
  import {
6
6
  getDb
7
- } from "./chunk-QWKB6NJV.js";
7
+ } from "./chunk-6I6HBOQM.js";
8
8
  import {
9
9
  SANDBOX_WORKSPACE_ROOT
10
10
  } from "./chunk-G3E7SCME.js";
@@ -13,12 +13,12 @@ import {
13
13
  isConversationScopedChannel,
14
14
  isDmChannel,
15
15
  normalizeSlackConversationId
16
- } from "./chunk-BAPRSWNW.js";
16
+ } from "./chunk-GGD6WK6V.js";
17
17
  import {
18
18
  botConfig,
19
19
  completeObject,
20
20
  embedTexts
21
- } from "./chunk-ALL7GG6U.js";
21
+ } from "./chunk-ZU2ALUVQ.js";
22
22
  import {
23
23
  isActorUserId,
24
24
  parseActorUserId
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getConversationStore
3
- } from "./chunk-QWKB6NJV.js";
3
+ } from "./chunk-6I6HBOQM.js";
4
4
  import {
5
5
  SANDBOX_DATA_ROOT,
6
6
  SANDBOX_WORKSPACE_ROOT,
@@ -9,15 +9,16 @@ import {
9
9
  import {
10
10
  getConnectedStateContext,
11
11
  getStateAdapter
12
- } from "./chunk-MJ4E2PK7.js";
12
+ } from "./chunk-GB5DFM4D.js";
13
13
  import {
14
14
  parseDestination
15
- } from "./chunk-BAPRSWNW.js";
15
+ } from "./chunk-GGD6WK6V.js";
16
16
  import {
17
17
  TURN_CONTEXT_TAG,
18
18
  botConfig,
19
+ escapeXml,
19
20
  getChatConfig
20
- } from "./chunk-ALL7GG6U.js";
21
+ } from "./chunk-ZU2ALUVQ.js";
21
22
  import {
22
23
  parseRequester,
23
24
  storedSlackRequesterSchema,
@@ -658,21 +659,16 @@ async function commitMessages(args) {
658
659
  };
659
660
  }
660
661
 
661
- // src/chat/xml.ts
662
- function escapeXml(value) {
663
- return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
664
- }
665
-
666
- // src/chat/prompt.ts
667
- import fs from "fs";
668
- import path from "path";
669
-
670
662
  // src/chat/interruption-marker.ts
671
663
  var INTERRUPTED_MARKER = "\n\n[Response interrupted before completion]";
672
664
  function getInterruptionMarker() {
673
665
  return INTERRUPTED_MARKER;
674
666
  }
675
667
 
668
+ // src/chat/prompt.ts
669
+ import fs from "fs";
670
+ import path from "path";
671
+
676
672
  // src/chat/slack/status-format.ts
677
673
  var SLACK_STATUS_MAX_LENGTH = 50;
678
674
  function truncateStatusText(text) {
@@ -1651,7 +1647,7 @@ function buildTurnContextPrompt(params) {
1651
1647
  const sections = [
1652
1648
  `<${TURN_CONTEXT_TAG}>`,
1653
1649
  TURN_CONTEXT_HEADER,
1654
- "The current user instruction appears after this block in the same message.",
1650
+ "The current user instruction appears after this block in `<current-instruction>` in the same message.",
1655
1651
  ...runtimeSections,
1656
1652
  `</${TURN_CONTEXT_TAG}>`
1657
1653
  ].filter((section) => Boolean(section));
@@ -1851,7 +1847,8 @@ async function recordConversationActivityMetadata(args) {
1851
1847
  destination: args.summary.destination,
1852
1848
  nowMs: args.nowMs,
1853
1849
  requester: sessionLogRequester(args.summary.requester),
1854
- source
1850
+ source,
1851
+ visibility: args.destinationVisibility
1855
1852
  });
1856
1853
  await conversationStore.recordExecution({
1857
1854
  channelName: args.summary.channelName,
@@ -1862,7 +1859,8 @@ async function recordConversationActivityMetadata(args) {
1862
1859
  lastActivityAtMs: args.summary.updatedAtMs,
1863
1860
  requester: sessionLogRequester(args.summary.requester),
1864
1861
  source,
1865
- updatedAtMs: args.nowMs
1862
+ updatedAtMs: args.nowMs,
1863
+ visibility: args.destinationVisibility
1866
1864
  });
1867
1865
  } catch (error) {
1868
1866
  logWarn(
@@ -2002,6 +2000,7 @@ async function setStoredRecord(args) {
2002
2000
  await appendAgentTurnSessionSummary(summary, args.ttlMs);
2003
2001
  await recordConversationActivityMetadata({
2004
2002
  conversationStore: args.conversationStore,
2003
+ destinationVisibility: args.destinationVisibility,
2005
2004
  nowMs: Date.now(),
2006
2005
  summary
2007
2006
  });
@@ -2058,6 +2057,7 @@ async function upsertAgentTurnSessionRecord(args) {
2058
2057
  });
2059
2058
  return await setStoredRecord({
2060
2059
  conversationStore: args.conversationStore,
2060
+ destinationVisibility: args.destinationVisibility,
2061
2061
  piMessages: args.piMessages,
2062
2062
  ttlMs,
2063
2063
  record: buildStoredRecord({
@@ -2122,6 +2122,7 @@ async function recordAgentTurnSessionSummary(args) {
2122
2122
  await appendAgentTurnSessionSummary(summary, ttlMs);
2123
2123
  await recordConversationActivityMetadata({
2124
2124
  conversationStore: args.conversationStore,
2125
+ destinationVisibility: args.destinationVisibility,
2125
2126
  nowMs,
2126
2127
  summary
2127
2128
  });
@@ -2189,7 +2190,6 @@ export {
2189
2190
  normalizeSlackStatusText,
2190
2191
  splitSlackReplyText,
2191
2192
  buildSlackOutputMessage,
2192
- escapeXml,
2193
2193
  JUNIOR_PERSONALITY,
2194
2194
  buildPluginSystemPromptContributions,
2195
2195
  buildSystemPrompt,
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  parseDestination,
3
3
  sameDestination
4
- } from "./chunk-BAPRSWNW.js";
4
+ } from "./chunk-GGD6WK6V.js";
5
5
  import {
6
6
  getChatConfig
7
- } from "./chunk-ALL7GG6U.js";
7
+ } from "./chunk-ZU2ALUVQ.js";
8
8
  import {
9
9
  parseStoredSlackRequester
10
10
  } from "./chunk-IXTBFABZ.js";
11
11
 
12
12
  // src/chat/conversations/sql/store.ts
13
13
  import { randomUUID } from "crypto";
14
- import { asc, desc, eq, sql as sql2 } from "drizzle-orm";
14
+ import { and, asc, desc, eq, sql as sql2 } from "drizzle-orm";
15
15
 
16
16
  // src/chat/conversations/sql/migrations.ts
17
17
  import { createHash } from "crypto";
@@ -298,8 +298,20 @@ CREATE INDEX IF NOT EXISTS junior_conversations_origin_idx
298
298
  ON junior_conversations (origin_type, origin_id, last_activity_at DESC)
299
299
  `
300
300
  ];
301
+ var destinationVisibilityBackfillStatements = [
302
+ `
303
+ UPDATE junior_destinations
304
+ SET visibility = 'private'
305
+ WHERE provider = 'slack'
306
+ AND visibility = 'public'
307
+ `
308
+ ];
301
309
  var migrations = [
302
- defineMigration("0001_conversation_core", coreMetadataStatements)
310
+ defineMigration("0001_conversation_core", coreMetadataStatements),
311
+ defineMigration(
312
+ "0002_slack_destination_visibility_backfill",
313
+ destinationVisibilityBackfillStatements
314
+ )
303
315
  ];
304
316
  function parseStoredMigrationRecord(value) {
305
317
  return migrationRecordSchema.parse(value);
@@ -436,13 +448,13 @@ function destinationUpsertFromDestination(args) {
436
448
  if (destination.platform === "slack") {
437
449
  const channelId = destination.channelId;
438
450
  const channelKind = channelId.startsWith("D") ? "dm" : channelId.startsWith("G") ? "group" : "channel";
439
- const visibility = channelId.startsWith("D") ? "direct" : channelId.startsWith("G") ? "private" : "public";
440
451
  return {
441
452
  kind: channelKind,
442
453
  provider: "slack",
443
454
  providerTenantId: destination.teamId,
444
455
  providerDestinationId: channelId,
445
- visibility,
456
+ refreshVisibility: args.visibility !== void 0,
457
+ visibility: args.visibility ?? "private",
446
458
  ...args.channelName ? { displayName: args.channelName } : {},
447
459
  metadata: { platform: "slack" }
448
460
  };
@@ -452,6 +464,7 @@ function destinationUpsertFromDestination(args) {
452
464
  provider: "local",
453
465
  providerTenantId: localWorkspaceFromConversationId(destination.conversationId) ?? localWorkspaceFromConversationId(args.conversationId ?? ""),
454
466
  providerDestinationId: destination.conversationId,
467
+ refreshVisibility: true,
455
468
  visibility: "direct",
456
469
  metadata: { platform: "local" }
457
470
  };
@@ -462,7 +475,15 @@ function executionStatusFromValue(value) {
462
475
  }
463
476
  throw new Error("Conversation record execution status is invalid");
464
477
  }
465
- function conversationFromRow(row) {
478
+ function privacyFromRow(row) {
479
+ if (row.destinationVisibility === null) {
480
+ return void 0;
481
+ }
482
+ return row.destinationVisibility === "public" ? "public" : "private";
483
+ }
484
+ function conversationFromRow(readRow) {
485
+ const row = readRow.conversation;
486
+ const visibility = privacyFromRow(readRow);
466
487
  if (row.schemaVersion !== 1) {
467
488
  throw new Error("Conversation record schema version is invalid");
468
489
  }
@@ -496,7 +517,8 @@ function conversationFromRow(row) {
496
517
  ...requester ? { requester } : {},
497
518
  ...row.channelName ? { channelName: row.channelName } : {},
498
519
  ...source ? { source } : {},
499
- ...row.title ? { title: row.title } : {}
520
+ ...row.title ? { title: row.title } : {},
521
+ ...visibility ? { visibility } : {}
500
522
  };
501
523
  }
502
524
  function emptyConversation(args) {
@@ -572,9 +594,10 @@ var SqlStore = class {
572
594
  nowMs,
573
595
  source: args.source
574
596
  });
597
+ const { visibility: _persisted, ...currentWithoutVisibility } = current2;
575
598
  await this.upsertConversation({
576
599
  conversation: {
577
- ...current2,
600
+ ...currentWithoutVisibility,
578
601
  destination: current2.destination ?? args.destination,
579
602
  source: current2.source ?? args.source,
580
603
  channelName: current2.channelName ?? args.channelName,
@@ -585,7 +608,8 @@ var SqlStore = class {
585
608
  execution: {
586
609
  ...current2.execution,
587
610
  updatedAtMs: current2.execution.updatedAtMs ?? nowMs
588
- }
611
+ },
612
+ ...args.visibility ? { visibility: args.visibility } : {}
589
613
  }
590
614
  });
591
615
  });
@@ -604,13 +628,15 @@ var SqlStore = class {
604
628
  ...args.requester ? { requester: args.requester } : {},
605
629
  ...args.source ? { source: args.source } : {},
606
630
  ...args.title ? { title: args.title } : {},
631
+ ...args.visibility ? { visibility: args.visibility } : {},
607
632
  execution: args.execution
608
633
  }
609
634
  });
610
635
  });
611
636
  }
612
637
  /** Copy one conversation record into SQL during backfill. */
613
- async backfillConversation(conversation) {
638
+ async backfillConversation(sourceConversation) {
639
+ const { visibility: _visibility, ...conversation } = sourceConversation;
614
640
  await this.withConversationMutation(
615
641
  conversation.conversationId,
616
642
  async () => {
@@ -646,7 +672,13 @@ var SqlStore = class {
646
672
  );
647
673
  }
648
674
  async listByActivity(args = {}) {
649
- const rows = await this.executor.db().select().from(juniorConversations).orderBy(
675
+ const rows = await this.executor.db().select({
676
+ conversation: juniorConversations,
677
+ destinationVisibility: juniorDestinations.visibility
678
+ }).from(juniorConversations).leftJoin(
679
+ juniorDestinations,
680
+ eq(juniorDestinations.id, juniorConversations.destinationId)
681
+ ).orderBy(
650
682
  desc(juniorConversations.lastActivityAt),
651
683
  asc(juniorConversations.conversationId)
652
684
  ).limit(Math.max(0, args.limit ?? 1e4)).offset(Math.max(0, args.offset ?? 0));
@@ -656,6 +688,28 @@ var SqlStore = class {
656
688
  }
657
689
  return conversations;
658
690
  }
691
+ async getDestinationVisibility(args) {
692
+ const rows = await this.executor.db().select({
693
+ visibility: juniorDestinations.visibility
694
+ }).from(juniorDestinations).where(
695
+ and(
696
+ eq(juniorDestinations.provider, args.provider),
697
+ eq(
698
+ juniorDestinations.providerTenantId,
699
+ tenantId(args.providerTenantId)
700
+ ),
701
+ eq(
702
+ juniorDestinations.providerDestinationId,
703
+ args.providerDestinationId
704
+ )
705
+ )
706
+ );
707
+ const row = rows[0];
708
+ if (!row) {
709
+ return void 0;
710
+ }
711
+ return row.visibility === "public" ? "public" : "private";
712
+ }
659
713
  /** Serialize all durable mutations for one conversation inside a SQL transaction. */
660
714
  async withConversationMutation(conversationId, callback) {
661
715
  return await this.executor.withLock(
@@ -664,7 +718,13 @@ var SqlStore = class {
664
718
  );
665
719
  }
666
720
  async readConversationRow(conversationId) {
667
- const rows = await this.executor.db().select().from(juniorConversations).where(eq(juniorConversations.conversationId, conversationId));
721
+ const rows = await this.executor.db().select({
722
+ conversation: juniorConversations,
723
+ destinationVisibility: juniorDestinations.visibility
724
+ }).from(juniorConversations).leftJoin(
725
+ juniorDestinations,
726
+ eq(juniorDestinations.id, juniorConversations.destinationId)
727
+ ).where(eq(juniorConversations.conversationId, conversationId));
668
728
  return rows[0];
669
729
  }
670
730
  /** Upsert the conversation row while preserving previously discovered nullable metadata fields. */
@@ -677,7 +737,8 @@ var SqlStore = class {
677
737
  destinationUpsertFromDestination({
678
738
  channelName: conversation.channelName,
679
739
  conversationId: conversation.conversationId,
680
- destination: conversation.destination
740
+ destination: conversation.destination,
741
+ ...conversation.visibility ? { visibility: conversation.visibility } : {}
681
742
  }),
682
743
  conversation.updatedAtMs
683
744
  );
@@ -779,6 +840,7 @@ var SqlStore = class {
779
840
  if (!destination) {
780
841
  return void 0;
781
842
  }
843
+ const visibilityUpdate = destination.refreshVisibility ? sql2`excluded.visibility` : juniorDestinations.visibility;
782
844
  const rows = await this.executor.db().insert(juniorDestinations).values({
783
845
  id: randomUUID(),
784
846
  provider: destination.provider,
@@ -800,7 +862,10 @@ var SqlStore = class {
800
862
  set: {
801
863
  kind: sql2`excluded.kind`,
802
864
  displayName: sql2`coalesce(excluded.display_name, ${juniorDestinations.displayName})`,
803
- visibility: sql2`excluded.visibility`,
865
+ // Signal-less writes insert as private but must not clobber an
866
+ // existing public/private value. Live source signals refresh this
867
+ // field so converted channels converge on the next message.
868
+ visibility: visibilityUpdate,
804
869
  metadata: sql2`coalesce(excluded.metadata_json, ${juniorDestinations.metadata})`,
805
870
  updatedAt: sql2`excluded.updated_at`
806
871
  }
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  destinationKey,
6
6
  parseDestination
7
- } from "./chunk-BAPRSWNW.js";
7
+ } from "./chunk-GGD6WK6V.js";
8
8
 
9
9
  // src/chat/task-execution/queue-signing.ts
10
10
  import { createHmac, timingSafeEqual } from "crypto";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getStateAdapter
3
- } from "./chunk-MJ4E2PK7.js";
3
+ } from "./chunk-GB5DFM4D.js";
4
4
  import {
5
5
  logException,
6
6
  logInfo,