@gitkraken/core-gitlens 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/README.md +13 -10
  3. package/dist/git/cache.d.ts +12 -6
  4. package/dist/git/cache.d.ts.map +1 -1
  5. package/dist/git/cache.js +40 -39
  6. package/dist/git/cache.js.map +1 -1
  7. package/dist/git/context.d.ts +23 -3
  8. package/dist/git/context.d.ts.map +1 -1
  9. package/dist/git/context.js.map +1 -1
  10. package/dist/git/features.d.ts +1 -1
  11. package/dist/git/features.d.ts.map +1 -1
  12. package/dist/git/features.js +1 -0
  13. package/dist/git/features.js.map +1 -1
  14. package/dist/git/models/graph.d.ts +96 -0
  15. package/dist/git/models/graph.d.ts.map +1 -1
  16. package/dist/git/models/graph.js +24 -1
  17. package/dist/git/models/graph.js.map +1 -1
  18. package/dist/git/models/remoteProvider.d.ts +1 -0
  19. package/dist/git/models/remoteProvider.d.ts.map +1 -1
  20. package/dist/git/models/remoteProvider.js.map +1 -1
  21. package/dist/git/models/repository.d.ts +21 -12
  22. package/dist/git/models/repository.d.ts.map +1 -1
  23. package/dist/git/models/repository.js +70 -84
  24. package/dist/git/models/repository.js.map +1 -1
  25. package/dist/git/models/statusFile.d.ts +9 -0
  26. package/dist/git/models/statusFile.d.ts.map +1 -1
  27. package/dist/git/models/statusFile.js +11 -0
  28. package/dist/git/models/statusFile.js.map +1 -1
  29. package/dist/git/providers/commits.d.ts +4 -0
  30. package/dist/git/providers/commits.d.ts.map +1 -1
  31. package/dist/git/providers/config.d.ts +1 -3
  32. package/dist/git/providers/config.d.ts.map +1 -1
  33. package/dist/git/providers/operations.d.ts +40 -13
  34. package/dist/git/providers/operations.d.ts.map +1 -1
  35. package/dist/git/providers/worktrees.d.ts +2 -0
  36. package/dist/git/providers/worktrees.d.ts.map +1 -1
  37. package/dist/git/remotes/custom.d.ts +2 -0
  38. package/dist/git/remotes/custom.d.ts.map +1 -1
  39. package/dist/git/remotes/custom.js +20 -0
  40. package/dist/git/remotes/custom.js.map +1 -1
  41. package/dist/git/utils/conflictResolution.utils.d.ts +32 -0
  42. package/dist/git/utils/conflictResolution.utils.d.ts.map +1 -1
  43. package/dist/git/utils/conflictResolution.utils.js +95 -0
  44. package/dist/git/utils/conflictResolution.utils.js.map +1 -1
  45. package/dist/git/utils/contributor.utils.d.ts +3 -0
  46. package/dist/git/utils/contributor.utils.d.ts.map +1 -1
  47. package/dist/git/utils/contributor.utils.js +22 -0
  48. package/dist/git/utils/contributor.utils.js.map +1 -1
  49. package/dist/git/utils/reachability.utils.d.ts +37 -0
  50. package/dist/git/utils/reachability.utils.d.ts.map +1 -0
  51. package/dist/git/utils/reachability.utils.js +96 -0
  52. package/dist/git/utils/reachability.utils.js.map +1 -0
  53. package/dist/git/watching/watchService.d.ts +4 -2
  54. package/dist/git/watching/watchService.d.ts.map +1 -1
  55. package/dist/git/watching/watchService.js +8 -3
  56. package/dist/git/watching/watchService.js.map +1 -1
  57. package/dist/git/watching/watchSession.d.ts +14 -12
  58. package/dist/git/watching/watchSession.d.ts.map +1 -1
  59. package/dist/git/watching/watchSession.js +56 -55
  60. package/dist/git/watching/watchSession.js.map +1 -1
  61. package/dist/git-cli/providers/branches.d.ts.map +1 -1
  62. package/dist/git-cli/providers/branches.js +1 -0
  63. package/dist/git-cli/providers/branches.js.map +1 -1
  64. package/dist/git-cli/providers/commits.d.ts +1 -0
  65. package/dist/git-cli/providers/commits.d.ts.map +1 -1
  66. package/dist/git-cli/providers/commits.js +12 -0
  67. package/dist/git-cli/providers/commits.js.map +1 -1
  68. package/dist/git-cli/providers/config.d.ts +8 -4
  69. package/dist/git-cli/providers/config.d.ts.map +1 -1
  70. package/dist/git-cli/providers/config.js +62 -39
  71. package/dist/git-cli/providers/config.js.map +1 -1
  72. package/dist/git-cli/providers/diff.d.ts.map +1 -1
  73. package/dist/git-cli/providers/diff.js +1 -1
  74. package/dist/git-cli/providers/diff.js.map +1 -1
  75. package/dist/git-cli/providers/graph.d.ts.map +1 -1
  76. package/dist/git-cli/providers/graph.js +132 -14
  77. package/dist/git-cli/providers/graph.js.map +1 -1
  78. package/dist/git-cli/providers/operations.d.ts +7 -5
  79. package/dist/git-cli/providers/operations.d.ts.map +1 -1
  80. package/dist/git-cli/providers/operations.js +118 -95
  81. package/dist/git-cli/providers/operations.js.map +1 -1
  82. package/dist/git-cli/providers/stash.d.ts.map +1 -1
  83. package/dist/git-cli/providers/stash.js +20 -2
  84. package/dist/git-cli/providers/stash.js.map +1 -1
  85. package/dist/git-cli/providers/status.d.ts.map +1 -1
  86. package/dist/git-cli/providers/status.js +5 -2
  87. package/dist/git-cli/providers/status.js.map +1 -1
  88. package/dist/git-cli/providers/worktrees.d.ts +2 -0
  89. package/dist/git-cli/providers/worktrees.d.ts.map +1 -1
  90. package/dist/git-cli/providers/worktrees.js +3 -0
  91. package/dist/git-cli/providers/worktrees.js.map +1 -1
  92. package/dist/ipc/discovery.d.ts +22 -0
  93. package/dist/ipc/discovery.d.ts.map +1 -1
  94. package/dist/ipc/discovery.js +111 -2
  95. package/dist/ipc/discovery.js.map +1 -1
  96. package/dist/plus/agents/providers/claudeCodeProvider.d.ts +73 -20
  97. package/dist/plus/agents/providers/claudeCodeProvider.d.ts.map +1 -1
  98. package/dist/plus/agents/providers/claudeCodeProvider.js +394 -88
  99. package/dist/plus/agents/providers/claudeCodeProvider.js.map +1 -1
  100. package/dist/plus/agents/types.d.ts +62 -11
  101. package/dist/plus/agents/types.d.ts.map +1 -1
  102. package/dist/plus/ai/models/model.d.ts +1 -1
  103. package/dist/plus/ai/models/model.d.ts.map +1 -1
  104. package/dist/plus/ai/models/model.js.map +1 -1
  105. package/dist/plus/ai/models/provider.d.ts +3 -0
  106. package/dist/plus/ai/models/provider.d.ts.map +1 -1
  107. package/dist/plus/ai/providers/gitkrakenProvider.d.ts +1 -1
  108. package/dist/plus/ai/providers/gitkrakenProvider.d.ts.map +1 -1
  109. package/dist/plus/ai/providers/gitkrakenProvider.js +4 -1
  110. package/dist/plus/ai/providers/gitkrakenProvider.js.map +1 -1
  111. package/dist/plus/ai/providers/openAICompatibleProviderBase.d.ts +4 -3
  112. package/dist/plus/ai/providers/openAICompatibleProviderBase.d.ts.map +1 -1
  113. package/dist/plus/ai/providers/openAICompatibleProviderBase.js +9 -6
  114. package/dist/plus/ai/providers/openAICompatibleProviderBase.js.map +1 -1
  115. package/dist/plus/ai/utils/ai.utils.d.ts.map +1 -1
  116. package/dist/plus/ai/utils/ai.utils.js +6 -4
  117. package/dist/plus/ai/utils/ai.utils.js.map +1 -1
  118. package/dist/plus/git-github/api/github.d.ts +8 -13
  119. package/dist/plus/git-github/api/github.d.ts.map +1 -1
  120. package/dist/plus/git-github/api/github.js +96 -14
  121. package/dist/plus/git-github/api/github.js.map +1 -1
  122. package/dist/plus/git-github/providers/github/refs.js +1 -1
  123. package/dist/plus/integrations/authentication/cloudIntegrationService.d.ts +11 -0
  124. package/dist/plus/integrations/authentication/cloudIntegrationService.d.ts.map +1 -0
  125. package/dist/plus/integrations/authentication/cloudIntegrationService.js +90 -0
  126. package/dist/plus/integrations/authentication/cloudIntegrationService.js.map +1 -0
  127. package/dist/plus/integrations/authentication/configuredIntegrationService.d.ts +67 -0
  128. package/dist/plus/integrations/authentication/configuredIntegrationService.d.ts.map +1 -0
  129. package/dist/plus/integrations/authentication/configuredIntegrationService.js +295 -0
  130. package/dist/plus/integrations/authentication/configuredIntegrationService.js.map +1 -0
  131. package/dist/plus/integrations/authentication/integrationAuthenticationProvider.d.ts +92 -0
  132. package/dist/plus/integrations/authentication/integrationAuthenticationProvider.d.ts.map +1 -0
  133. package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js +185 -0
  134. package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js.map +1 -0
  135. package/dist/plus/integrations/authentication/integrationAuthenticationService.d.ts +62 -0
  136. package/dist/plus/integrations/authentication/integrationAuthenticationService.d.ts.map +1 -0
  137. package/dist/plus/integrations/authentication/integrationAuthenticationService.js +122 -0
  138. package/dist/plus/integrations/authentication/integrationAuthenticationService.js.map +1 -0
  139. package/dist/plus/integrations/authentication/manualTokenProvider.d.ts +15 -0
  140. package/dist/plus/integrations/authentication/manualTokenProvider.d.ts.map +1 -0
  141. package/dist/plus/integrations/authentication/manualTokenProvider.js +49 -0
  142. package/dist/plus/integrations/authentication/manualTokenProvider.js.map +1 -0
  143. package/dist/plus/integrations/authentication/models.d.ts +87 -0
  144. package/dist/plus/integrations/authentication/models.d.ts.map +1 -0
  145. package/dist/plus/integrations/authentication/models.js +64 -0
  146. package/dist/plus/integrations/authentication/models.js.map +1 -0
  147. package/dist/plus/integrations/constants.d.ts +47 -0
  148. package/dist/plus/integrations/constants.d.ts.map +1 -0
  149. package/dist/plus/integrations/constants.js +128 -0
  150. package/dist/plus/integrations/constants.js.map +1 -0
  151. package/dist/plus/integrations/context.d.ts +354 -0
  152. package/dist/plus/integrations/context.d.ts.map +1 -0
  153. package/dist/plus/integrations/context.js +2 -0
  154. package/dist/plus/integrations/context.js.map +1 -0
  155. package/dist/plus/integrations/errors.d.ts +14 -0
  156. package/dist/plus/integrations/errors.d.ts.map +1 -0
  157. package/dist/plus/integrations/errors.js +19 -0
  158. package/dist/plus/integrations/errors.js.map +1 -0
  159. package/dist/plus/integrations/index.d.ts +32 -0
  160. package/dist/plus/integrations/index.d.ts.map +1 -0
  161. package/dist/plus/integrations/index.js +62 -0
  162. package/dist/plus/integrations/index.js.map +1 -0
  163. package/dist/plus/integrations/integrationService.d.ts +98 -0
  164. package/dist/plus/integrations/integrationService.d.ts.map +1 -0
  165. package/dist/plus/integrations/integrationService.js +627 -0
  166. package/dist/plus/integrations/integrationService.js.map +1 -0
  167. package/dist/plus/integrations/models/gitHostIntegration.d.ts +82 -0
  168. package/dist/plus/integrations/models/gitHostIntegration.d.ts.map +1 -0
  169. package/dist/plus/integrations/models/gitHostIntegration.js +522 -0
  170. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -0
  171. package/dist/plus/integrations/models/integration.d.ts +127 -0
  172. package/dist/plus/integrations/models/integration.d.ts.map +1 -0
  173. package/dist/plus/integrations/models/integration.js +456 -0
  174. package/dist/plus/integrations/models/integration.js.map +1 -0
  175. package/dist/plus/integrations/models/issuesIntegration.d.ts +28 -0
  176. package/dist/plus/integrations/models/issuesIntegration.d.ts.map +1 -0
  177. package/dist/plus/integrations/models/issuesIntegration.js +101 -0
  178. package/dist/plus/integrations/models/issuesIntegration.js.map +1 -0
  179. package/dist/plus/integrations/providers/apiClients.d.ts +25 -0
  180. package/dist/plus/integrations/providers/apiClients.d.ts.map +1 -0
  181. package/dist/plus/integrations/providers/apiClients.js +39 -0
  182. package/dist/plus/integrations/providers/apiClients.js.map +1 -0
  183. package/dist/plus/integrations/providers/azure/azure.d.ts +47 -0
  184. package/dist/plus/integrations/providers/azure/azure.d.ts.map +1 -0
  185. package/dist/plus/integrations/providers/azure/azure.js +444 -0
  186. package/dist/plus/integrations/providers/azure/azure.js.map +1 -0
  187. package/dist/plus/integrations/providers/azure/models.d.ts +286 -0
  188. package/dist/plus/integrations/providers/azure/models.d.ts.map +1 -0
  189. package/dist/plus/integrations/providers/azure/models.js +203 -0
  190. package/dist/plus/integrations/providers/azure/models.js.map +1 -0
  191. package/dist/plus/integrations/providers/azureDevOps.d.ts +95 -0
  192. package/dist/plus/integrations/providers/azureDevOps.d.ts.map +1 -0
  193. package/dist/plus/integrations/providers/azureDevOps.js +432 -0
  194. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -0
  195. package/dist/plus/integrations/providers/bitbucket/bitbucket.d.ts +42 -0
  196. package/dist/plus/integrations/providers/bitbucket/bitbucket.d.ts.map +1 -0
  197. package/dist/plus/integrations/providers/bitbucket/bitbucket.js +513 -0
  198. package/dist/plus/integrations/providers/bitbucket/bitbucket.js.map +1 -0
  199. package/dist/plus/integrations/providers/bitbucket/models.d.ts +222 -0
  200. package/dist/plus/integrations/providers/bitbucket/models.d.ts.map +1 -0
  201. package/dist/plus/integrations/providers/bitbucket/models.js +158 -0
  202. package/dist/plus/integrations/providers/bitbucket/models.js.map +1 -0
  203. package/dist/plus/integrations/providers/bitbucket-server/models.d.ts +120 -0
  204. package/dist/plus/integrations/providers/bitbucket-server/models.d.ts.map +1 -0
  205. package/dist/plus/integrations/providers/bitbucket-server/models.js +111 -0
  206. package/dist/plus/integrations/providers/bitbucket-server/models.js.map +1 -0
  207. package/dist/plus/integrations/providers/bitbucket-server.d.ts +60 -0
  208. package/dist/plus/integrations/providers/bitbucket-server.d.ts.map +1 -0
  209. package/dist/plus/integrations/providers/bitbucket-server.js +138 -0
  210. package/dist/plus/integrations/providers/bitbucket-server.js.map +1 -0
  211. package/dist/plus/integrations/providers/bitbucket.d.ts +50 -0
  212. package/dist/plus/integrations/providers/bitbucket.d.ts.map +1 -0
  213. package/dist/plus/integrations/providers/bitbucket.js +181 -0
  214. package/dist/plus/integrations/providers/bitbucket.js.map +1 -0
  215. package/dist/plus/integrations/providers/github/github.d.ts +5 -0
  216. package/dist/plus/integrations/providers/github/github.d.ts.map +1 -0
  217. package/dist/plus/integrations/providers/github/github.js +47 -0
  218. package/dist/plus/integrations/providers/github/github.js.map +1 -0
  219. package/dist/plus/integrations/providers/github/github.utils.d.ts +11 -0
  220. package/dist/plus/integrations/providers/github/github.utils.d.ts.map +1 -0
  221. package/dist/plus/integrations/providers/github/github.utils.js +6 -0
  222. package/dist/plus/integrations/providers/github/github.utils.js.map +1 -0
  223. package/dist/plus/integrations/providers/github/models.d.ts +3 -0
  224. package/dist/plus/integrations/providers/github/models.d.ts.map +1 -0
  225. package/dist/plus/integrations/providers/github/models.js +2 -0
  226. package/dist/plus/integrations/providers/github/models.js.map +1 -0
  227. package/dist/plus/integrations/providers/github.d.ts +73 -0
  228. package/dist/plus/integrations/providers/github.d.ts.map +1 -0
  229. package/dist/plus/integrations/providers/github.js +160 -0
  230. package/dist/plus/integrations/providers/github.js.map +1 -0
  231. package/dist/plus/integrations/providers/gitlab/gitlab.d.ts +63 -0
  232. package/dist/plus/integrations/providers/gitlab/gitlab.d.ts.map +1 -0
  233. package/dist/plus/integrations/providers/gitlab/gitlab.js +828 -0
  234. package/dist/plus/integrations/providers/gitlab/gitlab.js.map +1 -0
  235. package/dist/plus/integrations/providers/gitlab/gitlab.utils.d.ts +21 -0
  236. package/dist/plus/integrations/providers/gitlab/gitlab.utils.d.ts.map +1 -0
  237. package/dist/plus/integrations/providers/gitlab/gitlab.utils.js +45 -0
  238. package/dist/plus/integrations/providers/gitlab/gitlab.utils.js.map +1 -0
  239. package/dist/plus/integrations/providers/gitlab/models.d.ts +128 -0
  240. package/dist/plus/integrations/providers/gitlab/models.d.ts.map +1 -0
  241. package/dist/plus/integrations/providers/gitlab/models.js +96 -0
  242. package/dist/plus/integrations/providers/gitlab/models.js.map +1 -0
  243. package/dist/plus/integrations/providers/gitlab.d.ts +79 -0
  244. package/dist/plus/integrations/providers/gitlab.d.ts.map +1 -0
  245. package/dist/plus/integrations/providers/gitlab.js +253 -0
  246. package/dist/plus/integrations/providers/gitlab.js.map +1 -0
  247. package/dist/plus/integrations/providers/jira.d.ts +46 -0
  248. package/dist/plus/integrations/providers/jira.d.ts.map +1 -0
  249. package/dist/plus/integrations/providers/jira.js +255 -0
  250. package/dist/plus/integrations/providers/jira.js.map +1 -0
  251. package/dist/plus/integrations/providers/linear.d.ts +47 -0
  252. package/dist/plus/integrations/providers/linear.d.ts.map +1 -0
  253. package/dist/plus/integrations/providers/linear.js +198 -0
  254. package/dist/plus/integrations/providers/linear.js.map +1 -0
  255. package/dist/plus/integrations/providers/models.d.ts +433 -0
  256. package/dist/plus/integrations/providers/models.d.ts.map +1 -0
  257. package/dist/plus/integrations/providers/models.js +602 -0
  258. package/dist/plus/integrations/providers/models.js.map +1 -0
  259. package/dist/plus/integrations/providers/providersApi.d.ts +129 -0
  260. package/dist/plus/integrations/providers/providersApi.d.ts.map +1 -0
  261. package/dist/plus/integrations/providers/providersApi.js +602 -0
  262. package/dist/plus/integrations/providers/providersApi.js.map +1 -0
  263. package/dist/plus/integrations/providers/utils.d.ts +54 -0
  264. package/dist/plus/integrations/providers/utils.d.ts.map +1 -0
  265. package/dist/plus/integrations/providers/utils.js +262 -0
  266. package/dist/plus/integrations/providers/utils.js.map +1 -0
  267. package/dist/plus/integrations/telemetry.d.ts +26 -0
  268. package/dist/plus/integrations/telemetry.d.ts.map +1 -0
  269. package/dist/plus/integrations/telemetry.js +11 -0
  270. package/dist/plus/integrations/telemetry.js.map +1 -0
  271. package/dist/plus/integrations/utils/integration.utils.d.ts +14 -0
  272. package/dist/plus/integrations/utils/integration.utils.d.ts.map +1 -0
  273. package/dist/plus/integrations/utils/integration.utils.js +111 -0
  274. package/dist/plus/integrations/utils/integration.utils.js.map +1 -0
  275. package/dist/utils/date.d.ts.map +1 -1
  276. package/dist/utils/date.js +6 -0
  277. package/dist/utils/date.js.map +1 -1
  278. package/dist/utils/debounce.js +1 -1
  279. package/dist/utils/disposable.d.ts +15 -2
  280. package/dist/utils/disposable.d.ts.map +1 -1
  281. package/dist/utils/disposable.js.map +1 -1
  282. package/dist/utils/iterable.d.ts +7 -1
  283. package/dist/utils/iterable.d.ts.map +1 -1
  284. package/dist/utils/iterable.js +12 -4
  285. package/dist/utils/iterable.js.map +1 -1
  286. package/dist/utils/object.d.ts +22 -1
  287. package/dist/utils/object.d.ts.map +1 -1
  288. package/dist/utils/object.js +56 -3
  289. package/dist/utils/object.js.map +1 -1
  290. package/dist/utils/paging.js +14 -15
  291. package/dist/utils/paging.js.map +1 -1
  292. package/dist/utils/promiseCache.d.ts.map +1 -1
  293. package/dist/utils/promiseCache.js +12 -4
  294. package/dist/utils/promiseCache.js.map +1 -1
  295. package/dist/utils/string.js +1 -1
  296. package/package.json +43 -2
  297. package/src/git/cache.ts +58 -70
  298. package/src/git/context.ts +25 -3
  299. package/src/git/features.ts +2 -0
  300. package/src/git/models/graph.ts +97 -0
  301. package/src/git/models/remoteProvider.ts +1 -0
  302. package/src/git/models/repository.ts +84 -93
  303. package/src/git/models/statusFile.ts +12 -0
  304. package/src/git/providers/commits.ts +4 -0
  305. package/src/git/providers/config.ts +1 -5
  306. package/src/git/providers/operations.ts +42 -22
  307. package/src/git/providers/worktrees.ts +14 -2
  308. package/src/git/remotes/custom.ts +23 -0
  309. package/src/git/utils/conflictResolution.utils.ts +126 -0
  310. package/src/git/utils/contributor.utils.ts +24 -0
  311. package/src/git/utils/reachability.utils.ts +109 -0
  312. package/src/git/watching/watchService.ts +9 -4
  313. package/src/git/watching/watchSession.ts +61 -55
  314. package/src/git-cli/providers/branches.ts +1 -0
  315. package/src/git-cli/providers/commits.ts +24 -0
  316. package/src/git-cli/providers/config.ts +63 -49
  317. package/src/git-cli/providers/diff.ts +2 -1
  318. package/src/git-cli/providers/graph.ts +145 -14
  319. package/src/git-cli/providers/operations.ts +138 -130
  320. package/src/git-cli/providers/stash.ts +20 -2
  321. package/src/git-cli/providers/status.ts +11 -3
  322. package/src/git-cli/providers/worktrees.ts +17 -2
  323. package/src/ipc/discovery.ts +132 -2
  324. package/src/plus/agents/providers/claudeCodeProvider.ts +430 -90
  325. package/src/plus/agents/types.ts +59 -11
  326. package/src/plus/ai/models/model.ts +1 -0
  327. package/src/plus/ai/models/provider.ts +7 -1
  328. package/src/plus/ai/providers/gitkrakenProvider.ts +4 -0
  329. package/src/plus/ai/providers/openAICompatibleProviderBase.ts +21 -4
  330. package/src/plus/ai/utils/ai.utils.ts +6 -4
  331. package/src/plus/git-github/api/github.ts +131 -31
  332. package/src/plus/git-github/providers/github/refs.ts +1 -1
  333. package/src/plus/integrations/authentication/cloudIntegrationService.ts +120 -0
  334. package/src/plus/integrations/authentication/configuredIntegrationService.ts +414 -0
  335. package/src/plus/integrations/authentication/integrationAuthenticationProvider.ts +289 -0
  336. package/src/plus/integrations/authentication/integrationAuthenticationService.ts +144 -0
  337. package/src/plus/integrations/authentication/manualTokenProvider.ts +72 -0
  338. package/src/plus/integrations/authentication/models.ts +168 -0
  339. package/src/plus/integrations/constants.ts +169 -0
  340. package/src/plus/integrations/context.ts +383 -0
  341. package/src/plus/integrations/errors.ts +35 -0
  342. package/src/plus/integrations/index.ts +119 -0
  343. package/src/plus/integrations/integrationService.ts +924 -0
  344. package/src/plus/integrations/models/gitHostIntegration.ts +738 -0
  345. package/src/plus/integrations/models/integration.ts +657 -0
  346. package/src/plus/integrations/models/issuesIntegration.ts +124 -0
  347. package/src/plus/integrations/providers/apiClients.ts +72 -0
  348. package/src/plus/integrations/providers/azure/azure.ts +691 -0
  349. package/src/plus/integrations/providers/azure/models.ts +605 -0
  350. package/src/plus/integrations/providers/azureDevOps.ts +677 -0
  351. package/src/plus/integrations/providers/bitbucket/bitbucket.ts +774 -0
  352. package/src/plus/integrations/providers/bitbucket/models.ts +445 -0
  353. package/src/plus/integrations/providers/bitbucket-server/models.ts +252 -0
  354. package/src/plus/integrations/providers/bitbucket-server.ts +282 -0
  355. package/src/plus/integrations/providers/bitbucket.ts +350 -0
  356. package/src/plus/integrations/providers/github/github.ts +64 -0
  357. package/src/plus/integrations/providers/github/github.utils.ts +23 -0
  358. package/src/plus/integrations/providers/github/models.ts +29 -0
  359. package/src/plus/integrations/providers/github.ts +366 -0
  360. package/src/plus/integrations/providers/gitlab/gitlab.ts +1210 -0
  361. package/src/plus/integrations/providers/gitlab/gitlab.utils.ts +71 -0
  362. package/src/plus/integrations/providers/gitlab/models.ts +257 -0
  363. package/src/plus/integrations/providers/gitlab.ts +473 -0
  364. package/src/plus/integrations/providers/jira.ts +359 -0
  365. package/src/plus/integrations/providers/linear.ts +275 -0
  366. package/src/plus/integrations/providers/models.ts +1114 -0
  367. package/src/plus/integrations/providers/providersApi.ts +1126 -0
  368. package/src/plus/integrations/providers/utils.ts +359 -0
  369. package/src/plus/integrations/telemetry.ts +29 -0
  370. package/src/plus/integrations/utils/integration.utils.ts +138 -0
  371. package/src/utils/date.ts +5 -0
  372. package/src/utils/debounce.ts +1 -1
  373. package/src/utils/disposable.ts +15 -2
  374. package/src/utils/iterable.ts +14 -3
  375. package/src/utils/object.ts +55 -4
  376. package/src/utils/paging.ts +14 -14
  377. package/src/utils/promiseCache.ts +24 -16
  378. package/src/utils/string.ts +1 -1
