@cursor/july 0.1.75 → 0.1.76

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.
@@ -14,10 +14,22 @@
14
14
  * project's own node_modules; `fallbackAliases` only apply when that
15
15
  * fails (npx/global installs, projects without a local dependency), so a
16
16
  * real install in the user's project keeps precedence.
17
+ *
18
+ * A third case is first-party factory agents that relative-import this
19
+ * package's `src/` from a monorepo checkout. Hosted engines `npm install`
20
+ * only the agent directory, so files under `packages/agent-serve/src` have
21
+ * no `node_modules`. Bare specifiers from that tree (`zod`, octokit, …)
22
+ * retry against the running `@cursor/july` package.json, which does.
17
23
  */
18
24
  export interface AuthoredAliasHookData {
19
25
  directAliases: Record<string, string>;
20
26
  fallbackAliases: Record<string, string>;
27
+ /**
28
+ * `file:` URL of the running `@cursor/july` package.json. Bare imports
29
+ * from monorepo `packages/agent-serve/` source retry against this parent
30
+ * when the source tree has no `node_modules` (hosted factory engines).
31
+ */
32
+ sourceTreeFallbackParentURL?: string;
21
33
  }
22
34
  export declare function initialize(data: AuthoredAliasHookData): void;
23
35
  interface ResolveResult {
@@ -1 +1 @@
1
- {"version":3,"file":"authored-alias-hooks.d.ts","sourceRoot":"","sources":["../../src/internal/authored-alias-hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC;AAQD,wBAAgB,UAAU,CAAC,IAAI,EAAE,qBAAqB,GAAG,IAAI,CAG5D;AAED,UAAU,aAAa;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,KAAK,WAAW,GAAG,CACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,KACb,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;AAE5C,wBAAgB,OAAO,CACrB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,WAAW,GACvB,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAcxC"}
1
+ {"version":3,"file":"authored-alias-hooks.d.ts","sourceRoot":"","sources":["../../src/internal/authored-alias-hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAQH,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC;;;;OAIG;IACH,2BAA2B,CAAC,EAAE,MAAM,CAAC;CACtC;AAQD,wBAAgB,UAAU,CAAC,IAAI,EAAE,qBAAqB,GAAG,IAAI,CAM5D;AAED,UAAU,aAAa;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,KAAK,WAAW,GAAG,CACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,KACb,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;AAE5C,wBAAgB,OAAO,CACrB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,WAAW,GACvB,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAwBxC"}
@@ -14,17 +14,27 @@
14
14
  * project's own node_modules; `fallbackAliases` only apply when that
15
15
  * fails (npx/global installs, projects without a local dependency), so a
16
16
  * real install in the user's project keeps precedence.
17
+ *
18
+ * A third case is first-party factory agents that relative-import this
19
+ * package's `src/` from a monorepo checkout. Hosted engines `npm install`
20
+ * only the agent directory, so files under `packages/agent-serve/src` have
21
+ * no `node_modules`. Bare specifiers from that tree (`zod`, octokit, …)
22
+ * retry against the running `@cursor/july` package.json, which does.
17
23
  */
18
24
  // Null-prototype copies so specifiers like "constructor" or "__proto__"
19
25
  // can never resolve to inherited Object.prototype members.
20
26
  let directAliases = toNullProtoMap({});
21
27
  let fallbackAliases = toNullProtoMap({});
28
+ let sourceTreeFallbackParentURL;
22
29
  function toNullProtoMap(source) {
23
30
  return Object.assign(Object.create(null), source);
24
31
  }
25
32
  export function initialize(data) {
26
33
  directAliases = toNullProtoMap(data.directAliases);
27
34
  fallbackAliases = toNullProtoMap(data.fallbackAliases);
35
+ const parent = data.sourceTreeFallbackParentURL;
36
+ sourceTreeFallbackParentURL =
37
+ typeof parent === "string" && parent !== "" ? parent : undefined;
28
38
  }
29
39
  export function resolve(specifier, context, nextResolve) {
30
40
  const direct = directAliases[specifier];
@@ -32,12 +42,54 @@ export function resolve(specifier, context, nextResolve) {
32
42
  return { url: direct, shortCircuit: true };
33
43
  }
34
44
  const fallback = fallbackAliases[specifier];
35
- if (fallback === undefined) {
36
- // Not ours: pass through with no promise/try-catch overhead. This hook
37
- // runs on every module resolution in the process.
38
- return nextResolve(specifier, context);
39
- }
40
- return Promise.resolve()
41
- .then(() => nextResolve(specifier, context))
42
- .catch(() => ({ url: fallback, shortCircuit: true }));
45
+ if (fallback !== undefined) {
46
+ return Promise.resolve()
47
+ .then(() => nextResolve(specifier, context))
48
+ .catch(() => ({ url: fallback, shortCircuit: true }));
49
+ }
50
+ if (sourceTreeFallbackParentURL !== undefined &&
51
+ isBarePackageSpecifier(specifier) &&
52
+ isMonorepoAgentServeSource(parentURLOf(context))) {
53
+ const retryParent = sourceTreeFallbackParentURL;
54
+ return Promise.resolve()
55
+ .then(() => nextResolve(specifier, context))
56
+ .catch(() => nextResolve(specifier, withParentURL(context, retryParent)));
57
+ }
58
+ // Not ours: pass through with no promise/try-catch overhead. This hook
59
+ // runs on every module resolution in the process.
60
+ return nextResolve(specifier, context);
61
+ }
62
+ function parentURLOf(context) {
63
+ if (context === null || typeof context !== "object") {
64
+ return undefined;
65
+ }
66
+ const parentURL = context.parentURL;
67
+ return typeof parentURL === "string" ? parentURL : undefined;
68
+ }
69
+ function withParentURL(context, parentURL) {
70
+ if (context !== null && typeof context === "object") {
71
+ return Object.assign(Object.assign({}, context), { parentURL });
72
+ }
73
+ return { parentURL };
74
+ }
75
+ function isBarePackageSpecifier(specifier) {
76
+ if (specifier.length === 0) {
77
+ return false;
78
+ }
79
+ const first = specifier[0];
80
+ if (first === "." || first === "/" || first === "\\") {
81
+ return false;
82
+ }
83
+ return !(specifier.startsWith("node:") ||
84
+ specifier.startsWith("file:") ||
85
+ specifier.startsWith("data:") ||
86
+ specifier.startsWith("http:") ||
87
+ specifier.startsWith("https:"));
88
+ }
89
+ function isMonorepoAgentServeSource(parentURL) {
90
+ if (parentURL === undefined) {
91
+ return false;
92
+ }
93
+ return (parentURL.includes("/packages/agent-serve/") &&
94
+ !parentURL.includes("/node_modules/"));
43
95
  }
@@ -11,9 +11,11 @@
11
11
  * imports of the framework resolve against the running package: the
12
12
  * historical `@anysphere/agent-serve[/x]` name always, and
13
13
  * `@cursor/july[/x]` / `@cursor/agentkit[/x]` as fallbacks when the user's
14
- * project has no local install (npx / global). Source runs (tsx from the
15
- * monorepo) skip both the host loader and tsconfig paths already cover
16
- * them.
14
+ * project has no local install (npx / global). The same hooks retry bare
15
+ * npm specifiers from monorepo `packages/agent-serve/` source against this
16
+ * package (hosted factory engines install only the agent directory).
17
+ * Source runs (tsx from the monorepo) skip both — the host loader and
18
+ * tsconfig paths already cover them.
17
19
  */
18
20
  /**
19
21
  * ESM specifier for an authored agent/eval file.
@@ -1 +1 @@
1
- {"version":3,"file":"authored-loaders.d.ts","sourceRoot":"","sources":["../../src/internal/authored-loaders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAyBH;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,GACd,MAAM,CAIR;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAclC;AAID;;;;;GAKG;AACH,wBAAgB,6BAA6B,IAAI,OAAO,CAAC,IAAI,CAAC,CAS7D"}
1
+ {"version":3,"file":"authored-loaders.d.ts","sourceRoot":"","sources":["../../src/internal/authored-loaders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAyBH;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,GACd,MAAM,CAIR;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAclC;AAID;;;;;GAKG;AACH,wBAAgB,6BAA6B,IAAI,OAAO,CAAC,IAAI,CAAC,CAS7D"}
@@ -11,9 +11,11 @@
11
11
  * imports of the framework resolve against the running package: the
12
12
  * historical `@anysphere/agent-serve[/x]` name always, and
13
13
  * `@cursor/july[/x]` / `@cursor/agentkit[/x]` as fallbacks when the user's
14
- * project has no local install (npx / global). Source runs (tsx from the
15
- * monorepo) skip both the host loader and tsconfig paths already cover
16
- * them.
14
+ * project has no local install (npx / global). The same hooks retry bare
15
+ * npm specifiers from monorepo `packages/agent-serve/` source against this
16
+ * package (hosted factory engines install only the agent directory).
17
+ * Source runs (tsx from the monorepo) skip both — the host loader and
18
+ * tsconfig paths already cover them.
17
19
  */
18
20
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
19
21
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
@@ -160,7 +162,14 @@ function registerFrameworkAliases() {
160
162
  fallbackAliases[`${AGENTKIT_PACKAGE_NAME}${suffix}`] = target;
161
163
  }
162
164
  const hooksUrl = new URL("./authored-alias-hooks.js", import.meta.url);
163
- const data = { directAliases, fallbackAliases };
165
+ const data = {
166
+ directAliases,
167
+ fallbackAliases,
168
+ // Hosted factory agents relative-import this package's src/ from a
169
+ // monorepo checkout while npm install ran only in the agent directory.
170
+ // Bare specifiers from that tree retry against this package.json.
171
+ sourceTreeFallbackParentURL: pathToFileURL(join(packageRootDir(), "package.json")).href,
172
+ };
164
173
  // `node:module`'s register() is synchronous: it blocks until the hooks
165
174
  // thread has initialized, so the aliases are active when it returns.
166
175
  registerModuleHooks(hooksUrl, { data });
@@ -42,10 +42,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
42
42
  step((generator = generator.apply(thisArg, _arguments || [])).next());
43
43
  });
44
44
  };
