@sentry/junior-github 0.174.0 → 0.175.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.
@@ -98,10 +98,14 @@ function gitHubPullRequestResource(input) {
98
98
  }
99
99
  function gitHubPullRequestSubscribable(input) {
100
100
  if (!process.env.GITHUB_WEBHOOK_SECRET?.trim()) return void 0;
101
+ const omitted = new Set(input.omitSuggestedEvents ?? []);
102
+ const suggestedEvents = GITHUB_PULL_REQUEST_SUGGESTED_EVENTS.filter(
103
+ (eventType) => !omitted.has(eventType)
104
+ );
101
105
  return {
102
106
  ...gitHubPullRequestResource(input),
103
- suggestedEvents: GITHUB_PULL_REQUEST_SUGGESTED_EVENTS,
104
- supportedEvents: GITHUB_PULL_REQUEST_EVENTS,
107
+ ...suggestedEvents.length > 0 ? { suggestedEvents: [...suggestedEvents] } : {},
108
+ supportedEvents: [...GITHUB_PULL_REQUEST_EVENTS],
105
109
  type: "pull_request"
106
110
  };
107
111
  }
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  normalizeGitHubResourceEvents,
16
16
  parseCheckSuiteEnrichmentTarget,
17
17
  selectFailingChecks
18
- } from "./chunk-IG5VPAKL.js";
18
+ } from "./chunk-7CREKH4I.js";
19
19
 
20
20
  // src/plugin.ts
