@gitkraken/core-gitlens 0.5.113 → 0.5.115

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 (349) hide show
  1. package/CHANGELOG.md +42 -1
  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 +44 -1
  130. package/dist/plus/git-github/api/github.d.ts.map +1 -1
  131. package/dist/plus/git-github/api/github.js +355 -62
  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/collectionMetadata.d.ts +2 -0
  141. package/dist/plus/integrations/collectionMetadata.d.ts.map +1 -1
  142. package/dist/plus/integrations/collectionMetadata.js +17 -1
  143. package/dist/plus/integrations/collectionMetadata.js.map +1 -1
  144. package/dist/plus/integrations/context.d.ts +4 -1
  145. package/dist/plus/integrations/context.d.ts.map +1 -1
  146. package/dist/plus/integrations/index.d.ts +1 -0
  147. package/dist/plus/integrations/index.d.ts.map +1 -1
  148. package/dist/plus/integrations/integrationService.d.ts +20 -1
  149. package/dist/plus/integrations/integrationService.d.ts.map +1 -1
  150. package/dist/plus/integrations/integrationService.js +12 -4
  151. package/dist/plus/integrations/integrationService.js.map +1 -1
  152. package/dist/plus/integrations/manager.d.ts +48 -0
  153. package/dist/plus/integrations/manager.d.ts.map +1 -1
  154. package/dist/plus/integrations/models/gitHostIntegration.d.ts +53 -26
  155. package/dist/plus/integrations/models/gitHostIntegration.d.ts.map +1 -1
  156. package/dist/plus/integrations/models/gitHostIntegration.js +123 -47
  157. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  158. package/dist/plus/integrations/models/integration.d.ts +10 -1
  159. package/dist/plus/integrations/models/integration.d.ts.map +1 -1
  160. package/dist/plus/integrations/models/integration.js +52 -19
  161. package/dist/plus/integrations/models/integration.js.map +1 -1
  162. package/dist/plus/integrations/models/issueCache.d.ts +16 -0
  163. package/dist/plus/integrations/models/issueCache.d.ts.map +1 -0
  164. package/dist/plus/integrations/models/issueCache.js +9 -0
  165. package/dist/plus/integrations/models/issueCache.js.map +1 -0
  166. package/dist/plus/integrations/models/issueReads.d.ts +13 -0
  167. package/dist/plus/integrations/models/issueReads.d.ts.map +1 -1
  168. package/dist/plus/integrations/models/issuesIntegration.d.ts +11 -13
  169. package/dist/plus/integrations/models/issuesIntegration.d.ts.map +1 -1
  170. package/dist/plus/integrations/models/issuesIntegration.js +15 -1
  171. package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
  172. package/dist/plus/integrations/providers/azureDevOps.d.ts +3 -5
  173. package/dist/plus/integrations/providers/azureDevOps.d.ts.map +1 -1
  174. package/dist/plus/integrations/providers/azureDevOps.js +9 -8
  175. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
  176. package/dist/plus/integrations/providers/bitbucket-server.d.ts +3 -5
  177. package/dist/plus/integrations/providers/bitbucket-server.d.ts.map +1 -1
  178. package/dist/plus/integrations/providers/bitbucket-server.js +2 -2
  179. package/dist/plus/integrations/providers/bitbucket-server.js.map +1 -1
  180. package/dist/plus/integrations/providers/bitbucket.d.ts +3 -5
  181. package/dist/plus/integrations/providers/bitbucket.d.ts.map +1 -1
  182. package/dist/plus/integrations/providers/bitbucket.js +2 -1
  183. package/dist/plus/integrations/providers/bitbucket.js.map +1 -1
  184. package/dist/plus/integrations/providers/github.d.ts +12 -5
  185. package/dist/plus/integrations/providers/github.d.ts.map +1 -1
  186. package/dist/plus/integrations/providers/github.js +10 -3
  187. package/dist/plus/integrations/providers/github.js.map +1 -1
  188. package/dist/plus/integrations/providers/gitlab.d.ts +3 -5
  189. package/dist/plus/integrations/providers/gitlab.d.ts.map +1 -1
  190. package/dist/plus/integrations/providers/gitlab.js +2 -2
  191. package/dist/plus/integrations/providers/gitlab.js.map +1 -1
  192. package/dist/plus/integrations/providers/jira.d.ts +3 -7
  193. package/dist/plus/integrations/providers/jira.d.ts.map +1 -1
  194. package/dist/plus/integrations/providers/jira.js +35 -22
  195. package/dist/plus/integrations/providers/jira.js.map +1 -1
  196. package/dist/plus/integrations/providers/jiraIssueByKey.d.ts +7 -0
  197. package/dist/plus/integrations/providers/jiraIssueByKey.d.ts.map +1 -0
  198. package/dist/plus/integrations/providers/jiraIssueByKey.js +102 -0
  199. package/dist/plus/integrations/providers/jiraIssueByKey.js.map +1 -0
  200. package/dist/plus/integrations/providers/linear.d.ts +3 -7
  201. package/dist/plus/integrations/providers/linear.d.ts.map +1 -1
  202. package/dist/plus/integrations/providers/linear.js +23 -22
  203. package/dist/plus/integrations/providers/linear.js.map +1 -1
  204. package/dist/plus/integrations/providers/models.d.ts +3 -1
  205. package/dist/plus/integrations/providers/models.d.ts.map +1 -1
  206. package/dist/plus/integrations/providers/models.js +10 -4
  207. package/dist/plus/integrations/providers/models.js.map +1 -1
  208. package/dist/plus/integrations/providers/providerErrors.d.ts +7 -0
  209. package/dist/plus/integrations/providers/providerErrors.d.ts.map +1 -0
  210. package/dist/plus/integrations/providers/providerErrors.js +91 -0
  211. package/dist/plus/integrations/providers/providerErrors.js.map +1 -0
  212. package/dist/plus/integrations/providers/providersApi.d.ts +3 -2
  213. package/dist/plus/integrations/providers/providersApi.d.ts.map +1 -1
  214. package/dist/plus/integrations/providers/providersApi.js +18 -52
  215. package/dist/plus/integrations/providers/providersApi.js.map +1 -1
  216. package/dist/plus/integrations/providers/trello.d.ts +2 -7
  217. package/dist/plus/integrations/providers/trello.d.ts.map +1 -1
  218. package/dist/plus/integrations/providers/trello.js +3 -1
  219. package/dist/plus/integrations/providers/trello.js.map +1 -1
  220. package/dist/plus/integrations/providers/utils/providerPaging.d.ts +2 -0
  221. package/dist/plus/integrations/providers/utils/providerPaging.d.ts.map +1 -1
  222. package/dist/plus/integrations/providers/utils/providerPaging.js +12 -7
  223. package/dist/plus/integrations/providers/utils/providerPaging.js.map +1 -1
  224. package/dist/plus/integrations/reads/broaden.d.ts +47 -10
  225. package/dist/plus/integrations/reads/broaden.d.ts.map +1 -1
  226. package/dist/plus/integrations/reads/broaden.js +144 -33
  227. package/dist/plus/integrations/reads/broaden.js.map +1 -1
  228. package/dist/plus/integrations/reads/filters.d.ts +18 -0
  229. package/dist/plus/integrations/reads/filters.d.ts.map +1 -1
  230. package/dist/plus/integrations/reads/filters.js +37 -1
  231. package/dist/plus/integrations/reads/filters.js.map +1 -1
  232. package/dist/plus/integrations/reads/issueBatch.d.ts +51 -0
  233. package/dist/plus/integrations/reads/issueBatch.d.ts.map +1 -0
  234. package/dist/plus/integrations/reads/issueBatch.js +91 -0
  235. package/dist/plus/integrations/reads/issueBatch.js.map +1 -0
  236. package/dist/plus/integrations/reads/issueTracker.d.ts.map +1 -1
  237. package/dist/plus/integrations/reads/issueTracker.js +23 -17
  238. package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
  239. package/dist/plus/integrations/reads/sweeps.d.ts.map +1 -1
  240. package/dist/plus/integrations/reads/sweeps.js +5 -3
  241. package/dist/plus/integrations/reads/sweeps.js.map +1 -1
  242. package/dist/plus/integrations/reads/trackerIssue.d.ts +21 -0
  243. package/dist/plus/integrations/reads/trackerIssue.d.ts.map +1 -0
  244. package/dist/plus/integrations/reads/trackerIssue.js +51 -0
  245. package/dist/plus/integrations/reads/trackerIssue.js.map +1 -0
  246. package/dist/plus/integrations/reads/warnings.d.ts +2 -15
  247. package/dist/plus/integrations/reads/warnings.d.ts.map +1 -1
  248. package/dist/plus/integrations/reads/warnings.js +19 -1
  249. package/dist/plus/integrations/reads/warnings.js.map +1 -1
  250. package/dist/plus/integrations/results.d.ts +4 -20
  251. package/dist/plus/integrations/results.d.ts.map +1 -1
  252. package/dist/plus/integrations/results.js.map +1 -1
  253. package/dist/utils/promise.d.ts +2 -0
  254. package/dist/utils/promise.d.ts.map +1 -1
  255. package/dist/utils/promise.js +11 -0
  256. package/dist/utils/promise.js.map +1 -1
  257. package/dist/utils/promiseCache.d.ts +19 -0
  258. package/dist/utils/promiseCache.d.ts.map +1 -1
  259. package/dist/utils/promiseCache.js +30 -0
  260. package/dist/utils/promiseCache.js.map +1 -1
  261. package/dist/utils/resourceUsage.d.ts +5 -0
  262. package/dist/utils/resourceUsage.d.ts.map +1 -0
  263. package/dist/utils/resourceUsage.js +2 -0
  264. package/dist/utils/resourceUsage.js.map +1 -0
  265. package/dist/utils/string.d.ts +12 -0
  266. package/dist/utils/string.d.ts.map +1 -1
  267. package/dist/utils/string.js +36 -1
  268. package/dist/utils/string.js.map +1 -1
  269. package/docs/integrations.md +48 -33
  270. package/docs/kepler-read-api-parity.md +25 -4
  271. package/package.json +12 -4
  272. package/src/git/cache.ts +36 -0
  273. package/src/git/context.ts +11 -0
  274. package/src/git/errors.ts +13 -1
  275. package/src/git/features.ts +51 -0
  276. package/src/git/gitHealth.ts +522 -0
  277. package/src/git/models/search.ts +19 -4
  278. package/src/git/parsers/diffParser.ts +27 -4
  279. package/src/git/providers/config.ts +42 -1
  280. package/src/git/providers/graph.ts +12 -0
  281. package/src/git/providers/maintenance.ts +224 -0
  282. package/src/git/providers/operations.ts +10 -0
  283. package/src/git/providers/pausedOperations.ts +3 -0
  284. package/src/git/providers/provider.ts +2 -0
  285. package/src/git/providers/tags.ts +1 -0
  286. package/src/git/repositoryService.ts +1 -0
  287. package/src/git/run.types.ts +8 -0
  288. package/src/git/service.ts +3 -0
  289. package/src/git/utils/search.utils.ts +129 -5
  290. package/src/git/watching/watchService.ts +10 -0
  291. package/src/git-cli/cliGitProvider.ts +25 -0
  292. package/src/git-cli/exec/git.ts +120 -6
  293. package/src/git-cli/exec/gitQueue.ts +19 -17
  294. package/src/git-cli/parsers/logParser.ts +4 -1
  295. package/src/git-cli/providers/blame.ts +3 -8
  296. package/src/git-cli/providers/branches.ts +28 -8
  297. package/src/git-cli/providers/commits.ts +34 -2
  298. package/src/git-cli/providers/config.ts +38 -6
  299. package/src/git-cli/providers/graph.ts +107 -7
  300. package/src/git-cli/providers/maintenance.ts +2599 -0
  301. package/src/git-cli/providers/operations.ts +11 -0
  302. package/src/git-cli/providers/pausedOperations.ts +7 -2
  303. package/src/git-cli/providers/tags.ts +36 -1
  304. package/src/plus/agents/agentCapabilities.ts +286 -0
  305. package/src/plus/agents/providers/claudeCodeTranscript.ts +313 -58
  306. package/src/plus/agents/providers/{claudeCodeProvider.ts → gkAgentProvider.ts} +1123 -277
  307. package/src/plus/agents/types.ts +137 -40
  308. package/src/plus/ai/models/promptTemplates.ts +1 -1
  309. package/src/plus/ai/prompts.ts +62 -4
  310. package/src/plus/git-github/api/github.ts +404 -82
  311. package/src/plus/git-github/api/issueSearchQuery.ts +49 -1
  312. package/src/plus/git-github/providers/github/blame.ts +2 -1
  313. package/src/plus/integrations/collectionMetadata.ts +23 -1
  314. package/src/plus/integrations/context.ts +1 -1
  315. package/src/plus/integrations/index.ts +1 -0
  316. package/src/plus/integrations/integrationService.ts +29 -4
  317. package/src/plus/integrations/manager.ts +48 -0
  318. package/src/plus/integrations/models/gitHostIntegration.ts +165 -129
  319. package/src/plus/integrations/models/integration.ts +57 -22
  320. package/src/plus/integrations/models/issueCache.ts +29 -0
  321. package/src/plus/integrations/models/issueReads.ts +15 -0
  322. package/src/plus/integrations/models/issuesIntegration.ts +38 -8
  323. package/src/plus/integrations/providers/azureDevOps.ts +16 -13
  324. package/src/plus/integrations/providers/bitbucket-server.ts +4 -6
  325. package/src/plus/integrations/providers/bitbucket.ts +3 -4
  326. package/src/plus/integrations/providers/github.ts +23 -7
  327. package/src/plus/integrations/providers/gitlab.ts +4 -6
  328. package/src/plus/integrations/providers/jira.ts +52 -26
  329. package/src/plus/integrations/providers/jiraIssueByKey.ts +155 -0
  330. package/src/plus/integrations/providers/linear.ts +38 -25
  331. package/src/plus/integrations/providers/models.ts +17 -5
  332. package/src/plus/integrations/providers/providerErrors.ts +115 -0
  333. package/src/plus/integrations/providers/providersApi.ts +26 -68
  334. package/src/plus/integrations/providers/trello.ts +5 -4
  335. package/src/plus/integrations/providers/utils/providerPaging.ts +14 -7
  336. package/src/plus/integrations/reads/broaden.ts +282 -70
  337. package/src/plus/integrations/reads/filters.ts +39 -1
  338. package/src/plus/integrations/reads/issueBatch.ts +197 -0
  339. package/src/plus/integrations/reads/issueTracker.ts +26 -24
  340. package/src/plus/integrations/reads/sweeps.ts +19 -12
  341. package/src/plus/integrations/reads/trackerIssue.ts +105 -0
  342. package/src/plus/integrations/reads/warnings.ts +23 -17
  343. package/src/plus/integrations/results.ts +4 -20
  344. package/src/utils/promise.ts +15 -0
  345. package/src/utils/promiseCache.ts +43 -0
  346. package/src/utils/resourceUsage.ts +5 -0
  347. package/src/utils/string.ts +38 -1
  348. package/dist/plus/agents/providers/claudeCodeProvider.d.ts.map +0 -1
  349. 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