45
- // Node builtins only: hosted engines install the consuming agent's directory
46
- // alone and load this file as raw TypeScript source, so third-party imports
47
- // (even zod) do not resolve from here. See the tenant-mcp internals for the
48
- // same constraint.
45
+ // Hosted engines install the consuming agent's directory alone and load this
46
+ // file as raw TypeScript source. Relative and `node:` imports resolve here;
47
+ // this package's own npm dependencies retry against the running `@cursor/july`
48
+ // install (authored-alias-hooks). Do not add deps that package does not have.
49
49
  import { createHmac } from "node:crypto";
50
50
  import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
51
51
  import { join } from "node:path";
@@ -94,6 +94,38 @@ export type GetReviewCommentsArgs = {
94
94
  export type ReviewCommentReaderImplementation = {
95
95
  getReviewComments: (args: GetReviewCommentsArgs) => Promise<ExistingReviewComment[]>;
96
96
  };
97
+ export type ResolveReviewThreadsArgs = {
98
+ target: ScmReviewTarget;
99
+ /** REST comment ids whose threads should be resolved. */
100
+ commentIds: readonly number[];
101
+ signal?: AbortSignal;
102
+ };
103
+ /**
104
+ * Outcome of resolving review threads for a set of comment ids. Callers
105
+ * treat this as best-effort actuation: a failed resolve must not fail the
106
+ * review that already posted.
107
+ */
108
+ export type ResolveReviewThreadsResult = {
109
+ requested: number;
110
+ resolved: number;
111
+ alreadyResolved: number;
112
+ notFound: number;
113
+ failed: number;
114
+ detail: string;
115
+ };
116
+ /**
117
+ * Write side of thread resolution, the counterpart of
118
+ * {@link ReviewCommentReaderImplementation}. Bugbot's equivalent is
119
+ * `IGitProvider.resolveReviewThreadForComment` / `resolveThreads`.
120
+ *
121
+ * GitHub only exposes resolve via GraphQL (`resolveReviewThread`). REST
122
+ * has no equivalent. The mutation runs as whatever identity minted the
123
+ * bound GitHub client — for `githubChannel({ cursorAccount })` that is
124
+ * the Cursor installation, the same identity that posted the comments.
125
+ */
126
+ export type ReviewThreadResolverImplementation = {
127
+ resolveReviewThreadsForComments: (args: ResolveReviewThreadsArgs) => Promise<ResolveReviewThreadsResult>;
128
+ };
97
129
  /** Parse a PR URL into the SCM target to read comments from. */
98
130
  export declare function resolveScmReviewTarget(prUrl: string): ScmReviewTarget | undefined;
99
131
  /**
@@ -105,11 +137,41 @@ export declare function createReviewCommentReader(args: {
105
137
  target: ScmReviewTarget;
106
138
  github?: HostGitHubClient;
107
139
  }): ReviewCommentReaderImplementation | undefined;
140
+ /**
141
+ * Resolver for a resolved target. Returns undefined when the host cannot
142
+ * serve that provider, so callers degrade instead of throwing.
143
+ */
144
+ export declare function createReviewThreadResolver(args: {
145
+ target: ScmReviewTarget;
146
+ github?: HostGitHubClient;
147
+ }): ReviewThreadResolverImplementation | undefined;
148
+ /**
149
+ * Resolve the review threads that own `commentIds` on `prUrl`.
150
+ *
151
+ * No-ops (never throws) when the PR is on an SCM this host cannot write,
152
+ * no client is bound, or `commentIds` is empty. Same degrade-don't-fail
153
+ * posture as {@link createReviewCommentReader}.
154
+ */
155
+ export declare function resolveReviewThreadsForComments(args: {
156
+ prUrl: string;
157
+ commentIds: readonly number[];
158
+ github?: HostGitHubClient;
159
+ resolver?: ReviewThreadResolverImplementation;
160
+ signal?: AbortSignal;
161
+ }): Promise<ResolveReviewThreadsResult>;
108
162
  /**
109
163
  * Reads inline review comments through the host GitHub client (Cursor
110
164
  * account lease, App installation token, or PAT).
111
165
  */
112
166
  export declare function createHostGithubReviewCommentReader(github: HostGitHubClient): ReviewCommentReaderImplementation;
167
+ /**
168
+ * Resolves inline review threads through the host GitHub client (Cursor
169
+ * account lease, App installation token, or PAT). GitHub GraphQL has no
170
+ * comment→thread field, so this pages the PR's review threads and matches
171
+ * each REST comment id by `databaseId` — the same approach as Bugbot's
172
+ * `resolveReviewThreadForComment`.
173
+ */
174
+ export declare function createHostGithubReviewThreadResolver(github: HostGitHubClient): ReviewThreadResolverImplementation;
113
175
  type GithubReviewCommentLike = {
114
176
  id: number;
115
177
  path: string;
@@ -1 +1 @@
1
- {"version":3,"file":"review-comments.d.ts","sourceRoot":"","sources":["../../src/internal/review-comments.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"review-comments.d.ts","sourceRoot":"","sources":["../../src/internal/review-comments.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,MAAM,sBAAsB,GAAG,UAAU,GAAG,aAAa,CAAC;AAEhE,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,sBAAsB,CAAC;IAClC,IAAI,EAAE,6BAA6B,GAAG,kBAAkB,GAAG,SAAS,CAAC;IACrE,kBAAkB,EAAE,MAAM,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,mBAAmB,EAAE,CAAC;CACxC,CAAC;AAEF,qEAAqE;AACrE,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,oCAAoC,GAAG;IACjD,UAAU,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,OAAO,CAAC,uBAAuB,CAAC,CAAC;CAC/E,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAEzC,mDAAmD;AACnD,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,EAAE,eAAe,CAAC;IACxB,mEAAmE;IACnE,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,MAAM,iCAAiC,GAAG;IAC9C,iBAAiB,EAAE,CACjB,IAAI,EAAE,qBAAqB,KACxB,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,MAAM,EAAE,eAAe,CAAC;IACxB,yDAAyD;IACzD,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,MAAM,kCAAkC,GAAG;IAC/C,+BAA+B,EAAE,CAC/B,IAAI,EAAE,wBAAwB,KAC3B,OAAO,CAAC,0BAA0B,CAAC,CAAC;CAC1C,CAAC;AAEF,gEAAgE;AAChE,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,GACZ,eAAe,GAAG,SAAS,CAS7B;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAC9C,MAAM,EAAE,eAAe,CAAC;IACxB,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC3B,GAAG,iCAAiC,GAAG,SAAS,CAOhD;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE;IAC/C,MAAM,EAAE,eAAe,CAAC;IACxB,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC3B,GAAG,kCAAkC,GAAG,SAAS,CAOjD;AAED;;;;;;GAMG;AACH,wBAAsB,+BAA+B,CAAC,IAAI,EAAE;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,QAAQ,CAAC,EAAE,kCAAkC,CAAC;IAC9C,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAsBtC;AASD;;;GAGG;AACH,wBAAgB,mCAAmC,CACjD,MAAM,EAAE,gBAAgB,GACvB,iCAAiC,CA6BnC;AA2BD;;;;;;GAMG;AACH,wBAAgB,oCAAoC,CAClD,MAAM,EAAE,gBAAgB,GACvB,kCAAkC,CAkHpC;AAkCD,KAAK,uBAAuB,GAAG;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,uBAAuB,GAC/B,qBAAqB,GAAG,SAAS,CAoBnC;AAOD;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,qBAAqB,GAEzB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,qBAAqB,CAAA;CAAE,GACpD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,uBAAuB,CAAA;CAAE,CAkBvD;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,IAAI,oCAAoC,CAMnF;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,gBAAgB,GACvB,oCAAoC,CAMtC"}
@@ -32,9 +32,55 @@ export function createReviewCommentReader(args) {
32
32
  : createHostGithubReviewCommentReader(args.github);
33
33
  }
34
34
  }
35
+ /**
36
+ * Resolver for a resolved target. Returns undefined when the host cannot
37
+ * serve that provider, so callers degrade instead of throwing.
38
+ */
39
+ export function createReviewThreadResolver(args) {
40
+ switch (args.target.provider) {
41
+ case "github":
42
+ return args.github === undefined
43
+ ? undefined
44
+ : createHostGithubReviewThreadResolver(args.github);
45
+ }
46
+ }
47
+ /**
48
+ * Resolve the review threads that own `commentIds` on `prUrl`.
49
+ *
50
+ * No-ops (never throws) when the PR is on an SCM this host cannot write,
51
+ * no client is bound, or `commentIds` is empty. Same degrade-don't-fail
52
+ * posture as {@link createReviewCommentReader}.
53
+ */
54
+ export function resolveReviewThreadsForComments(args) {
55
+ return __awaiter(this, void 0, void 0, function* () {
56
+ var _a;
57
+ if (args.commentIds.length === 0) {
58
+ return skippedResolve("no comments to resolve");
59
+ }
60
+ const target = resolveScmReviewTarget(args.prUrl);
61
+ if (target === undefined) {
62
+ return skippedResolve("no SCM resolver for this PR URL");
63
+ }
64
+ const resolver = (_a = args.resolver) !== null && _a !== void 0 ? _a : createReviewThreadResolver({
65
+ target,
66
+ github: args.github,
67
+ });
68
+ if (resolver === undefined) {
69
+ return skippedResolve("no GitHub client bound to resolve threads");
70
+ }
71
+ return resolver.resolveReviewThreadsForComments({
72
+ target,
73
+ commentIds: args.commentIds,
74
+ signal: args.signal,
75
+ });
76
+ });
77
+ }
35
78
  /** Cap on pages pulled per PR; GitHub serves 100 comments per page. */
36
79
  const REVIEW_COMMENTS_MAX_PAGES = 5;
37
80
  const REVIEW_COMMENTS_PAGE_SIZE = 100;
81
+ /** Cap on review-thread pages when mapping comment ids to GraphQL thread ids. */
82
+ const REVIEW_THREADS_MAX_PAGES = 20;
83
+ const REVIEW_THREADS_PAGE_SIZE = 100;
38
84
  /**
39
85
  * Reads inline review comments through the host GitHub client (Cursor
40
86
  * account lease, App installation token, or PAT).
@@ -72,6 +118,149 @@ export function createHostGithubReviewCommentReader(github) {
72
118
  },
73
119
  };
74
120
  }
121
+ /**
122
+ * Resolves inline review threads through the host GitHub client (Cursor
123
+ * account lease, App installation token, or PAT). GitHub GraphQL has no
124
+ * comment→thread field, so this pages the PR's review threads and matches
125
+ * each REST comment id by `databaseId` — the same approach as Bugbot's
126
+ * `resolveReviewThreadForComment`.
127
+ */
128
+ export function createHostGithubReviewThreadResolver(github) {
129
+ return {
130
+ resolveReviewThreadsForComments(_a) {
131
+ return __awaiter(this, arguments, void 0, function* ({ target, commentIds, signal }) {
132
+ var _b, _c, _d, _e, _f;
133
+ var _g, _h;
134
+ const uniqueIds = [...new Set(commentIds.filter((id) => id > 0))];
135
+ if (uniqueIds.length === 0) {
136
+ return skippedResolve("no comments to resolve");
137
+ }
138
+ const [owner, repo] = target.repo.split("/", 2);
139
+ if (owner === undefined || repo === undefined || repo === "") {
140
+ return skippedResolve("unparseable owner/repo from PR URL");
141
+ }
142
+ try {
143
+ const octokit = yield github.getOctokit();
144
+ const wanted = new Set(uniqueIds);
145
+ const matched = new Map();
146
+ let cursor = null;
147
+ for (let page = 0; page < REVIEW_THREADS_MAX_PAGES; page++) {
148
+ signal === null || signal === void 0 ? void 0 : signal.throwIfAborted();
149
+ const response = (yield octokit.graphql(REVIEW_THREADS_FOR_PR_QUERY, {
150
+ owner,
151
+ repo,
152
+ number: target.prNumber,
153
+ cursor,
154
+ }));
155
+ const reviewThreads = (_c = (_b = response.repository) === null || _b === void 0 ? void 0 : _b.pullRequest) === null || _c === void 0 ? void 0 : _c.reviewThreads;
156
+ for (const thread of (_g = reviewThreads === null || reviewThreads === void 0 ? void 0 : reviewThreads.nodes) !== null && _g !== void 0 ? _g : []) {
157
+ if ((thread === null || thread === void 0 ? void 0 : thread.id) === undefined || thread.id === "") {
158
+ continue;
159
+ }
160
+ for (const comment of (_h = (_d = thread.comments) === null || _d === void 0 ? void 0 : _d.nodes) !== null && _h !== void 0 ? _h : []) {
161
+ const databaseId = comment === null || comment === void 0 ? void 0 : comment.databaseId;
162
+ if (databaseId !== undefined &&
163
+ databaseId !== null &&
164
+ wanted.has(databaseId) &&
165
+ !matched.has(databaseId)) {
166
+ matched.set(databaseId, {
167
+ threadId: thread.id,
168
+ isResolved: thread.isResolved === true,
169
+ });
170
+ }
171
+ }
172
+ }
173
+ if (matched.size === wanted.size) {
174
+ break;
175
+ }
176
+ const pageInfo = reviewThreads === null || reviewThreads === void 0 ? void 0 : reviewThreads.pageInfo;
177
+ if ((pageInfo === null || pageInfo === void 0 ? void 0 : pageInfo.hasNextPage) !== true || !pageInfo.endCursor) {
178
+ break;
179
+ }
180
+ cursor = pageInfo.endCursor;
181
+ }
182
+ const toResolve = [
183
+ ...new Map([...matched.values()]
184
+ .filter((thread) => !thread.isResolved)
185
+ .map((thread) => [thread.threadId, thread.threadId])).values(),
186
+ ];
187
+ let resolved = 0;
188
+ let failed = 0;
189
+ for (const threadId of toResolve) {
190
+ signal === null || signal === void 0 ? void 0 : signal.throwIfAborted();
191
+ try {
192
+ const mutation = (yield octokit.graphql(RESOLVE_REVIEW_THREAD_MUTATION, { threadId }));
193
+ if (((_f = (_e = mutation.resolveReviewThread) === null || _e === void 0 ? void 0 : _e.thread) === null || _f === void 0 ? void 0 : _f.isResolved) === true) {
194
+ resolved += 1;
195
+ }
196
+ else {
197
+ failed += 1;
198
+ }
199
+ }
200
+ catch (_j) {
201
+ failed += 1;
202
+ }
203
+ }
204
+ const alreadyResolved = new Set([...matched.values()]
205
+ .filter((thread) => thread.isResolved)
206
+ .map((thread) => thread.threadId)).size;
207
+ const notFound = wanted.size - matched.size;
208
+ return {
209
+ requested: uniqueIds.length,
210
+ resolved,
211
+ alreadyResolved,
212
+ notFound,
213
+ failed,
214
+ detail: `resolved ${resolved} of ${toResolve.length} open thread(s) for ${uniqueIds.length} comment(s) via host GitHub (${alreadyResolved} already resolved, ${notFound} not found, ${failed} failed)`,
215
+ };
216
+ }
217
+ catch (error) {
218
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
219
+ throw error;
220
+ }
221
+ return {
222
+ requested: uniqueIds.length,
223
+ resolved: 0,
224
+ alreadyResolved: 0,
225
+ notFound: 0,
226
+ failed: uniqueIds.length,
227
+ detail: `host GitHub thread resolve failed: ${error instanceof Error ? error.message : String(error)}`,
228
+ };
229
+ }
230
+ });
231
+ },
232
+ };
233
+ }
234
+ const REVIEW_THREADS_FOR_PR_QUERY = `
235
+ query ReviewThreadsForPr(
236
+ $owner: String!
237
+ $repo: String!
238
+ $number: Int!
239
+ $cursor: String
240
+ ) {
241
+ repository(owner: $owner, name: $repo) {
242
+ pullRequest(number: $number) {
243
+ reviewThreads(first: ${REVIEW_THREADS_PAGE_SIZE}, after: $cursor) {
244
+ nodes {
245
+ id
246
+ isResolved
247
+ comments(first: ${REVIEW_THREADS_PAGE_SIZE}) {
248
+ nodes { databaseId }
249
+ }
250
+ }
251
+ pageInfo { hasNextPage endCursor }
252
+ }
253
+ }
254
+ }
255
+ }
256
+ `;
257
+ const RESOLVE_REVIEW_THREAD_MUTATION = `
258
+ mutation ResolveReviewThread($threadId: ID!) {
259
+ resolveReviewThread(input: { threadId: $threadId }) {
260
+ thread { isResolved }
261
+ }
262
+ }
263
+ `;
75
264
  /**
76
265
  * GitHub review comment → {@link ExistingReviewComment}.
77
266
  *
@@ -334,6 +523,16 @@ function skippedHost(detail) {
334
523
  detail,
335
524
  };
336
525
  }
526
+ function skippedResolve(detail) {
527
+ return {
528
+ requested: 0,
529
+ resolved: 0,
530
+ alreadyResolved: 0,
531
+ notFound: 0,
532
+ failed: 0,
533
+ detail,
534
+ };
535
+ }
337
536
  function parseGithubPrUrl(prUrl) {
338
537
  let url;
339
538
  try {