@gitkraken/core-gitlens 0.5.112 → 0.5.114

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 (255) hide show
  1. package/CHANGELOG.md +19 -3
  2. package/dist/git/cache.d.ts +4 -0
  3. package/dist/git/cache.d.ts.map +1 -1
  4. package/dist/git/cache.js +33 -0
  5. package/dist/git/cache.js.map +1 -1
  6. package/dist/git/context.d.ts +10 -0
  7. package/dist/git/context.d.ts.map +1 -1
  8. package/dist/git/context.js.map +1 -1
  9. package/dist/git/errors.d.ts +6 -3
  10. package/dist/git/errors.d.ts.map +1 -1
  11. package/dist/git/errors.js +9 -1
  12. package/dist/git/errors.js.map +1 -1
  13. package/dist/git/features.d.ts +1 -1
  14. package/dist/git/features.d.ts.map +1 -1
  15. package/dist/git/features.js +38 -0
  16. package/dist/git/features.js.map +1 -1
  17. package/dist/git/gitHealth.d.ts +153 -0
  18. package/dist/git/gitHealth.d.ts.map +1 -0
  19. package/dist/git/gitHealth.js +353 -0
  20. package/dist/git/gitHealth.js.map +1 -0
  21. package/dist/git/models/search.d.ts +6 -3
  22. package/dist/git/models/search.d.ts.map +1 -1
  23. package/dist/git/models/search.js +5 -1
  24. package/dist/git/models/search.js.map +1 -1
  25. package/dist/git/parsers/diffParser.d.ts +8 -2
  26. package/dist/git/parsers/diffParser.d.ts.map +1 -1
  27. package/dist/git/parsers/diffParser.js +27 -4
  28. package/dist/git/parsers/diffParser.js.map +1 -1
  29. package/dist/git/providers/config.d.ts +36 -3
  30. package/dist/git/providers/config.d.ts.map +1 -1
  31. package/dist/git/providers/graph.d.ts +9 -0
  32. package/dist/git/providers/graph.d.ts.map +1 -1
  33. package/dist/git/providers/maintenance.d.ts +222 -0
  34. package/dist/git/providers/maintenance.d.ts.map +1 -0
  35. package/dist/git/providers/maintenance.js +20 -0
  36. package/dist/git/providers/maintenance.js.map +1 -0
  37. package/dist/git/providers/operations.d.ts +10 -0
  38. package/dist/git/providers/operations.d.ts.map +1 -1
  39. package/dist/git/providers/pausedOperations.d.ts +3 -0
  40. package/dist/git/providers/pausedOperations.d.ts.map +1 -1
  41. package/dist/git/providers/provider.d.ts +2 -0
  42. package/dist/git/providers/provider.d.ts.map +1 -1
  43. package/dist/git/providers/tags.d.ts +3 -0
  44. package/dist/git/providers/tags.d.ts.map +1 -1
  45. package/dist/git/repositoryService.d.ts +1 -0
  46. package/dist/git/repositoryService.d.ts.map +1 -1
  47. package/dist/git/repositoryService.js.map +1 -1
  48. package/dist/git/run.types.d.ts +8 -0
  49. package/dist/git/run.types.d.ts.map +1 -1
  50. package/dist/git/service.js +3 -0
  51. package/dist/git/service.js.map +1 -1
  52. package/dist/git/utils/search.utils.d.ts.map +1 -1
  53. package/dist/git/utils/search.utils.js +112 -5
  54. package/dist/git/utils/search.utils.js.map +1 -1
  55. package/dist/git/watching/watchService.d.ts +3 -0
  56. package/dist/git/watching/watchService.d.ts.map +1 -1
  57. package/dist/git/watching/watchService.js +8 -0
  58. package/dist/git/watching/watchService.js.map +1 -1
  59. package/dist/git-cli/cliGitProvider.d.ts +11 -0
  60. package/dist/git-cli/cliGitProvider.d.ts.map +1 -1
  61. package/dist/git-cli/cliGitProvider.js +12 -0
  62. package/dist/git-cli/cliGitProvider.js.map +1 -1
  63. package/dist/git-cli/exec/git.d.ts +23 -1
  64. package/dist/git-cli/exec/git.d.ts.map +1 -1
  65. package/dist/git-cli/exec/git.js +83 -8
  66. package/dist/git-cli/exec/git.js.map +1 -1
  67. package/dist/git-cli/exec/gitQueue.d.ts +2 -0
  68. package/dist/git-cli/exec/gitQueue.d.ts.map +1 -1
  69. package/dist/git-cli/exec/gitQueue.js +19 -17
  70. package/dist/git-cli/exec/gitQueue.js.map +1 -1
  71. package/dist/git-cli/parsers/logParser.d.ts.map +1 -1
  72. package/dist/git-cli/parsers/logParser.js +4 -1
  73. package/dist/git-cli/parsers/logParser.js.map +1 -1
  74. package/dist/git-cli/providers/blame.d.ts.map +1 -1
  75. package/dist/git-cli/providers/blame.js +4 -7
  76. package/dist/git-cli/providers/blame.js.map +1 -1
  77. package/dist/git-cli/providers/branches.d.ts.map +1 -1
  78. package/dist/git-cli/providers/branches.js +13 -3
  79. package/dist/git-cli/providers/branches.js.map +1 -1
  80. package/dist/git-cli/providers/commits.d.ts.map +1 -1
  81. package/dist/git-cli/providers/commits.js +34 -3
  82. package/dist/git-cli/providers/commits.js.map +1 -1
  83. package/dist/git-cli/providers/config.d.ts +27 -0
  84. package/dist/git-cli/providers/config.d.ts.map +1 -1
  85. package/dist/git-cli/providers/config.js +31 -6
  86. package/dist/git-cli/providers/config.js.map +1 -1
  87. package/dist/git-cli/providers/graph.d.ts +8 -0
  88. package/dist/git-cli/providers/graph.d.ts.map +1 -1
  89. package/dist/git-cli/providers/graph.js +85 -6
  90. package/dist/git-cli/providers/graph.js.map +1 -1
  91. package/dist/git-cli/providers/maintenance.d.ts +306 -0
  92. package/dist/git-cli/providers/maintenance.d.ts.map +1 -0
  93. package/dist/git-cli/providers/maintenance.js +2129 -0
  94. package/dist/git-cli/providers/maintenance.js.map +1 -0
  95. package/dist/git-cli/providers/operations.d.ts +1 -0
  96. package/dist/git-cli/providers/operations.d.ts.map +1 -1
  97. package/dist/git-cli/providers/operations.js +8 -0
  98. package/dist/git-cli/providers/operations.js.map +1 -1
  99. package/dist/git-cli/providers/pausedOperations.d.ts +1 -0
  100. package/dist/git-cli/providers/pausedOperations.d.ts.map +1 -1
  101. package/dist/git-cli/providers/pausedOperations.js +1 -1
  102. package/dist/git-cli/providers/pausedOperations.js.map +1 -1
  103. package/dist/git-cli/providers/tags.d.ts +3 -0
  104. package/dist/git-cli/providers/tags.d.ts.map +1 -1
  105. package/dist/git-cli/providers/tags.js +23 -1
  106. package/dist/git-cli/providers/tags.js.map +1 -1
  107. package/dist/plus/agents/agentCapabilities.d.ts +77 -0
  108. package/dist/plus/agents/agentCapabilities.d.ts.map +1 -0
  109. package/dist/plus/agents/agentCapabilities.js +197 -0
  110. package/dist/plus/agents/agentCapabilities.js.map +1 -0
  111. package/dist/plus/agents/providers/claudeCodeTranscript.d.ts +77 -13
  112. package/dist/plus/agents/providers/claudeCodeTranscript.d.ts.map +1 -1
  113. package/dist/plus/agents/providers/claudeCodeTranscript.js +263 -55
  114. package/dist/plus/agents/providers/claudeCodeTranscript.js.map +1 -1
  115. package/dist/plus/agents/providers/{claudeCodeProvider.d.ts → gkAgentProvider.d.ts} +52 -19
  116. package/dist/plus/agents/providers/gkAgentProvider.d.ts.map +1 -0
  117. package/dist/plus/agents/providers/{claudeCodeProvider.js → gkAgentProvider.js} +935 -262
  118. package/dist/plus/agents/providers/gkAgentProvider.js.map +1 -0
  119. package/dist/plus/agents/types.d.ts +113 -35
  120. package/dist/plus/agents/types.d.ts.map +1 -1
  121. package/dist/plus/agents/types.js +7 -4
  122. package/dist/plus/agents/types.js.map +1 -1
  123. package/dist/plus/ai/models/promptTemplates.d.ts +1 -1
  124. package/dist/plus/ai/models/promptTemplates.d.ts.map +1 -1
  125. package/dist/plus/ai/prompts.d.ts +4 -0
  126. package/dist/plus/ai/prompts.d.ts.map +1 -1
  127. package/dist/plus/ai/prompts.js +59 -4
  128. package/dist/plus/ai/prompts.js.map +1 -1
  129. package/dist/plus/git-github/api/github.d.ts +50 -1
  130. package/dist/plus/git-github/api/github.d.ts.map +1 -1
  131. package/dist/plus/git-github/api/github.js +333 -29
  132. package/dist/plus/git-github/api/github.js.map +1 -1
  133. package/dist/plus/git-github/api/issueSearchQuery.d.ts +13 -0
  134. package/dist/plus/git-github/api/issueSearchQuery.d.ts.map +1 -1
  135. package/dist/plus/git-github/api/issueSearchQuery.js +41 -0
  136. package/dist/plus/git-github/api/issueSearchQuery.js.map +1 -1
  137. package/dist/plus/git-github/providers/github/blame.d.ts.map +1 -1
  138. package/dist/plus/git-github/providers/github/blame.js +2 -1
  139. package/dist/plus/git-github/providers/github/blame.js.map +1 -1
  140. package/dist/plus/integrations/integrationService.d.ts +15 -0
  141. package/dist/plus/integrations/integrationService.d.ts.map +1 -1
  142. package/dist/plus/integrations/integrationService.js +4 -0
  143. package/dist/plus/integrations/integrationService.js.map +1 -1
  144. package/dist/plus/integrations/manager.d.ts +43 -0
  145. package/dist/plus/integrations/manager.d.ts.map +1 -1
  146. package/dist/plus/integrations/models/gitHostIntegration.d.ts +49 -17
  147. package/dist/plus/integrations/models/gitHostIntegration.d.ts.map +1 -1
  148. package/dist/plus/integrations/models/gitHostIntegration.js +40 -0
  149. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  150. package/dist/plus/integrations/models/integration.d.ts +1 -1
  151. package/dist/plus/integrations/models/integration.d.ts.map +1 -1
  152. package/dist/plus/integrations/models/integration.js.map +1 -1
  153. package/dist/plus/integrations/providers/github.d.ts +10 -0
  154. package/dist/plus/integrations/providers/github.d.ts.map +1 -1
  155. package/dist/plus/integrations/providers/github.js +8 -0
  156. package/dist/plus/integrations/providers/github.js.map +1 -1
  157. package/dist/plus/integrations/providers/models.d.ts +2 -1
  158. package/dist/plus/integrations/providers/models.d.ts.map +1 -1
  159. package/dist/plus/integrations/providers/models.js.map +1 -1
  160. package/dist/plus/integrations/reads/broaden.d.ts +47 -10
  161. package/dist/plus/integrations/reads/broaden.d.ts.map +1 -1
  162. package/dist/plus/integrations/reads/broaden.js +144 -33
  163. package/dist/plus/integrations/reads/broaden.js.map +1 -1
  164. package/dist/plus/integrations/reads/filters.d.ts +18 -0
  165. package/dist/plus/integrations/reads/filters.d.ts.map +1 -1
  166. package/dist/plus/integrations/reads/filters.js +37 -1
  167. package/dist/plus/integrations/reads/filters.js.map +1 -1
  168. package/dist/plus/integrations/reads/issueBatch.d.ts +51 -0
  169. package/dist/plus/integrations/reads/issueBatch.d.ts.map +1 -0
  170. package/dist/plus/integrations/reads/issueBatch.js +91 -0
  171. package/dist/plus/integrations/reads/issueBatch.js.map +1 -0
  172. package/dist/plus/integrations/reads/pullRequests.d.ts +6 -0
  173. package/dist/plus/integrations/reads/pullRequests.d.ts.map +1 -1
  174. package/dist/plus/integrations/reads/pullRequests.js +28 -40
  175. package/dist/plus/integrations/reads/pullRequests.js.map +1 -1
  176. package/dist/plus/integrations/reads/searchPullRequests.d.ts +2 -0
  177. package/dist/plus/integrations/reads/searchPullRequests.d.ts.map +1 -1
  178. package/dist/plus/integrations/reads/searchPullRequests.js +1 -0
  179. package/dist/plus/integrations/reads/searchPullRequests.js.map +1 -1
  180. package/dist/plus/integrations/reads/sweeps.d.ts.map +1 -1
  181. package/dist/plus/integrations/reads/sweeps.js +5 -3
  182. package/dist/plus/integrations/reads/sweeps.js.map +1 -1
  183. package/dist/utils/promiseCache.d.ts +19 -0
  184. package/dist/utils/promiseCache.d.ts.map +1 -1
  185. package/dist/utils/promiseCache.js +30 -0
  186. package/dist/utils/promiseCache.js.map +1 -1
  187. package/dist/utils/resourceUsage.d.ts +5 -0
  188. package/dist/utils/resourceUsage.d.ts.map +1 -0
  189. package/dist/utils/resourceUsage.js +2 -0
  190. package/dist/utils/resourceUsage.js.map +1 -0
  191. package/dist/utils/string.d.ts +12 -0
  192. package/dist/utils/string.d.ts.map +1 -1
  193. package/dist/utils/string.js +36 -1
  194. package/dist/utils/string.js.map +1 -1
  195. package/docs/integrations.md +29 -25
  196. package/docs/kepler-read-api-parity.md +12 -4
  197. package/package.json +12 -4
  198. package/src/git/cache.ts +36 -0
  199. package/src/git/context.ts +11 -0
  200. package/src/git/errors.ts +13 -1
  201. package/src/git/features.ts +51 -0
  202. package/src/git/gitHealth.ts +522 -0
  203. package/src/git/models/search.ts +19 -4
  204. package/src/git/parsers/diffParser.ts +27 -4
  205. package/src/git/providers/config.ts +42 -1
  206. package/src/git/providers/graph.ts +12 -0
  207. package/src/git/providers/maintenance.ts +224 -0
  208. package/src/git/providers/operations.ts +10 -0
  209. package/src/git/providers/pausedOperations.ts +3 -0
  210. package/src/git/providers/provider.ts +2 -0
  211. package/src/git/providers/tags.ts +1 -0
  212. package/src/git/repositoryService.ts +1 -0
  213. package/src/git/run.types.ts +8 -0
  214. package/src/git/service.ts +3 -0
  215. package/src/git/utils/search.utils.ts +129 -5
  216. package/src/git/watching/watchService.ts +10 -0
  217. package/src/git-cli/cliGitProvider.ts +25 -0
  218. package/src/git-cli/exec/git.ts +120 -6
  219. package/src/git-cli/exec/gitQueue.ts +19 -17
  220. package/src/git-cli/parsers/logParser.ts +4 -1
  221. package/src/git-cli/providers/blame.ts +3 -8
  222. package/src/git-cli/providers/branches.ts +28 -8
  223. package/src/git-cli/providers/commits.ts +34 -2
  224. package/src/git-cli/providers/config.ts +38 -6
  225. package/src/git-cli/providers/graph.ts +107 -7
  226. package/src/git-cli/providers/maintenance.ts +2599 -0
  227. package/src/git-cli/providers/operations.ts +11 -0
  228. package/src/git-cli/providers/pausedOperations.ts +7 -2
  229. package/src/git-cli/providers/tags.ts +36 -1
  230. package/src/plus/agents/agentCapabilities.ts +286 -0
  231. package/src/plus/agents/providers/claudeCodeTranscript.ts +313 -58
  232. package/src/plus/agents/providers/{claudeCodeProvider.ts → gkAgentProvider.ts} +1123 -277
  233. package/src/plus/agents/types.ts +137 -40
  234. package/src/plus/ai/models/promptTemplates.ts +1 -1
  235. package/src/plus/ai/prompts.ts +62 -4
  236. package/src/plus/git-github/api/github.ts +381 -31
  237. package/src/plus/git-github/api/issueSearchQuery.ts +49 -1
  238. package/src/plus/git-github/providers/github/blame.ts +2 -1
  239. package/src/plus/integrations/integrationService.ts +19 -0
  240. package/src/plus/integrations/manager.ts +43 -0
  241. package/src/plus/integrations/models/gitHostIntegration.ts +77 -18
  242. package/src/plus/integrations/models/integration.ts +1 -0
  243. package/src/plus/integrations/providers/github.ts +20 -0
  244. package/src/plus/integrations/providers/models.ts +6 -0
  245. package/src/plus/integrations/reads/broaden.ts +282 -70
  246. package/src/plus/integrations/reads/filters.ts +39 -1
  247. package/src/plus/integrations/reads/issueBatch.ts +197 -0
  248. package/src/plus/integrations/reads/pullRequests.ts +51 -70
  249. package/src/plus/integrations/reads/searchPullRequests.ts +3 -0
  250. package/src/plus/integrations/reads/sweeps.ts +19 -12
  251. package/src/utils/promiseCache.ts +43 -0
  252. package/src/utils/resourceUsage.ts +5 -0
  253. package/src/utils/string.ts +38 -1
  254. package/dist/plus/agents/providers/claudeCodeProvider.d.ts.map +0 -1
  255. package/dist/plus/agents/providers/claudeCodeProvider.js.map +0 -1