21
21
  import {
@@ -1623,12 +1623,13 @@ import {
1623
1623
  definePluginTool as definePluginTool4,
1624
1624
  EgressAuthRequired as EgressAuthRequired2,
1625
1625
  PluginToolInputError as PluginToolInputError5,
1626
- pluginToolOutputSchema as pluginToolOutputSchema4
1626
+ pluginToolOutputSchema as pluginToolOutputSchema4,
1627
+ resourceEventSubscriptionResultSchema,
1628
+ subscribableResourceSchema as subscribableResourceSchema3
1627
1629
  } from "@sentry/junior-plugin-api";
1628
1630
  import { Type as Type2 } from "@sinclair/typebox";
1629
1631
  import { Value as Value2 } from "@sinclair/typebox/value";
1630
1632
  import { z as z4 } from "zod";
1631
- import { subscribableResourceSchema as subscribableResourceSchema3 } from "@sentry/junior-plugin-api";
1632
1633
  var GITHUB_PULL_REQUEST_CREATE_IDEMPOTENCY_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
1633
1634
  var GITHUB_PULL_REQUEST_CREATE_LOCK_TTL_MS = 6e4;
1634
1635
  var GitHubPullRequestCreateRejectedError = class extends Error {
@@ -1697,7 +1698,8 @@ var createPullRequestStateSchema = Type2.Union([
1697
1698
  var gitHubPullRequestDataSchema = z4.object({
1698
1699
  number: z4.number(),
1699
1700
  url: z4.string(),
1700
- subscribable: subscribableResourceSchema3.optional()
1701
+ subscribable: subscribableResourceSchema3.optional(),
1702
+ subscription: resourceEventSubscriptionResultSchema.optional()
1701
1703
  });
1702
1704
  var gitHubPullRequestOutputSchema = pluginToolOutputSchema4.extend({
1703
1705
  target: z4.literal("createPullRequest"),
@@ -1850,11 +1852,12 @@ async function createGitHubPullRequest(ctx, request) {
1850
1852
  url: pullRequest.html_url
1851
1853
  };
1852
1854
  }
1853
- function gitHubPullRequestToolResult(input, result, canSubscribe) {
1855
+ function gitHubPullRequestToolResult(input, result, canSubscribe, omitSuggestedEvents) {
1854
1856
  const repo = parseRepo4(input.repo);
1855
1857
  const subscribable = canSubscribe ? gitHubPullRequestSubscribable({
1856
1858
  number: result.number,
1857
- repo: `${repo.owner}/${repo.name}`
1859
+ repo: `${repo.owner}/${repo.name}`,
1860
+ ...omitSuggestedEvents ? { omitSuggestedEvents } : {}
1858
1861
  }) : void 0;
1859
1862
  return { ...result, ...subscribable ? { subscribable } : {} };
1860
1863
  }
@@ -1868,14 +1871,48 @@ async function annotatePullRequest(ctx, input, result) {
1868
1871
  status: input.draft ? "draft" : "open"
1869
1872
  });
1870
1873
  }
1871
- function gitHubPullRequestStructuredResult(input, result, canSubscribe) {
1872
- const data = gitHubPullRequestToolResult(input, result, canSubscribe);
1873
- return {
1874
- target: "createPullRequest",
1875
- ...data
1876
- };
1874
+ async function gitHubPullRequestStructuredResult(ctx, input, result, subscriptionConfig) {
1875
+ const base = gitHubPullRequestToolResult(
1876
+ input,
1877
+ result,
1878
+ ctx.resourceEvents.canSubscribe
1879
+ );
1880
+ if (!subscriptionConfig || !base.subscribable) {
1881
+ return {
1882
+ target: "createPullRequest",
1883
+ ...base
1884
+ };
1885
+ }
1886
+ try {
1887
+ const subscription = await ctx.resourceEvents.subscribe({
1888
+ events: subscriptionConfig.events,
1889
+ intent: subscriptionConfig.intent,
1890
+ resource: base.subscribable
1891
+ });
1892
+ const data = gitHubPullRequestToolResult(
1893
+ input,
1894
+ result,
1895
+ ctx.resourceEvents.canSubscribe,
1896
+ subscriptionConfig.events
1897
+ );
1898
+ return {
1899
+ target: "createPullRequest",
1900
+ ...data,
1901
+ subscription
1902
+ };
1903
+ } catch (error) {
1904
+ ctx.log.warn("github.pull_request.subscribe_after_create.failed", {
1905
+ error: error instanceof Error ? error.message : String(error),
1906
+ number: result.number,
1907
+ repo: input.repo
1908
+ });
1909
+ return {
1910
+ target: "createPullRequest",
1911
+ ...base
1912
+ };
1913
+ }
1877
1914
  }
1878
- function createGitHubPullRequestTool(ctx) {
1915
+ function createGitHubPullRequestTool(ctx, subscriptionConfig) {
1879
1916
  return definePluginTool4({
1880
1917
  annotations: {
1881
1918
  destructiveHint: false,
@@ -1906,10 +1943,11 @@ function createGitHubPullRequestTool(ctx) {
1906
1943
  url: state.url
1907
1944
  };
1908
1945
  await annotatePullRequest(ctx, completedInput, completedResult);
1909
- return gitHubPullRequestStructuredResult(
1946
+ return await gitHubPullRequestStructuredResult(
1947
+ ctx,
1910
1948
  completedInput,
1911
1949
  completedResult,
1912
- ctx.resourceEvents.canSubscribe
1950
+ subscriptionConfig
1913
1951
  );
1914
1952
  }
1915
1953
  if (state?.status === "pending") {
@@ -1949,10 +1987,11 @@ function createGitHubPullRequestTool(ctx) {
1949
1987
  );
1950
1988
  }
1951
1989
  await annotatePullRequest(ctx, parsedInput, result);
1952
- return gitHubPullRequestStructuredResult(
1990
+ return await gitHubPullRequestStructuredResult(
1991
+ ctx,
1953
1992
  parsedInput,
1954
1993
  result,
1955
- ctx.resourceEvents.canSubscribe
1994
+ subscriptionConfig
1956
1995
  );
1957
1996
  } catch (error) {
1958
1997
  if (isEgressAuthRequired2(error) || isDefinitiveGitHubPullRequestCreateRejection(error)) {
@@ -2628,17 +2667,20 @@ import {
2628
2667
  import { z as z10 } from "zod";
2629
2668
 
2630
2669
  // src/webhooks/ownership.ts
2631
- var GITHUB_NOREPLY_DOMAIN = "users.noreply.github.com";
2670
+ var GITHUB_BOT_NOREPLY_EMAIL = /^(?<userId>[1-9]\d*)\+(?<login>[^@]+\[bot\])@users\.noreply\.github\.com$/i;
2671
+ function parseGitHubBotIdentity(value) {
2672
+ const match = GITHUB_BOT_NOREPLY_EMAIL.exec(value?.trim() ?? "");
2673
+ if (!match?.groups) return void 0;
2674
+ return {
2675
+ login: match.groups.login,
2676
+ userId: Number(match.groups.userId)
2677
+ };
2678
+ }
2632
2679
  function botLoginFromEmail(value) {
2633
- const email = value?.trim();
2634
- if (!email) return void 0;
2635
- const separator = email.lastIndexOf("@");
2636
- if (separator <= 0) return void 0;
2637
- const domain = email.slice(separator + 1).toLowerCase();
2638
- if (domain !== GITHUB_NOREPLY_DOMAIN) return void 0;
2639
- const localPart = email.slice(0, separator);
2640
- const login = localPart.slice(localPart.indexOf("+") + 1).trim();
2641
- return login.toLowerCase().endsWith("[bot]") ? login : void 0;
2680
+ return parseGitHubBotIdentity(value)?.login;
2681
+ }
2682
+ function botUserIdFromEmail(value) {
2683
+ return parseGitHubBotIdentity(value)?.userId;
2642
2684
  }
2643
2685
 
2644
2686
  // src/tools/resolve-pull-request-review-thread.ts
@@ -2700,8 +2742,8 @@ function createGitHubResolvePullRequestReviewThreadTool(ctx, botEmail) {
2700
2742
  );
2701
2743
  }
2702
2744
  const repo = parseRepo10(parsedInput.data.repo);
2703
- const botLogin = botLoginFromEmail(botEmail)?.toLowerCase();
2704
- if (!botLogin) {
2745
+ const botUserId = botUserIdFromEmail(botEmail);
2746
+ if (botUserId === void 0) {
2705
2747
  throw new Error("GitHub App bot identity is not configured.");
2706
2748
  }
2707
2749
  const query = `query ReviewThreadOwnership($threadId: ID!) {
@@ -2712,7 +2754,7 @@ function createGitHubResolvePullRequestReviewThreadTool(ctx, botEmail) {
2712
2754
  pullRequest {
2713
2755
  number
2714
2756
  repository { nameWithOwner }
2715
- author { login }
2757
+ author { ... on Bot { databaseId } }
2716
2758
  }
2717
2759
  }
2718
2760
  }
@@ -2742,7 +2784,7 @@ function createGitHubResolvePullRequestReviewThreadTool(ctx, botEmail) {
2742
2784
  id: z10.string(),
2743
2785
  isResolved: z10.boolean(),
2744
2786
  pullRequest: z10.object({
2745
- author: z10.object({ login: z10.string() }),
2787
+ author: z10.object({ databaseId: z10.number().optional() }).nullable(),
2746
2788
  number: z10.number(),
2747
2789
  repository: z10.object({ nameWithOwner: z10.string() })
2748
2790
  })
@@ -2753,7 +2795,7 @@ function createGitHubResolvePullRequestReviewThreadTool(ctx, botEmail) {
2753
2795
  throw new PluginToolInputError11("GitHub review thread was not found.");
2754
2796
  }
2755
2797
  const pullRequest = thread.pullRequest;
2756
- const ownsPullRequest = pullRequest.repository.nameWithOwner.toLowerCase() === repo.ref.toLowerCase() && pullRequest.author.login.toLowerCase() === botLogin;
2798
+ const ownsPullRequest = pullRequest.repository.nameWithOwner.toLowerCase() === repo.ref.toLowerCase() && pullRequest.author?.databaseId === botUserId;
2757
2799
  if (!ownsPullRequest) {
2758
2800
  throw new PluginToolInputError11(
2759
2801
  "Junior can only resolve review threads on pull requests it authored."
@@ -2814,11 +2856,14 @@ function createGitHubResolvePullRequestReviewThreadTool(ctx, botEmail) {
2814
2856
  }
2815
2857
 
2816
2858
  // src/tools.ts
2817
- function createGitHubTools(ctx, botEmail) {
2859
+ function createGitHubTools(ctx, botEmail, pullRequestSubscription) {
2818
2860
  return {
2819
2861
  cloneRepository: createGitHubCloneRepositoryTool(ctx),
2820
2862
  createIssue: createGitHubIssueTool(ctx),
2821
- createPullRequest: createGitHubPullRequestTool(ctx),
2863
+ createPullRequest: createGitHubPullRequestTool(
2864
+ ctx,
2865
+ pullRequestSubscription
2866
+ ),
2822
2867
  getDeployment: createGitHubGetDeploymentTool(ctx),
2823
2868
  getPullRequest: createGitHubGetPullRequestTool(ctx),
2824
2869
  getRelease: createGitHubGetReleaseTool(ctx),
@@ -5629,7 +5674,8 @@ function githubPlugin(options = {}) {
5629
5674
  {
5630
5675
  type: "pull_request",
5631
5676
  supportedEvents: [...GITHUB_PULL_REQUEST_EVENTS],
5632
- suggestedEvents: [...GITHUB_PULL_REQUEST_SUGGESTED_EVENTS]
5677
+ suggestedEvents: [...GITHUB_PULL_REQUEST_SUGGESTED_EVENTS],
5678
+ ...options.pullRequestEvents?.guidance ? { guidance: options.pullRequestEvents.guidance } : {}
5633
5679
  },
5634
5680
  {
5635
5681
  type: "release_source",
@@ -5792,7 +5838,11 @@ function githubPlugin(options = {}) {
5792
5838
  });
5793
5839
  },
5794
5840
  tools(ctx) {
5795
- return createGitHubTools(ctx, readEnv(botEmailEnv));
5841
+ return createGitHubTools(
5842
+ ctx,
5843
+ readEnv(botEmailEnv),
5844
+ options.pullRequestEvents?.subscribeAfterCreate
5845
+ );
5796
5846
  },
5797
5847
  workspacePrepare: prepareWorkspace,
5798
5848
  async sandboxPrepare(ctx) {
package/dist/plugin.d.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { type PluginRegistration } from "@sentry/junior-plugin-api";
7
7
  import { type GitHubAppPermissions } from "./permissions.js";
8
+ import { type GitHubPullRequestEventOptions } from "./resource-events/pull-request.js";
8
9
  /** Configure the built-in GitHub plugin manifest and hooks. */
9
10
  export interface GitHubPluginOptions {
10
11
  /**
@@ -39,6 +40,8 @@ export interface GitHubPluginOptions {
39
40
  installationIdEnv?: string;
40
41
  /** Environment variable containing the GitHub App private key. */
41
42
  privateKeyEnv?: string;
43
+ /** Install-local pull request resource event behavior. */
44
+ pullRequestEvents?: GitHubPullRequestEventOptions;
42
45
  }
43
46
  /** Register GitHub runtime hooks for repository workflows. */
44
47
  export declare function githubPlugin(options?: GitHubPluginOptions): PluginRegistration;
@@ -1,6 +1,19 @@
1
1
  import type { SubscribableResource } from "@sentry/junior-plugin-api";
2
- export declare const GITHUB_PULL_REQUEST_EVENTS: string[];
3
- export declare const GITHUB_PULL_REQUEST_SUGGESTED_EVENTS: string[];
2
+ export declare const GITHUB_PULL_REQUEST_EVENTS: readonly ["pull_request.checks.failed", "pull_request.checks.recovered", "pull_request.comment.created", "pull_request.opened", "pull_request.ready_for_review", "pull_request.review.approved", "pull_request.review.changes_requested", "pull_request.review.commented", "pull_request.review_comment.created", "pull_request.merged", "pull_request.closed_unmerged"];
3
+ export type GitHubPullRequestEvent = (typeof GITHUB_PULL_REQUEST_EVENTS)[number];
4
+ /** Install-local pull request resource event behavior. */
5
+ export interface GitHubPullRequestEventOptions {
6
+ /** Guidance added when one pull request event reaches the agent. */
7
+ guidance?: Partial<Record<GitHubPullRequestEvent, string>>;
8
+ /** Temporary subscription created after Junior creates a pull request. */
9
+ subscribeAfterCreate?: GitHubPullRequestSubscriptionConfig;
10
+ }
11
+ /** Temporary subscription created after Junior creates a pull request. */
12
+ export interface GitHubPullRequestSubscriptionConfig {
13
+ events: GitHubPullRequestEvent[];
14
+ intent: string;
15
+ }
16
+ export declare const GITHUB_PULL_REQUEST_SUGGESTED_EVENTS: readonly ["pull_request.checks.failed", "pull_request.comment.created", "pull_request.ready_for_review", "pull_request.review.changes_requested", "pull_request.review.commented", "pull_request.review_comment.created", "pull_request.merged", "pull_request.closed_unmerged"];
4
17
  /** Build the stable pull request identity shared by tools and webhooks. */
5
18
  export declare function gitHubPullRequestResource(input: {
6
19
  number: number;
@@ -10,4 +23,6 @@ export declare function gitHubPullRequestResource(input: {
10
23
  export declare function gitHubPullRequestSubscribable(input: {
11
24
  number: number;
12
25
  repo: string;
26
+ /** Events already covered by a forced subscription; omit them from suggestions. */
27
+ omitSuggestedEvents?: readonly string[];
13
28
  }): SubscribableResource | undefined;
package/dist/testing.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  normalizeGitHubResourceEvents
3
- } from "./chunk-IG5VPAKL.js";
3
+ } from "./chunk-7CREKH4I.js";
4
4
  export {
5
5
  normalizeGitHubResourceEvents
6
6
  };
@@ -1,6 +1,7 @@
1
1
  import { type ToolRegistrationHookContext } from "@sentry/junior-plugin-api";
2
+ import { type GitHubPullRequestSubscriptionConfig } from "../resource-events/pull-request.js";
2
3
  /** Own PR creation so provider writes use host egress and the footer stays deterministic. */
3
- export declare function createGitHubPullRequestTool(ctx: ToolRegistrationHookContext): import("@sentry/junior-plugin-api").PluginToolDefinition<{
4
+ export declare function createGitHubPullRequestTool(ctx: ToolRegistrationHookContext, subscriptionConfig?: GitHubPullRequestSubscriptionConfig): import("@sentry/junior-plugin-api").PluginToolDefinition<{
4
5
  repo: string;
5
6
  title: string;
6
7
  head: string;
@@ -25,6 +26,10 @@ export declare function createGitHubPullRequestTool(ctx: ToolRegistrationHookCon
25
26
  type: string;
26
27
  suggestedEvents?: string[] | undefined;
27
28
  } | undefined;
29
+ subscription?: {
30
+ events: string[];
31
+ id: string;
32
+ } | undefined;
28
33
  }, {
29
34
  [x: string]: unknown;
30
35
  number: number;
@@ -43,4 +48,8 @@ export declare function createGitHubPullRequestTool(ctx: ToolRegistrationHookCon
43
48
  type: string;
44
49
  suggestedEvents?: string[] | undefined;
45
50
  } | undefined;
51
+ subscription?: {
52
+ events: string[];
53
+ id: string;
54
+ } | undefined;
46
55
  }>;
package/dist/tools.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  import type { PluginToolDefinition, ToolRegistrationHookContext } from "@sentry/junior-plugin-api";
2
+ import type { GitHubPullRequestSubscriptionConfig } from "./resource-events/pull-request.js";
2
3
  /** Build the GitHub plugin's runtime tools from their per-tool modules. */
3
- export declare function createGitHubTools(ctx: ToolRegistrationHookContext, botEmail?: string): Record<string, PluginToolDefinition>;
4
+ export declare function createGitHubTools(ctx: ToolRegistrationHookContext, botEmail?: string, pullRequestSubscription?: GitHubPullRequestSubscriptionConfig): Record<string, PluginToolDefinition>;
@@ -1,2 +1,4 @@
1
1
  /** Derive the provider login encoded in a standard GitHub noreply address. */
2
2
  export declare function botLoginFromEmail(value: string | undefined): string | undefined;
3
+ /** Derive the GitHub user id encoded in a standard GitHub noreply address. */
4
+ export declare function botUserIdFromEmail(value: string | undefined): number | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior-github",
3
- "version": "0.174.0",
3
+ "version": "0.175.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -31,7 +31,7 @@
31
31
  "@sinclair/typebox": "^0.34.49",
32
32
  "drizzle-orm": "^0.45.2",
33
33
  "zod": "^4.4.3",
34
- "@sentry/junior-plugin-api": "0.174.0"
34
+ "@sentry/junior-plugin-api": "0.175.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/node": "^25.9.1",