+ }
@@ -2,7 +2,7 @@ import type { CollectionMetadata } from '@gitkraken/provider-apis';
2
2
  import type { IssueShape, IssueSorting } from '../../../git/models/issue.js';
3
3
  import type { ResourceDescriptor } from '../../../git/models/resourceDescriptor.js';
4
4
  import { mapBounded } from '../../../utils/promise.js';
5
- import { mergeAssessmentInto } from '../collectionMetadata.js';
5
+ import { assessCollectionMetadata, mergeAssessmentInto } from '../collectionMetadata.js';
6
6
  import type { IntegrationIds } from '../constants.js';
7
7
  import { providerFanOutConcurrency } from '../constants.js';
8
8
  import { isIssuesIntegration } from '../models/issuesIntegration.js';
@@ -452,10 +452,7 @@ export async function listIssueTrackerIssuesPage(
452
452
  // Partial project discovery means some projects' issues are missing from this page; propagate it so the
453
453
  // page reports fetchFailed even when every discovered project's own read succeeded.
454
454
  let fetchFailed = projectDiscoveryFailed || accountLookupFailed;
455
- // A project whose internal page-drain hit its backstop (Jira/Linear cap at maxPagesPerRequest) reports
456
- // `truncated`; surface it as `page.truncated` so a windowed read isn't published as having drained each
457
- // project completely.
458
- let projectTruncated = projectDiscoveryTruncated;
455
+ let truncationRecovery: 'narrow-scope' | 'none' | undefined = projectDiscoveryTruncated ? 'none' : undefined;
459
456
  let drainMetadata: CollectionMetadata | undefined;
460
457
  for (const { project, value: result, warning } of perProject) {
461
458
  const key = projectKey(project);
@@ -465,7 +462,7 @@ export async function listIssueTrackerIssuesPage(
465
462
  // single failing token repeats verbatim once per project in the window.
466
463
  appendDedupedWarning(warnings, warning);
467
464
  fetchFailed = true;
468
- projectTruncated = true;
465
+ truncationRecovery = 'none';
469
466
  }
470
467
  // A thrown/unsupported read (e.g. Linear not-implemented) surfaces as a warning with no value;
471
468
  // mark the aggregate as fetchFailed so an empty result isn't mistaken for "no issues".
@@ -475,7 +472,8 @@ export async function listIssueTrackerIssuesPage(
475
472
  if (result != null) {
476
473
  items.push(...result.values);
477
474
  if (result.truncated) {
478
- projectTruncated = true;
475
+ const recovery = result.recovery;
476
+ truncationRecovery = truncationRecovery == null || truncationRecovery === recovery ? recovery : 'none';
479
477
  }
480
478
  if (result.metadata != null) {
481
479
  drainMetadata = mergeCollectionMetadata(drainMetadata, result.metadata);
@@ -497,29 +495,33 @@ export async function listIssueTrackerIssuesPage(
497
495
  // publishes. A no-op when nothing merged, since the tracker already ordered that single run.
498
496
  const orderedItems = ordering.order(items);
499
497
 
500
- const drainAssessment = mergeAssessmentInto(
501
- warnings,
502
- options.providerId,
503
- domain,
504
- options.connectionId,
505
- drainMetadata,
506
- );
498
+ const projectReadReportedTruncation = truncationRecovery != null;
499
+ const drainAssessment = assessCollectionMetadata(options.providerId, domain, options.connectionId, drainMetadata);
500
+ for (const warning of drainAssessment.warnings) {
501
+ // A project result already supplies the recovery semantics. Replace metadata's unstructured fallback with
502
+ // the structured warning below, while retaining specific failures and omissions.
503
+ if (projectReadReportedTruncation && !drainAssessment.fetchFailed && warning.omission == null) continue;
504
+
505
+ appendDedupedWarning(warnings, warning);
506
+ }
507
507
  fetchFailed = fetchFailed || drainAssessment.fetchFailed;
508
- projectTruncated = projectTruncated || drainAssessment.truncated;
508
+ if (drainAssessment.truncated) {
509
+ truncationRecovery = 'none';
510
+ }
509
511
 
510
- // A per-project read that returned data but couldn't confirm completeness (e.g. Trello's provider-native
511
- // cap) sets `truncated` without a structured failure. Add one provider-neutral incompleteness warning so
512
- // the caller sees the truncation, but only when no warning already explains it (avoid duplicate noise).
513
- if (projectTruncated && warnings.length === 0) {
514
- warnings.push(
512
+ const projectTruncated = truncationRecovery != null;
513
+ const scopeTooLarge = !fetchFailed && truncationRecovery === 'narrow-scope';
514
+ if (scopeTooLarge || (!fetchFailed && projectTruncated && !warnings.some(warning => warning.omission != null))) {
515
+ appendDedupedWarning(
516
+ warnings,
515
517
  incompleteReadWarning(
516
518
  options.providerId,
517
519
  domain,
518
520
  options.connectionId,
519
- 'Some issues were omitted; the provider returned an incomplete result.',
520
- // `exhausted`, not `page-budget`: the per-project drain's backstop is an internal constant
521
- // (`maxPagesPerRequest`), not an option this read exposes, so no caller can raise it.
522
- fetchFailed ? 'interrupted' : 'exhausted',
521
+ scopeTooLarge
522
+ ? 'Some projects hold more issues than one read can return; narrow the scope to read the rest.'
523
+ : 'Some issues were omitted; the provider returned an incomplete result.',
524
+ scopeTooLarge ? 'scope-too-large' : 'exhausted',
523
525
  ),
524
526
  );
525
527
  }
@@ -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
  };
@@ -0,0 +1,105 @@
1
+ import type { IssueShape } from '../../../git/models/issue.js';
2
+ import type { IntegrationIds } from '../constants.js';
3
+ import { IssuesCloudHostIntegrationId } from '../constants.js';
4
+ import { isIssuesIntegration } from '../models/issuesIntegration.js';
5
+ import type { ProviderResult, ProviderWarning } from '../results.js';
6
+ import { isIssuesHostIntegrationId } from '../utils/integration.utils.js';
7
+ import type { ProviderReadContext } from './context.js';
8
+ import { runCaptured } from './drains.js';
9
+ import { issueTrackerOnlySurfaceWarning, otherWarning } from './warnings.js';
10
+
11
+ export interface TrackerIssueResult {
12
+ key: string;
13
+ /**
14
+ * The resolved issue, or `undefined` when it provably does not exist or is not visible to this connection.
15
+ * A failed read returns no item and sets `fetchFailed`.
16
+ */
17
+ issue?: IssueShape;
18
+ }
19
+
20
+ /** Issue trackers are cloud-only, so this read takes no `domain`. */
21
+ export async function getTrackerIssue(
22
+ ctx: ProviderReadContext,
23
+ options: {
24
+ providerId: IntegrationIds;
25
+ resourceId: string;
26
+ resourceUrl?: string;
27
+ key: string;
28
+ connectionId?: string;
29
+ },
30
+ ): Promise<ProviderResult<TrackerIssueResult>> {
31
+ const refused = (warning: ProviderWarning): ProviderResult<TrackerIssueResult> => ({
32
+ items: [],
33
+ warnings: [warning],
34
+ fetchFailed: true,
35
+ });
36
+ const surface = 'Issue resolution by key';
37
+
38
+ if (!isIssuesHostIntegrationId(options.providerId)) {
39
+ return refused(issueTrackerOnlySurfaceWarning(options.providerId, options.connectionId, surface));
40
+ }
41
+
42
+ if (options.resourceId.trim().length === 0) {
43
+ return refused(
44
+ otherWarning(options.providerId, undefined, options.connectionId, `${surface} requires a resource id.`),
45
+ );
46
+ }
47
+
48
+ if (options.key.trim().length === 0) {
49
+ return refused(
50
+ otherWarning(options.providerId, undefined, options.connectionId, `${surface} requires an issue key.`),
51
+ );
52
+ }
53
+
54
+ const resourceUrl = options.resourceUrl?.trim() || undefined;
55
+ if (options.providerId === IssuesCloudHostIntegrationId.Jira && resourceUrl == null) {
56
+ return refused(
57
+ otherWarning(
58
+ options.providerId,
59
+ undefined,
60
+ options.connectionId,
61
+ `${surface} requires the Jira resource URL so the result contains a browser link without resource discovery.`,
62
+ ),
63
+ );
64
+ }
65
+
66
+ const integration = await ctx.getIntegrationForRead(options.providerId, options.connectionId);
67
+ if (integration == null) {
68
+ const early = ctx.earlyReturnConnectionWarnings(options.providerId, options.connectionId);
69
+ return { items: [], warnings: early.warnings, fetchFailed: early.fetchFailed || undefined };
70
+ }
71
+ if (!isIssuesIntegration(integration)) {
72
+ return refused(issueTrackerOnlySurfaceWarning(options.providerId, options.connectionId, surface));
73
+ }
74
+ if (!integration.supportsIssueLookupByResourceId) {
75
+ return refused(
76
+ otherWarning(
77
+ options.providerId,
78
+ undefined,
79
+ options.connectionId,
80
+ `${surface} is not supported by '${options.providerId}'; its single-issue read cannot prove an absence, so a miss would not be safe to cache.`,
81
+ ),
82
+ );
83
+ }
84
+
85
+ const domain = ctx.domainForRead(integration, options.providerId, options.connectionId);
86
+ const issue = await runCaptured(
87
+ options.providerId,
88
+ domain,
89
+ options.connectionId,
90
+ () =>
91
+ integration.getIssueByResourceIdResult(options.resourceId, options.key, {
92
+ connectionId: options.connectionId,
93
+ resourceUrl: resourceUrl,
94
+ }),
95
+ { warnOnMissingSession: true },
96
+ );
97
+ if (issue.warning != null) {
98
+ return refused(issue.warning);
99
+ }
100
+
101
+ return {
102
+ items: [{ key: options.key, ...(issue.value != null ? { issue: issue.value } : {}) }],
103
+ warnings: [],
104
+ };
105
+ }
@@ -8,7 +8,7 @@ import type {
8
8
  PullRequestSorting,
9
9
  } from '../providerFilters.js';
10
10
  import { providersMetadata } from '../providers/models.js';
11
- import type { ProviderWarning } from '../results.js';
11
+ import type { ProviderWarning, ProviderWarningOmissionRecovery } from '../results.js';
12
12
  import type { IssueSearchCriteriaRejection, PullRequestSearchCriteriaRejection } from './filters.js';
13
13
  import type { UnmergeableIssueSort, UnsupportedIssueSortRejection } from './ordering.js';
14
14
 
@@ -101,21 +101,23 @@ export function issueTrackerOnlySurfaceWarning(
101
101
  );
102
102
  }
103
103
 
104
- /**
105
- * Why a read the facade drove itself returned less than everything. The two questions a consumer cannot
106
- * answer from `truncated` or from the message, kept together because they are decided together:
107
- *
108
- * - `interrupted`: the read did NOT succeed — the session went away, a later page failed. It leaves an unread
109
- * tail like the others, but it is a failure: it sets `fetchFailed`, and a retry is exactly the right move.
110
- * Gets no omission at all, since the omission asserts the opposite.
111
- * - `page-budget`: the drain stopped at its own `maxPages`, with a usable cursor still in hand. The items ARE
112
- * reachable and were simply not fetched, so re-running with a higher budget returns them.
113
- * - `exhausted`: everything else that succeeded and came back short — the provider capped the page it served,
114
- * advertised another page without a usable cursor (or with one it had already handed out), or a read could
115
- * not confirm it had drained everything. The default of the three: choose it whenever a raisable budget is
116
- * not demonstrably what stopped the read, so a consumer is never offered a fetch that cannot deliver.
117
- */
118
- export type IncompleteReadCause = 'interrupted' | 'page-budget' | 'exhausted';
104
+ /** Why a facade-driven read returned fewer results than requested. */
105
+ export type IncompleteReadCause = 'interrupted' | 'page-budget' | 'exhausted' | 'scope-too-large';
106
+
107
+ function recoveryForIncompleteReadCause(cause: IncompleteReadCause): ProviderWarningOmissionRecovery {
108
+ switch (cause) {
109
+ case 'page-budget':
110
+ return 'page-budget';
111
+ case 'scope-too-large':
112
+ return 'narrow-scope';
113
+ case 'interrupted':
114
+ case 'exhausted':
115
+ return 'none';
116
+ default:
117
+ cause satisfies never;
118
+ return 'none';
119
+ }
120
+ }
119
121
 
120
122
  /**
121
123
  * Builds the warning for a read that returned less than everything, carrying the two facts a consumer cannot
@@ -143,7 +145,7 @@ export function incompleteReadWarning(
143
145
  ...warning,
144
146
  omission: {
145
147
  kind: 'pagination-incomplete',
146
- recovery: cause === 'page-budget' ? 'page-budget' : 'none',
148
+ recovery: recoveryForIncompleteReadCause(cause),
147
149
  },
148
150
  };
149
151
  }
@@ -192,6 +194,10 @@ function truncationMessage(id: IntegrationIds, readKind: TruncatedReadKind, caus
192
194
  return `${readKind} read for '${id}' stopped at its page budget; more results can be read by raising it.`;
193
195
  case 'exhausted':
194
196
  return `${readKind} read for '${id}' was truncated and cannot be continued; results may be incomplete.`;
197
+ case 'scope-too-large':
198
+ // Says what is true — the scope is larger than a read of it — rather than blaming the provider for a
199
+ // ceiling that is ours, and names the remedy that exists, since no retry or budget has one.
200
+ return `${readKind} read for '${id}' covers more than one read can return; narrow the scope to read the rest.`;
195
201
  }
196
202
  // No `default`: `IncompleteReadCause` is declared in this file, so `noImplicitReturns` already fails the
197
203
  // build here if a cause is added without its own wording. (`collectionOmissionMessage`'s `satisfies never`
@@ -35,27 +35,11 @@ export type ProviderWarningKind = 'auth' | 'rate-limit' | 'not-found' | 'no-conn
35
35
  export type ProviderWarningOmissionKind = 'provider-limit' | 'recovery-budget' | 'pagination-incomplete';
36
36
 
37
37
  /**
38
- * What, if anything, would fetch the withheld results the question `kind` cannot answer.
39
- *
40
- * `kind` says WHY results are missing, and two omissions of the same kind can need opposite handling: a drain
41
- * that stopped at a caller-settable page budget and a provider that advertised another page without a usable
42
- * cursor are both `pagination-incomplete`, but only the first can be fetched. A consumer offering a "load
43
- * more" affordance gates it on this, never on `kind`.
44
- *
45
- * - `none`: nothing the consumer can call returns the missing items — a provider-enforced cap, an internal
46
- * budget it does not control, or a continuation the provider refused to hand back. Say the results are
47
- * capped; do not offer to fetch more.
48
- * - `page-budget`: re-run the SAME read with a higher page budget (`maxPages` on the sweep options). Note this
49
- * re-reads from the start rather than continuing — a sweep exposes no cursor — so it is a deliberate,
50
- * user-initiated action, not something to retry automatically.
51
- *
52
- * Required, not optional: an absent value would be indistinguishable from `none` while actually meaning "this
53
- * producer didn't say", which is the ambiguity {@link ProviderWarning.omission} exists to remove. And a
54
- * conservative union on purpose — it names only what a producer can PROVE. A value is added when some layer
55
- * can vouch for it, never so that a plausible-looking case has something to map to; see
56
- * `collectionMetadata.ts` for the SDK shape that looks recoverable and is not.
38
+ * How a consumer can recover omitted results. `page-budget` reruns with a larger budget; `narrow-scope`
39
+ * requests a smaller server-side scope; `none` promises neither. Switch on the exact value because only
40
+ * `page-budget` is a fetch-more action.
57
41
  */
58
- export type ProviderWarningOmissionRecovery = 'none' | 'page-budget';
42
+ export type ProviderWarningOmissionRecovery = 'none' | 'page-budget' | 'narrow-scope';
59
43
 
60
44
  /** Which repository / project / resource an omission is attributed to. All fields optional; a scope may name none. */
61
45
  export interface ProviderWarningOmissionScope {
@@ -121,6 +121,21 @@ export async function mapBounded<T, R>(
121
121
  return results;
122
122
  }
123
123
 
124
+ /** Settles each task without stopping the sliding window when one of them rejects. */
125
+ export function mapSettledBounded<T, R>(
126
+ items: readonly T[],
127
+ concurrency: number,
128
+ task: (item: T, index: number) => Promise<R>,
129
+ ): Promise<PromiseSettledResult<Awaited<R>>[]> {
130
+ return mapBounded(items, concurrency, async (item, index): Promise<PromiseSettledResult<Awaited<R>>> => {
131
+ try {
132
+ return { status: 'fulfilled', value: await task(item, index) };
133
+ } catch (reason) {
134
+ return { status: 'rejected', reason: reason };
135
+ }
136
+ });
137
+ }
138
+
124
139
  export class PromiseCancelledError<T extends Promise<any> = Promise<any>> extends Error {
125
140
  constructor(
126
141
  public readonly promise: T,
@@ -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>>;