@@ -0,0 +1,197 @@
1
+ import type { IssueShape } from '../../../git/models/issue.js';
2
+ import { chunk } from '../../../utils/array.js';
3
+ import { mapBounded } from '../../../utils/promise.js';
4
+ import type { IntegrationIds } from '../constants.js';
5
+ import { providerFanOutConcurrency } from '../constants.js';
6
+ import type { ProviderResult, ProviderWarning } from '../results.js';
7
+ import { appendDedupedWarning } from '../results.js';
8
+ import {
9
+ isGitHostIntegration,
10
+ isIssuesHostIntegrationId,
11
+ warnOnMissingSessionForDomain,
12
+ } from '../utils/integration.utils.js';
13
+ import type { ProviderReadContext } from './context.js';
14
+ import { runCaptured } from './drains.js';
15
+ import { gitHostOnlySurfaceWarning, issuesUnsupportedWarning, otherWarning } from './warnings.js';
16
+
17
+ /**
18
+ * The BATCH issue read: resolve N `(owner, repo, number)` coordinates in one request.
19
+ *
20
+ * A sibling of the issue searches rather than a mode of one, because it answers a different question. A search
21
+ * asks "what matches"; this asks "does this exact issue exist", which is what a caller correlating a branch name
22
+ * to the issue it references is actually asking. Three consequences follow, and they are the reason this exists:
23
+ *
24
+ * - It resolves by EXACT NUMBER, so relevance never enters into it.
25
+ * - No result ceiling applies, so there is no partial window to reason about and no omission to report.
26
+ * - An absent slot is a PROVEN ABSENCE, not "not found within a page budget". That is the property that matters
27
+ * most: a caller can CACHE a miss. Emulating this with a paged list cannot prove absence without walking the
28
+ * whole scope, so a miss stays unproven and the walk repeats on every pass, forever.
29
+ *
30
+ * Modeled on `countIssues` for its shape — caller-owned `key` echoed back, per-target isolation, chunked into as
31
+ * few requests as possible — because both take a set of independent questions and answer them together.
32
+ */
33
+
34
+ /** One issue to resolve, identified by coordinate and echoed back under the caller's own `key`. */
35
+ export interface IssueBatchTarget {
36
+ /** Caller-owned identifier, echoed on the result so no positional matching is needed. Must be unique. */
37
+ key: string;
38
+ owner: string;
39
+ repo: string;
40
+ number: number;
41
+ }
42
+
43
+ /** The answer for one {@link IssueBatchTarget}. */
44
+ export interface IssueBatchResult {
45
+ key: string;
46
+ /**
47
+ * The resolved issue, or `undefined` when it PROVABLY does not exist (or is not visible to this connection).
48
+ *
49
+ * Absent is an answer here, unlike every paged read on this facade: a target whose chunk FAILED is not
50
+ * returned at all and sets `fetchFailed`, so a caller can tell "proven absent" from "unknown" and cache the
51
+ * first without ever caching the second.
52
+ */
53
+ issue?: IssueShape;
54
+ }
55
+
56
+ /**
57
+ * How many coordinates go into one upstream request.
58
+ *
59
+ * Measured rather than inherited from `issueCountChunkSize`, since that one rests on a zero-node selection and
60
+ * this carries the full issue projection per alias. Against the live API on all-resolving coordinates: 10 took
61
+ * 875ms, 25 took 890ms — so up to 25 is effectively free — 50 took 1.3s, and 75 took 2.7s. No complexity refusal
62
+ * appeared up to 100.
63
+ *
64
+ * 25 is where latency is still flat, with room before it degrades. It also puts any realistic correlation batch
65
+ * in ONE request, which is the whole point of the read.
66
+ */
67
+ const issueBatchChunkSize = 25;
68
+
69
+ export async function getIssuesBatch(
70
+ ctx: ProviderReadContext,
71
+ options: {
72
+ providerId: IntegrationIds;
73
+ targets: readonly IssueBatchTarget[];
74
+ connectionId?: string;
75
+ /**
76
+ * Explicit self-managed host domain. Used only when the requested connection has no configured domain;
77
+ * it must come from the trusted authentication configuration, not repository or remote data.
78
+ */
79
+ domain?: string;
80
+ },
81
+ ): Promise<ProviderResult<IssueBatchResult>> {
82
+ const refused = (warning: ProviderWarning): ProviderResult<IssueBatchResult> => ({
83
+ items: [],
84
+ warnings: [warning],
85
+ fetchFailed: true,
86
+ });
87
+
88
+ if (isIssuesHostIntegrationId(options.providerId)) {
89
+ return refused(
90
+ gitHostOnlySurfaceWarning(options.providerId, undefined, options.connectionId, 'Batch issue resolution'),
91
+ );
92
+ }
93
+
94
+ // Nothing was asked for, so nothing is missing: an empty success, not a refusal.
95
+ if (options.targets.length === 0) return { items: [], warnings: [] };
96
+
97
+ const duplicateKey = findDuplicateKey(options.targets);
98
+ if (duplicateKey != null) {
99
+ // Refuses the whole call rather than deduping: `key` exists so the caller can match results without
100
+ // positional bookkeeping, and two results under one key make that ambiguous for EVERY target, not just
101
+ // the repeated one.
102
+ return refused(
103
+ otherWarning(
104
+ options.providerId,
105
+ undefined,
106
+ options.connectionId,
107
+ `Duplicate issue batch target key '${duplicateKey}'; keys identify results, so each must be unique.`,
108
+ ),
109
+ );
110
+ }
111
+
112
+ const integration = await ctx.getIntegrationForRead(options.providerId, options.connectionId, options.domain);
113
+ if (integration == null) {
114
+ // A supplied connection or domain that no longer resolves is a broken target, not an empty account.
115
+ const early = ctx.earlyReturnConnectionWarnings(options.providerId, options.connectionId, options.domain);
116
+ return { items: [], warnings: early.warnings, fetchFailed: early.fetchFailed || undefined };
117
+ }
118
+ if (!isGitHostIntegration(integration)) {
119
+ return refused(
120
+ gitHostOnlySurfaceWarning(options.providerId, undefined, options.connectionId, 'Batch issue resolution'),
121
+ );
122
+ }
123
+
124
+ const domain = ctx.domainForRead(integration, options.providerId, options.connectionId, options.domain);
125
+ const warnOnMissingSession = warnOnMissingSessionForDomain(options.providerId, options.domain);
126
+
127
+ if (!integration.supportsIssues) {
128
+ return refused(issuesUnsupportedWarning(options.providerId, domain, options.connectionId));
129
+ }
130
+
131
+ const warnings: ProviderWarning[] = [];
132
+ let fetchFailed = false;
133
+
134
+ // Chunks are independent requests over their own slice of targets — nothing in one reads what another
135
+ // produced, and `runCaptured` never throws — so they run concurrently, bounded like every other fan-out here.
136
+ // `mapBounded` returns in input order, so `items` stays in target order.
137
+ const batches = await mapBounded(
138
+ chunk([...options.targets], issueBatchChunkSize),
139
+ providerFanOutConcurrency,
140
+ batch =>
141
+ runCaptured(
142
+ options.providerId,
143
+ domain,
144
+ options.connectionId,
145
+ () =>
146
+ integration.getIssuesBatchResult(
147
+ batch.map(t => ({ owner: t.owner, repo: t.repo, number: t.number })),
148
+ undefined,
149
+ options.connectionId,
150
+ ),
151
+ { warnOnMissingSession: warnOnMissingSession },
152
+ ).then(result => ({ batch: batch, ...result })),
153
+ );
154
+
155
+ const items: IssueBatchResult[] = [];
156
+ for (const { batch, value, warning } of batches) {
157
+ if (warning != null) {
158
+ appendDedupedWarning(warnings, warning);
159
+ }
160
+ if (value == null) {
161
+ // A provider that doesn't implement the batch hook answers `undefined` with no error. Either way this
162
+ // chunk contributes nothing while the chunks around it still do, so its targets are DROPPED rather
163
+ // than reported as absent — the difference between "unknown" and "proven absent" is the read's whole
164
+ // value, and a failure must never be cached as an answer.
165
+ if (warning == null) {
166
+ appendDedupedWarning(
167
+ warnings,
168
+ otherWarning(
169
+ options.providerId,
170
+ domain,
171
+ options.connectionId,
172
+ `Batch issue resolution is not supported by '${options.providerId}'; resolve issues individually instead.`,
173
+ ),
174
+ );
175
+ }
176
+ fetchFailed = true;
177
+ continue;
178
+ }
179
+
180
+ for (let i = 0; i < batch.length; i++) {
181
+ const issue = value[i];
182
+ items.push({ key: batch[i].key, ...(issue != null ? { issue: issue } : {}) });
183
+ }
184
+ }
185
+
186
+ return { items: items, warnings: warnings, fetchFailed: fetchFailed || undefined };
187
+ }
188
+
189
+ function findDuplicateKey(targets: readonly { key: string }[]): string | undefined {
190
+ const seen = new Set<string>();
191
+ for (const target of targets) {
192
+ if (seen.has(target.key)) return target.key;
193
+
194
+ seen.add(target.key);
195
+ }
196
+ return undefined;
197
+ }
@@ -48,6 +48,12 @@ export async function listPullRequestsPage(
48
48
  includeReviewRequested?: boolean;
49
49
  page?: number;
50
50
  cursor?: string;
51
+ /**
52
+ * Requests the lightweight row shape (see {@link IntegrationManager.listPullRequestsPage}). Opt-in
53
+ * only, and only on the repo-scoped branch: the account-wide branch has always read the lightweight
54
+ * shape and keeps doing so, so an explicit `false` does not widen it back to the full projection.
55
+ */
56
+ summary?: boolean;
51
57
  itemsPerPage?: number;
52
58
  forceSync?: boolean;
53
59
  connectionId?: string;
@@ -115,45 +121,49 @@ export async function listPullRequestsPage(
115
121
  );
116
122
  }