@@ -135,22 +135,46 @@ export interface AgentSession {
135
135
  * identity. For "what repo is this session in", use {@link commonPath}. */
136
136
  readonly workspacePath?: string;
137
137
  readonly cwd?: string;
138
+ /** The session's `cwd` at first observation. Set once and never overwritten — captures the
139
+ * agent's launch directory so consumers can detect drift (e.g. an agent that `cd`'d into a
140
+ * sibling worktree). Pairs with {@link initialWorktreePath} / {@link initialCommonPath}; for
141
+ * the live cwd, see {@link cwd}. */
142
+ readonly initialCwd?: string;
143
+ /** The session's `worktreePath` at first successful git-info resolution. Set once and never
144
+ * overwritten — compare against {@link worktreePath} to detect cross-worktree drift. Undefined
145
+ * if the session has never resolved into a git repo. */
146
+ readonly initialWorktreePath?: string;
147
+ /** The session's `commonPath` at first successful git-info resolution. Set once and never
148
+ * overwritten — compare against {@link commonPath} to detect cross-repo drift. Undefined if
149
+ * the session has never resolved into a git repo. */
150
+ readonly initialCommonPath?: string;
138
151
  readonly planFile?: string;
139
152
  readonly isInWorkspace: boolean;
140
153
  readonly lastPrompt?: string;
141
154
  readonly firstPrompt?: string;
142
- /** Absolute paths of files targeted by in-flight file-editing tool calls (Edit/Write/MultiEdit/
143
- * NotebookEdit). Populated on PreToolUse, drained on PostToolUse/PermissionDenied, cleared on
144
- * Stop/SessionEnd. Empty/undefined when no file-editing tool is active.
155
+ /** Per-file activity record covering both read-class (Read/NotebookRead) and edit-class
156
+ * (Edit/Write/MultiEdit/NotebookEdit) tool calls. Populated on PreToolUse; each path is retained
157
+ * and fades for the configured decay window measured from its own last PostToolUse — the tail
158
+ * survives the turn-end Stop (which only drops the live `editing`/`reading` flags) and is fully
159
+ * cleared only on SessionEnd or once the decay window elapses.
145
160
  *
146
- * Mutable array form so `Shape<AgentSession>` projects it as `string[]` (the `Shape<>` type
147
- * mangles `readonly T[]` into a mapped object that loses its iterator). Treat as immutable. */
148
- currentFiles?: string[];
149
- /** Absolute paths of files the agent recently *read* (Read/NotebookRead). Populated on
150
- * PreToolUse, drained on PostToolUse/PermissionDenied with the same cooldown as
151
- * `currentFiles`. Distinct from `currentFiles` which tracks write-class tools so consumers can
152
- * visualize "looking at" vs "working on" differently. Treat as immutable. */
153
- currentReads?: string[];
161
+ * A sub-agent's file activity appears on its PARENT's `fileActivity`, not the sub-agent's own:
162
+ * the GK CLI keys a sub-agent's tool events under the parent session id (with `agentId` set), so
163
+ * they accumulate in the parent's bookkeeping directly. Consumers never need to recurse into
164
+ * `subagents[]`; sub-agent `AgentSession` objects carry `fileActivity: undefined`.
165
+ *
166
+ * Field semantics:
167
+ * - `path`: absolute path on the agent's filesystem.
168
+ * - `readAt` / `editedAt`: milliseconds since the last PreToolUse for that kind, computed by
169
+ * the host at serialization time (relative — avoids clock-skew). Omitted when never touched
170
+ * by that kind within the window.
171
+ * - `reading` / `editing`: `true` when at least one tool of that kind is in flight on this
172
+ * path right now (refcount > 0 in the host bookkeeping). Omitted when false to keep the
173
+ * wire minimal.
174
+ *
175
+ * Mutable array form so `Shape<AgentSession>` projects it cleanly (the `Shape<>` type mangles
176
+ * `readonly T[]` into a mapped object that loses its iterator). Treat as immutable. */
177
+ fileActivity?: { path: string; readAt?: number; editedAt?: number; reading?: true; editing?: true }[];
154
178
  /** `true` when the session was discovered via peer IPC sync (i.e. another GitLens window hosts
155
179
  * the agent's hook flow and Claude Code extension panel). Locally-owned sessions leave this
156
180
  * unset. The dispatcher uses this to route opens through the peer's IPC route + an OS-level
@@ -186,6 +210,11 @@ export interface AgentSessionProvider extends UnifiedDisposable {
186
210
  stop(): void;
187
211
  updateWorkspacePaths?(workspacePaths: string[]): void;
188
212
 
213
+ /** Pushed by the host from its cached agent detection. Lets the provider gate its reconciliation
214
+ * poll (the CLI `list-sessions` call) so an idle window with no sessions and no installed hooks
215
+ * doesn't spawn the CLI every interval. */
216
+ setClaudeHooksInstalled?(installed: boolean): void;
217
+
189
218
  /** Resolves a pending permission. Returns `true` when the resolve was routed (the local IPC
190
219
  * owns the session's pending entry); `false` when no local entry exists (typically a peer-
191
220
  * discovered session owned by another GitLens window). Callers use this to give the user
@@ -231,6 +260,13 @@ export interface AgentProviderCallbacks {
231
260
  /** Host-supplied IPC service. Required for agents to receive hook events from the GK CLI. */
232
261
  ipc: IpcRegistrar;
233
262
 
263
+ /** Returns the current "activity decay" window in milliseconds — the cooldown between a tool
264
+ * call finishing and the file being dropped from `AgentSession.fileActivity`. Optional — when
265
+ * omitted the provider falls back to its built-in default (5 min). Read by the provider at
266
+ * schedule time so changes to the host setting take effect on the next tool call without
267
+ * needing a re-wiring step. */
268
+ getActivityDecayMs?(): number;
269
+
234
270
  /** Report that an agent session started. No-op if the host has no telemetry. */
235
271
  onSessionStarted?(provider: string): void;
236
272
 
@@ -240,6 +276,18 @@ export interface AgentProviderCallbacks {
240
276
  /** Report that a permission request was resolved. No-op if the host has no telemetry. */
241
277
  onPermissionResolved?(info: { provider: string; tool: string; decision: PermissionDecision }): void;
242
278
 
279
+ /** Report that a reconciliation poll found the polled session set drift from what the live IPC
280
+ * path had already tracked — `discovered` sessions the poll saw but we weren't tracking,
281
+ * `missing` sessions we track that the poll no longer reports alive. Ideally always zero. No-op
282
+ * if the host has no telemetry. */
283
+ onSyncDiscrepancy?(info: {
284
+ provider: string;
285
+ discovered: number;
286
+ missing: number;
287
+ polled: number;
288
+ tracked: number;
289
+ }): void;
290
+
243
291
  /** Notify the host that a branch has agent activity. */
244
292
  onBranchAgentActivity?(cwd: string): void;
245
293
 
@@ -28,6 +28,7 @@ export type AIActionType =
28
28
  | 'generate-changelog'
29
29
  | `generate-create-${'cloudPatch' | 'codeSuggestion' | 'pullRequest'}`
30
30
  | 'generate-commits'
31
+ | 'conflict-resolution'
31
32
  | 'generate-searchQuery';
32
33
 
33
34
  export interface AIProviderDescriptor<T extends AIProviders = AIProviders> {
@@ -47,6 +47,12 @@ export interface AIProvider<Provider extends AIProviders = AIProviders> extends
47
47
  model: AIModel<Provider>,
48
48
  apiKey: string,
49
49
  getMessages: (maxInputTokens: number, retries: number) => Promise<AIChatMessage[]>,
50
- options: { signal: AbortSignal; modelOptions?: { outputTokens?: number; temperature?: number } },
50
+ options: {
51
+ signal: AbortSignal;
52
+ modelOptions?: { outputTokens?: number; temperature?: number };
53
+ /** Opaque session ID the GitKraken provider sends as `GK-Conversation-ID` so the backend
54
+ * charges its per-feature fee once per session; ignored by all other providers. */
55
+ conversationId?: string;
56
+ },
51
57
  ): Promise<AIProviderResponse<void> | undefined>;
52
58
  }
@@ -87,11 +87,15 @@ export class GitKrakenProvider extends OpenAICompatibleProviderBase<typeof provi
87
87
  apiKey: string,
88
88
  _model: AIModel<typeof provider.id>,
89
89
  _url: string,
90
+ conversationId?: string,
90
91
  ): Record<string, string> {
91
92
  return {
92
93
  Accept: 'application/json',
93
94
  Authorization: `Bearer ${apiKey}`,
94
95
  'GK-Action': action,
96
+ // Scopes the backend's once-per-conversation feature fee — without it every request in a
97
+ // multi-call session (e.g. conflict resolution) is charged the full flat fee.
98
+ ...(conversationId ? { 'GK-Conversation-ID': conversationId } : {}),
95
99
  };
96
100
  }