117
123
 
118
- const includeReviewRequested = accountWide ? (options.includeReviewRequested ?? false) : false;
119
- const { value, warning } = await runCaptured(
120
- options.providerId,
121
- domain,
122
- options.connectionId,
123
- () =>
124
- accountWide
125
- ? integration.getMyPullRequestsForUserResult(
126
- {
127
- state: options.states,
128
- cursor: cursor,
129
- includeReviewRequested: includeReviewRequested,
130
- filters: resolvedFilters.filters,
131
- summary: true,
132
- },
133
- options.connectionId,
134
- )
135
- : integration.getMyPullRequestsForReposResult(
136
- options.repos ?? [],
137
- // Forward `page`/`pageSize` alongside the cursor so PagingMode.Repo hosts (GitLab, Bitbucket,
138
- // Azure), whose per-repo cursor path ignores a synthesized page-number cursor, still honor the
139
- // requested page and page size instead of always returning page 1. `filters` scopes the read to
140
- // the current user (the core resolves the account for these), so it returns the user's PRs.
141
- {
142
- state: options.states,
143
- filters: resolvedFilters.filters,
144
- cursor: cursor,
145
- // The normalized `page`, so the number forwarded to the provider can't disagree with the
146
- // page-number cursor beside it — both are built from the same value. Only when the caller
147
- // asked for one: an omitted page is the provider's own first page.
148
- page: options.page != null ? page : undefined,
149
- pageSize: options.itemsPerPage,
150
- },
151
- options.connectionId,
152
- ),
153
- {
154
- warnOnMissingSession: warnOnMissingSessionForDomain(options.providerId, options.domain),
155
- },
156
- );
124
+ // Everything but the cursor, shared by the initial read and the drain's re-reads below so the two cannot
125
+ // drift. `summary` is the reason this matters beyond tidiness: a re-read that dropped it would both
126
+ // revert to the full payload — on the very path that issues the most requests — and return rows of a
127
+ // different shape than the first page, since a summary row reports `headCommit`/`commitCount` as absent.
128
+ // `summary` is pinned true here rather than forwarded: this branch has never had a full projection to
129
+ // fall back to (see the option's doc), so honoring an explicit `false` would promise a widening the read
130
+ // cannot deliver.
131
+ const accountWideInput = {
132
+ state: options.states,
133
+ includeReviewRequested: options.includeReviewRequested ?? false,
134
+ filters: resolvedFilters.filters,
135
+ summary: true,
136
+ };
137
+ // `filters` scopes the read to the current user (the core resolves the account for these), so it returns
138
+ // the user's PRs. `pageSize` rides along so PagingMode.Repo hosts (GitLab, Bitbucket, Azure), whose
139
+ // per-repo cursor path ignores a synthesized page-number cursor, honor the requested size.
140
+ const scopedInput = {
141
+ state: options.states,
142
+ filters: resolvedFilters.filters,
143
+ pageSize: options.itemsPerPage,
144
+ summary: options.summary,
145
+ };
146
+ const readPage = (pageCursor: string | undefined, requestedPage?: number) =>
147
+ runCaptured(
148
+ options.providerId,
149
+ domain,
150
+ options.connectionId,
151
+ () =>
152
+ accountWide
153
+ ? integration.getMyPullRequestsForUserResult(
154
+ { ...accountWideInput, cursor: pageCursor },
155
+ options.connectionId,
156
+ )
157
+ : integration.getMyPullRequestsForReposResult(
158
+ options.repos ?? [],
159
+ { ...scopedInput, cursor: pageCursor, page: requestedPage },
160
+ options.connectionId,
161
+ ),
162
+ {
163
+ warnOnMissingSession: warnOnMissingSessionForDomain(options.providerId, options.domain),
164
+ },
165
+ );
166
+ const { value, warning } = await readPage(cursor, options.page != null ? page : undefined);
157
167
 
158
168
  let items = value?.values ?? [];
159
169
  // Cursor-only account-wide reads start at page 1; a page-only request is advanced through opaque
@@ -180,37 +190,8 @@ export async function listPullRequestsPage(
180
190
  requestedPage: page,
181
191
  itemsPerPage: options.itemsPerPage,
182
192
  warnings: warnings,
183
- readPage: (cursor: string) =>
184
- runCaptured(
185
- options.providerId,
186
- domain,
187
- options.connectionId,
188
- () =>
189
- accountWide
190
- ? integration.getMyPullRequestsForUserResult(
191
- {
192
- state: options.states,
193
- cursor: cursor,
194
- includeReviewRequested: includeReviewRequested,
195
- filters: resolvedFilters.filters,
196
- summary: true,
197
- },
198
- options.connectionId,
199
- )
200
- : integration.getMyPullRequestsForReposResult(
201
- options.repos ?? [],
202
- {
203
- state: options.states,
204
- filters: resolvedFilters.filters,
205
- cursor: cursor,
206
- pageSize: options.itemsPerPage,
207
- },
208
- options.connectionId,
209
- ),
210
- {
211
- warnOnMissingSession: warnOnMissingSessionForDomain(options.providerId, options.domain),
212
- },
213
- ),
193
+ // A drain advances through provider continuations, so only the cursor positions these reads.
194
+ readPage: (cursor: string) => readPage(cursor),
214
195
  },