97
101
 
@@ -61,6 +61,9 @@ export abstract class OpenAICompatibleProviderBase<T extends AIProviders> implem
61
61
  apiKey: string,
62
62
  _model: AIModel<T>,
63
63
  _url: string,
64
+ // Deliberately unused here: only the GitKraken provider forwards the conversation ID (as
65
+ // `GK-Conversation-ID`); it must never reach third-party APIs.
66
+ _conversationId?: string,
64
67
  ): Record<string, string> | Promise<Record<string, string>> {
65
68
  return {
66
69
  Accept: 'application/json',
@@ -74,12 +77,24 @@ export abstract class OpenAICompatibleProviderBase<T extends AIProviders> implem
74
77
  model: AIModel<T>,
75
78
  apiKey: string,
76
79
  getMessages: (maxInputTokens: number, retries: number) => Promise<AIChatMessage[]>,
77
- options: { signal: AbortSignal; modelOptions?: { outputTokens?: number; temperature?: number } },
80
+ options: {
81
+ signal: AbortSignal;
82
+ modelOptions?: { outputTokens?: number; temperature?: number };
83
+ conversationId?: string;
84
+ },
78
85
  ): Promise<AIProviderResponse<void> | undefined> {
79
86
  using scope = maybeStartScopedLogger(`${getLoggableName(this)}.sendRequest`);
80
87
 
81
88
  try {
82
- const result = await this.fetch(action, model, apiKey, getMessages, options.modelOptions, options.signal);
89
+ const result = await this.fetch(
90
+ action,
91
+ model,
92
+ apiKey,
93
+ getMessages,
94
+ options.modelOptions,
95
+ options.signal,
96
+ options.conversationId,
97
+ );
83
98
  return result;
84
99
  } catch (ex) {
85
100
  if (isCancellationError(ex)) {
@@ -104,6 +119,7 @@ export abstract class OpenAICompatibleProviderBase<T extends AIProviders> implem
104
119
  messages: (maxInputTokens: number, retries: number) => Promise<AIChatMessage[]>,
105
120
  modelOptions?: { outputTokens?: number; temperature?: number },
106
121
  signal?: AbortSignal,
122
+ conversationId?: string,
107
123
  ): Promise<AIProviderResponse<void>> {
108
124
  let retries = 0;
109
125
  let maxInputTokens = model.maxTokens.input;
@@ -123,7 +139,7 @@ export abstract class OpenAICompatibleProviderBase<T extends AIProviders> implem
123
139
  ),
124
140
  };
125
141
 
126
- const rsp = await this.fetchCore(action, model, apiKey, request, signal);
142
+ const rsp = await this.fetchCore(action, model, apiKey, request, signal, conversationId);
127
143
  if (!rsp.ok) {
128
144
  const result = await this.handleFetchFailure(rsp, action, model, retries, maxInputTokens);
129
145
  if (result.retry) {
@@ -207,6 +223,7 @@ export abstract class OpenAICompatibleProviderBase<T extends AIProviders> implem
207
223
  apiKey: string,
208
224
  request: object,
209
225
  signal: AbortSignal | undefined,
226
+ conversationId?: string,
210
227
  ): Promise<Response> {
211
228
  const url = this.getUrl(model);
212
229
  if (!url) {
@@ -215,7 +232,7 @@ export abstract class OpenAICompatibleProviderBase<T extends AIProviders> implem
215
232
 
216
233
  try {
217
234
  return await this.context.fetch(url, {
218
- headers: await this.getHeaders(action, apiKey, model, url),
235
+ headers: await this.getHeaders(action, apiKey, model, url, conversationId),
219
236
  method: 'POST',
220
237
  body: JSON.stringify(request),
221
238
  signal: signal,
@@ -11,17 +11,19 @@ export function getActionName(action: AIActionType): string {
11
11
  case 'generate-stashMessage':
12
12
  return 'Generate Stash Message';
13
13
  case 'generate-changelog':
14
- return 'Generate Changelog (Preview)';
14
+ return 'Generate Changelog';
15
15
  case 'generate-create-cloudPatch':
16
16
  return 'Create Cloud Patch Details';
17
17
  case 'generate-create-codeSuggestion':
18
18
  return 'Create Code Suggestion Details';
19
19
  case 'generate-create-pullRequest':
20
- return 'Create Pull Request Details (Preview)';
20
+ return 'Create Pull Request Details';
21
21
  case 'generate-commits':
22
- return 'Generate Commits (Preview)';
22
+ return 'Generate Commits';
23
+ case 'conflict-resolution':
24
+ return 'Resolve Conflicts (Preview)';
23
25
  case 'generate-searchQuery':
24
- return 'Generate Search Query (Preview)';
26
+ return 'Generate Search Query';
25
27
  }
26
28
  }
27
29
 
@@ -66,14 +66,49 @@ import {
66
66
  import type { GitHubApiConfig } from './config.js';
67
67
  import type { GitHubTokenInfo } from './token.js';
68
68
 
69
- interface CancellationToken {
70
- isCancellationRequested: boolean;
71
- onCancellationRequested(fn: () => void): { dispose(): void };
72
- }
73
-
74
69
  const emptyPagedResult: PagedResult<any> = Object.freeze({ values: [] });
75
70
  const emptyBlameResult: GitHubBlame = Object.freeze({ ranges: [] });
76
71
 
72
+ // Transient gateway/network failures (e.g. an upstream `502 Bad Gateway`) are worth a few quick
73
+ // retries before surfacing to the caller. octokit provides no built-in retry for the standalone
74
+ // `request`/`graphql` functions we use (its retry/throttle plugins only attach to the `Octokit`
75
+ // class), so we retry here — but only for idempotent reads (REST GET/HEAD, GraphQL queries),
76
+ // never for mutations.
77
+ const maxRequestRetries = 2;
78
+ const requestRetryBaseDelay = 300; // ms
79
+ const requestRetryMaxDelay = 2000; // ms
80
+
81
+ function isRetryableTransientError(ex: unknown): ex is RequestError {
82
+ // An aborted request is rethrown as the original `AbortError` (not a `RequestError`), so it is
83
+ // excluded here. octokit maps a fetch/network failure to a `RequestError` with status 500 and
84
+ // no response — those, along with real gateway statuses, are transient and safe to retry.
85
+ if (!(ex instanceof RequestError)) return false;
86
+
87
+ switch (ex.status) {
88
+ case 502: // Bad Gateway
89
+ case 503: // Service Unavailable
90
+ case 504: // Gateway Timeout
91
+ return true;
92
+ case 500: // Internal Server Error — only the network-failure variant (no response)
93
+ return ex.response == null;
94
+ default:
95
+ return false;
96
+ }
97
+ }
98
+
99
+ function getRequestRetryDelay(attempt: number): number {
100
+ // Exponential backoff with equal jitter, capped — spreads retries so a brief upstream blip
101
+ // isn't hammered by every in-flight request landing on the same schedule.
102
+ const backoff = Math.min(requestRetryMaxDelay, requestRetryBaseDelay * 2 ** (attempt - 1));
103
+ return Math.round(backoff / 2 + Math.random() * (backoff / 2));
104
+ }
105
+
106
+ // Matches a GraphQL document whose operation is definitely a read query — the `query` keyword as
107
+ // the first significant token, after any leading whitespace or `#` comment lines. We retry only
108
+ // when this matches; a mutation (even one prefixed by a comment), a subscription, or anything we
109
+ // can't classify is left non-retryable so a transient failure can never re-run a mutation.
110
+ const graphqlReadQueryRegex = /^(?:\s|#[^\n]*\n?)*query\b/;
111
+
77
112
  const gqlIssueOrPullRequestFragment = `
78
113
  closed
79
114
  closedAt
@@ -919,7 +954,7 @@ export class GitHubApi {
919
954
  baseUrl?: string;
920
955
  avatarSize?: number;
921
956
  },
922
- cancellation?: CancellationToken,
957
+ cancellation?: AbortSignal,
923
958
  ): Promise<PullRequest | undefined> {
924
959
  const scope = getScopedLogger();
925
960
 
@@ -1009,7 +1044,7 @@ export class GitHubApi {
1009
1044
  options?: {
1010
1045
  baseUrl?: string;
1011
1046
  },
1012
- cancellation?: CancellationToken,
1047
+ cancellation?: AbortSignal,
1013
1048
  ): Promise<RepositoryMetadata | undefined> {
1014
1049
  const scope = getScopedLogger();
1015
1050
 
@@ -2866,6 +2901,55 @@ export class GitHubApi {
2866
2901
  return version ?? undefined;
2867
2902
  }
2868
2903
 
2904
+ // Runs `execute`, retrying transient gateway/network failures with backoff. `retryable` gates
2905
+ // retries to idempotent reads; `signal` lets an in-flight cancellation abort the backoff wait.
2906
+ private async requestWithRetries<T>(
2907
+ execute: () => Promise<T>,
2908
+ retryable: boolean,
2909
+ signal: AbortSignal | undefined,
2910
+ scope: ScopedLogger | undefined,
2911
+ ): Promise<T> {
2912
+ let attempt = 0;
2913
+ while (true) {
2914
+ try {
2915
+ return await execute();
2916
+ } catch (ex) {
2917
+ if (!retryable || attempt >= maxRequestRetries || signal?.aborted || !isRetryableTransientError(ex)) {
2918
+ throw ex;
2919
+ }
2920
+
2921
+ attempt++;
2922
+ const delay = getRequestRetryDelay(attempt);
2923
+ scope?.warn(
2924
+ `Transient request failure (status ${ex.status}); retrying ${attempt}/${maxRequestRetries} in ${delay}ms`,
2925
+ );
2926
+ await this.delayWithAbort(delay, signal);
2927
+ // Bail rather than retry if we were cancelled while waiting
2928
+ if (signal?.aborted) throw new CancellationError();
2929
+ }
2930
+ }
2931
+ }
2932
+
2933
+ private delayWithAbort(ms: number, signal: AbortSignal | undefined): Promise<void> {
2934
+ return new Promise<void>(resolve => {
2935
+ if (signal?.aborted) {
2936
+ resolve();
2937
+ return;
2938
+ }
2939
+
2940
+ let timer: ReturnType<typeof setTimeout>;
2941
+ const onAbort = () => {
2942
+ clearTimeout(timer);
2943
+ resolve();
2944
+ };
2945
+ timer = setTimeout(() => {
2946
+ signal?.removeEventListener('abort', onAbort);
2947
+ resolve();
2948
+ }, ms);
2949
+ signal?.addEventListener('abort', onAbort, { once: true });
2950
+ });
2951
+ }
2952
+
2869
2953
  // Inflight dedupe: identical concurrent GraphQL requests share a single promise
2870
2954
  // to cut redundant traffic during graph/details enrichment bursts.
2871
2955
  private readonly _pendingGraphQL = new Map<string, Promise<unknown>>();
@@ -2876,10 +2960,9 @@ export class GitHubApi {
2876
2960
  query: string,
2877
2961
  variables: RequestParameters,
2878
2962
  scope: ScopedLogger | undefined,
2879
- cancellation?: CancellationToken | undefined,
2963
+ cancellation?: AbortSignal | undefined,
2880
2964
  ): Promise<T | undefined> {
2881
2965
  const { accessToken, ...tokenInfo } = token;
2882
-
2883
2966
  // Only dedupe when no cancellation/request option is in play — sharing a promise that
2884
2967
  // carries one caller's AbortSignal would let one cancellation cancel for everyone.
2885
2968
  const dedupable = cancellation == null && variables?.request == null;
@@ -2899,21 +2982,26 @@ export class GitHubApi {
2899
2982
 
2900
2983
  const run = async (): Promise<T | undefined> => {
2901
2984
  try {
2902
- let aborter: AbortController | undefined;
2903
2985
  if (cancellation != null) {
2904
- if (cancellation.isCancellationRequested) throw new CancellationError();
2905
-
2906
- aborter = new AbortController();
2907
- cancellation.onCancellationRequested(() => aborter!.abort());
2986
+ if (cancellation.aborted) throw new CancellationError();
2908
2987
 
2909
2988
  variables = {
2910
2989
  ...variables,
2911
- request: { ...variables?.request, signal: aborter.signal },
2990
+ request: { ...variables?.request, signal: cancellation },
2912
2991
  };
2913
2992
  }
2914
2993
 
2915
- return await this.config.wrapForForcedInsecureSSL(provider?.getIgnoreSSLErrors() ?? false, () =>
2916
- this.getDefaults(accessToken, graphql)(query, variables),
2994
+ // Retry transient gateway/network failures, but only confirmed read queries — never
2995
+ // mutations (re-running one isn't idempotent)
2996
+ const retryable = graphqlReadQueryRegex.test(query);
2997
+ return await this.requestWithRetries(
2998
+ () =>
2999
+ this.config.wrapForForcedInsecureSSL(provider?.getIgnoreSSLErrors() ?? false, () =>
3000
+ this.getDefaults(accessToken, graphql)(query, variables),
3001
+ ),
3002
+ retryable,
3003
+ cancellation,
3004
+ scope,
2917
3005
  );
2918
3006
  } catch (ex) {
2919
3007
  if (ex instanceof GraphqlResponseError) {
@@ -2955,12 +3043,15 @@ export class GitHubApi {
2955
3043
  const promise = run();
2956
3044
  this._pendingGraphQL.set(dedupeKey, promise);
2957
3045
  // Clear from the inflight map once the promise settles — successes and failures both
2958
- // clear, so failed requests don't poison subsequent retries.
2959
- void promise.finally(() => {
3046
+ // clear, so failed requests don't poison subsequent retries. Use `then(cleanup, cleanup)`
3047
+ // rather than `finally` so a rejected request doesn't spawn an orphaned (unhandled) promise;
3048
+ // the real caller still receives the rejection via the returned `promise`.
3049
+ const cleanup = () => {
2960
3050
  if (this._pendingGraphQL.get(dedupeKey) === promise) {
2961
3051
  this._pendingGraphQL.delete(dedupeKey);
2962
3052
  }
2963
- });
3053
+ };
3054
+ promise.then(cleanup, cleanup);
2964
3055
  return promise;
2965
3056
  }
2966
3057
 
@@ -2970,20 +3061,29 @@ export class GitHubApi {
2970
3061
  route: R,
2971
3062
  options: (Endpoints[R]['parameters'] & RequestParameters) | undefined,
2972
3063
  scope: ScopedLogger | undefined,
2973
- cancellation?: CancellationToken | undefined,
3064
+ cancellation?: AbortSignal | undefined,
2974
3065
  ): Promise<Endpoints[R]['response']> {
2975
3066
  const { accessToken } = token;
2976
3067
  try {
3068
+ let signal: AbortSignal | undefined;
2977
3069
  if (cancellation != null) {
2978
- if (cancellation.isCancellationRequested) throw new CancellationError();
3070
+ if (cancellation.aborted) throw new CancellationError();
2979
3071
 
2980
- const aborter = new AbortController();
2981
- cancellation.onCancellationRequested(() => aborter.abort());
2982
- options = { ...options, request: { ...options?.request, signal: aborter.signal } };
3072
+ signal = cancellation;
3073
+ options = { ...options, request: { ...options?.request, signal: signal } };
2983
3074
  }
2984
3075
 
2985
- return (await this.config.wrapForForcedInsecureSSL(provider?.getIgnoreSSLErrors() ?? false, () =>
2986
- this.getDefaults(accessToken, request)(route as string, options),
3076
+ // Retry transient gateway/network failures on idempotent reads only
3077
+ const method = route.split(' ', 1)[0].toUpperCase();
3078
+ const retryable = method === 'GET' || method === 'HEAD';
3079
+ return (await this.requestWithRetries(
3080
+ () =>
3081
+ this.config.wrapForForcedInsecureSSL(provider?.getIgnoreSSLErrors() ?? false, () =>
3082
+ this.getDefaults(accessToken, request)(route as string, options),
3083
+ ),
3084
+ retryable,
3085
+ signal,
3086
+ scope,
2987
3087
  )) as Endpoints[R]['response'];
2988
3088
  } catch (ex) {
2989
3089
  if (ex instanceof RequestError || ex.name === 'AbortError') {
@@ -3218,7 +3318,7 @@ export class GitHubApi {
3218
3318
  avatarSize?: number;
3219
3319
  silent?: boolean;
3220
3320
  },
3221
- cancellation?: CancellationToken,
3321
+ cancellation?: AbortSignal,
3222
3322
  ): Promise<PullRequest[]> {
3223
3323
  const scope = getScopedLogger();
3224
3324
 
@@ -3333,7 +3433,7 @@ export class GitHubApi {
3333
3433
  avatarSize?: number;
3334
3434
  includeBody?: boolean;
3335
3435
  },
3336
- cancellation?: CancellationToken,
3436
+ cancellation?: AbortSignal,
3337
3437
  ): Promise<IssueShape[] | undefined> {
3338
3438
  const scope = getScopedLogger();
3339
3439
 
@@ -3436,7 +3536,7 @@ export class GitHubApi {
3436
3536
  provider: Provider,
3437
3537
  token: GitHubTokenInfo,
3438
3538
  options?: { search?: string; user?: string; repos?: string[]; baseUrl?: string; avatarSize?: number },
3439
- cancellation?: CancellationToken,
3539
+ cancellation?: AbortSignal,
3440
3540
  ): Promise<PullRequest[]> {
3441
3541
  const scope = getScopedLogger();
3442
3542
 
@@ -3506,7 +3606,7 @@ export class GitHubApi {
3506
3606
  nodeId: string,
3507
3607
  expectedSourceSha: string,
3508
3608
  options?: { mergeMethod?: PullRequestMergeMethod; baseUrl?: string },
3509
- cancellation?: CancellationToken,
3609
+ cancellation?: AbortSignal,
3510
3610
  ): Promise<boolean> {
3511
3611
  const scope = getScopedLogger();
3512
3612
  interface QueryResult {
@@ -17,7 +17,7 @@ import { toTokenInfo } from '../../api/tokenUtils.js';
17
17
  import type { GitHubGitProviderInternal } from '../githubProvider.js';
18
18
 
19
19
  // Since negative lookbehind isn't supported in all browsers, this leaves out the negative lookbehind condition `(?<!\.lock)` to ensure the branch name doesn't end with `.lock`
20
- // eslint-disable-next-line no-control-regex
20
+ // oxlint-disable-next-line no-control-regex
21
21
  const validBranchOrTagRegex = /^[^/](?!.*\/\.)(?!.*\.\.)(?!.*\/\/)(?!.*@\{)[^\x00-\x1F\x7F ~^:?*[\\]+[^./]$/;
22
22
 
23
23
  export class RefsGitSubProvider implements GitRefsSubProvider {
@@ -0,0 +1,120 @@
1
+ import { getScopedLogger } from '../../../utils/logger.scoped.js';
2
+ import type { IntegrationIds } from '../constants.js';
3
+ import type { IntegrationServiceContext } from '../context.js';
4
+ import type { CloudIntegrationAuthenticationSession, CloudIntegrationConnection } from './models.js';
5
+ import { toCloudIntegrationType } from './models.js';
6
+
7
+ export class CloudIntegrationService {
8
+ constructor(private readonly ctx: IntegrationServiceContext) {}
9
+
10
+ async getConnections(): Promise<CloudIntegrationConnection[] | undefined> {
11
+ const scope = getScopedLogger();
12
+
13
+ const providersRsp = await this.ctx.account.fetchGkApi('v1/provider-tokens', { method: 'GET' });
14
+ if (!providersRsp.ok) {
15
+ const error = ((await providersRsp.json()) as { error?: unknown })?.error;
16
+ const errorMessage =
17
+ typeof error === 'string'
18
+ ? error
19
+ : ((error as { message?: string })?.message ?? providersRsp.statusText);
20
+ if (error != null) {
21
+ scope?.error(undefined, `Failed to get connected providers from cloud: ${errorMessage}`);
22
+ }
23
+ this.ctx.hooks?.connection?.onConnectionsFetchFailed?.({ code: providersRsp.status });
24
+ return undefined;
25
+ }
26
+
27
+ return ((await providersRsp.json()) as { data?: unknown })?.data as CloudIntegrationConnection[] | undefined;
28
+ }
29
+
30
+ async getConnectionSession(
31
+ id: IntegrationIds,
32
+ refreshToken?: string,
33
+ ): Promise<CloudIntegrationAuthenticationSession | undefined> {
34
+ const scope = getScopedLogger();
35
+
36
+ const refresh = Boolean(refreshToken);
37
+ const cloudIntegrationType = toCloudIntegrationType[id];
38
+ if (cloudIntegrationType == null) {
39
+ scope?.error(undefined, `Unsupported cloud integration type: ${id}`);
40
+ return undefined;
41
+ }
42
+
43
+ const reqInitOptions = refreshToken
44
+ ? {
45
+ method: 'POST',
46
+ body: JSON.stringify({
47
+ access_token: refreshToken,
48
+ }),
49
+ }
50
+ : { method: 'GET' };
51
+
52
+ const tokenRsp = await this.ctx.account.fetchGkApi(
53
+ `v1/provider-tokens/${cloudIntegrationType}${refresh ? '/refresh' : ''}`,
54
+ reqInitOptions,
55
+ );
56
+ if (!tokenRsp.ok) {
57
+ const error = ((await tokenRsp.json()) as { error?: unknown })?.error;
58
+ const errorMessage =
59
+ typeof error === 'string' ? error : ((error as { message?: string })?.message ?? tokenRsp.statusText);
60
+ if (error != null) {
61
+ scope?.error(
62
+ undefined,
63
+ `Failed to ${refresh ? 'refresh' : 'get'} ${id} token from cloud: ${errorMessage}`,
64
+ );
65
+ }
66
+ this.ctx.hooks?.connection?.onConnectionFetchFailed?.({
67
+ id: id,
68
+ code: tokenRsp.status,
69
+ refreshing: refresh,
70
+ });
71
+
72
+ if (refresh) {
73
+ // try once to just get the latest token if the refresh fails, and give up if that fails too
74
+ const newTokenRsp = await this.ctx.account.fetchGkApi(`v1/provider-tokens/${cloudIntegrationType}`, {
75
+ method: 'GET',
76
+ });
77
+ if (newTokenRsp.ok) {
78
+ return ((await newTokenRsp.json()) as { data?: unknown })?.data as
79
+ | CloudIntegrationAuthenticationSession
80
+ | undefined;
81
+ }
82
+ }
83
+
84
+ return undefined;
85
+ }
86
+
87
+ return ((await tokenRsp.json()) as { data?: unknown })?.data as
88
+ | CloudIntegrationAuthenticationSession
89
+ | undefined;
90
+ }
91
+
92
+ async disconnect(id: IntegrationIds): Promise<boolean> {
93
+ const scope = getScopedLogger();
94
+
95
+ const cloudIntegrationType = toCloudIntegrationType[id];
96
+ if (cloudIntegrationType == null) {
97
+ scope?.error(undefined, `Unsupported cloud integration type: ${id}`);
98
+ return false;
99
+ }
100
+
101
+ const tokenRsp = await this.ctx.account.fetchGkApi(`v1/provider-tokens/${cloudIntegrationType}`, {
102
+ method: 'DELETE',
103
+ });
104
+ if (!tokenRsp.ok) {
105
+ const error = ((await tokenRsp.json()) as { error?: unknown })?.error;
106
+ const errorMessage =
107
+ typeof error === 'string' ? error : ((error as { message?: string })?.message ?? tokenRsp.statusText);
108
+ if (error != null) {
109
+ scope?.error(undefined, `Failed to disconnect ${id} token from cloud: ${errorMessage}`);
110
+ }
111
+ this.ctx.hooks?.connection?.onDisconnectFailed?.({
112
+ id: id,
113
+ code: tokenRsp.status,
114
+ });
115
+ return false;
116
+ }
117
+
118
+ return true;
119
+ }
120
+ }