215
196
  );
216
197
  items = drained.items;
@@ -48,6 +48,8 @@ export async function searchPullRequestsPage(
48
48
  forceSync?: boolean;
49
49
  connectionId?: string;
50
50
  domain?: string;
51
+ /** See {@link IntegrationManager.searchPullRequestsPage}. */
52
+ summary?: boolean;
51
53
  },
52
54
  ): Promise<ProviderPagedResult<PullRequestShape>> {
53
55
  const page = Math.max(1, Math.trunc(options.page ?? 1));
@@ -142,6 +144,7 @@ export async function searchPullRequestsPage(
142
144
  criteria: options.criteria,
143
145
  cursor: cursor,
144
146
  pageSize: options.itemsPerPage,
147
+ summary: options.summary,
145
148
  },
146
149
  undefined,
147
150
  options.connectionId,
@@ -151,13 +151,18 @@ function sliceOutcome(slice: SweepSlice | undefined): ProviderSweepTargetEvent['
151
151
  *
152
152
  * The try/catch is what makes the observer observation-only: called from the fan-out's success path, a throwing
153
153
  * callback would otherwise propagate out of the `mapBounded` task and reject the entire sweep — corrupting the
154
- * read, not just the metric. Swallowed silently; the consumer owns its own aggregation.
154
+ * read, not just the metric. Swallowed silently; the consumer owns its own aggregation. The `void`-typed
155
+ * callback also admits an `async` consumer, whose rejection is created only after `observe` returns — the
156
+ * `Promise.resolve(...).catch` swallows that too, so it cannot escape as an unhandled rejection.
155
157
  *
156
158
  * The domain is resolved here rather than carried out of {@link sweepTarget}, so every target reports it by the
157
159
  * same rule no matter how far it got. `resolveDomainForRead` needs no integration instance, which is what makes
158
160
  * that possible: a target rejected by the first guard resolves the same host a fully drained one does.
159
161
  */
160
- function startSweepReporting(ctx: ProviderReadContext, observe: (event: ProviderSweepTargetEvent) => void) {
162
+ function startSweepReporting(
163
+ ctx: ProviderReadContext,
164
+ observe: (event: ProviderSweepTargetEvent) => void | PromiseLike<unknown>,
165
+ ) {
161
166
  const fanOutStartedAt = performance.now();
162
167
 
163
168
  return function beginTarget(target: ProviderSweepTarget) {
@@ -165,16 +170,18 @@ function startSweepReporting(ctx: ProviderReadContext, observe: (event: Provider
165
170
 
166
171
  return function reportSettled(slice: SweepSlice | undefined) {
167
172
  try {
168
- observe({
169
- providerId: target.providerId,
170
- domain: ctx.resolveDomainForRead(target.providerId, target.connectionId, target.domain),
171
- connectionId: target.connectionId,
172
- count: slice?.items.length ?? 0,
173
- durationMs: performance.now() - startedAt,
174
- queueWaitMs: startedAt - fanOutStartedAt,
175
- outcome: sliceOutcome(slice),
176
- truncated: slice?.truncated ?? false,
177
- });
173
+ void Promise.resolve(
174
+ observe({
175
+ providerId: target.providerId,
176
+ domain: ctx.resolveDomainForRead(target.providerId, target.connectionId, target.domain),
177
+ connectionId: target.connectionId,
178
+ count: slice?.items.length ?? 0,
179
+ durationMs: performance.now() - startedAt,
180
+ queueWaitMs: startedAt - fanOutStartedAt,
181
+ outcome: sliceOutcome(slice),
182
+ truncated: slice?.truncated ?? false,
183
+ }),
184
+ ).catch(() => {});
178
185
  } catch {}
179
186
  };
180
187
  };
@@ -97,6 +97,14 @@ export class PromiseCache<K, V> {
97
97
  return this.cache.keys();
98
98
  }
99
99
 
100
+ /**
101
+ * Returns the number of entries in the cache, including expired entries that haven't been evicted yet
102
+ * (they are only evicted on access), so this reflects what's actually retained in memory.
103
+ */
104
+ get size(): number {
105
+ return this.cache.size;
106
+ }
107
+
100
108
  /**
101
109
  * Gets a promise from the cache without creating it and updates accessed time
102
110
  * @param key - The cache key
@@ -139,6 +147,11 @@ export class PromiseCache<K, V> {
139
147
  accessTTL?: number;
140
148
  /** Whether to expire the entry if the promise fails */
141
149
  expireOnError?: boolean;
150
+ /**
151
+ * Called after the factory resolves. If the predicate resolves to true, the entry is evicted while
152
+ * the current caller still receives the resolved value.
153
+ */
154
+ evictWhen?: (value: V) => boolean | Promise<boolean>;
142
155
  /**
143
156
  * TTL (time-to-live) in milliseconds for caching errors. When the factory rejects and `errorTTL` is set,
144
157
  * the cache entry is replaced with `Promise.resolve(undefined)` for subsequent callers, and the error
@@ -282,6 +295,20 @@ export class PromiseCache<K, V> {
282
295
  }
283
296
  }
284
297
 
298
+ const evictWhen = options?.evictWhen;
299
+ if (evictWhen != null) {
300
+ const candidate = entry;
301
+ void candidate.promise
302
+ .then(value => evictWhen(value))
303
+ .then(evict => {
304
+ if (evict) {
305
+ deleteIfOwned(this.cache, key, candidate);
306
+ }
307
+ })
308
+ // A retention policy must not surface a rejection independently of the caller-facing operation.
309
+ .catch(() => {});
310
+ }
311
+
285
312
  // Clean up expired entries and enforce capacity limit in one pass
286
313
  if (this.cache.size > 1) {
287
314
  queueMicrotask(() => this.cleanup());
@@ -609,6 +636,11 @@ export class RepoPromiseCacheMap<K, V> {
609
636
  accessTTL?: number;
610
637
  /** Whether to expire the entry if the promise fails */
611
638
  expireOnError?: boolean;
639
+ /**
640
+ * Called after the factory resolves. If the predicate resolves to true, the entry is evicted while
641
+ * the current caller still receives the resolved value.
642
+ */
643
+ evictWhen?: (value: V) => boolean | Promise<boolean>;
612
644
  /**
613
645
  * TTL (time-to-live) in milliseconds for caching errors. When the factory rejects and `errorTTL` is set,
614
646
  * the cache entry is replaced with `Promise.resolve(undefined)` for subsequent callers, and the error
@@ -755,6 +787,17 @@ export class RepoPromiseCacheMap<K, V> {
755
787
  this.cache.clear();
756
788
  }
757
789
 
790
+ /**
791
+ * Returns the total number of entries across all repository caches.
792
+ */
793
+ get entryCount(): number {
794
+ let count = 0;
795
+ for (const repoCache of this.cache.values()) {
796
+ count += repoCache.size;
797
+ }
798
+ return count;
799
+ }
800
+
758
801
  /**
759
802
  * Returns the number of repositories in the cache.
760
803
  */
@@ -0,0 +1,5 @@
1
+ export type ResourceUsageUnit = 'bytes' | 'count';
2
+ export type ResourceUsageMetric = `${string}.${ResourceUsageUnit}`;
3
+
4
+ /** A flat resource-usage snapshot whose metric names carry their units. */
5
+ export type ResourceUsage = Partial<Record<ResourceUsageMetric, number>>;
@@ -430,12 +430,40 @@ export function getTokensFromTemplateRegex(template: string): TokenMatch[] {
430
430
  return tokens;
431
431
  }
432
432
 
433
+ /**
434
+ * Above this size, a source string split via {@link iterateByDelimiter} has its yielded records
435
+ * flattened (see {@link flattenString}) — below it, the parent-pinning cost of sliced strings is
436
+ * bounded and not worth the copy.
437
+ */
438
+ const maxDelimiterSourceLength = 128 * 1024;
439
+
440
+ /**
441
+ * Forces V8 to create a flat copy of `s`, breaking any sliced-string reference to a parent string.
442
+ *
443
+ * V8 represents `substring()` results of 13+ characters as SlicedString views that retain their
444
+ * entire parent string. A single retained field parsed out of a large buffer (e.g. a git walk's
445
+ * full stdout) can therefore keep megabytes alive for as long as that field survives. Flattening
446
+ * copies only the view's own characters, releasing the parent.
447
+ *
448
+ * No-op for strings shorter than `minLength`, where pinning is bounded anyway and the copy isn't
449
+ * worth the allocation.
450
+ */
451
+ export function flattenString(s: string, minLength = 1024): string {
452
+ if (s.length < minLength) return s;
453
+
454
+ // Concatenating a prefix then slicing it off forces V8 to materialize a flat copy of `s` —
455
+ // deliberately not a template literal, which V8 can represent as another cons/sliced view.
456
+ // oxlint-disable-next-line prefer-template
457
+ return (' ' + s).slice(1);
458
+ }
459
+
433
460
  /**
434
461
  * Fast string delimiter iterator
435
462
  * Optimized for simple string splitting without generator overhead
436
463
  */
437
464
  class StringDelimiterIterator implements IterableIterator<string> {
438
465
  private readonly delimiterLen: number;
466
+ private readonly flatten: boolean;
439
467
  private index = 0;
440
468
  private done = false;
441
469
 
@@ -444,6 +472,11 @@ class StringDelimiterIterator implements IterableIterator<string> {
444
472
  private readonly delimiter: string,
445
473
  ) {
446
474
  this.delimiterLen = delimiter.length;
475
+ // Yielded substrings are V8 SlicedStrings that retain their parent; when splitting a large
476
+ // buffer (e.g. a git walk's full stdout) that parent pinning keeps megabytes alive for as
477
+ // long as any single parsed field survives. Above this threshold, flatten each yielded
478
+ // record so it only holds its own characters.
479
+ this.flatten = data.length > maxDelimiterSourceLength;
447
480
  }
448
481
 
449
482
  next(): IteratorResult<string> {
@@ -455,7 +488,11 @@ class StringDelimiterIterator implements IterableIterator<string> {
455
488
  const j = this.data.indexOf(this.delimiter, this.index);
456
489
  const endIndex = j === -1 ? this.data.length : j;
457
490
 
458
- const value = this.data.substring(this.index, endIndex);
491
+ // Always flatten here: even a small record pins the entire (potentially huge) parent buffer
492
+ // through its sliced string, so the minLength guard in flattenString must not apply.
493
+ const value = this.flatten
494
+ ? flattenString(this.data.substring(this.index, endIndex), 1)
495
+ : this.data.substring(this.index, endIndex);
459
496
  this.index = endIndex + this.delimiterLen;
460
497
 
461
498
  return { done: false, value: value };
@@ -1 +0,0 @@
1
- {"version":3,"file":"claudeCodeProvider.d.ts","sourceRoot":"","sources":["../../../../src/plus/agents/providers/claudeCodeProvider.ts"],"names":[],"mappings":"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"}