@gitkraken/core-gitlens 0.5.116 → 0.7.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 (1915) hide show
  1. package/CHANGELOG.md +32 -1
  2. package/README.md +9 -2
  3. package/dist/_virtual/@oxc-project+runtime/helpers/esm/decorate.js +9 -0
  4. package/dist/_virtual/@oxc-project+runtime/helpers/esm/usingCtx.js +57 -0
  5. package/dist/git/cache.d.ts +297 -297
  6. package/dist/git/cache.js +948 -1276
  7. package/dist/git/cache.js.map +1 -1
  8. package/dist/git/context.d.ts +202 -197
  9. package/dist/git/context.js +12 -8
  10. package/dist/git/context.js.map +1 -1
  11. package/dist/git/errors.d.ts +268 -281
  12. package/dist/git/errors.js +928 -706
  13. package/dist/git/errors.js.map +1 -1
  14. package/dist/git/features.d.ts +3 -2
  15. package/dist/git/features.js +34 -55
  16. package/dist/git/features.js.map +1 -1
  17. package/dist/git/gitHealth.d.ts +88 -81
  18. package/dist/git/gitHealth.js +259 -329
  19. package/dist/git/gitHealth.js.map +1 -1
  20. package/dist/git/models/author.d.ts +12 -10
  21. package/dist/git/models/author.js +0 -2
  22. package/dist/git/models/autolink.d.ts +36 -37
  23. package/dist/git/models/autolink.js +0 -2
  24. package/dist/git/models/blame.d.ts +25 -23
  25. package/dist/git/models/blame.js +0 -2
  26. package/dist/git/models/branch.d.ts +74 -72
  27. package/dist/git/models/branch.js +132 -171
  28. package/dist/git/models/branch.js.map +1 -1
  29. package/dist/git/models/commit.d.ts +135 -133
  30. package/dist/git/models/commit.js +381 -484
  31. package/dist/git/models/commit.js.map +1 -1
  32. package/dist/git/models/contributor.d.ts +30 -29
  33. package/dist/git/models/contributor.js +56 -64
  34. package/dist/git/models/contributor.js.map +1 -1
  35. package/dist/git/models/defaultBranch.d.ts +5 -3
  36. package/dist/git/models/defaultBranch.js +0 -2
  37. package/dist/git/models/diff.d.ts +49 -47
  38. package/dist/git/models/diff.js +0 -2
  39. package/dist/git/models/file.d.ts +20 -18
  40. package/dist/git/models/file.js +0 -2
  41. package/dist/git/models/fileChange.d.ts +45 -43
  42. package/dist/git/models/fileChange.js +60 -66
  43. package/dist/git/models/fileChange.js.map +1 -1
  44. package/dist/git/models/fileStatus.d.ts +25 -23
  45. package/dist/git/models/fileStatus.js +30 -26
  46. package/dist/git/models/fileStatus.js.map +1 -1
  47. package/dist/git/models/graph.d.ts +328 -320
  48. package/dist/git/models/graph.js +31 -27
  49. package/dist/git/models/graph.js.map +1 -1
  50. package/dist/git/models/graphSearch.d.ts +37 -35
  51. package/dist/git/models/graphSearch.js +0 -2
  52. package/dist/git/models/graphSession.d.ts +158 -66
  53. package/dist/git/models/graphSession.js +72 -1
  54. package/dist/git/models/graphSession.js.map +1 -1
  55. package/dist/git/models/issue.d.ts +174 -140
  56. package/dist/git/models/issue.js +86 -84
  57. package/dist/git/models/issue.js.map +1 -1
  58. package/dist/git/models/issueOrPullRequest.d.ts +16 -14
  59. package/dist/git/models/issueOrPullRequest.js +0 -2
  60. package/dist/git/models/lineRange.d.ts +6 -4
  61. package/dist/git/models/lineRange.js +0 -2
  62. package/dist/git/models/log.d.ts +18 -16
  63. package/dist/git/models/log.js +0 -2
  64. package/dist/git/models/mergeConflicts.d.ts +17 -15
  65. package/dist/git/models/mergeConflicts.js +0 -2
  66. package/dist/git/models/patch.d.ts +14 -12
  67. package/dist/git/models/patch.js +0 -2
  68. package/dist/git/models/pausedOperationStatus.d.ts +42 -40
  69. package/dist/git/models/pausedOperationStatus.js +0 -2
  70. package/dist/git/models/pullRequest.d.ts +238 -230
  71. package/dist/git/models/pullRequest.js +151 -163
  72. package/dist/git/models/pullRequest.js.map +1 -1
  73. package/dist/git/models/rebase.d.ts +30 -28
  74. package/dist/git/models/rebase.js +0 -2
  75. package/dist/git/models/reference.d.ts +57 -55
  76. package/dist/git/models/reference.js +0 -2
  77. package/dist/git/models/reflog.d.ts +29 -27
  78. package/dist/git/models/reflog.js +63 -82
  79. package/dist/git/models/reflog.js.map +1 -1
  80. package/dist/git/models/remote.d.ts +34 -32
  81. package/dist/git/models/remote.js +74 -95
  82. package/dist/git/models/remote.js.map +1 -1
  83. package/dist/git/models/remoteProvider.d.ts +86 -84
  84. package/dist/git/models/remoteProvider.js +145 -150
  85. package/dist/git/models/remoteProvider.js.map +1 -1
  86. package/dist/git/models/remoteResource.d.ts +58 -56
  87. package/dist/git/models/remoteResource.js +15 -12
  88. package/dist/git/models/remoteResource.js.map +1 -1
  89. package/dist/git/models/repository.d.ts +144 -142
  90. package/dist/git/models/repository.js +358 -406
  91. package/dist/git/models/repository.js.map +1 -1
  92. package/dist/git/models/repositoryChangeEvent.d.ts +8 -6
  93. package/dist/git/models/repositoryChangeEvent.js +26 -24
  94. package/dist/git/models/repositoryChangeEvent.js.map +1 -1
  95. package/dist/git/models/repositoryIdentities.d.ts +50 -46
  96. package/dist/git/models/repositoryIdentities.js +7 -5
  97. package/dist/git/models/repositoryIdentities.js.map +1 -1
  98. package/dist/git/models/repositoryMetadata.d.ts +9 -7
  99. package/dist/git/models/repositoryMetadata.js +0 -2
  100. package/dist/git/models/resourceDescriptor.d.ts +11 -9
  101. package/dist/git/models/resourceDescriptor.js +0 -2
  102. package/dist/git/models/revision.d.ts +2 -0
  103. package/dist/git/models/revision.js +8 -5
  104. package/dist/git/models/revision.js.map +1 -1
  105. package/dist/git/models/search.d.ts +48 -47
  106. package/dist/git/models/search.js +53 -49
  107. package/dist/git/models/search.js.map +1 -1
  108. package/dist/git/models/shortlog.d.ts +5 -3
  109. package/dist/git/models/shortlog.js +0 -2
  110. package/dist/git/models/signature.d.ts +28 -26
  111. package/dist/git/models/signature.js +0 -2
  112. package/dist/git/models/staging.d.ts +18 -16
  113. package/dist/git/models/staging.js +0 -2
  114. package/dist/git/models/stash.d.ts +5 -3
  115. package/dist/git/models/stash.js +0 -2
  116. package/dist/git/models/status.d.ts +51 -49
  117. package/dist/git/models/status.js +172 -205
  118. package/dist/git/models/status.js.map +1 -1
  119. package/dist/git/models/statusFile.d.ts +35 -34
  120. package/dist/git/models/statusFile.js +112 -125
  121. package/dist/git/models/statusFile.js.map +1 -1
  122. package/dist/git/models/tag.d.ts +34 -32
  123. package/dist/git/models/tag.js +64 -77
  124. package/dist/git/models/tag.js.map +1 -1
  125. package/dist/git/models/tree.d.ts +8 -6
  126. package/dist/git/models/tree.js +0 -2
  127. package/dist/git/models/user.d.ts +6 -4
  128. package/dist/git/models/user.js +0 -2
  129. package/dist/git/models/worktree.d.ts +50 -48
  130. package/dist/git/models/worktree.js +102 -133
  131. package/dist/git/models/worktree.js.map +1 -1
  132. package/dist/git/parsers/diffParser.d.ts +7 -5
  133. package/dist/git/parsers/diffParser.js +438 -587
  134. package/dist/git/parsers/diffParser.js.map +1 -1
  135. package/dist/git/parsers/rebaseTodoParser.d.ts +3 -1
  136. package/dist/git/parsers/rebaseTodoParser.js +165 -208
  137. package/dist/git/parsers/rebaseTodoParser.js.map +1 -1
  138. package/dist/git/providers/blame.d.ts +12 -10
  139. package/dist/git/providers/blame.js +0 -2
  140. package/dist/git/providers/branches.d.ts +96 -94
  141. package/dist/git/providers/branches.js +0 -2
  142. package/dist/git/providers/commits.d.ts +105 -104
  143. package/dist/git/providers/commits.js +0 -2
  144. package/dist/git/providers/config.d.ts +80 -78
  145. package/dist/git/providers/config.js +0 -2
  146. package/dist/git/providers/contributors.d.ts +23 -21
  147. package/dist/git/providers/contributors.js +0 -2
  148. package/dist/git/providers/diff.d.ts +68 -66
  149. package/dist/git/providers/diff.js +0 -2
  150. package/dist/git/providers/graph.d.ts +73 -62
  151. package/dist/git/providers/graph.js +0 -2
  152. package/dist/git/providers/maintenance.d.ts +180 -178
  153. package/dist/git/providers/maintenance.js +0 -20
  154. package/dist/git/providers/operations.d.ts +114 -112
  155. package/dist/git/providers/operations.js +0 -2
  156. package/dist/git/providers/patch.d.ts +26 -24
  157. package/dist/git/providers/patch.js +0 -2
  158. package/dist/git/providers/pausedOperations.d.ts +19 -17
  159. package/dist/git/providers/pausedOperations.js +0 -2
  160. package/dist/git/providers/provider.d.ts +52 -50
  161. package/dist/git/providers/provider.js +0 -2
  162. package/dist/git/providers/refs.d.ts +42 -40
  163. package/dist/git/providers/refs.js +0 -2
  164. package/dist/git/providers/remotes.d.ts +24 -22
  165. package/dist/git/providers/remotes.js +0 -2
  166. package/dist/git/providers/revision.d.ts +25 -23
  167. package/dist/git/providers/revision.js +0 -2
  168. package/dist/git/providers/shared/remotes.d.ts +24 -22
  169. package/dist/git/providers/shared/remotes.js +52 -77
  170. package/dist/git/providers/shared/remotes.js.map +1 -1
  171. package/dist/git/providers/staging.d.ts +37 -35
  172. package/dist/git/providers/staging.js +0 -2
  173. package/dist/git/providers/stash.d.ts +38 -36
  174. package/dist/git/providers/stash.js +0 -2
  175. package/dist/git/providers/status.d.ts +32 -30
  176. package/dist/git/providers/status.js +0 -2
  177. package/dist/git/providers/tags.d.ts +20 -18
  178. package/dist/git/providers/tags.js +0 -2
  179. package/dist/git/providers/types.d.ts +19 -17
  180. package/dist/git/providers/types.js +0 -2
  181. package/dist/git/providers/worktrees.d.ts +26 -24
  182. package/dist/git/providers/worktrees.js +0 -2
  183. package/dist/git/remotes/azure-devops.d.ts +49 -38
  184. package/dist/git/remotes/azure-devops.js +254 -224
  185. package/dist/git/remotes/azure-devops.js.map +1 -1
  186. package/dist/git/remotes/bitbucket-server.d.ts +30 -29
  187. package/dist/git/remotes/bitbucket-server.js +141 -142
  188. package/dist/git/remotes/bitbucket-server.js.map +1 -1
  189. package/dist/git/remotes/bitbucket.d.ts +27 -26
  190. package/dist/git/remotes/bitbucket.js +114 -111
  191. package/dist/git/remotes/bitbucket.js.map +1 -1
  192. package/dist/git/remotes/custom.d.ts +26 -25
  193. package/dist/git/remotes/custom.js +130 -130
  194. package/dist/git/remotes/custom.js.map +1 -1
  195. package/dist/git/remotes/gerrit.d.ts +29 -28
  196. package/dist/git/remotes/gerrit.js +140 -143
  197. package/dist/git/remotes/gerrit.js.map +1 -1
  198. package/dist/git/remotes/gitea.d.ts +28 -27
  199. package/dist/git/remotes/gitea.js +141 -145
  200. package/dist/git/remotes/gitea.js.map +1 -1
  201. package/dist/git/remotes/github.d.ts +35 -34
  202. package/dist/git/remotes/github.js +196 -190
  203. package/dist/git/remotes/github.js.map +1 -1
  204. package/dist/git/remotes/gitlab.d.ts +31 -30
  205. package/dist/git/remotes/gitlab.js +245 -227
  206. package/dist/git/remotes/gitlab.js.map +1 -1
  207. package/dist/git/remotes/google-source.d.ts +16 -14
  208. package/dist/git/remotes/google-source.js +33 -31
  209. package/dist/git/remotes/google-source.js.map +1 -1
  210. package/dist/git/remotes/matcher.d.ts +16 -14
  211. package/dist/git/remotes/matcher.js +141 -124
  212. package/dist/git/remotes/matcher.js.map +1 -1
  213. package/dist/git/repositoryService.d.ts +47 -47
  214. package/dist/git/repositoryService.js +47 -44
  215. package/dist/git/repositoryService.js.map +1 -1
  216. package/dist/git/run.types.d.ts +91 -76
  217. package/dist/git/run.types.js +0 -2
  218. package/dist/git/service.d.ts +73 -71
  219. package/dist/git/service.js +286 -302
  220. package/dist/git/service.js.map +1 -1
  221. package/dist/git/utils/autolink.utils.d.ts +4 -2
  222. package/dist/git/utils/autolink.utils.js +103 -139
  223. package/dist/git/utils/autolink.utils.js.map +1 -1
  224. package/dist/git/utils/blame.utils.d.ts +22 -20
  225. package/dist/git/utils/blame.utils.js +196 -230
  226. package/dist/git/utils/blame.utils.js.map +1 -1
  227. package/dist/git/utils/branch.utils.d.ts +7 -9
  228. package/dist/git/utils/branch.utils.js +89 -115
  229. package/dist/git/utils/branch.utils.js.map +1 -1
  230. package/dist/git/utils/commit.utils.d.ts +10 -16
  231. package/dist/git/utils/commit.utils.js +28 -47
  232. package/dist/git/utils/commit.utils.js.map +1 -1
  233. package/dist/git/utils/config.utils.d.ts +2 -0
  234. package/dist/git/utils/config.utils.js +14 -5
  235. package/dist/git/utils/config.utils.js.map +1 -1
  236. package/dist/git/utils/conflictResolution.utils.d.ts +16 -13
  237. package/dist/git/utils/conflictResolution.utils.js +116 -113
  238. package/dist/git/utils/conflictResolution.utils.js.map +1 -1
  239. package/dist/git/utils/contributor.utils.d.ts +10 -8
  240. package/dist/git/utils/contributor.utils.js +47 -69
  241. package/dist/git/utils/contributor.utils.js.map +1 -1
  242. package/dist/git/utils/fetch.utils.d.ts +2 -0
  243. package/dist/git/utils/fetch.utils.js +33 -44
  244. package/dist/git/utils/fetch.utils.js.map +1 -1
  245. package/dist/git/utils/fileStatus.utils.d.ts +22 -21
  246. package/dist/git/utils/fileStatus.utils.js +59 -45
  247. package/dist/git/utils/fileStatus.utils.js.map +1 -1
  248. package/dist/git/utils/graph.utils.d.ts +19 -1
  249. package/dist/git/utils/graph.utils.js +60 -35
  250. package/dist/git/utils/graph.utils.js.map +1 -1
  251. package/dist/git/utils/issue.utils.d.ts +3 -1
  252. package/dist/git/utils/issue.utils.js +154 -149
  253. package/dist/git/utils/issue.utils.js.map +1 -1
  254. package/dist/git/utils/issueOrPullRequest.utils.d.ts +3 -1
  255. package/dist/git/utils/issueOrPullRequest.utils.js +23 -20
  256. package/dist/git/utils/issueOrPullRequest.utils.js.map +1 -1
  257. package/dist/git/utils/mergeConflicts.utils.d.ts +4 -1
  258. package/dist/git/utils/mergeConflicts.utils.js +28 -15
  259. package/dist/git/utils/mergeConflicts.utils.js.map +1 -1
  260. package/dist/git/utils/pausedOperationStatus.utils.d.ts +7 -42
  261. package/dist/git/utils/pausedOperationStatus.utils.js +83 -146
  262. package/dist/git/utils/pausedOperationStatus.utils.js.map +1 -1
  263. package/dist/git/utils/pullRequest.utils.d.ts +7 -5
  264. package/dist/git/utils/pullRequest.utils.js +180 -187
  265. package/dist/git/utils/pullRequest.utils.js.map +1 -1
  266. package/dist/git/utils/reachability.utils.d.ts +6 -6
  267. package/dist/git/utils/reachability.utils.js +120 -147
  268. package/dist/git/utils/reachability.utils.js.map +1 -1
  269. package/dist/git/utils/rebase.utils.d.ts +3 -1
  270. package/dist/git/utils/rebase.utils.js +87 -71
  271. package/dist/git/utils/rebase.utils.js.map +1 -1
  272. package/dist/git/utils/reference.utils.d.ts +33 -32
  273. package/dist/git/utils/reference.utils.js +152 -179
  274. package/dist/git/utils/reference.utils.js.map +1 -1
  275. package/dist/git/utils/remote.utils.d.ts +5 -3
  276. package/dist/git/utils/remote.utils.js +77 -116
  277. package/dist/git/utils/remote.utils.js.map +1 -1
  278. package/dist/git/utils/repository.utils.d.ts +3 -1
  279. package/dist/git/utils/repository.utils.js +12 -11
  280. package/dist/git/utils/repository.utils.js.map +1 -1
  281. package/dist/git/utils/resourceDescriptor.utils.d.ts +3 -1
  282. package/dist/git/utils/resourceDescriptor.utils.js +8 -14
  283. package/dist/git/utils/resourceDescriptor.utils.js.map +1 -1
  284. package/dist/git/utils/revision.utils.d.ts +11 -10
  285. package/dist/git/utils/revision.utils.js +69 -83
  286. package/dist/git/utils/revision.utils.js.map +1 -1
  287. package/dist/git/utils/search.utils.d.ts +5 -3
  288. package/dist/git/utils/search.utils.js +383 -522
  289. package/dist/git/utils/search.utils.js.map +1 -1
  290. package/dist/git/utils/sorting.d.ts +22 -20
  291. package/dist/git/utils/sorting.js +133 -227
  292. package/dist/git/utils/sorting.js.map +1 -1
  293. package/dist/git/utils/sshKey.utils.d.ts +2 -0
  294. package/dist/git/utils/sshKey.utils.js +10 -15
  295. package/dist/git/utils/sshKey.utils.js.map +1 -1
  296. package/dist/git/utils/status.utils.d.ts +20 -15
  297. package/dist/git/utils/status.utils.js +93 -79
  298. package/dist/git/utils/status.utils.js.map +1 -1
  299. package/dist/git/utils/statusFile.utils.d.ts +6 -4
  300. package/dist/git/utils/statusFile.utils.js +71 -72
  301. package/dist/git/utils/statusFile.utils.js.map +1 -1
  302. package/dist/git/utils/tag.utils.d.ts +4 -2
  303. package/dist/git/utils/tag.utils.js +10 -8
  304. package/dist/git/utils/tag.utils.js.map +1 -1
  305. package/dist/git/utils/tooltip.utils.d.ts +5 -2
  306. package/dist/git/utils/tooltip.utils.js +49 -24
  307. package/dist/git/utils/tooltip.utils.js.map +1 -1
  308. package/dist/git/utils/uriAuthority.d.ts +9 -7
  309. package/dist/git/utils/uriAuthority.js +19 -16
  310. package/dist/git/utils/uriAuthority.js.map +1 -1
  311. package/dist/git/utils/user.utils.d.ts +3 -1
  312. package/dist/git/utils/user.utils.js +6 -10
  313. package/dist/git/utils/user.utils.js.map +1 -1
  314. package/dist/git/utils/worktree.utils.d.ts +5 -3
  315. package/dist/git/utils/worktree.utils.js +14 -12
  316. package/dist/git/utils/worktree.utils.js.map +1 -1
  317. package/dist/git/watching/changeEvent.d.ts +38 -36
  318. package/dist/git/watching/changeEvent.js +60 -67
  319. package/dist/git/watching/changeEvent.js.map +1 -1
  320. package/dist/git/watching/classifyChange.d.ts +3 -1
  321. package/dist/git/watching/classifyChange.js +50 -79
  322. package/dist/git/watching/classifyChange.js.map +1 -1
  323. package/dist/git/watching/gitIgnoreFilter.d.ts +43 -41
  324. package/dist/git/watching/gitIgnoreFilter.js +92 -107
  325. package/dist/git/watching/gitIgnoreFilter.js.map +1 -1
  326. package/dist/git/watching/initWatcher.d.ts +22 -20
  327. package/dist/git/watching/initWatcher.js +58 -57
  328. package/dist/git/watching/initWatcher.js.map +1 -1
  329. package/dist/git/watching/provider.d.ts +8 -7
  330. package/dist/git/watching/provider.js +0 -2
  331. package/dist/git/watching/watchGroup.d.ts +29 -28
  332. package/dist/git/watching/watchGroup.js +105 -137
  333. package/dist/git/watching/watchGroup.js.map +1 -1
  334. package/dist/git/watching/watchService.d.ts +70 -68
  335. package/dist/git/watching/watchService.js +275 -339
  336. package/dist/git/watching/watchService.js.map +1 -1
  337. package/dist/git/watching/watchSession.d.ts +112 -111
  338. package/dist/git/watching/watchSession.js +260 -314
  339. package/dist/git/watching/watchSession.js.map +1 -1
  340. package/dist/git/watching/watcherPatterns.d.ts +4 -2
  341. package/dist/git/watching/watcherPatterns.js +21 -24
  342. package/dist/git/watching/watcherPatterns.js.map +1 -1
  343. package/dist/git-cli/cliGitProvider.d.ts +117 -114
  344. package/dist/git-cli/cliGitProvider.js +261 -283
  345. package/dist/git-cli/cliGitProvider.js.map +1 -1
  346. package/dist/git-cli/exec/eventLoopMonitor.d.ts +26 -0
  347. package/dist/git-cli/exec/eventLoopMonitor.js +61 -0
  348. package/dist/git-cli/exec/eventLoopMonitor.js.map +1 -0
  349. package/dist/git-cli/exec/exec.d.ts +3 -4
  350. package/dist/git-cli/exec/exec.errors.d.ts +2 -2
  351. package/dist/git-cli/exec/exec.errors.js +2 -2
  352. package/dist/git-cli/exec/exec.js +3 -3
  353. package/dist/git-cli/exec/exec.types.d.ts +2 -2
  354. package/dist/git-cli/exec/exec.types.js +0 -2
  355. package/dist/git-cli/exec/features.d.ts +2 -3
  356. package/dist/git-cli/exec/features.js +2 -2
  357. package/dist/git-cli/exec/git.d.ts +224 -205
  358. package/dist/git-cli/exec/git.js +834 -1042
  359. package/dist/git-cli/exec/git.js.map +1 -1
  360. package/dist/git-cli/exec/gitQueue.d.ts +51 -48
  361. package/dist/git-cli/exec/gitQueue.js +225 -260
  362. package/dist/git-cli/exec/gitQueue.js.map +1 -1
  363. package/dist/git-cli/exec/locator.d.ts +8 -6
  364. package/dist/git-cli/exec/locator.js +95 -121
  365. package/dist/git-cli/exec/locator.js.map +1 -1
  366. package/dist/git-cli/parsers/blameParser.d.ts +22 -20
  367. package/dist/git-cli/parsers/blameParser.js +238 -332
  368. package/dist/git-cli/parsers/blameParser.js.map +1 -1
  369. package/dist/git-cli/parsers/indexParser.d.ts +3 -1
  370. package/dist/git-cli/parsers/indexParser.js +146 -221
  371. package/dist/git-cli/parsers/indexParser.js.map +1 -1
  372. package/dist/git-cli/parsers/logParser.d.ts +103 -110
  373. package/dist/git-cli/parsers/logParser.js +998 -1199
  374. package/dist/git-cli/parsers/logParser.js.map +1 -1
  375. package/dist/git-cli/parsers/mergeTreeParser.d.ts +5 -3
  376. package/dist/git-cli/parsers/mergeTreeParser.js +28 -72
  377. package/dist/git-cli/parsers/mergeTreeParser.js.map +1 -1
  378. package/dist/git-cli/parsers/refParser.d.ts +5 -4
  379. package/dist/git-cli/parsers/refParser.js +92 -137
  380. package/dist/git-cli/parsers/refParser.js.map +1 -1
  381. package/dist/git-cli/parsers/reflogParser.d.ts +7 -6
  382. package/dist/git-cli/parsers/reflogParser.js +100 -173
  383. package/dist/git-cli/parsers/reflogParser.js.map +1 -1
  384. package/dist/git-cli/parsers/remoteParser.d.ts +4 -2
  385. package/dist/git-cli/parsers/remoteParser.js +74 -126
  386. package/dist/git-cli/parsers/remoteParser.js.map +1 -1
  387. package/dist/git-cli/parsers/shortlogParser.d.ts +4 -2
  388. package/dist/git-cli/parsers/shortlogParser.js +44 -49
  389. package/dist/git-cli/parsers/shortlogParser.js.map +1 -1
  390. package/dist/git-cli/parsers/signatureParser.d.ts +3 -1
  391. package/dist/git-cli/parsers/signatureParser.js +82 -124
  392. package/dist/git-cli/parsers/signatureParser.js.map +1 -1
  393. package/dist/git-cli/parsers/sshSignatureParser.d.ts +5 -3
  394. package/dist/git-cli/parsers/sshSignatureParser.js +55 -64
  395. package/dist/git-cli/parsers/sshSignatureParser.js.map +1 -1
  396. package/dist/git-cli/parsers/statusParser.d.ts +4 -2
  397. package/dist/git-cli/parsers/statusParser.js +148 -205
  398. package/dist/git-cli/parsers/statusParser.js.map +1 -1
  399. package/dist/git-cli/parsers/treeParser.d.ts +3 -1
  400. package/dist/git-cli/parsers/treeParser.js +57 -108
  401. package/dist/git-cli/parsers/treeParser.js.map +1 -1
  402. package/dist/git-cli/parsers/worktreeParser.d.ts +4 -3
  403. package/dist/git-cli/parsers/worktreeParser.js +76 -132
  404. package/dist/git-cli/parsers/worktreeParser.js.map +1 -1
  405. package/dist/git-cli/providers/blame.d.ts +25 -23
  406. package/dist/git-cli/providers/blame.js +404 -413
  407. package/dist/git-cli/providers/blame.js.map +1 -1
  408. package/dist/git-cli/providers/branches.d.ts +117 -116
  409. package/dist/git-cli/providers/branches.js +1132 -1468
  410. package/dist/git-cli/providers/branches.js.map +1 -1
  411. package/dist/git-cli/providers/commitFilesetUtils.d.ts +4 -2
  412. package/dist/git-cli/providers/commitFilesetUtils.js +36 -18
  413. package/dist/git-cli/providers/commitFilesetUtils.js.map +1 -1
  414. package/dist/git-cli/providers/commits.d.ts +58 -56
  415. package/dist/git-cli/providers/commits.js +1084 -1423
  416. package/dist/git-cli/providers/commits.js.map +1 -1
  417. package/dist/git-cli/providers/config.d.ts +129 -127
  418. package/dist/git-cli/providers/config.js +612 -831
  419. package/dist/git-cli/providers/config.js.map +1 -1
  420. package/dist/git-cli/providers/contributors.d.ts +29 -28
  421. package/dist/git-cli/providers/contributors.js +246 -351
  422. package/dist/git-cli/providers/contributors.js.map +1 -1
  423. package/dist/git-cli/providers/diff.d.ts +97 -94
  424. package/dist/git-cli/providers/diff.js +638 -845
  425. package/dist/git-cli/providers/diff.js.map +1 -1
  426. package/dist/git-cli/providers/graph.d.ts +66 -68
  427. package/dist/git-cli/providers/graph.js +1400 -1848
  428. package/dist/git-cli/providers/graph.js.map +1 -1
  429. package/dist/git-cli/providers/maintenance.d.ts +305 -303
  430. package/dist/git-cli/providers/maintenance.js +1526 -2078
  431. package/dist/git-cli/providers/maintenance.js.map +1 -1
  432. package/dist/git-cli/providers/operations.d.ts +96 -94
  433. package/dist/git-cli/providers/operations.js +664 -724
  434. package/dist/git-cli/providers/operations.js.map +1 -1
  435. package/dist/git-cli/providers/patch.d.ts +37 -35
  436. package/dist/git-cli/providers/patch.js +206 -307
  437. package/dist/git-cli/providers/patch.js.map +1 -1
  438. package/dist/git-cli/providers/pausedOperations.d.ts +30 -28
  439. package/dist/git-cli/providers/pausedOperations.js +475 -506
  440. package/dist/git-cli/providers/pausedOperations.js.map +1 -1
  441. package/dist/git-cli/providers/refs.d.ts +50 -48
  442. package/dist/git-cli/providers/refs.js +335 -479
  443. package/dist/git-cli/providers/refs.js.map +1 -1
  444. package/dist/git-cli/providers/remotes.d.ts +23 -21
  445. package/dist/git-cli/providers/remotes.js +71 -92
  446. package/dist/git-cli/providers/remotes.js.map +1 -1
  447. package/dist/git-cli/providers/revision.d.ts +27 -25
  448. package/dist/git-cli/providers/revision.js +288 -304
  449. package/dist/git-cli/providers/revision.js.map +1 -1
  450. package/dist/git-cli/providers/staging.d.ts +31 -29
  451. package/dist/git-cli/providers/staging.js +140 -189
  452. package/dist/git-cli/providers/staging.js.map +1 -1
  453. package/dist/git-cli/providers/stash.d.ts +44 -42
  454. package/dist/git-cli/providers/stash.js +288 -387
  455. package/dist/git-cli/providers/stash.js.map +1 -1
  456. package/dist/git-cli/providers/status.d.ts +61 -59
  457. package/dist/git-cli/providers/status.js +332 -453
  458. package/dist/git-cli/providers/status.js.map +1 -1
  459. package/dist/git-cli/providers/tags.d.ts +31 -29
  460. package/dist/git-cli/providers/tags.js +158 -215
  461. package/dist/git-cli/providers/tags.js.map +1 -1
  462. package/dist/git-cli/providers/worktrees.d.ts +35 -33
  463. package/dist/git-cli/providers/worktrees.js +178 -203
  464. package/dist/git-cli/providers/worktrees.js.map +1 -1
  465. package/dist/git-cli/service.d.ts +28 -26
  466. package/dist/git-cli/service.js +38 -40
  467. package/dist/git-cli/service.js.map +1 -1
  468. package/dist/ipc/discovery.d.ts +19 -17
  469. package/dist/ipc/discovery.js +104 -128
  470. package/dist/ipc/discovery.js.map +1 -1
  471. package/dist/ipc/ipcServer.d.ts +16 -14
  472. package/dist/ipc/ipcServer.js +139 -160
  473. package/dist/ipc/ipcServer.js.map +1 -1
  474. package/dist/plus/agents/agentCapabilities.d.ts +50 -37
  475. package/dist/plus/agents/agentCapabilities.js +138 -173
  476. package/dist/plus/agents/agentCapabilities.js.map +1 -1
  477. package/dist/plus/agents/providers/claudeCodeTranscript.d.ts +153 -151
  478. package/dist/plus/agents/providers/claudeCodeTranscript.js +605 -679
  479. package/dist/plus/agents/providers/claudeCodeTranscript.js.map +1 -1
  480. package/dist/plus/agents/providers/gkAgentProvider.d.ts +237 -240
  481. package/dist/plus/agents/providers/gkAgentProvider.js +2162 -3334
  482. package/dist/plus/agents/providers/gkAgentProvider.js.map +1 -1
  483. package/dist/plus/agents/sanitizePrompt.d.ts +2 -0
  484. package/dist/plus/agents/sanitizePrompt.js +58 -91
  485. package/dist/plus/agents/sanitizePrompt.js.map +1 -1
  486. package/dist/plus/agents/stateMachine.d.ts +7 -5
  487. package/dist/plus/agents/stateMachine.js +135 -163
  488. package/dist/plus/agents/stateMachine.js.map +1 -1
  489. package/dist/plus/agents/types.d.ts +333 -336
  490. package/dist/plus/agents/types.js +48 -48
  491. package/dist/plus/agents/types.js.map +1 -1
  492. package/dist/plus/ai/constants.d.ts +3 -1
  493. package/dist/plus/ai/constants.js +88 -84
  494. package/dist/plus/ai/constants.js.map +1 -1
  495. package/dist/plus/ai/errors.d.ts +22 -18
  496. package/dist/plus/ai/errors.js +158 -138
  497. package/dist/plus/ai/errors.js.map +1 -1
  498. package/dist/plus/ai/models/conversation.d.ts +11 -9
  499. package/dist/plus/ai/models/conversation.js +30 -26
  500. package/dist/plus/ai/models/conversation.js.map +1 -1
  501. package/dist/plus/ai/models/model.d.ts +29 -27
  502. package/dist/plus/ai/models/model.js +663 -471
  503. package/dist/plus/ai/models/model.js.map +1 -1
  504. package/dist/plus/ai/models/promptTemplates.d.ts +56 -55
  505. package/dist/plus/ai/models/promptTemplates.js +0 -2
  506. package/dist/plus/ai/models/provider.d.ts +79 -77
  507. package/dist/plus/ai/models/provider.js +0 -2
  508. package/dist/plus/ai/models/results.d.ts +27 -21
  509. package/dist/plus/ai/models/results.js +0 -2
  510. package/dist/plus/ai/prompts.d.ts +4 -2
  511. package/dist/plus/ai/prompts.js +301 -190
  512. package/dist/plus/ai/prompts.js.map +1 -1
  513. package/dist/plus/ai/providers/anthropicProvider.d.ts +33 -32
  514. package/dist/plus/ai/providers/anthropicProvider.js +484 -381
  515. package/dist/plus/ai/providers/anthropicProvider.js.map +1 -1
  516. package/dist/plus/ai/providers/azureProvider.d.ts +18 -16
  517. package/dist/plus/ai/providers/azureProvider.js +53 -53
  518. package/dist/plus/ai/providers/azureProvider.js.map +1 -1
  519. package/dist/plus/ai/providers/context.d.ts +23 -20
  520. package/dist/plus/ai/providers/context.js +0 -2
  521. package/dist/plus/ai/providers/deepSeekProvider.d.ts +17 -15
  522. package/dist/plus/ai/providers/deepSeekProvider.js +45 -39
  523. package/dist/plus/ai/providers/deepSeekProvider.js.map +1 -1
  524. package/dist/plus/ai/providers/geminiProvider.d.ts +16 -14
  525. package/dist/plus/ai/providers/geminiProvider.js +316 -230
  526. package/dist/plus/ai/providers/geminiProvider.js.map +1 -1
  527. package/dist/plus/ai/providers/gitkrakenProvider.d.ts +22 -20
  528. package/dist/plus/ai/providers/gitkrakenProvider.js +165 -260
  529. package/dist/plus/ai/providers/gitkrakenProvider.js.map +1 -1
  530. package/dist/plus/ai/providers/huggingFaceProvider.d.ts +16 -14
  531. package/dist/plus/ai/providers/huggingFaceProvider.js +45 -46
  532. package/dist/plus/ai/providers/huggingFaceProvider.js.map +1 -1
  533. package/dist/plus/ai/providers/mistralProvider.d.ts +22 -20
  534. package/dist/plus/ai/providers/mistralProvider.js +263 -195
  535. package/dist/plus/ai/providers/mistralProvider.js.map +1 -1
  536. package/dist/plus/ai/providers/ollamaProvider.d.ts +26 -24
  537. package/dist/plus/ai/providers/ollamaProvider.js +156 -189
  538. package/dist/plus/ai/providers/ollamaProvider.js.map +1 -1
  539. package/dist/plus/ai/providers/openAICompatibleProvider.d.ts +18 -16
  540. package/dist/plus/ai/providers/openAICompatibleProvider.js +50 -50
  541. package/dist/plus/ai/providers/openAICompatibleProvider.js.map +1 -1
  542. package/dist/plus/ai/providers/openAICompatibleProviderBase.d.ts +187 -185
  543. package/dist/plus/ai/providers/openAICompatibleProviderBase.js +356 -475
  544. package/dist/plus/ai/providers/openAICompatibleProviderBase.js.map +1 -1
  545. package/dist/plus/ai/providers/openRouterProvider.d.ts +20 -19
  546. package/dist/plus/ai/providers/openRouterProvider.js +60 -65
  547. package/dist/plus/ai/providers/openRouterProvider.js.map +1 -1
  548. package/dist/plus/ai/providers/openaiProvider.d.ts +17 -15
  549. package/dist/plus/ai/providers/openaiProvider.js +35 -34
  550. package/dist/plus/ai/providers/openaiProvider.js.map +1 -1
  551. package/dist/plus/ai/providers/responseFormatCache.d.ts +4 -2
  552. package/dist/plus/ai/providers/responseFormatCache.js +21 -17
  553. package/dist/plus/ai/providers/responseFormatCache.js.map +1 -1
  554. package/dist/plus/ai/providers/xaiProvider.d.ts +16 -14
  555. package/dist/plus/ai/providers/xaiProvider.js +61 -46
  556. package/dist/plus/ai/providers/xaiProvider.js.map +1 -1
  557. package/dist/plus/ai/utils/ai.utils.d.ts +4 -2
  558. package/dist/plus/ai/utils/ai.utils.js +50 -77
  559. package/dist/plus/ai/utils/ai.utils.js.map +1 -1
  560. package/dist/plus/ai/utils/results.utils.d.ts +7 -5
  561. package/dist/plus/ai/utils/results.utils.js +235 -269
  562. package/dist/plus/ai/utils/results.utils.js.map +1 -1
  563. package/dist/plus/ai/utils/truncation.utils.d.ts +3 -2
  564. package/dist/plus/ai/utils/truncation.utils.js +242 -205
  565. package/dist/plus/ai/utils/truncation.utils.js.map +1 -1
  566. package/dist/plus/git-github/api/config.d.ts +58 -56
  567. package/dist/plus/git-github/api/config.js +15 -11
  568. package/dist/plus/git-github/api/config.js.map +1 -1
  569. package/dist/plus/git-github/api/github.d.ts +500 -497
  570. package/dist/plus/git-github/api/github.js +2326 -3121
  571. package/dist/plus/git-github/api/github.js.map +1 -1
  572. package/dist/plus/git-github/api/github.utils.d.ts +5 -3
  573. package/dist/plus/git-github/api/github.utils.js +24 -19
  574. package/dist/plus/git-github/api/github.utils.js.map +1 -1
  575. package/dist/plus/git-github/api/issueSearchQuery.d.ts +5 -3
  576. package/dist/plus/git-github/api/issueSearchQuery.js +204 -221
  577. package/dist/plus/git-github/api/issueSearchQuery.js.map +1 -1
  578. package/dist/plus/git-github/api/pullRequestSearchQuery.d.ts +6 -5
  579. package/dist/plus/git-github/api/pullRequestSearchQuery.js +89 -116
  580. package/dist/plus/git-github/api/pullRequestSearchQuery.js.map +1 -1
  581. package/dist/plus/git-github/api/token.d.ts +10 -8
  582. package/dist/plus/git-github/api/token.js +0 -2
  583. package/dist/plus/git-github/api/tokenUtils.d.ts +4 -2
  584. package/dist/plus/git-github/api/tokenUtils.js +14 -10
  585. package/dist/plus/git-github/api/tokenUtils.js.map +1 -1
  586. package/dist/plus/git-github/api/types.d.ts +94 -92
  587. package/dist/plus/git-github/api/types.js +0 -2
  588. package/dist/plus/git-github/context.d.ts +65 -63
  589. package/dist/plus/git-github/context.js +13 -9
  590. package/dist/plus/git-github/context.js.map +1 -1
  591. package/dist/plus/git-github/models.d.ts +182 -182
  592. package/dist/plus/git-github/models.js +199 -265
  593. package/dist/plus/git-github/models.js.map +1 -1
  594. package/dist/plus/git-github/providers/github/blame.d.ts +32 -31
  595. package/dist/plus/git-github/providers/github/blame.js +137 -150
  596. package/dist/plus/git-github/providers/github/blame.js.map +1 -1
  597. package/dist/plus/git-github/providers/github/branches.d.ts +42 -40
  598. package/dist/plus/git-github/providers/github/branches.js +199 -274
  599. package/dist/plus/git-github/providers/github/branches.js.map +1 -1
  600. package/dist/plus/git-github/providers/github/commits.d.ts +35 -33
  601. package/dist/plus/git-github/providers/github/commits.js +527 -606
  602. package/dist/plus/git-github/providers/github/commits.js.map +1 -1
  603. package/dist/plus/git-github/providers/github/config.d.ts +10 -8
  604. package/dist/plus/git-github/providers/github/config.js +35 -45
  605. package/dist/plus/git-github/providers/github/config.js.map +1 -1
  606. package/dist/plus/git-github/providers/github/contributors.d.ts +25 -24
  607. package/dist/plus/git-github/providers/github/contributors.js +174 -216
  608. package/dist/plus/git-github/providers/github/contributors.js.map +1 -1
  609. package/dist/plus/git-github/providers/github/diff.d.ts +34 -32
  610. package/dist/plus/git-github/providers/github/diff.js +154 -194
  611. package/dist/plus/git-github/providers/github/diff.js.map +1 -1
  612. package/dist/plus/git-github/providers/github/graph.d.ts +33 -31
  613. package/dist/plus/git-github/providers/github/graph.js +400 -452
  614. package/dist/plus/git-github/providers/github/graph.js.map +1 -1
  615. package/dist/plus/git-github/providers/github/refs.d.ts +33 -31
  616. package/dist/plus/git-github/providers/github/refs.js +97 -131
  617. package/dist/plus/git-github/providers/github/refs.js.map +1 -1
  618. package/dist/plus/git-github/providers/github/remotes.d.ts +8 -6
  619. package/dist/plus/git-github/providers/github/remotes.js +30 -35
  620. package/dist/plus/git-github/providers/github/remotes.js.map +1 -1
  621. package/dist/plus/git-github/providers/github/revision.d.ts +13 -11
  622. package/dist/plus/git-github/providers/github/revision.js +106 -129
  623. package/dist/plus/git-github/providers/github/revision.js.map +1 -1
  624. package/dist/plus/git-github/providers/github/status.d.ts +24 -22
  625. package/dist/plus/git-github/providers/github/status.js +49 -47
  626. package/dist/plus/git-github/providers/github/status.js.map +1 -1
  627. package/dist/plus/git-github/providers/github/tags.d.ts +21 -19
  628. package/dist/plus/git-github/providers/github/tags.js +78 -103
  629. package/dist/plus/git-github/providers/github/tags.js.map +1 -1
  630. package/dist/plus/git-github/providers/githubGitProvider.d.ts +67 -64
  631. package/dist/plus/git-github/providers/githubGitProvider.js +131 -128
  632. package/dist/plus/git-github/providers/githubGitProvider.js.map +1 -1
  633. package/dist/plus/git-github/providers/githubProvider.d.ts +46 -44
  634. package/dist/plus/git-github/providers/githubProvider.js +0 -2
  635. package/dist/plus/integrations/authentication/cloudIntegrationService.d.ts +29 -27
  636. package/dist/plus/integrations/authentication/cloudIntegrationService.js +173 -208
  637. package/dist/plus/integrations/authentication/cloudIntegrationService.js.map +1 -1
  638. package/dist/plus/integrations/authentication/configuredIntegrationService.d.ts +126 -107
  639. package/dist/plus/integrations/authentication/configuredIntegrationService.js +399 -497
  640. package/dist/plus/integrations/authentication/configuredIntegrationService.js.map +1 -1
  641. package/dist/plus/integrations/authentication/integrationAuthenticationProvider.d.ts +73 -72
  642. package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js +153 -232
  643. package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js.map +1 -1
  644. package/dist/plus/integrations/authentication/integrationAuthenticationService.d.ts +61 -59
  645. package/dist/plus/integrations/authentication/integrationAuthenticationService.js +100 -124
  646. package/dist/plus/integrations/authentication/integrationAuthenticationService.js.map +1 -1
  647. package/dist/plus/integrations/authentication/manualTokenProvider.d.ts +14 -12
  648. package/dist/plus/integrations/authentication/manualTokenProvider.js +26 -51
  649. package/dist/plus/integrations/authentication/manualTokenProvider.js.map +1 -1
  650. package/dist/plus/integrations/authentication/models.d.ts +102 -85
  651. package/dist/plus/integrations/authentication/models.js +55 -54
  652. package/dist/plus/integrations/authentication/models.js.map +1 -1
  653. package/dist/plus/integrations/authentication/rejectedTokenTracker.d.ts +19 -17
  654. package/dist/plus/integrations/authentication/rejectedTokenTracker.js +53 -51
  655. package/dist/plus/integrations/authentication/rejectedTokenTracker.js.map +1 -1
  656. package/dist/plus/integrations/collectionMetadata.d.ts +12 -10
  657. package/dist/plus/integrations/collectionMetadata.js +204 -254
  658. package/dist/plus/integrations/collectionMetadata.js.map +1 -1
  659. package/dist/plus/integrations/constants.d.ts +60 -34
  660. package/dist/plus/integrations/constants.js +157 -132
  661. package/dist/plus/integrations/constants.js.map +1 -1
  662. package/dist/plus/integrations/context.d.ts +253 -252
  663. package/dist/plus/integrations/context.js +0 -2
  664. package/dist/plus/integrations/errors.d.ts +26 -25
  665. package/dist/plus/integrations/errors.js +116 -140
  666. package/dist/plus/integrations/errors.js.map +1 -1
  667. package/dist/plus/integrations/index.d.ts +36 -40
  668. package/dist/plus/integrations/index.js +46 -57
  669. package/dist/plus/integrations/index.js.map +1 -1
  670. package/dist/plus/integrations/integrationService.d.ts +445 -440
  671. package/dist/plus/integrations/integrationService.js +1054 -1324
  672. package/dist/plus/integrations/integrationService.js.map +1 -1
  673. package/dist/plus/integrations/lite.d.ts +33 -31
  674. package/dist/plus/integrations/lite.js +84 -122
  675. package/dist/plus/integrations/lite.js.map +1 -1
  676. package/dist/plus/integrations/manager.d.ts +592 -580
  677. package/dist/plus/integrations/manager.js +0 -2
  678. package/dist/plus/integrations/models/gitHostIntegration.d.ts +420 -415
  679. package/dist/plus/integrations/models/gitHostIntegration.js +1180 -1293
  680. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  681. package/dist/plus/integrations/models/integration.d.ts +200 -190
  682. package/dist/plus/integrations/models/integration.js +577 -722
  683. package/dist/plus/integrations/models/integration.js.map +1 -1
  684. package/dist/plus/integrations/models/issueCache.d.ts +15 -13
  685. package/dist/plus/integrations/models/issueCache.js +9 -7
  686. package/dist/plus/integrations/models/issueCache.js.map +1 -1
  687. package/dist/plus/integrations/models/issueReads.d.ts +108 -85
  688. package/dist/plus/integrations/models/issueReads.js +0 -2
  689. package/dist/plus/integrations/models/issuesIntegration.d.ts +72 -71
  690. package/dist/plus/integrations/models/issuesIntegration.js +160 -186
  691. package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
  692. package/dist/plus/integrations/models/pullRequestReads.d.ts +9 -7
  693. package/dist/plus/integrations/models/pullRequestReads.js +0 -2
  694. package/dist/plus/integrations/providerFilters.d.ts +8 -20
  695. package/dist/plus/integrations/providerFilters.js +11 -7
  696. package/dist/plus/integrations/providerFilters.js.map +1 -1
  697. package/dist/plus/integrations/providers/accounts.d.ts +7 -5
  698. package/dist/plus/integrations/providers/accounts.js +21 -31
  699. package/dist/plus/integrations/providers/accounts.js.map +1 -1
  700. package/dist/plus/integrations/providers/apiClients.d.ts +13 -10
  701. package/dist/plus/integrations/providers/apiClients.js +51 -36
  702. package/dist/plus/integrations/providers/apiClients.js.map +1 -1
  703. package/dist/plus/integrations/providers/apiConfig.d.ts +31 -29
  704. package/dist/plus/integrations/providers/apiConfig.js +18 -16
  705. package/dist/plus/integrations/providers/apiConfig.js.map +1 -1
  706. package/dist/plus/integrations/providers/azure/azure.d.ts +69 -54
  707. package/dist/plus/integrations/providers/azure/azure.js +453 -515
  708. package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
  709. package/dist/plus/integrations/providers/azure/models.d.ts +258 -229
  710. package/dist/plus/integrations/providers/azure/models.js +241 -203
  711. package/dist/plus/integrations/providers/azure/models.js.map +1 -1
  712. package/dist/plus/integrations/providers/azureDevOps.d.ts +132 -130
  713. package/dist/plus/integrations/providers/azureDevOps.js +775 -921
  714. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
  715. package/dist/plus/integrations/providers/bitbucket/bitbucket.d.ts +50 -48
  716. package/dist/plus/integrations/providers/bitbucket/bitbucket.js +417 -585
  717. package/dist/plus/integrations/providers/bitbucket/bitbucket.js.map +1 -1
  718. package/dist/plus/integrations/providers/bitbucket/models.d.ts +179 -179
  719. package/dist/plus/integrations/providers/bitbucket/models.js +96 -144
  720. package/dist/plus/integrations/providers/bitbucket/models.js.map +1 -1
  721. package/dist/plus/integrations/providers/bitbucket-server/models.d.ts +111 -109
  722. package/dist/plus/integrations/providers/bitbucket-server/models.js +102 -121
  723. package/dist/plus/integrations/providers/bitbucket-server/models.js.map +1 -1
  724. package/dist/plus/integrations/providers/bitbucket-server.d.ts +79 -79
  725. package/dist/plus/integrations/providers/bitbucket-server.js +205 -241
  726. package/dist/plus/integrations/providers/bitbucket-server.js.map +1 -1
  727. package/dist/plus/integrations/providers/bitbucket.d.ts +80 -79
  728. package/dist/plus/integrations/providers/bitbucket.js +390 -477
  729. package/dist/plus/integrations/providers/bitbucket.js.map +1 -1
  730. package/dist/plus/integrations/providers/github/github.d.ts +5 -3
  731. package/dist/plus/integrations/providers/github/github.js +38 -45
  732. package/dist/plus/integrations/providers/github/github.js.map +1 -1
  733. package/dist/plus/integrations/providers/github/github.utils.d.ts +8 -5
  734. package/dist/plus/integrations/providers/github/github.utils.js +7 -4
  735. package/dist/plus/integrations/providers/github/github.utils.js.map +1 -1
  736. package/dist/plus/integrations/providers/github/models.d.ts +2 -3
  737. package/dist/plus/integrations/providers/github/models.js +2 -2
  738. package/dist/plus/integrations/providers/github.d.ts +174 -172
  739. package/dist/plus/integrations/providers/github.js +431 -523
  740. package/dist/plus/integrations/providers/github.js.map +1 -1
  741. package/dist/plus/integrations/providers/gitlab/gitlab.d.ts +67 -66
  742. package/dist/plus/integrations/providers/gitlab/gitlab.js +546 -757
  743. package/dist/plus/integrations/providers/gitlab/gitlab.js.map +1 -1
  744. package/dist/plus/integrations/providers/gitlab/gitlab.utils.d.ts +7 -5
  745. package/dist/plus/integrations/providers/gitlab/gitlab.utils.js +45 -46
  746. package/dist/plus/integrations/providers/gitlab/gitlab.utils.js.map +1 -1
  747. package/dist/plus/integrations/providers/gitlab/models.d.ts +109 -107
  748. package/dist/plus/integrations/providers/gitlab/models.js +74 -89
  749. package/dist/plus/integrations/providers/gitlab/models.js.map +1 -1
  750. package/dist/plus/integrations/providers/gitlab.d.ts +115 -114
  751. package/dist/plus/integrations/providers/gitlab.js +440 -528
  752. package/dist/plus/integrations/providers/gitlab.js.map +1 -1
  753. package/dist/plus/integrations/providers/issueSorts.d.ts +3 -1
  754. package/dist/plus/integrations/providers/issueSorts.js +66 -62
  755. package/dist/plus/integrations/providers/issueSorts.js.map +1 -1
  756. package/dist/plus/integrations/providers/jira-server.d.ts +90 -0
  757. package/dist/plus/integrations/providers/jira-server.js +322 -0
  758. package/dist/plus/integrations/providers/jira-server.js.map +1 -0
  759. package/dist/plus/integrations/providers/jira.d.ts +41 -39
  760. package/dist/plus/integrations/providers/jira.js +378 -440
  761. package/dist/plus/integrations/providers/jira.js.map +1 -1
  762. package/dist/plus/integrations/providers/jiraIssueByKey.d.ts +3 -1
  763. package/dist/plus/integrations/providers/jiraIssueByKey.js +85 -89
  764. package/dist/plus/integrations/providers/jiraIssueByKey.js.map +1 -1
  765. package/dist/plus/integrations/providers/linear.d.ts +63 -62
  766. package/dist/plus/integrations/providers/linear.js +315 -358
  767. package/dist/plus/integrations/providers/linear.js.map +1 -1
  768. package/dist/plus/integrations/providers/models.d.ts +514 -503
  769. package/dist/plus/integrations/providers/models.js +818 -856
  770. package/dist/plus/integrations/providers/models.js.map +1 -1
  771. package/dist/plus/integrations/providers/providerErrors.d.ts +35 -2
  772. package/dist/plus/integrations/providers/providerErrors.js +120 -81
  773. package/dist/plus/integrations/providers/providerErrors.js.map +1 -1
  774. package/dist/plus/integrations/providers/providersApi.d.ts +299 -271
  775. package/dist/plus/integrations/providers/providersApi.js +1048 -1001
  776. package/dist/plus/integrations/providers/providersApi.js.map +1 -1
  777. package/dist/plus/integrations/providers/pullRequestReviews.d.ts +21 -21
  778. package/dist/plus/integrations/providers/pullRequestReviews.js +64 -90
  779. package/dist/plus/integrations/providers/pullRequestReviews.js.map +1 -1
  780. package/dist/plus/integrations/providers/trello.d.ts +35 -34
  781. package/dist/plus/integrations/providers/trello.js +136 -143
  782. package/dist/plus/integrations/providers/trello.js.map +1 -1
  783. package/dist/plus/integrations/providers/utils/providerPaging.d.ts +4 -2
  784. package/dist/plus/integrations/providers/utils/providerPaging.js +131 -156
  785. package/dist/plus/integrations/providers/utils/providerPaging.js.map +1 -1
  786. package/dist/plus/integrations/providers/utils.d.ts +51 -50
  787. package/dist/plus/integrations/providers/utils.js +185 -296
  788. package/dist/plus/integrations/providers/utils.js.map +1 -1
  789. package/dist/plus/integrations/reads/broaden.d.ts +10 -8
  790. package/dist/plus/integrations/reads/broaden.js +320 -439
  791. package/dist/plus/integrations/reads/broaden.js.map +1 -1
  792. package/dist/plus/integrations/reads/context.d.ts +47 -45
  793. package/dist/plus/integrations/reads/context.js +0 -2
  794. package/dist/plus/integrations/reads/counts.d.ts +74 -72
  795. package/dist/plus/integrations/reads/counts.js +233 -314
  796. package/dist/plus/integrations/reads/counts.js.map +1 -1
  797. package/dist/plus/integrations/reads/cursors.d.ts +26 -24
  798. package/dist/plus/integrations/reads/cursors.js +80 -115
  799. package/dist/plus/integrations/reads/cursors.js.map +1 -1
  800. package/dist/plus/integrations/reads/drains.d.ts +24 -23
  801. package/dist/plus/integrations/reads/drains.js +225 -298
  802. package/dist/plus/integrations/reads/drains.js.map +1 -1
  803. package/dist/plus/integrations/reads/filters.d.ts +84 -80
  804. package/dist/plus/integrations/reads/filters.js +326 -414
  805. package/dist/plus/integrations/reads/filters.js.map +1 -1
  806. package/dist/plus/integrations/reads/hierarchy.d.ts +14 -12
  807. package/dist/plus/integrations/reads/hierarchy.js +248 -332
  808. package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
  809. package/dist/plus/integrations/reads/hierarchy.utils.d.ts +11 -9
  810. package/dist/plus/integrations/reads/hierarchy.utils.js +109 -108
  811. package/dist/plus/integrations/reads/hierarchy.utils.js.map +1 -1
  812. package/dist/plus/integrations/reads/issueBatch.d.ts +28 -26
  813. package/dist/plus/integrations/reads/issueBatch.js +83 -85
  814. package/dist/plus/integrations/reads/issueBatch.js.map +1 -1
  815. package/dist/plus/integrations/reads/issueTracker.d.ts +42 -33
  816. package/dist/plus/integrations/reads/issueTracker.js +265 -386
  817. package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
  818. package/dist/plus/integrations/reads/issues.d.ts +50 -47
  819. package/dist/plus/integrations/reads/issues.js +202 -331
  820. package/dist/plus/integrations/reads/issues.js.map +1 -1
  821. package/dist/plus/integrations/reads/ordering.d.ts +32 -30
  822. package/dist/plus/integrations/reads/ordering.js +47 -43
  823. package/dist/plus/integrations/reads/ordering.js.map +1 -1
  824. package/dist/plus/integrations/reads/paging.d.ts +59 -57
  825. package/dist/plus/integrations/reads/paging.js +244 -261
  826. package/dist/plus/integrations/reads/paging.js.map +1 -1
  827. package/dist/plus/integrations/reads/pullRequests.d.ts +37 -35
  828. package/dist/plus/integrations/reads/pullRequests.js +95 -148
  829. package/dist/plus/integrations/reads/pullRequests.js.map +1 -1
  830. package/dist/plus/integrations/reads/resolveRepository.d.ts +15 -13
  831. package/dist/plus/integrations/reads/resolveRepository.js +147 -229
  832. package/dist/plus/integrations/reads/resolveRepository.js.map +1 -1
  833. package/dist/plus/integrations/reads/searchIssues.d.ts +29 -27
  834. package/dist/plus/integrations/reads/searchIssues.js +104 -153
  835. package/dist/plus/integrations/reads/searchIssues.js.map +1 -1
  836. package/dist/plus/integrations/reads/searchPullRequests.d.ts +20 -18
  837. package/dist/plus/integrations/reads/searchPullRequests.js +91 -113
  838. package/dist/plus/integrations/reads/searchPullRequests.js.map +1 -1
  839. package/dist/plus/integrations/reads/sweeps.d.ts +6 -4
  840. package/dist/plus/integrations/reads/sweeps.js +143 -178
  841. package/dist/plus/integrations/reads/sweeps.js.map +1 -1
  842. package/dist/plus/integrations/reads/trackerIssue.d.ts +24 -16
  843. package/dist/plus/integrations/reads/trackerIssue.js +52 -49
  844. package/dist/plus/integrations/reads/trackerIssue.js.map +1 -1
  845. package/dist/plus/integrations/reads/warnings.d.ts +10 -7
  846. package/dist/plus/integrations/reads/warnings.js +264 -291
  847. package/dist/plus/integrations/reads/warnings.js.map +1 -1
  848. package/dist/plus/integrations/results.d.ts +201 -199
  849. package/dist/plus/integrations/results.js +105 -119
  850. package/dist/plus/integrations/results.js.map +1 -1
  851. package/dist/plus/integrations/telemetry.d.ts +7 -5
  852. package/dist/plus/integrations/telemetry.js +9 -9
  853. package/dist/plus/integrations/telemetry.js.map +1 -1
  854. package/dist/plus/integrations/utils/domain.utils.d.ts +21 -0
  855. package/dist/plus/integrations/utils/domain.utils.js +55 -22
  856. package/dist/plus/integrations/utils/domain.utils.js.map +1 -1
  857. package/dist/plus/integrations/utils/integration.utils.d.ts +30 -6
  858. package/dist/plus/integrations/utils/integration.utils.js +154 -161
  859. package/dist/plus/integrations/utils/integration.utils.js.map +1 -1
  860. package/dist/utils/array.d.ts +9 -7
  861. package/dist/utils/array.js +110 -126
  862. package/dist/utils/array.js.map +1 -1
  863. package/dist/utils/base64.d.ts +2 -2
  864. package/dist/utils/base64.js +2 -2
  865. package/dist/utils/brand.d.ts +4 -3
  866. package/dist/utils/brand.js +0 -2
  867. package/dist/utils/cancellation.d.ts +24 -24
  868. package/dist/utils/cancellation.js +152 -167
  869. package/dist/utils/cancellation.js.map +1 -1
  870. package/dist/utils/charCode.d.ts +31 -37
  871. package/dist/utils/charCode.js +41 -37
  872. package/dist/utils/charCode.js.map +1 -1
  873. package/dist/utils/coalescedRun.d.ts +10 -8
  874. package/dist/utils/coalescedRun.js +39 -41
  875. package/dist/utils/coalescedRun.js.map +1 -1
  876. package/dist/utils/color.d.ts +137 -135
  877. package/dist/utils/color.js +535 -660
  878. package/dist/utils/color.js.map +1 -1
  879. package/dist/utils/counter.d.ts +5 -3
  880. package/dist/utils/counter.js +19 -17
  881. package/dist/utils/counter.js.map +1 -1
  882. package/dist/utils/crypto.d.ts +2 -2
  883. package/dist/utils/crypto.js +2 -2
  884. package/dist/utils/date.d.ts +10 -9
  885. package/dist/utils/date.js +312 -350
  886. package/dist/utils/date.js.map +1 -1
  887. package/dist/utils/debounce.d.ts +45 -43
  888. package/dist/utils/debounce.js +175 -206
  889. package/dist/utils/debounce.js.map +1 -1
  890. package/dist/utils/decorators/debounce.d.ts +3 -1
  891. package/dist/utils/decorators/debounce.js +16 -19
  892. package/dist/utils/decorators/debounce.js.map +1 -1
  893. package/dist/utils/decorators/gate.d.ts +13 -11
  894. package/dist/utils/decorators/gate.js +97 -124
  895. package/dist/utils/decorators/gate.js.map +1 -1
  896. package/dist/utils/decorators/log.d.ts +25 -24
  897. package/dist/utils/decorators/log.js +166 -227
  898. package/dist/utils/decorators/log.js.map +1 -1
  899. package/dist/utils/decorators/memoize.d.ts +10 -8
  900. package/dist/utils/decorators/memoize.js +49 -57
  901. package/dist/utils/decorators/memoize.js.map +1 -1
  902. package/dist/utils/decorators/resolver.d.ts +2 -0
  903. package/dist/utils/decorators/resolver.js +64 -75
  904. package/dist/utils/decorators/resolver.js.map +1 -1
  905. package/dist/utils/decorators/sequentialize.d.ts +15 -14
  906. package/dist/utils/decorators/sequentialize.js +61 -80
  907. package/dist/utils/decorators/sequentialize.js.map +1 -1
  908. package/dist/utils/decorators/serializable.d.ts +2 -0
  909. package/dist/utils/decorators/serializable.js +30 -39
  910. package/dist/utils/decorators/serializable.js.map +1 -1
  911. package/dist/utils/dedupedAsyncCache.d.ts +23 -21
  912. package/dist/utils/dedupedAsyncCache.js +63 -66
  913. package/dist/utils/dedupedAsyncCache.js.map +1 -1
  914. package/dist/utils/diff.d.ts +2 -0
  915. package/dist/utils/diff.js +41 -51
  916. package/dist/utils/diff.js.map +1 -1
  917. package/dist/utils/disposable.d.ts +15 -13
  918. package/dist/utils/disposable.js +71 -80
  919. package/dist/utils/disposable.js.map +1 -1
  920. package/dist/utils/dom.d.ts +45 -0
  921. package/dist/utils/dom.js +106 -0
  922. package/dist/utils/dom.js.map +1 -0
  923. package/dist/utils/encoding.d.ts +2 -0
  924. package/dist/utils/encoding.js +7 -5
  925. package/dist/utils/encoding.js.map +1 -1
  926. package/dist/utils/env/browser/base64.d.ts +2 -0
  927. package/dist/utils/env/browser/base64.js +32 -34
  928. package/dist/utils/env/browser/base64.js.map +1 -1
  929. package/dist/utils/env/browser/crypto.d.ts +2 -0
  930. package/dist/utils/env/browser/crypto.js +17 -13
  931. package/dist/utils/env/browser/crypto.js.map +1 -1
  932. package/dist/utils/env/browser/fs.d.ts +2 -0
  933. package/dist/utils/env/browser/fs.js +6 -2
  934. package/dist/utils/env/browser/fs.js.map +1 -1
  935. package/dist/utils/env/browser/hex.d.ts +2 -0
  936. package/dist/utils/env/browser/hex.js +271 -29
  937. package/dist/utils/env/browser/hex.js.map +1 -1
  938. package/dist/utils/env/browser/hrtime.d.ts +2 -0
  939. package/dist/utils/env/browser/hrtime.js +17 -13
  940. package/dist/utils/env/browser/hrtime.js.map +1 -1
  941. package/dist/utils/env/browser/logScope.d.ts +3 -1
  942. package/dist/utils/env/browser/logScope.js +39 -43
  943. package/dist/utils/env/browser/logScope.js.map +1 -1
  944. package/dist/utils/env/browser/md5.d.ts +2 -0
  945. package/dist/utils/env/browser/md5.js +211 -184
  946. package/dist/utils/env/browser/md5.js.map +1 -1
  947. package/dist/utils/env/browser/platform.d.ts +3 -0
  948. package/dist/utils/env/browser/platform.js +11 -4
  949. package/dist/utils/env/browser/platform.js.map +1 -1
  950. package/dist/utils/env/node/base64.d.ts +2 -0
  951. package/dist/utils/env/node/base64.js +13 -11
  952. package/dist/utils/env/node/base64.js.map +1 -1
  953. package/dist/utils/env/node/crypto.d.ts +2 -0
  954. package/dist/utils/env/node/crypto.js +13 -9
  955. package/dist/utils/env/node/crypto.js.map +1 -1
  956. package/dist/utils/env/node/exec.d.ts +66 -65
  957. package/dist/utils/env/node/exec.js +255 -267
  958. package/dist/utils/env/node/exec.js.map +1 -1
  959. package/dist/utils/env/node/fs.d.ts +2 -0
  960. package/dist/utils/env/node/fs.js +7 -3
  961. package/dist/utils/env/node/fs.js.map +1 -1
  962. package/dist/utils/env/node/hex.d.ts +2 -0
  963. package/dist/utils/env/node/hex.js +10 -6
  964. package/dist/utils/env/node/hex.js.map +1 -1
  965. package/dist/utils/env/node/hrtime.d.ts +2 -2
  966. package/dist/utils/env/node/hrtime.js +2 -2
  967. package/dist/utils/env/node/logScope.d.ts +3 -1
  968. package/dist/utils/env/node/logScope.js +52 -42
  969. package/dist/utils/env/node/logScope.js.map +1 -1
  970. package/dist/utils/env/node/platform.d.ts +3 -0
  971. package/dist/utils/env/node/platform.js +11 -4
  972. package/dist/utils/env/node/platform.js.map +1 -1
  973. package/dist/utils/error.d.ts +8 -6
  974. package/dist/utils/error.js +16 -14
  975. package/dist/utils/error.js.map +1 -1
  976. package/dist/utils/event.d.ts +27 -25
  977. package/dist/utils/event.js +141 -170
  978. package/dist/utils/event.js.map +1 -1
  979. package/dist/utils/formatter.d.ts +15 -14
  980. package/dist/utils/formatter.js +96 -129
  981. package/dist/utils/formatter.js.map +1 -1
  982. package/dist/utils/fs.d.ts +2 -2
  983. package/dist/utils/fs.js +2 -2
  984. package/dist/utils/function.d.ts +5 -6
  985. package/dist/utils/function.js +73 -90
  986. package/dist/utils/function.js.map +1 -1
  987. package/dist/utils/fuzzy.d.ts +18 -16
  988. package/dist/utils/fuzzy.js +121 -127
  989. package/dist/utils/fuzzy.js.map +1 -1
  990. package/dist/utils/gitRefs.d.ts +11 -0
  991. package/dist/utils/gitRefs.js +21 -0
  992. package/dist/utils/gitRefs.js.map +1 -0
  993. package/dist/utils/hash.d.ts +2 -0
  994. package/dist/utils/hash.js +49 -47
  995. package/dist/utils/hash.js.map +1 -1
  996. package/dist/utils/hex.d.ts +2 -2
  997. package/dist/utils/hex.js +2 -2
  998. package/dist/utils/hostingServiceType.d.ts +8 -0
  999. package/dist/utils/hostingServiceType.js +0 -0
  1000. package/dist/utils/hrtime.d.ts +4 -2
  1001. package/dist/utils/hrtime.js +8 -5
  1002. package/dist/utils/hrtime.js.map +1 -1
  1003. package/dist/utils/iterable.d.ts +3 -1
  1004. package/dist/utils/iterable.js +591 -616
  1005. package/dist/utils/iterable.js.map +1 -1
  1006. package/dist/utils/keys/chord.d.ts +27 -25
  1007. package/dist/utils/keys/chord.js +108 -122
  1008. package/dist/utils/keys/chord.js.map +1 -1
  1009. package/dist/utils/keys/keybinding.d.ts +98 -22
  1010. package/dist/utils/keys/keybinding.js +151 -53
  1011. package/dist/utils/keys/keybinding.js.map +1 -1
  1012. package/dist/utils/keys/keymapDispatcher.d.ts +66 -0
  1013. package/dist/utils/keys/keymapDispatcher.js +187 -0
  1014. package/dist/utils/keys/keymapDispatcher.js.map +1 -0
  1015. package/dist/utils/lazy.d.ts +14 -12
  1016. package/dist/utils/lazy.js +35 -36
  1017. package/dist/utils/lazy.js.map +1 -1
  1018. package/dist/utils/loggable.d.ts +3 -1
  1019. package/dist/utils/loggable.js +6 -2
  1020. package/dist/utils/loggable.js.map +1 -1
  1021. package/dist/utils/logger.d.ts +48 -46
  1022. package/dist/utils/logger.js +219 -304
  1023. package/dist/utils/logger.js.map +1 -1
  1024. package/dist/utils/logger.scoped.d.ts +30 -28
  1025. package/dist/utils/logger.scoped.js +133 -147
  1026. package/dist/utils/logger.scoped.js.map +1 -1
  1027. package/dist/utils/lruMap.d.ts +34 -32
  1028. package/dist/utils/lruMap.js +97 -101
  1029. package/dist/utils/lruMap.js.map +1 -1
  1030. package/dist/utils/markdown.d.ts +5 -3
  1031. package/dist/utils/markdown.js +52 -88
  1032. package/dist/utils/markdown.js.map +1 -1
  1033. package/dist/utils/mru.d.ts +13 -11
  1034. package/dist/utils/mru.js +57 -62
  1035. package/dist/utils/mru.js.map +1 -1
  1036. package/dist/utils/object.d.ts +9 -34
  1037. package/dist/utils/object.js +132 -191
  1038. package/dist/utils/object.js.map +1 -1
  1039. package/dist/utils/paging.d.ts +36 -34
  1040. package/dist/utils/paging.js +42 -45
  1041. package/dist/utils/paging.js.map +1 -1
  1042. package/dist/utils/path.d.ts +4 -1
  1043. package/dist/utils/path.js +129 -162
  1044. package/dist/utils/path.js.map +1 -1
  1045. package/dist/utils/pausedOperation.d.ts +42 -0
  1046. package/dist/utils/pausedOperation.js +62 -0
  1047. package/dist/utils/pausedOperation.js.map +1 -0
  1048. package/dist/utils/platform.d.ts +2 -2
  1049. package/dist/utils/platform.js +2 -2
  1050. package/dist/utils/plural.d.ts +21 -0
  1051. package/dist/utils/plural.js +136 -0
  1052. package/dist/utils/plural.js.map +1 -0
  1053. package/dist/utils/promise.d.ts +19 -18
  1054. package/dist/utils/promise.js +323 -360
  1055. package/dist/utils/promise.js.map +1 -1
  1056. package/dist/utils/promiseCache.d.ts +331 -330
  1057. package/dist/utils/promiseCache.js +602 -717
  1058. package/dist/utils/promiseCache.js.map +1 -1
  1059. package/dist/utils/resourceUsage.d.ts +2 -0
  1060. package/dist/utils/resourceUsage.js +0 -2
  1061. package/dist/utils/searchTree.d.ts +51 -49
  1062. package/dist/utils/searchTree.js +306 -396
  1063. package/dist/utils/searchTree.js.map +1 -1
  1064. package/dist/utils/stopwatch.d.ts +33 -32
  1065. package/dist/utils/stopwatch.js +67 -81
  1066. package/dist/utils/stopwatch.js.map +1 -1
  1067. package/dist/utils/string.d.ts +17 -24
  1068. package/dist/utils/string.js +787 -803
  1069. package/dist/utils/string.js.map +1 -1
  1070. package/dist/utils/subscriptionManager.d.ts +15 -14
  1071. package/dist/utils/subscriptionManager.js +36 -33
  1072. package/dist/utils/subscriptionManager.js.map +1 -1
  1073. package/dist/utils/trie.d.ts +64 -62
  1074. package/dist/utils/trie.js +438 -580
  1075. package/dist/utils/trie.js.map +1 -1
  1076. package/dist/utils/types.d.ts +8 -16
  1077. package/dist/utils/types.js +0 -2
  1078. package/dist/utils/uri.d.ts +27 -25
  1079. package/dist/utils/uri.js +42 -42
  1080. package/dist/utils/uri.js.map +1 -1
  1081. package/dist/utils/validation.d.ts +13 -12
  1082. package/dist/utils/validation.js +17 -23
  1083. package/dist/utils/validation.js.map +1 -1
  1084. package/dist/utils/version.d.ts +6 -5
  1085. package/dist/utils/version.js +77 -104
  1086. package/dist/utils/version.js.map +1 -1
  1087. package/docs/integrations.md +101 -24
  1088. package/docs/kepler-read-api-parity.md +15 -12
  1089. package/package.json +18 -8
  1090. package/dist/git/cache.d.ts.map +0 -1
  1091. package/dist/git/context.d.ts.map +0 -1
  1092. package/dist/git/errors.d.ts.map +0 -1
  1093. package/dist/git/features.d.ts.map +0 -1
  1094. package/dist/git/gitHealth.d.ts.map +0 -1
  1095. package/dist/git/models/author.d.ts.map +0 -1
  1096. package/dist/git/models/author.js.map +0 -1
  1097. package/dist/git/models/autolink.d.ts.map +0 -1
  1098. package/dist/git/models/autolink.js.map +0 -1
  1099. package/dist/git/models/blame.d.ts.map +0 -1
  1100. package/dist/git/models/blame.js.map +0 -1
  1101. package/dist/git/models/branch.d.ts.map +0 -1
  1102. package/dist/git/models/commit.d.ts.map +0 -1
  1103. package/dist/git/models/contributor.d.ts.map +0 -1
  1104. package/dist/git/models/defaultBranch.d.ts.map +0 -1
  1105. package/dist/git/models/defaultBranch.js.map +0 -1
  1106. package/dist/git/models/diff.d.ts.map +0 -1
  1107. package/dist/git/models/diff.js.map +0 -1
  1108. package/dist/git/models/file.d.ts.map +0 -1
  1109. package/dist/git/models/file.js.map +0 -1
  1110. package/dist/git/models/fileChange.d.ts.map +0 -1
  1111. package/dist/git/models/fileStatus.d.ts.map +0 -1
  1112. package/dist/git/models/graph.d.ts.map +0 -1
  1113. package/dist/git/models/graphSearch.d.ts.map +0 -1
  1114. package/dist/git/models/graphSearch.js.map +0 -1
  1115. package/dist/git/models/graphSession.d.ts.map +0 -1
  1116. package/dist/git/models/issue.d.ts.map +0 -1
  1117. package/dist/git/models/issueOrPullRequest.d.ts.map +0 -1
  1118. package/dist/git/models/issueOrPullRequest.js.map +0 -1
  1119. package/dist/git/models/lineRange.d.ts.map +0 -1
  1120. package/dist/git/models/lineRange.js.map +0 -1
  1121. package/dist/git/models/log.d.ts.map +0 -1
  1122. package/dist/git/models/log.js.map +0 -1
  1123. package/dist/git/models/mergeConflicts.d.ts.map +0 -1
  1124. package/dist/git/models/mergeConflicts.js.map +0 -1
  1125. package/dist/git/models/patch.d.ts.map +0 -1
  1126. package/dist/git/models/patch.js.map +0 -1
  1127. package/dist/git/models/pausedOperationStatus.d.ts.map +0 -1
  1128. package/dist/git/models/pausedOperationStatus.js.map +0 -1
  1129. package/dist/git/models/pullRequest.d.ts.map +0 -1
  1130. package/dist/git/models/rebase.d.ts.map +0 -1
  1131. package/dist/git/models/rebase.js.map +0 -1
  1132. package/dist/git/models/reference.d.ts.map +0 -1
  1133. package/dist/git/models/reference.js.map +0 -1
  1134. package/dist/git/models/reflog.d.ts.map +0 -1
  1135. package/dist/git/models/remote.d.ts.map +0 -1
  1136. package/dist/git/models/remoteProvider.d.ts.map +0 -1
  1137. package/dist/git/models/remoteResource.d.ts.map +0 -1
  1138. package/dist/git/models/repository.d.ts.map +0 -1
  1139. package/dist/git/models/repositoryChangeEvent.d.ts.map +0 -1
  1140. package/dist/git/models/repositoryIdentities.d.ts.map +0 -1
  1141. package/dist/git/models/repositoryMetadata.d.ts.map +0 -1
  1142. package/dist/git/models/repositoryMetadata.js.map +0 -1
  1143. package/dist/git/models/resourceDescriptor.d.ts.map +0 -1
  1144. package/dist/git/models/resourceDescriptor.js.map +0 -1
  1145. package/dist/git/models/revision.d.ts.map +0 -1
  1146. package/dist/git/models/search.d.ts.map +0 -1
  1147. package/dist/git/models/shortlog.d.ts.map +0 -1
  1148. package/dist/git/models/shortlog.js.map +0 -1
  1149. package/dist/git/models/signature.d.ts.map +0 -1
  1150. package/dist/git/models/signature.js.map +0 -1
  1151. package/dist/git/models/staging.d.ts.map +0 -1
  1152. package/dist/git/models/staging.js.map +0 -1
  1153. package/dist/git/models/stash.d.ts.map +0 -1
  1154. package/dist/git/models/stash.js.map +0 -1
  1155. package/dist/git/models/status.d.ts.map +0 -1
  1156. package/dist/git/models/statusFile.d.ts.map +0 -1
  1157. package/dist/git/models/tag.d.ts.map +0 -1
  1158. package/dist/git/models/tree.d.ts.map +0 -1
  1159. package/dist/git/models/tree.js.map +0 -1
  1160. package/dist/git/models/user.d.ts.map +0 -1
  1161. package/dist/git/models/user.js.map +0 -1
  1162. package/dist/git/models/worktree.d.ts.map +0 -1
  1163. package/dist/git/parsers/diffParser.d.ts.map +0 -1
  1164. package/dist/git/parsers/rebaseTodoParser.d.ts.map +0 -1
  1165. package/dist/git/providers/blame.d.ts.map +0 -1
  1166. package/dist/git/providers/blame.js.map +0 -1
  1167. package/dist/git/providers/branches.d.ts.map +0 -1
  1168. package/dist/git/providers/branches.js.map +0 -1
  1169. package/dist/git/providers/commits.d.ts.map +0 -1
  1170. package/dist/git/providers/commits.js.map +0 -1
  1171. package/dist/git/providers/config.d.ts.map +0 -1
  1172. package/dist/git/providers/config.js.map +0 -1
  1173. package/dist/git/providers/contributors.d.ts.map +0 -1
  1174. package/dist/git/providers/contributors.js.map +0 -1
  1175. package/dist/git/providers/diff.d.ts.map +0 -1
  1176. package/dist/git/providers/diff.js.map +0 -1
  1177. package/dist/git/providers/graph.d.ts.map +0 -1
  1178. package/dist/git/providers/graph.js.map +0 -1
  1179. package/dist/git/providers/maintenance.d.ts.map +0 -1
  1180. package/dist/git/providers/maintenance.js.map +0 -1
  1181. package/dist/git/providers/operations.d.ts.map +0 -1
  1182. package/dist/git/providers/operations.js.map +0 -1
  1183. package/dist/git/providers/patch.d.ts.map +0 -1
  1184. package/dist/git/providers/patch.js.map +0 -1
  1185. package/dist/git/providers/pausedOperations.d.ts.map +0 -1
  1186. package/dist/git/providers/pausedOperations.js.map +0 -1
  1187. package/dist/git/providers/provider.d.ts.map +0 -1
  1188. package/dist/git/providers/provider.js.map +0 -1
  1189. package/dist/git/providers/refs.d.ts.map +0 -1
  1190. package/dist/git/providers/refs.js.map +0 -1
  1191. package/dist/git/providers/remotes.d.ts.map +0 -1
  1192. package/dist/git/providers/remotes.js.map +0 -1
  1193. package/dist/git/providers/revision.d.ts.map +0 -1
  1194. package/dist/git/providers/revision.js.map +0 -1
  1195. package/dist/git/providers/shared/remotes.d.ts.map +0 -1
  1196. package/dist/git/providers/staging.d.ts.map +0 -1
  1197. package/dist/git/providers/staging.js.map +0 -1
  1198. package/dist/git/providers/stash.d.ts.map +0 -1
  1199. package/dist/git/providers/stash.js.map +0 -1
  1200. package/dist/git/providers/status.d.ts.map +0 -1
  1201. package/dist/git/providers/status.js.map +0 -1
  1202. package/dist/git/providers/tags.d.ts.map +0 -1
  1203. package/dist/git/providers/tags.js.map +0 -1
  1204. package/dist/git/providers/types.d.ts.map +0 -1
  1205. package/dist/git/providers/types.js.map +0 -1
  1206. package/dist/git/providers/worktrees.d.ts.map +0 -1
  1207. package/dist/git/providers/worktrees.js.map +0 -1
  1208. package/dist/git/remotes/azure-devops.d.ts.map +0 -1
  1209. package/dist/git/remotes/bitbucket-server.d.ts.map +0 -1
  1210. package/dist/git/remotes/bitbucket.d.ts.map +0 -1
  1211. package/dist/git/remotes/custom.d.ts.map +0 -1
  1212. package/dist/git/remotes/gerrit.d.ts.map +0 -1
  1213. package/dist/git/remotes/gitea.d.ts.map +0 -1
  1214. package/dist/git/remotes/github.d.ts.map +0 -1
  1215. package/dist/git/remotes/gitlab.d.ts.map +0 -1
  1216. package/dist/git/remotes/google-source.d.ts.map +0 -1
  1217. package/dist/git/remotes/matcher.d.ts.map +0 -1
  1218. package/dist/git/repositoryService.d.ts.map +0 -1
  1219. package/dist/git/run.types.d.ts.map +0 -1
  1220. package/dist/git/run.types.js.map +0 -1
  1221. package/dist/git/service.d.ts.map +0 -1
  1222. package/dist/git/utils/autolink.utils.d.ts.map +0 -1
  1223. package/dist/git/utils/blame.utils.d.ts.map +0 -1
  1224. package/dist/git/utils/branch.utils.d.ts.map +0 -1
  1225. package/dist/git/utils/commit.utils.d.ts.map +0 -1
  1226. package/dist/git/utils/config.utils.d.ts.map +0 -1
  1227. package/dist/git/utils/conflictResolution.utils.d.ts.map +0 -1
  1228. package/dist/git/utils/contributor.utils.d.ts.map +0 -1
  1229. package/dist/git/utils/fetch.utils.d.ts.map +0 -1
  1230. package/dist/git/utils/fileStatus.utils.d.ts.map +0 -1
  1231. package/dist/git/utils/graph.utils.d.ts.map +0 -1
  1232. package/dist/git/utils/issue.utils.d.ts.map +0 -1
  1233. package/dist/git/utils/issueOrPullRequest.utils.d.ts.map +0 -1
  1234. package/dist/git/utils/mergeConflicts.utils.d.ts.map +0 -1
  1235. package/dist/git/utils/pausedOperationStatus.utils.d.ts.map +0 -1
  1236. package/dist/git/utils/pullRequest.utils.d.ts.map +0 -1
  1237. package/dist/git/utils/reachability.utils.d.ts.map +0 -1
  1238. package/dist/git/utils/rebase.utils.d.ts.map +0 -1
  1239. package/dist/git/utils/reference.utils.d.ts.map +0 -1
  1240. package/dist/git/utils/remote.utils.d.ts.map +0 -1
  1241. package/dist/git/utils/repository.utils.d.ts.map +0 -1
  1242. package/dist/git/utils/resourceDescriptor.utils.d.ts.map +0 -1
  1243. package/dist/git/utils/revision.utils.d.ts.map +0 -1
  1244. package/dist/git/utils/search.utils.d.ts.map +0 -1
  1245. package/dist/git/utils/sorting.d.ts.map +0 -1
  1246. package/dist/git/utils/sshKey.utils.d.ts.map +0 -1
  1247. package/dist/git/utils/status.utils.d.ts.map +0 -1
  1248. package/dist/git/utils/statusFile.utils.d.ts.map +0 -1
  1249. package/dist/git/utils/tag.utils.d.ts.map +0 -1
  1250. package/dist/git/utils/tooltip.utils.d.ts.map +0 -1
  1251. package/dist/git/utils/uriAuthority.d.ts.map +0 -1
  1252. package/dist/git/utils/user.utils.d.ts.map +0 -1
  1253. package/dist/git/utils/worktree.utils.d.ts.map +0 -1
  1254. package/dist/git/watching/changeEvent.d.ts.map +0 -1
  1255. package/dist/git/watching/classifyChange.d.ts.map +0 -1
  1256. package/dist/git/watching/gitIgnoreFilter.d.ts.map +0 -1
  1257. package/dist/git/watching/initWatcher.d.ts.map +0 -1
  1258. package/dist/git/watching/provider.d.ts.map +0 -1
  1259. package/dist/git/watching/provider.js.map +0 -1
  1260. package/dist/git/watching/watchGroup.d.ts.map +0 -1
  1261. package/dist/git/watching/watchService.d.ts.map +0 -1
  1262. package/dist/git/watching/watchSession.d.ts.map +0 -1
  1263. package/dist/git/watching/watcherPatterns.d.ts.map +0 -1
  1264. package/dist/git-cli/cliGitProvider.d.ts.map +0 -1
  1265. package/dist/git-cli/exec/exec.d.ts.map +0 -1
  1266. package/dist/git-cli/exec/exec.errors.d.ts.map +0 -1
  1267. package/dist/git-cli/exec/exec.errors.js.map +0 -1
  1268. package/dist/git-cli/exec/exec.js.map +0 -1
  1269. package/dist/git-cli/exec/exec.types.d.ts.map +0 -1
  1270. package/dist/git-cli/exec/exec.types.js.map +0 -1
  1271. package/dist/git-cli/exec/features.d.ts.map +0 -1
  1272. package/dist/git-cli/exec/features.js.map +0 -1
  1273. package/dist/git-cli/exec/git.d.ts.map +0 -1
  1274. package/dist/git-cli/exec/gitQueue.d.ts.map +0 -1
  1275. package/dist/git-cli/exec/locator.d.ts.map +0 -1
  1276. package/dist/git-cli/parsers/blameParser.d.ts.map +0 -1
  1277. package/dist/git-cli/parsers/indexParser.d.ts.map +0 -1
  1278. package/dist/git-cli/parsers/logParser.d.ts.map +0 -1
  1279. package/dist/git-cli/parsers/mergeTreeParser.d.ts.map +0 -1
  1280. package/dist/git-cli/parsers/refParser.d.ts.map +0 -1
  1281. package/dist/git-cli/parsers/reflogParser.d.ts.map +0 -1
  1282. package/dist/git-cli/parsers/remoteParser.d.ts.map +0 -1
  1283. package/dist/git-cli/parsers/shortlogParser.d.ts.map +0 -1
  1284. package/dist/git-cli/parsers/signatureParser.d.ts.map +0 -1
  1285. package/dist/git-cli/parsers/sshSignatureParser.d.ts.map +0 -1
  1286. package/dist/git-cli/parsers/statusParser.d.ts.map +0 -1
  1287. package/dist/git-cli/parsers/treeParser.d.ts.map +0 -1
  1288. package/dist/git-cli/parsers/worktreeParser.d.ts.map +0 -1
  1289. package/dist/git-cli/providers/blame.d.ts.map +0 -1
  1290. package/dist/git-cli/providers/branches.d.ts.map +0 -1
  1291. package/dist/git-cli/providers/commitFilesetUtils.d.ts.map +0 -1
  1292. package/dist/git-cli/providers/commits.d.ts.map +0 -1
  1293. package/dist/git-cli/providers/config.d.ts.map +0 -1
  1294. package/dist/git-cli/providers/contributors.d.ts.map +0 -1
  1295. package/dist/git-cli/providers/diff.d.ts.map +0 -1
  1296. package/dist/git-cli/providers/graph.d.ts.map +0 -1
  1297. package/dist/git-cli/providers/maintenance.d.ts.map +0 -1
  1298. package/dist/git-cli/providers/operations.d.ts.map +0 -1
  1299. package/dist/git-cli/providers/patch.d.ts.map +0 -1
  1300. package/dist/git-cli/providers/pausedOperations.d.ts.map +0 -1
  1301. package/dist/git-cli/providers/refs.d.ts.map +0 -1
  1302. package/dist/git-cli/providers/remotes.d.ts.map +0 -1
  1303. package/dist/git-cli/providers/revision.d.ts.map +0 -1
  1304. package/dist/git-cli/providers/staging.d.ts.map +0 -1
  1305. package/dist/git-cli/providers/stash.d.ts.map +0 -1
  1306. package/dist/git-cli/providers/status.d.ts.map +0 -1
  1307. package/dist/git-cli/providers/tags.d.ts.map +0 -1
  1308. package/dist/git-cli/providers/worktrees.d.ts.map +0 -1
  1309. package/dist/git-cli/service.d.ts.map +0 -1
  1310. package/dist/ipc/discovery.d.ts.map +0 -1
  1311. package/dist/ipc/ipcServer.d.ts.map +0 -1
  1312. package/dist/plus/agents/agentCapabilities.d.ts.map +0 -1
  1313. package/dist/plus/agents/providers/claudeCodeTranscript.d.ts.map +0 -1
  1314. package/dist/plus/agents/providers/gkAgentProvider.d.ts.map +0 -1
  1315. package/dist/plus/agents/sanitizePrompt.d.ts.map +0 -1
  1316. package/dist/plus/agents/stateMachine.d.ts.map +0 -1
  1317. package/dist/plus/agents/types.d.ts.map +0 -1
  1318. package/dist/plus/ai/constants.d.ts.map +0 -1
  1319. package/dist/plus/ai/errors.d.ts.map +0 -1
  1320. package/dist/plus/ai/models/conversation.d.ts.map +0 -1
  1321. package/dist/plus/ai/models/model.d.ts.map +0 -1
  1322. package/dist/plus/ai/models/promptTemplates.d.ts.map +0 -1
  1323. package/dist/plus/ai/models/promptTemplates.js.map +0 -1
  1324. package/dist/plus/ai/models/provider.d.ts.map +0 -1
  1325. package/dist/plus/ai/models/provider.js.map +0 -1
  1326. package/dist/plus/ai/models/results.d.ts.map +0 -1
  1327. package/dist/plus/ai/models/results.js.map +0 -1
  1328. package/dist/plus/ai/prompts.d.ts.map +0 -1
  1329. package/dist/plus/ai/providers/anthropicProvider.d.ts.map +0 -1
  1330. package/dist/plus/ai/providers/azureProvider.d.ts.map +0 -1
  1331. package/dist/plus/ai/providers/context.d.ts.map +0 -1
  1332. package/dist/plus/ai/providers/context.js.map +0 -1
  1333. package/dist/plus/ai/providers/deepSeekProvider.d.ts.map +0 -1
  1334. package/dist/plus/ai/providers/geminiProvider.d.ts.map +0 -1
  1335. package/dist/plus/ai/providers/gitkrakenProvider.d.ts.map +0 -1
  1336. package/dist/plus/ai/providers/huggingFaceProvider.d.ts.map +0 -1
  1337. package/dist/plus/ai/providers/mistralProvider.d.ts.map +0 -1
  1338. package/dist/plus/ai/providers/ollamaProvider.d.ts.map +0 -1
  1339. package/dist/plus/ai/providers/openAICompatibleProvider.d.ts.map +0 -1
  1340. package/dist/plus/ai/providers/openAICompatibleProviderBase.d.ts.map +0 -1
  1341. package/dist/plus/ai/providers/openRouterProvider.d.ts.map +0 -1
  1342. package/dist/plus/ai/providers/openaiProvider.d.ts.map +0 -1
  1343. package/dist/plus/ai/providers/responseFormatCache.d.ts.map +0 -1
  1344. package/dist/plus/ai/providers/xaiProvider.d.ts.map +0 -1
  1345. package/dist/plus/ai/utils/ai.utils.d.ts.map +0 -1
  1346. package/dist/plus/ai/utils/results.utils.d.ts.map +0 -1
  1347. package/dist/plus/ai/utils/truncation.utils.d.ts.map +0 -1
  1348. package/dist/plus/git-github/api/config.d.ts.map +0 -1
  1349. package/dist/plus/git-github/api/github.d.ts.map +0 -1
  1350. package/dist/plus/git-github/api/github.utils.d.ts.map +0 -1
  1351. package/dist/plus/git-github/api/issueSearchQuery.d.ts.map +0 -1
  1352. package/dist/plus/git-github/api/pullRequestSearchQuery.d.ts.map +0 -1
  1353. package/dist/plus/git-github/api/token.d.ts.map +0 -1
  1354. package/dist/plus/git-github/api/token.js.map +0 -1
  1355. package/dist/plus/git-github/api/tokenUtils.d.ts.map +0 -1
  1356. package/dist/plus/git-github/api/types.d.ts.map +0 -1
  1357. package/dist/plus/git-github/api/types.js.map +0 -1
  1358. package/dist/plus/git-github/context.d.ts.map +0 -1
  1359. package/dist/plus/git-github/models.d.ts.map +0 -1
  1360. package/dist/plus/git-github/providers/github/blame.d.ts.map +0 -1
  1361. package/dist/plus/git-github/providers/github/branches.d.ts.map +0 -1
  1362. package/dist/plus/git-github/providers/github/commits.d.ts.map +0 -1
  1363. package/dist/plus/git-github/providers/github/config.d.ts.map +0 -1
  1364. package/dist/plus/git-github/providers/github/contributors.d.ts.map +0 -1
  1365. package/dist/plus/git-github/providers/github/diff.d.ts.map +0 -1
  1366. package/dist/plus/git-github/providers/github/graph.d.ts.map +0 -1
  1367. package/dist/plus/git-github/providers/github/refs.d.ts.map +0 -1
  1368. package/dist/plus/git-github/providers/github/remotes.d.ts.map +0 -1
  1369. package/dist/plus/git-github/providers/github/revision.d.ts.map +0 -1
  1370. package/dist/plus/git-github/providers/github/status.d.ts.map +0 -1
  1371. package/dist/plus/git-github/providers/github/tags.d.ts.map +0 -1
  1372. package/dist/plus/git-github/providers/githubGitProvider.d.ts.map +0 -1
  1373. package/dist/plus/git-github/providers/githubProvider.d.ts.map +0 -1
  1374. package/dist/plus/git-github/providers/githubProvider.js.map +0 -1
  1375. package/dist/plus/integrations/authentication/cloudIntegrationService.d.ts.map +0 -1
  1376. package/dist/plus/integrations/authentication/configuredIntegrationService.d.ts.map +0 -1
  1377. package/dist/plus/integrations/authentication/integrationAuthenticationProvider.d.ts.map +0 -1
  1378. package/dist/plus/integrations/authentication/integrationAuthenticationService.d.ts.map +0 -1
  1379. package/dist/plus/integrations/authentication/manualTokenProvider.d.ts.map +0 -1
  1380. package/dist/plus/integrations/authentication/models.d.ts.map +0 -1
  1381. package/dist/plus/integrations/authentication/rejectedTokenTracker.d.ts.map +0 -1
  1382. package/dist/plus/integrations/collectionMetadata.d.ts.map +0 -1
  1383. package/dist/plus/integrations/constants.d.ts.map +0 -1
  1384. package/dist/plus/integrations/context.d.ts.map +0 -1
  1385. package/dist/plus/integrations/context.js.map +0 -1
  1386. package/dist/plus/integrations/errors.d.ts.map +0 -1
  1387. package/dist/plus/integrations/index.d.ts.map +0 -1
  1388. package/dist/plus/integrations/integrationService.d.ts.map +0 -1
  1389. package/dist/plus/integrations/lite.d.ts.map +0 -1
  1390. package/dist/plus/integrations/manager.d.ts.map +0 -1
  1391. package/dist/plus/integrations/manager.js.map +0 -1
  1392. package/dist/plus/integrations/models/gitHostIntegration.d.ts.map +0 -1
  1393. package/dist/plus/integrations/models/integration.d.ts.map +0 -1
  1394. package/dist/plus/integrations/models/issueCache.d.ts.map +0 -1
  1395. package/dist/plus/integrations/models/issueReads.d.ts.map +0 -1
  1396. package/dist/plus/integrations/models/issueReads.js.map +0 -1
  1397. package/dist/plus/integrations/models/issuesIntegration.d.ts.map +0 -1
  1398. package/dist/plus/integrations/models/pullRequestReads.d.ts.map +0 -1
  1399. package/dist/plus/integrations/models/pullRequestReads.js.map +0 -1
  1400. package/dist/plus/integrations/providerFilters.d.ts.map +0 -1
  1401. package/dist/plus/integrations/providers/accounts.d.ts.map +0 -1
  1402. package/dist/plus/integrations/providers/apiClients.d.ts.map +0 -1
  1403. package/dist/plus/integrations/providers/apiConfig.d.ts.map +0 -1
  1404. package/dist/plus/integrations/providers/azure/azure.d.ts.map +0 -1
  1405. package/dist/plus/integrations/providers/azure/models.d.ts.map +0 -1
  1406. package/dist/plus/integrations/providers/azureDevOps.d.ts.map +0 -1
  1407. package/dist/plus/integrations/providers/bitbucket/bitbucket.d.ts.map +0 -1
  1408. package/dist/plus/integrations/providers/bitbucket/models.d.ts.map +0 -1
  1409. package/dist/plus/integrations/providers/bitbucket-server/models.d.ts.map +0 -1
  1410. package/dist/plus/integrations/providers/bitbucket-server.d.ts.map +0 -1
  1411. package/dist/plus/integrations/providers/bitbucket.d.ts.map +0 -1
  1412. package/dist/plus/integrations/providers/github/github.d.ts.map +0 -1
  1413. package/dist/plus/integrations/providers/github/github.utils.d.ts.map +0 -1
  1414. package/dist/plus/integrations/providers/github/models.d.ts.map +0 -1
  1415. package/dist/plus/integrations/providers/github/models.js.map +0 -1
  1416. package/dist/plus/integrations/providers/github.d.ts.map +0 -1
  1417. package/dist/plus/integrations/providers/gitlab/gitlab.d.ts.map +0 -1
  1418. package/dist/plus/integrations/providers/gitlab/gitlab.utils.d.ts.map +0 -1
  1419. package/dist/plus/integrations/providers/gitlab/models.d.ts.map +0 -1
  1420. package/dist/plus/integrations/providers/gitlab.d.ts.map +0 -1
  1421. package/dist/plus/integrations/providers/issueSorts.d.ts.map +0 -1
  1422. package/dist/plus/integrations/providers/jira.d.ts.map +0 -1
  1423. package/dist/plus/integrations/providers/jiraIssueByKey.d.ts.map +0 -1
  1424. package/dist/plus/integrations/providers/linear.d.ts.map +0 -1
  1425. package/dist/plus/integrations/providers/models.d.ts.map +0 -1
  1426. package/dist/plus/integrations/providers/providerErrors.d.ts.map +0 -1
  1427. package/dist/plus/integrations/providers/providersApi.d.ts.map +0 -1
  1428. package/dist/plus/integrations/providers/pullRequestReviews.d.ts.map +0 -1
  1429. package/dist/plus/integrations/providers/trello.d.ts.map +0 -1
  1430. package/dist/plus/integrations/providers/utils/providerPaging.d.ts.map +0 -1
  1431. package/dist/plus/integrations/providers/utils.d.ts.map +0 -1
  1432. package/dist/plus/integrations/reads/broaden.d.ts.map +0 -1
  1433. package/dist/plus/integrations/reads/context.d.ts.map +0 -1
  1434. package/dist/plus/integrations/reads/context.js.map +0 -1
  1435. package/dist/plus/integrations/reads/counts.d.ts.map +0 -1
  1436. package/dist/plus/integrations/reads/cursors.d.ts.map +0 -1
  1437. package/dist/plus/integrations/reads/drains.d.ts.map +0 -1
  1438. package/dist/plus/integrations/reads/filters.d.ts.map +0 -1
  1439. package/dist/plus/integrations/reads/hierarchy.d.ts.map +0 -1
  1440. package/dist/plus/integrations/reads/hierarchy.utils.d.ts.map +0 -1
  1441. package/dist/plus/integrations/reads/issueBatch.d.ts.map +0 -1
  1442. package/dist/plus/integrations/reads/issueTracker.d.ts.map +0 -1
  1443. package/dist/plus/integrations/reads/issues.d.ts.map +0 -1
  1444. package/dist/plus/integrations/reads/ordering.d.ts.map +0 -1
  1445. package/dist/plus/integrations/reads/paging.d.ts.map +0 -1
  1446. package/dist/plus/integrations/reads/pullRequests.d.ts.map +0 -1
  1447. package/dist/plus/integrations/reads/resolveRepository.d.ts.map +0 -1
  1448. package/dist/plus/integrations/reads/searchIssues.d.ts.map +0 -1
  1449. package/dist/plus/integrations/reads/searchPullRequests.d.ts.map +0 -1
  1450. package/dist/plus/integrations/reads/sweeps.d.ts.map +0 -1
  1451. package/dist/plus/integrations/reads/trackerIssue.d.ts.map +0 -1
  1452. package/dist/plus/integrations/reads/warnings.d.ts.map +0 -1
  1453. package/dist/plus/integrations/results.d.ts.map +0 -1
  1454. package/dist/plus/integrations/telemetry.d.ts.map +0 -1
  1455. package/dist/plus/integrations/utils/domain.utils.d.ts.map +0 -1
  1456. package/dist/plus/integrations/utils/integration.utils.d.ts.map +0 -1
  1457. package/dist/utils/array.d.ts.map +0 -1
  1458. package/dist/utils/base64.d.ts.map +0 -1
  1459. package/dist/utils/base64.js.map +0 -1
  1460. package/dist/utils/brand.d.ts.map +0 -1
  1461. package/dist/utils/brand.js.map +0 -1
  1462. package/dist/utils/cancellation.d.ts.map +0 -1
  1463. package/dist/utils/charCode.d.ts.map +0 -1
  1464. package/dist/utils/coalescedRun.d.ts.map +0 -1
  1465. package/dist/utils/color.d.ts.map +0 -1
  1466. package/dist/utils/counter.d.ts.map +0 -1
  1467. package/dist/utils/crypto.d.ts.map +0 -1
  1468. package/dist/utils/crypto.js.map +0 -1
  1469. package/dist/utils/date.d.ts.map +0 -1
  1470. package/dist/utils/debounce.d.ts.map +0 -1
  1471. package/dist/utils/decorators/debounce.d.ts.map +0 -1
  1472. package/dist/utils/decorators/gate.d.ts.map +0 -1
  1473. package/dist/utils/decorators/log.d.ts.map +0 -1
  1474. package/dist/utils/decorators/memoize.d.ts.map +0 -1
  1475. package/dist/utils/decorators/resolver.d.ts.map +0 -1
  1476. package/dist/utils/decorators/sequentialize.d.ts.map +0 -1
  1477. package/dist/utils/decorators/serializable.d.ts.map +0 -1
  1478. package/dist/utils/dedupedAsyncCache.d.ts.map +0 -1
  1479. package/dist/utils/diff.d.ts.map +0 -1
  1480. package/dist/utils/disposable.d.ts.map +0 -1
  1481. package/dist/utils/encoding.d.ts.map +0 -1
  1482. package/dist/utils/env/browser/base64.d.ts.map +0 -1
  1483. package/dist/utils/env/browser/crypto.d.ts.map +0 -1
  1484. package/dist/utils/env/browser/fs.d.ts.map +0 -1
  1485. package/dist/utils/env/browser/hex.d.ts.map +0 -1
  1486. package/dist/utils/env/browser/hrtime.d.ts.map +0 -1
  1487. package/dist/utils/env/browser/logScope.d.ts.map +0 -1
  1488. package/dist/utils/env/browser/md5.d.ts.map +0 -1
  1489. package/dist/utils/env/browser/platform.d.ts.map +0 -1
  1490. package/dist/utils/env/node/base64.d.ts.map +0 -1
  1491. package/dist/utils/env/node/crypto.d.ts.map +0 -1
  1492. package/dist/utils/env/node/exec.d.ts.map +0 -1
  1493. package/dist/utils/env/node/fs.d.ts.map +0 -1
  1494. package/dist/utils/env/node/hex.d.ts.map +0 -1
  1495. package/dist/utils/env/node/hrtime.d.ts.map +0 -1
  1496. package/dist/utils/env/node/hrtime.js.map +0 -1
  1497. package/dist/utils/env/node/logScope.d.ts.map +0 -1
  1498. package/dist/utils/env/node/platform.d.ts.map +0 -1
  1499. package/dist/utils/error.d.ts.map +0 -1
  1500. package/dist/utils/event.d.ts.map +0 -1
  1501. package/dist/utils/formatter.d.ts.map +0 -1
  1502. package/dist/utils/fs.d.ts.map +0 -1
  1503. package/dist/utils/fs.js.map +0 -1
  1504. package/dist/utils/function.d.ts.map +0 -1
  1505. package/dist/utils/fuzzy.d.ts.map +0 -1
  1506. package/dist/utils/hash.d.ts.map +0 -1
  1507. package/dist/utils/hex.d.ts.map +0 -1
  1508. package/dist/utils/hex.js.map +0 -1
  1509. package/dist/utils/hrtime.d.ts.map +0 -1
  1510. package/dist/utils/iterable.d.ts.map +0 -1
  1511. package/dist/utils/keys/chord.d.ts.map +0 -1
  1512. package/dist/utils/keys/keybinding.d.ts.map +0 -1
  1513. package/dist/utils/lazy.d.ts.map +0 -1
  1514. package/dist/utils/loggable.d.ts.map +0 -1
  1515. package/dist/utils/logger.d.ts.map +0 -1
  1516. package/dist/utils/logger.scoped.d.ts.map +0 -1
  1517. package/dist/utils/lruMap.d.ts.map +0 -1
  1518. package/dist/utils/markdown.d.ts.map +0 -1
  1519. package/dist/utils/mru.d.ts.map +0 -1
  1520. package/dist/utils/object.d.ts.map +0 -1
  1521. package/dist/utils/paging.d.ts.map +0 -1
  1522. package/dist/utils/path.d.ts.map +0 -1
  1523. package/dist/utils/platform.d.ts.map +0 -1
  1524. package/dist/utils/platform.js.map +0 -1
  1525. package/dist/utils/promise.d.ts.map +0 -1
  1526. package/dist/utils/promiseCache.d.ts.map +0 -1
  1527. package/dist/utils/resourceUsage.d.ts.map +0 -1
  1528. package/dist/utils/resourceUsage.js.map +0 -1
  1529. package/dist/utils/searchTree.d.ts.map +0 -1
  1530. package/dist/utils/stopwatch.d.ts.map +0 -1
  1531. package/dist/utils/string.d.ts.map +0 -1
  1532. package/dist/utils/subscriptionManager.d.ts.map +0 -1
  1533. package/dist/utils/trie.d.ts.map +0 -1
  1534. package/dist/utils/types.d.ts.map +0 -1
  1535. package/dist/utils/types.js.map +0 -1
  1536. package/dist/utils/uri.d.ts.map +0 -1
  1537. package/dist/utils/validation.d.ts.map +0 -1
  1538. package/dist/utils/version.d.ts.map +0 -1
  1539. package/src/git/cache.ts +0 -1859
  1540. package/src/git/context.ts +0 -301
  1541. package/src/git/errors.ts +0 -1168
  1542. package/src/git/features.ts +0 -98
  1543. package/src/git/gitHealth.ts +0 -522
  1544. package/src/git/models/author.ts +0 -19
  1545. package/src/git/models/autolink.ts +0 -59
  1546. package/src/git/models/blame.ts +0 -46
  1547. package/src/git/models/branch.ts +0 -237
  1548. package/src/git/models/commit.ts +0 -673
  1549. package/src/git/models/contributor.ts +0 -84
  1550. package/src/git/models/defaultBranch.ts +0 -6
  1551. package/src/git/models/diff.ts +0 -74
  1552. package/src/git/models/file.ts +0 -29
  1553. package/src/git/models/fileChange.ts +0 -82
  1554. package/src/git/models/fileStatus.ts +0 -36
  1555. package/src/git/models/graph.ts +0 -444
  1556. package/src/git/models/graphSearch.ts +0 -53
  1557. package/src/git/models/graphSession.ts +0 -102
  1558. package/src/git/models/issue.ts +0 -258
  1559. package/src/git/models/issueOrPullRequest.ts +0 -20
  1560. package/src/git/models/lineRange.ts +0 -7
  1561. package/src/git/models/log.ts +0 -21
  1562. package/src/git/models/mergeConflicts.ts +0 -19
  1563. package/src/git/models/patch.ts +0 -25
  1564. package/src/git/models/pausedOperationStatus.ts +0 -63
  1565. package/src/git/models/pullRequest.ts +0 -353
  1566. package/src/git/models/rebase.ts +0 -60
  1567. package/src/git/models/reference.ts +0 -80
  1568. package/src/git/models/reflog.ts +0 -88
  1569. package/src/git/models/remote.ts +0 -100
  1570. package/src/git/models/remoteProvider.ts +0 -291
  1571. package/src/git/models/remoteResource.ts +0 -87
  1572. package/src/git/models/repository.ts +0 -510
  1573. package/src/git/models/repositoryChangeEvent.ts +0 -30
  1574. package/src/git/models/repositoryIdentities.ts +0 -99
  1575. package/src/git/models/repositoryMetadata.ts +0 -12
  1576. package/src/git/models/resourceDescriptor.ts +0 -28
  1577. package/src/git/models/revision.ts +0 -12
  1578. package/src/git/models/search.ts +0 -138
  1579. package/src/git/models/shortlog.ts +0 -6
  1580. package/src/git/models/signature.ts +0 -45
  1581. package/src/git/models/staging.ts +0 -27
  1582. package/src/git/models/stash.ts +0 -6
  1583. package/src/git/models/status.ts +0 -246
  1584. package/src/git/models/statusFile.ts +0 -131
  1585. package/src/git/models/tag.ts +0 -76
  1586. package/src/git/models/tree.ts +0 -10
  1587. package/src/git/models/user.ts +0 -7
  1588. package/src/git/models/worktree.ts +0 -160
  1589. package/src/git/parsers/diffParser.ts +0 -648
  1590. package/src/git/parsers/rebaseTodoParser.ts +0 -169
  1591. package/src/git/providers/blame.ts +0 -40
  1592. package/src/git/providers/branches.ts +0 -143
  1593. package/src/git/providers/commits.ts +0 -174
  1594. package/src/git/providers/config.ts +0 -167
  1595. package/src/git/providers/contributors.ts +0 -34
  1596. package/src/git/providers/diff.ts +0 -133
  1597. package/src/git/providers/graph.ts +0 -95
  1598. package/src/git/providers/maintenance.ts +0 -224
  1599. package/src/git/providers/operations.ts +0 -164
  1600. package/src/git/providers/patch.ts +0 -31
  1601. package/src/git/providers/pausedOperations.ts +0 -23
  1602. package/src/git/providers/provider.ts +0 -64
  1603. package/src/git/providers/refs.ts +0 -68
  1604. package/src/git/providers/remotes.ts +0 -33
  1605. package/src/git/providers/revision.ts +0 -32
  1606. package/src/git/providers/shared/remotes.ts +0 -95
  1607. package/src/git/providers/staging.ts +0 -36
  1608. package/src/git/providers/stash.ts +0 -51
  1609. package/src/git/providers/status.ts +0 -48
  1610. package/src/git/providers/tags.ts +0 -29
  1611. package/src/git/providers/types.ts +0 -33
  1612. package/src/git/providers/worktrees.ts +0 -37
  1613. package/src/git/remotes/azure-devops.ts +0 -307
  1614. package/src/git/remotes/bitbucket-server.ts +0 -189
  1615. package/src/git/remotes/bitbucket.ts +0 -142
  1616. package/src/git/remotes/custom.ts +0 -162
  1617. package/src/git/remotes/gerrit.ts +0 -186
  1618. package/src/git/remotes/gitea.ts +0 -184
  1619. package/src/git/remotes/github.ts +0 -236
  1620. package/src/git/remotes/gitlab.ts +0 -270
  1621. package/src/git/remotes/google-source.ts +0 -51
  1622. package/src/git/remotes/matcher.ts +0 -215
  1623. package/src/git/repositoryService.ts +0 -180
  1624. package/src/git/run.types.ts +0 -156
  1625. package/src/git/service.ts +0 -369
  1626. package/src/git/utils/autolink.utils.ts +0 -202
  1627. package/src/git/utils/blame.utils.ts +0 -298
  1628. package/src/git/utils/branch.utils.ts +0 -165
  1629. package/src/git/utils/commit.utils.ts +0 -89
  1630. package/src/git/utils/config.utils.ts +0 -8
  1631. package/src/git/utils/conflictResolution.utils.ts +0 -157
  1632. package/src/git/utils/contributor.utils.ts +0 -105
  1633. package/src/git/utils/fetch.utils.ts +0 -48
  1634. package/src/git/utils/fileStatus.utils.ts +0 -62
  1635. package/src/git/utils/graph.utils.ts +0 -46
  1636. package/src/git/utils/issue.utils.ts +0 -177
  1637. package/src/git/utils/issueOrPullRequest.utils.ts +0 -23
  1638. package/src/git/utils/mergeConflicts.utils.ts +0 -21
  1639. package/src/git/utils/pausedOperationStatus.utils.ts +0 -184
  1640. package/src/git/utils/pullRequest.utils.ts +0 -244
  1641. package/src/git/utils/reachability.utils.ts +0 -180
  1642. package/src/git/utils/rebase.utils.ts +0 -128
  1643. package/src/git/utils/reference.utils.ts +0 -267
  1644. package/src/git/utils/remote.utils.ts +0 -146
  1645. package/src/git/utils/repository.utils.ts +0 -18
  1646. package/src/git/utils/resourceDescriptor.utils.ts +0 -26
  1647. package/src/git/utils/revision.utils.ts +0 -149
  1648. package/src/git/utils/search.utils.ts +0 -624
  1649. package/src/git/utils/sorting.ts +0 -329
  1650. package/src/git/utils/sshKey.utils.ts +0 -30
  1651. package/src/git/utils/status.utils.ts +0 -115
  1652. package/src/git/utils/statusFile.utils.ts +0 -182
  1653. package/src/git/utils/tag.utils.ts +0 -17
  1654. package/src/git/utils/tooltip.utils.ts +0 -37
  1655. package/src/git/utils/uriAuthority.ts +0 -34
  1656. package/src/git/utils/user.utils.ts +0 -20
  1657. package/src/git/utils/worktree.utils.ts +0 -21
  1658. package/src/git/watching/changeEvent.ts +0 -95
  1659. package/src/git/watching/classifyChange.ts +0 -104
  1660. package/src/git/watching/gitIgnoreFilter.ts +0 -134
  1661. package/src/git/watching/initWatcher.ts +0 -81
  1662. package/src/git/watching/provider.ts +0 -24
  1663. package/src/git/watching/watchGroup.ts +0 -185
  1664. package/src/git/watching/watchService.ts +0 -430
  1665. package/src/git/watching/watchSession.ts +0 -439
  1666. package/src/git/watching/watcherPatterns.ts +0 -51
  1667. package/src/git-cli/cliGitProvider.ts +0 -461
  1668. package/src/git-cli/exec/exec.errors.ts +0 -1
  1669. package/src/git-cli/exec/exec.ts +0 -11
  1670. package/src/git-cli/exec/exec.types.ts +0 -8
  1671. package/src/git-cli/exec/features.ts +0 -2
  1672. package/src/git-cli/exec/git.ts +0 -1371
  1673. package/src/git-cli/exec/gitQueue.ts +0 -326
  1674. package/src/git-cli/exec/locator.ts +0 -157
  1675. package/src/git-cli/parsers/blameParser.ts +0 -374
  1676. package/src/git-cli/parsers/indexParser.ts +0 -192
  1677. package/src/git-cli/parsers/logParser.ts +0 -1365
  1678. package/src/git-cli/parsers/mergeTreeParser.ts +0 -22
  1679. package/src/git-cli/parsers/refParser.ts +0 -110
  1680. package/src/git-cli/parsers/reflogParser.ts +0 -153
  1681. package/src/git-cli/parsers/remoteParser.ts +0 -95
  1682. package/src/git-cli/parsers/shortlogParser.ts +0 -59
  1683. package/src/git-cli/parsers/signatureParser.ts +0 -162
  1684. package/src/git-cli/parsers/sshSignatureParser.ts +0 -85
  1685. package/src/git-cli/parsers/statusParser.ts +0 -209
  1686. package/src/git-cli/parsers/treeParser.ts +0 -61
  1687. package/src/git-cli/parsers/worktreeParser.ts +0 -111
  1688. package/src/git-cli/providers/blame.ts +0 -598
  1689. package/src/git-cli/providers/branches.ts +0 -1931
  1690. package/src/git-cli/providers/commitFilesetUtils.ts +0 -45
  1691. package/src/git-cli/providers/commits.ts +0 -1856
  1692. package/src/git-cli/providers/config.ts +0 -1025
  1693. package/src/git-cli/providers/contributors.ts +0 -360
  1694. package/src/git-cli/providers/diff.ts +0 -1172
  1695. package/src/git-cli/providers/graph.ts +0 -2237
  1696. package/src/git-cli/providers/maintenance.ts +0 -2599
  1697. package/src/git-cli/providers/operations.ts +0 -1016
  1698. package/src/git-cli/providers/patch.ts +0 -305
  1699. package/src/git-cli/providers/pausedOperations.ts +0 -670
  1700. package/src/git-cli/providers/refs.ts +0 -486
  1701. package/src/git-cli/providers/remotes.ts +0 -115
  1702. package/src/git-cli/providers/revision.ts +0 -383
  1703. package/src/git-cli/providers/staging.ts +0 -210
  1704. package/src/git-cli/providers/stash.ts +0 -577
  1705. package/src/git-cli/providers/status.ts +0 -568
  1706. package/src/git-cli/providers/tags.ts +0 -224
  1707. package/src/git-cli/providers/worktrees.ts +0 -285
  1708. package/src/git-cli/service.ts +0 -82
  1709. package/src/ipc/discovery.ts +0 -186
  1710. package/src/ipc/ipcServer.ts +0 -171
  1711. package/src/plus/agents/agentCapabilities.ts +0 -286
  1712. package/src/plus/agents/providers/claudeCodeTranscript.ts +0 -867
  1713. package/src/plus/agents/providers/gkAgentProvider.ts +0 -4036
  1714. package/src/plus/agents/sanitizePrompt.ts +0 -109
  1715. package/src/plus/agents/stateMachine.ts +0 -197
  1716. package/src/plus/agents/types.ts +0 -500
  1717. package/src/plus/ai/constants.ts +0 -121
  1718. package/src/plus/ai/errors.ts +0 -152
  1719. package/src/plus/ai/models/conversation.ts +0 -35
  1720. package/src/plus/ai/models/model.ts +0 -528
  1721. package/src/plus/ai/models/promptTemplates.ts +0 -171
  1722. package/src/plus/ai/models/provider.ts +0 -125
  1723. package/src/plus/ai/models/results.ts +0 -31
  1724. package/src/plus/ai/prompts.ts +0 -849
  1725. package/src/plus/ai/providers/anthropicProvider.ts +0 -458
  1726. package/src/plus/ai/providers/azureProvider.ts +0 -74
  1727. package/src/plus/ai/providers/context.ts +0 -29
  1728. package/src/plus/ai/providers/deepSeekProvider.ts +0 -46
  1729. package/src/plus/ai/providers/geminiProvider.ts +0 -245
  1730. package/src/plus/ai/providers/gitkrakenProvider.ts +0 -347
  1731. package/src/plus/ai/providers/huggingFaceProvider.ts +0 -64
  1732. package/src/plus/ai/providers/mistralProvider.ts +0 -243
  1733. package/src/plus/ai/providers/ollamaProvider.ts +0 -292
  1734. package/src/plus/ai/providers/openAICompatibleProvider.ts +0 -64
  1735. package/src/plus/ai/providers/openAICompatibleProviderBase.ts +0 -654
  1736. package/src/plus/ai/providers/openRouterProvider.ts +0 -101
  1737. package/src/plus/ai/providers/openaiProvider.ts +0 -47
  1738. package/src/plus/ai/providers/responseFormatCache.ts +0 -39
  1739. package/src/plus/ai/providers/xaiProvider.ts +0 -54
  1740. package/src/plus/ai/utils/ai.utils.ts +0 -95
  1741. package/src/plus/ai/utils/results.utils.ts +0 -407
  1742. package/src/plus/ai/utils/truncation.utils.ts +0 -296
  1743. package/src/plus/git-github/api/config.ts +0 -91
  1744. package/src/plus/git-github/api/github.ts +0 -5462
  1745. package/src/plus/git-github/api/github.utils.ts +0 -36
  1746. package/src/plus/git-github/api/issueSearchQuery.ts +0 -273
  1747. package/src/plus/git-github/api/pullRequestSearchQuery.ts +0 -154
  1748. package/src/plus/git-github/api/token.ts +0 -15
  1749. package/src/plus/git-github/api/tokenUtils.ts +0 -15
  1750. package/src/plus/git-github/api/types.ts +0 -197
  1751. package/src/plus/git-github/context.ts +0 -107
  1752. package/src/plus/git-github/models.ts +0 -623
  1753. package/src/plus/git-github/providers/github/blame.ts +0 -225
  1754. package/src/plus/git-github/providers/github/branches.ts +0 -401
  1755. package/src/plus/git-github/providers/github/commits.ts +0 -929
  1756. package/src/plus/git-github/providers/github/config.ts +0 -47
  1757. package/src/plus/git-github/providers/github/contributors.ts +0 -313
  1758. package/src/plus/git-github/providers/github/diff.ts +0 -281
  1759. package/src/plus/git-github/providers/github/graph.ts +0 -635
  1760. package/src/plus/git-github/providers/github/refs.ts +0 -172
  1761. package/src/plus/git-github/providers/github/remotes.ts +0 -44
  1762. package/src/plus/git-github/providers/github/revision.ts +0 -140
  1763. package/src/plus/git-github/providers/github/status.ts +0 -69
  1764. package/src/plus/git-github/providers/github/tags.ts +0 -148
  1765. package/src/plus/git-github/providers/githubGitProvider.ts +0 -168
  1766. package/src/plus/git-github/providers/githubProvider.ts +0 -65
  1767. package/src/plus/integrations/authentication/cloudIntegrationService.ts +0 -292
  1768. package/src/plus/integrations/authentication/configuredIntegrationService.ts +0 -656
  1769. package/src/plus/integrations/authentication/integrationAuthenticationProvider.ts +0 -385
  1770. package/src/plus/integrations/authentication/integrationAuthenticationService.ts +0 -148
  1771. package/src/plus/integrations/authentication/manualTokenProvider.ts +0 -79
  1772. package/src/plus/integrations/authentication/models.ts +0 -202
  1773. package/src/plus/integrations/authentication/rejectedTokenTracker.ts +0 -56
  1774. package/src/plus/integrations/collectionMetadata.ts +0 -325
  1775. package/src/plus/integrations/constants.ts +0 -190
  1776. package/src/plus/integrations/context.ts +0 -386
  1777. package/src/plus/integrations/errors.ts +0 -188
  1778. package/src/plus/integrations/index.ts +0 -231
  1779. package/src/plus/integrations/integrationService.ts +0 -2003
  1780. package/src/plus/integrations/lite.ts +0 -233
  1781. package/src/plus/integrations/manager.ts +0 -728
  1782. package/src/plus/integrations/models/gitHostIntegration.ts +0 -1862
  1783. package/src/plus/integrations/models/integration.ts +0 -1005
  1784. package/src/plus/integrations/models/issueCache.ts +0 -29
  1785. package/src/plus/integrations/models/issueReads.ts +0 -152
  1786. package/src/plus/integrations/models/issuesIntegration.ts +0 -248
  1787. package/src/plus/integrations/models/pullRequestReads.ts +0 -18
  1788. package/src/plus/integrations/providerFilters.ts +0 -36
  1789. package/src/plus/integrations/providers/accounts.ts +0 -38
  1790. package/src/plus/integrations/providers/apiClients.ts +0 -69
  1791. package/src/plus/integrations/providers/apiConfig.ts +0 -71
  1792. package/src/plus/integrations/providers/azure/azure.ts +0 -822
  1793. package/src/plus/integrations/providers/azure/models.ts +0 -621
  1794. package/src/plus/integrations/providers/azureDevOps.ts +0 -1346
  1795. package/src/plus/integrations/providers/bitbucket/bitbucket.ts +0 -904
  1796. package/src/plus/integrations/providers/bitbucket/models.ts +0 -446
  1797. package/src/plus/integrations/providers/bitbucket-server/models.ts +0 -283
  1798. package/src/plus/integrations/providers/bitbucket-server.ts +0 -445
  1799. package/src/plus/integrations/providers/bitbucket.ts +0 -793
  1800. package/src/plus/integrations/providers/github/github.ts +0 -64
  1801. package/src/plus/integrations/providers/github/github.utils.ts +0 -23
  1802. package/src/plus/integrations/providers/github/models.ts +0 -29
  1803. package/src/plus/integrations/providers/github.ts +0 -940
  1804. package/src/plus/integrations/providers/gitlab/gitlab.ts +0 -1320
  1805. package/src/plus/integrations/providers/gitlab/gitlab.utils.ts +0 -80
  1806. package/src/plus/integrations/providers/gitlab/models.ts +0 -271
  1807. package/src/plus/integrations/providers/gitlab.ts +0 -841
  1808. package/src/plus/integrations/providers/issueSorts.ts +0 -89
  1809. package/src/plus/integrations/providers/jira.ts +0 -596
  1810. package/src/plus/integrations/providers/jiraIssueByKey.ts +0 -155
  1811. package/src/plus/integrations/providers/linear.ts +0 -484
  1812. package/src/plus/integrations/providers/models.ts +0 -1816
  1813. package/src/plus/integrations/providers/providerErrors.ts +0 -115
  1814. package/src/plus/integrations/providers/providersApi.ts +0 -1881
  1815. package/src/plus/integrations/providers/pullRequestReviews.ts +0 -122
  1816. package/src/plus/integrations/providers/trello.ts +0 -208
  1817. package/src/plus/integrations/providers/utils/providerPaging.ts +0 -212
  1818. package/src/plus/integrations/providers/utils.ts +0 -419
  1819. package/src/plus/integrations/reads/broaden.ts +0 -704
  1820. package/src/plus/integrations/reads/context.ts +0 -99
  1821. package/src/plus/integrations/reads/counts.ts +0 -610
  1822. package/src/plus/integrations/reads/cursors.ts +0 -224
  1823. package/src/plus/integrations/reads/drains.ts +0 -420
  1824. package/src/plus/integrations/reads/filters.ts +0 -614
  1825. package/src/plus/integrations/reads/hierarchy.ts +0 -486
  1826. package/src/plus/integrations/reads/hierarchy.utils.ts +0 -155
  1827. package/src/plus/integrations/reads/issueBatch.ts +0 -197
  1828. package/src/plus/integrations/reads/issueTracker.ts +0 -556
  1829. package/src/plus/integrations/reads/issues.ts +0 -600
  1830. package/src/plus/integrations/reads/ordering.ts +0 -124
  1831. package/src/plus/integrations/reads/paging.ts +0 -379
  1832. package/src/plus/integrations/reads/pullRequests.ts +0 -260
  1833. package/src/plus/integrations/reads/resolveRepository.ts +0 -272
  1834. package/src/plus/integrations/reads/searchIssues.ts +0 -274
  1835. package/src/plus/integrations/reads/searchPullRequests.ts +0 -235
  1836. package/src/plus/integrations/reads/sweeps.ts +0 -266
  1837. package/src/plus/integrations/reads/trackerIssue.ts +0 -105
  1838. package/src/plus/integrations/reads/warnings.ts +0 -535
  1839. package/src/plus/integrations/results.ts +0 -443
  1840. package/src/plus/integrations/telemetry.ts +0 -29
  1841. package/src/plus/integrations/utils/domain.utils.ts +0 -26
  1842. package/src/plus/integrations/utils/integration.utils.ts +0 -215
  1843. package/src/utils/array.ts +0 -194
  1844. package/src/utils/base64.ts +0 -1
  1845. package/src/utils/brand.ts +0 -7
  1846. package/src/utils/cancellation.ts +0 -199
  1847. package/src/utils/charCode.ts +0 -38
  1848. package/src/utils/coalescedRun.ts +0 -45
  1849. package/src/utils/color.ts +0 -817
  1850. package/src/utils/counter.ts +0 -21
  1851. package/src/utils/crypto.ts +0 -1
  1852. package/src/utils/date.ts +0 -474
  1853. package/src/utils/debounce.ts +0 -303
  1854. package/src/utils/decorators/debounce.ts +0 -31
  1855. package/src/utils/decorators/gate.ts +0 -167
  1856. package/src/utils/decorators/log.ts +0 -320
  1857. package/src/utils/decorators/memoize.ts +0 -98
  1858. package/src/utils/decorators/resolver.ts +0 -95
  1859. package/src/utils/decorators/sequentialize.ts +0 -118
  1860. package/src/utils/decorators/serializable.ts +0 -43
  1861. package/src/utils/dedupedAsyncCache.ts +0 -74
  1862. package/src/utils/diff.ts +0 -61
  1863. package/src/utils/disposable.ts +0 -156
  1864. package/src/utils/encoding.ts +0 -8
  1865. package/src/utils/env/browser/base64.ts +0 -50
  1866. package/src/utils/env/browser/crypto.ts +0 -21
  1867. package/src/utils/env/browser/fs.ts +0 -3
  1868. package/src/utils/env/browser/hex.ts +0 -41
  1869. package/src/utils/env/browser/hrtime.ts +0 -14
  1870. package/src/utils/env/browser/logScope.ts +0 -57
  1871. package/src/utils/env/browser/md5.ts +0 -217
  1872. package/src/utils/env/browser/platform.ts +0 -10
  1873. package/src/utils/env/node/base64.ts +0 -19
  1874. package/src/utils/env/node/crypto.ts +0 -17
  1875. package/src/utils/env/node/exec.ts +0 -488
  1876. package/src/utils/env/node/fs.ts +0 -5
  1877. package/src/utils/env/node/hex.ts +0 -11
  1878. package/src/utils/env/node/hrtime.ts +0 -1
  1879. package/src/utils/env/node/logScope.ts +0 -63
  1880. package/src/utils/env/node/platform.ts +0 -5
  1881. package/src/utils/error.ts +0 -18
  1882. package/src/utils/event.ts +0 -230
  1883. package/src/utils/formatter.ts +0 -198
  1884. package/src/utils/fs.ts +0 -1
  1885. package/src/utils/function.ts +0 -141
  1886. package/src/utils/fuzzy.ts +0 -185
  1887. package/src/utils/hash.ts +0 -60
  1888. package/src/utils/hex.ts +0 -1
  1889. package/src/utils/hrtime.ts +0 -8
  1890. package/src/utils/iterable.ts +0 -797
  1891. package/src/utils/keys/chord.ts +0 -184
  1892. package/src/utils/keys/keybinding.ts +0 -116
  1893. package/src/utils/lazy.ts +0 -39
  1894. package/src/utils/loggable.ts +0 -7
  1895. package/src/utils/logger.scoped.ts +0 -237
  1896. package/src/utils/logger.ts +0 -403
  1897. package/src/utils/lruMap.ts +0 -112
  1898. package/src/utils/markdown.ts +0 -115
  1899. package/src/utils/mru.ts +0 -68
  1900. package/src/utils/object.ts +0 -278
  1901. package/src/utils/paging.ts +0 -97
  1902. package/src/utils/path.ts +0 -204
  1903. package/src/utils/platform.ts +0 -1
  1904. package/src/utils/promise.ts +0 -657
  1905. package/src/utils/promiseCache.ts +0 -908
  1906. package/src/utils/resourceUsage.ts +0 -5
  1907. package/src/utils/searchTree.ts +0 -444
  1908. package/src/utils/stopwatch.ts +0 -125
  1909. package/src/utils/string.ts +0 -1003
  1910. package/src/utils/subscriptionManager.ts +0 -40
  1911. package/src/utils/trie.ts +0 -740
  1912. package/src/utils/types.ts +0 -31
  1913. package/src/utils/uri.ts +0 -118
  1914. package/src/utils/validation.ts +0 -38
  1915. package/src/utils/version.ts +0 -128
@@ -1,4036 +0,0 @@
1
- import { readdir, readFile } from 'fs/promises';
2
- import { join } from 'path';
3
- import { gate } from '../../../utils/decorators/gate.js';
4
- import type { UnifiedDisposable } from '../../../utils/disposable.js';
5
- import { disposableInterval } from '../../../utils/disposable.js';
6
- import { Emitter } from '../../../utils/event.js';
7
- import { Logger } from '../../../utils/logger.js';
8
- import { arePathsEqual, normalizePath } from '../../../utils/path.js';
9
- import type { AgentCapabilities } from '../agentCapabilities.js';
10
- import {
11
- agentCapabilities,
12
- claudeCodeCapabilities,
13
- getAgentCapabilities,
14
- getAgentCapabilitiesByProviderId,
15
- resolveCanonicalHookEvent,
16
- resolveCanonicalToolName,
17
- } from '../agentCapabilities.js';
18
- import { prepareStoredPrompt } from '../sanitizePrompt.js';
19
- import {
20
- classifyPermissionKind,
21
- deriveStatusFromEvent,
22
- describeToolInput,
23
- extractPlanSummary,
24
- extractQuestionDetails,
25
- getToolFilePath,
26
- getToolReadPath,
27
- isProcessAlive,
28
- rehydrateSubagents,
29
- } from '../stateMachine.js';
30
- import type {
31
- AgentProviderCallbacks,
32
- AgentSession,
33
- AgentSessionHistoryItem,
34
- AgentSessionHistoryOptions,
35
- AgentSessionHistoryResult,
36
- AgentSessionPhase,
37
- AgentSessionProvider,
38
- AgentSessionResumeOutcome,
39
- AgentSessionResumeTarget,
40
- AgentSessionStatus,
41
- LiveAgentSession,
42
- PendingPermission,
43
- PendingPermissionKind,
44
- PermissionDecision,
45
- PermissionSuggestion,
46
- } from '../types.js';
47
- import { getPhaseForStatus } from '../types.js';
48
- import type { EndedTranscriptDetails } from './claudeCodeTranscript.js';
49
- import { ClaudeCodeTranscriptReader } from './claudeCodeTranscript.js';
50
-
51
- interface AgentSessionEvent {
52
- /** The originating agent's NATIVE hook event name, relayed verbatim by the CLI — NOT yet a
53
- * canonical `AgentHookEvent`. Resolve it through {@link resolveCanonicalHookEvent} before
54
- * switching on it. */
55
- event: string;
56
- sessionId: string;
57
- /** `gk ai hook` client id of the originating host (`claude-code`, `codex`, …) — NOT
58
- * `AgentSession.providerId`. Absent on older CLIs. */
59
- providerId?: string;
60
- cwd: string;
61
- /** The agent's launch directory, captured first-hand by the CLI and preserved across events.
62
- * Optional — older CLIs don't send it, in which case consumers fall back to deriving it from
63
- * the first-seen `cwd`. */
64
- initialCwd?: string;
65
- pid?: number;
66
- source?: string;
67
- model?: string;
68
- reason?: string;
69
- toolName?: string;
70
- agentId?: string;
71
- agentType?: string;
72
- planFile?: string;
73
- notificationType?: string;
74
- sessionName?: string;
75
- prompt?: string;
76
- firstPrompt?: string;
77
- toolInput?: Record<string, unknown>;
78
- permissionSuggestions?: PermissionSuggestion[];
79
- hookInput?: Record<string, unknown>;
80
- /** CLI-resolved worktree roots for this session's cwd visits, newest last — same shape as
81
- * {@link SessionFileData.cwdTimeline}. The CLI runs `rev-parse` at hook time and includes this
82
- * on every event, even though it isn't part of the raw hook payload. */
83
- cwdTimeline?: { cwd: string; worktree?: string; at?: string }[];
84
- }
85
-
86
- interface PermissionResponse {
87
- hookSpecificOutput: {
88
- hookEventName: 'PermissionRequest';
89
- decision: {
90
- behavior: PermissionDecision;
91
- updatedPermissions?: PermissionSuggestion[];
92
- };
93
- };
94
- }
95
-
96
- interface PendingPermissionEntry {
97
- /** All connections waiting on this ask — duplicate deliveries (CLI retry after its 60s
98
- * response-header timeout) attach here and settle together. */
99
- resolvers: { resolve(response: PermissionResponse): void; reject(reason: Error): void }[];
100
- toolName: string;
101
- toolDescription: string;
102
- /** From `hookInput.tool_use_id` when the CLI surfaces it; identity gate for stale-clearing. */
103
- toolUseId?: string;
104
- /** Serialized tool_input, for duplicate-delivery detection when `toolUseId` is absent. */
105
- toolInputJson?: string;
106
- }
107
-
108
- interface SessionBookkeeping {
109
- activeToolCount: number;
110
- pendingPermission?: PendingPermission;
111
- /** Refcount of in-flight file-mutating tool calls per absolute path. Keyed by path (not
112
- * `tool_use_id`) because the GK CLI hook payload doesn't reliably surface `tool_use_id` —
113
- * refcounting per path is order-independent and tolerant of out-of-order Pre/Post pairs. */
114
- currentFileCounts: Map<string, number>;
115
- /** Per-path cooldown timers scheduled when a path's refcount hits zero, so back-to-back
116
- * Edit/Write to the same path doesn't flicker the WIP decoration. A fresh Pre on the same
117
- * path cancels its pending clear. */
118
- pendingFileClears: Map<string, NodeJS.Timeout>;
119
- /** Refcount of in-flight read-only file tool calls (Read/NotebookRead) per absolute path.
120
- * Mirrors {@link currentFileCounts} so consumers can distinguish "looking at" from "working
121
- * on". */
122
- currentReadCounts: Map<string, number>;
123
- /** Per-path cooldown timers for reads — same semantics as {@link pendingFileClears}, keeps a
124
- * recently-read file visible briefly after the read completes. */
125
- pendingReadClears: Map<string, NodeJS.Timeout>;
126
- /** Epoch-ms timestamps of the last `PreToolUse` for each kind, per absolute path. Stamped on
127
- * every Pre; the per-path `edit`/`read` slot survives through the cooldown window and is
128
- * dropped only when the corresponding count map drops the path. Serialized into
129
- * `AgentSession.fileActivity` as `editedAt = now - timestamp.edit` (relative ms), so the
130
- * webview can compute heat decay without dealing with host clock skew. */
131
- lastTouchedAt: Map<string, { edit?: number; read?: number }>;
132
- /** Set to true when `resolveGitInfo` for the session's current cwd returned `undefined`
133
- * (cwd is not a git repo). Prevents the `ensureSession` retry from firing forever on
134
- * every subsequent hook event. Cleared when the session's cwd changes (re-resolution
135
- * warranted) or on resetBookkeeping (Stop/SessionStart). */
136
- gitInfoUnresolvable: boolean;
137
- /** Set while the session's `worktreePath` came from the CLI (an event's `cwdTimeline`, or a poll
138
- * record's). The CLI resolves the worktree for the exact cwd at hook time; the local probe answers
139
- * for whichever repo the host registry matches, which for a nested cwd can be the parent worktree.
140
- * While set, `resolveGitInfo` keeps the seated value instead of overwriting it. Cleared when the
141
- * cwd moves without the CLI explaining the new location — attribution goes back to the probe. */
142
- cliSeatedWorktree?: boolean;
143
- /** Phase that immediately preceded the current one, with its original `phaseSince`. Used by
144
- * `resolvePhaseSince` to restore continuity when phase oscillates back within a short
145
- * window (e.g., Stop → idle → working after a continuation crosses the debounce). */
146
- priorPhase?: { phase: AgentSessionPhase; phaseSince: Date };
147
- /** Set once the reconciliation poll has seen this session's durable `ended` record. Gates
148
- * ended-session removal: a session the live `SessionEnd` path just transitioned is never
149
- * reconcile-removed by an already-in-flight poll that legitimately missed it — only after a poll
150
- * has confirmed the CLI still lists it. */
151
- polledAtLeastOnce?: boolean;
152
- /** The durable record's cwd that a live listing superseded when a resume moved the session
153
- * elsewhere. While the record still reports this exact cwd, a poll that no longer finds the
154
- * session in the live listing must not re-seat the row (or trust the record's `endedAt`) —
155
- * the record predates the resume. Cleared when the record's cwd changes (the CLI caught up). */
156
- supersededRecordCwd?: string;
157
- /** Set once an ended session's transcript has been read on demand (via
158
- * `resolveEndedSessionDetails`). Terminal transcripts don't change, so the read happens at
159
- * most once regardless of how many times the row is opened. */
160
- endedDetailsResolved?: boolean;
161
- }
162
-
163
- const staleCheckIntervalMs = 15 * 60 * 1000; // 15 minutes
164
- /** Minimum spacing between gated reconciliation polls while NOTHING is running. The quarter-hourly
165
- * tick exists to reap agents that died without a `SessionEnd`; with no live session there's nothing
166
- * to reap, leaving only terminal-history reconciliation and the CLI's retention sweep — both fine
167
- * hourly. Window startup polls ungated, so this never delays a fresh window. */
168
- const idleReconcileIntervalMs = 60 * 60 * 1000; // 1 hour
169
- /** How long a freshly-ended row is protected from absence-based removal. The CLI persists the
170
- * ended record (atomic temp+rename) BEFORE broadcasting the hook event, so a poll that starts after
171
- * we mark a row ended will list it — this guards only against that ordering changing on the CLI
172
- * side, where the cost of being wrong is an ended row silently vanishing. */
173
- const endedRemovalGraceMs = 30 * 1000; // 30 seconds
174
- /** How long the machine-global archived-session id list (fetched via a separate `--status archived`
175
- * CLI query) is cached. `listSessionHistory` asks for it once per worktree on panel open, so without
176
- * this a multi-worktree graph spawns N identical CLI processes. `archiveSession` invalidates the
177
- * cache, so a just-archived row never lingers under "Past". */
178
- const archivedSessionIdsCacheTtlMs = 10 * 1000; // 10 seconds
179
- /** Ended sessions defer git + transcript resolution (a 30-day cold-start would otherwise fan out
180
- * hundreds of probes). Exception: sessions ended within this window still surface on WIP rows /
181
- * branch cards, which match strictly by resolved `worktreePath` — so their git info is resolved
182
- * eagerly at poll time (a handful), keeping the older tail lazy. Mirrors the 24h WIP-row window. */
183
- const recentEndedGitResolveThresholdMs = 24 * 60 * 60 * 1000; // 24 hours
184
- /** Default cooldown between PostToolUse and dropping the file from `fileActivity`. Held long
185
- * enough that the treemap activity overlay can render a decay tail well past the moment the tool
186
- * call completed. The host may override per-call via `AgentProviderCallbacks.getActivityDecayMs`
187
- * (driven by the user's `gitlens.graph.experimental.visualizations.activityDecay` setting). */
188
- const defaultActivityDecayMs = 5 * 60 * 1000; // 5 minutes
189
-
190
- /** Grace period before `Stop` commits to `idle`. Long enough to absorb a same-turn continuation
191
- * (hook-driven re-prompt, IPC event reordering, auto-resume); short enough that legitimate idle
192
- * is visible promptly. */
193
- const stopToIdleDebounceMs = 750;
194
-
195
- /** If a phase transitions away and then back within this window, restore the prior `phaseSince`
196
- * so the displayed elapsed time doesn't snap to 0 on transient oscillations. Slightly longer
197
- * than `stopToIdleDebounceMs` so a continuation that just barely crosses the debounce still
198
- * restores continuity. */
199
- const phaseSinceRestoreWindowMs = 2000;
200
-
201
- interface SessionFileData {
202
- sessionId: string;
203
- /** `gk ai hook` client id of the owning host — see {@link AgentSessionEvent.providerId}. */
204
- providerId?: string;
205
- /** The owning agent's NATIVE hook event name — see {@link AgentSessionEvent.event}. */
206
- event: string;
207
- /** The agent's RAW hook payload for {@link event}, relayed verbatim by the CLI — same field the
208
- * live path reads off {@link AgentSessionEvent.hookInput}. The poll needs it because some agents
209
- * encode an event's meaning in the payload rather than its name (OpenCode's `session.status`),
210
- * and {@link resolveCanonicalHookEvent} can't canonicalize those without being handed it. */
211
- hookInput?: Record<string, unknown>;
212
- cwd: string;
213
- /** CLI-provided launch directory; absent on older CLIs (fall back to `cwd`). */
214
- initialCwd?: string;
215
- /** Worktree roots (`git rev-parse --show-toplevel`) the CLI resolved for this session's cwd
216
- * visits, newest last. Absent on older CLIs. Lets us read `worktreePath` straight from the
217
- * durable store — so ended sessions attach to branch cards / WIP rows / the resume picker at
218
- * any age without a git probe (the CLI already ran `rev-parse` at hook time). */
219
- cwdTimeline?: { cwd: string; worktree?: string; at?: string }[];
220
- worktrees?: string[];
221
- pid: number;
222
- toolName?: string | null;
223
- agentId?: string | null;
224
- agentType?: string | null;
225
- source?: string | null;
226
- model?: string | null;
227
- updatedAt: string;
228
- subagents?: { agentId: string; agentType: string }[];
229
- planFile?: string | null;
230
- sessionName?: string | null;
231
- prompt?: string | null;
232
- firstPrompt?: string | null;
233
- /** CLI durable-session lifecycle status. Absent on legacy files → treated as `active`. `ended`
234
- * records are surfaced as terminal `ended` sessions; `archived` is excluded by the query. */
235
- status?: 'active' | 'ended' | 'archived';
236
- /** Why the session ended (`session-end`, `rotated`, `stale`, `dead-pid`, `pid-zero-idle`,
237
- * `archived`). Present only on `ended`/`archived` records. */
238
- endReason?: string;
239
- /** RFC3339 timestamp the session ended; used as `lastActivity`/`phaseSince` for ended rows. */
240
- endedAt?: string;
241
- }
242
-
243
- /** The session's current worktree root from the CLI's durable record — the last cwd-visit's resolved
244
- * worktree, falling back to the last distinct worktree seen. `undefined` on older CLIs that don't
245
- * record it, so the caller resolves git the slow way instead. */
246
- function worktreeRootFromData(data: SessionFileData): string | undefined {
247
- const fromTimeline = data.cwdTimeline?.at(-1)?.worktree;
248
- if (fromTimeline) return fromTimeline;
249
-
250
- return data.worktrees?.at(-1) || undefined;
251
- }
252
-
253
- /** The CLI resolves the worktree for `event.cwd` at hook time and includes it on every event —
254
- * using its answer seats `worktreePath` synchronously, closing the in-flight-probe window where
255
- * a session that just moved would otherwise still show its previous worktree until `resolveGitInfo`
256
- * catches up. Scans from the end for the timeline entry matching the event's current cwd. */
257
- function worktreeFromEvent(event: AgentSessionEvent): string | undefined {
258
- const timeline = event.cwdTimeline;
259
- if (timeline == null) return undefined;
260
-
261
- for (let i = timeline.length - 1; i >= 0; i--) {
262
- const entry = timeline[i];
263
- if (entry.cwd === event.cwd) return entry.worktree || undefined;
264
- }
265
-
266
- return undefined;
267
- }
268
-
269
- /** Reorders `paths` so each distinct (already-normalized) value is positioned by its LAST
270
- * occurrence — an earlier occurrence of a value that recurs later is dropped, and the value's one
271
- * remaining slot sits where that last occurrence falls. Implemented via delete-then-reinsert into
272
- * a `Map`: re-inserting a key after deleting it moves it to the end of `Map` iteration order,
273
- * which is exactly "this value's position is its most recent occurrence". */
274
- function orderByLastOccurrence(paths: readonly string[]): string[] {
275
- const seen = new Map<string, true>();
276
- for (const path of paths) {
277
- seen.delete(path);
278
- seen.set(path, true);
279
- }
280
- return [...seen.keys()];
281
- }
282
-
283
- /** Distinct, normalized, non-empty `worktree` values from a `cwdTimeline`, ordered by recency —
284
- * positioned by each path's LAST occurrence in the (chronological) timeline, so a path the
285
- * session returns to later sorts after one it doesn't. `undefined` when the timeline is absent or
286
- * names no worktree — callers should NOT overwrite an existing `visitedWorktreePaths` with an
287
- * empty array in that case. */
288
- function visitedFromTimeline(
289
- timeline: { cwd: string; worktree?: string; at?: string }[] | undefined,
290
- ): string[] | undefined {
291
- if (timeline == null) return undefined;
292
-
293
- const paths: string[] = [];
294
- for (const entry of timeline) {
295
- if (!entry.worktree) continue;
296
-
297
- paths.push(normalizePath(entry.worktree));
298
- }
299
- if (paths.length === 0) return undefined;
300
-
301
- return orderByLastOccurrence(paths);
302
- }
303
-
304
- /** Unions `adds` (each an array or a single path, `undefined` skipped) into `existing`, normalizing
305
- * every path, and reorders the result by recency: every add — including one that names a path
306
- * already in `existing` — moves that path to the end, same "last occurrence wins the slot" rule
307
- * {@link orderByLastOccurrence} applies to a single timeline. Adds arrive in chronological order at
308
- * every call site (timeline-derived paths first, the current `worktreePath` folded last), so the
309
- * current worktree normally ends up last.
310
- *
311
- * **Reference-stability contract — load-bearing**: returns `existing` BY REFERENCE, unchanged,
312
- * when the resulting sequence is element-for-element identical to `existing`. Callers
313
- * (`ensureSession`'s identity gate) rely on this to avoid firing spurious change events on every
314
- * hook event / poll cycle when the visited set hasn't actually changed — re-processing an
315
- * unchanged timeline plus the same current worktree reproduces the identical sequence (each path
316
- * "moves" to the same slot it already occupies), so the comparison below still holds. */
317
- function unionVisited(
318
- existing: readonly string[] | undefined,
319
- ...adds: (string | readonly string[] | undefined)[]
320
- ): readonly string[] | undefined {
321
- const combined = existing != null ? [...existing] : [];
322
- for (const add of adds) {
323
- if (add == null) continue;
324
-
325
- const values = typeof add === 'string' ? [add] : add;
326
- for (const value of values) {
327
- if (!value) continue;
328
-
329
- combined.push(normalizePath(value));
330
- }
331
- }
332
-
333
- const next = orderByLastOccurrence(combined);
334
- if (existing != null && next.length === existing.length && next.every((path, i) => path === existing[i])) {
335
- return existing;
336
- }
337
- return next.length > 0 ? next : undefined;
338
- }
339
-
340
- /** Peer-merge variant of {@link unionVisited}: same normalize-and-dedupe, but APPEND-ONLY —
341
- * `existing`'s order is preserved and any path the peer names that we don't already have is
342
- * appended in the peer's own order. A peer's order can't be trusted as fresher than ours without
343
- * timestamps, so peer input never reorders what we already hold. Returns `existing` BY REFERENCE
344
- * when nothing new is added — same rationale as {@link unionVisited}. */
345
- function unionVisitedAppendOnly(
346
- existing: readonly string[] | undefined,
347
- ...adds: (string | readonly string[] | undefined)[]
348
- ): readonly string[] | undefined {
349
- const next = new Set(existing ?? []);
350
- let grew = false;
351
- for (const add of adds) {
352
- if (add == null) continue;
353
-
354
- const values = typeof add === 'string' ? [add] : add;
355
- for (const value of values) {
356
- if (!value) continue;
357
-
358
- const normalized = normalizePath(value);
359
- if (!next.has(normalized)) {
360
- next.add(normalized);
361
- grew = true;
362
- }
363
- }
364
- }
365
- return grew ? [...next] : existing;
366
- }
367
-
368
- /** The event's tool name, translated from the agent's native vocabulary into the canonical (Claude
369
- * Code) one. Prefers the raw `hookInput.tool_name` passthrough and falls back to the projected
370
- * top-level field for older CLIs. Everything downstream — `describeToolInput`,
371
- * `classifyPermissionKind`, `getToolFilePath`, `getToolReadPath`, and the display detail — expects
372
- * canonical names, and Claude Code has no alias table, so its names pass through untouched. */
373
- function canonicalToolNameFromEvent(capabilities: AgentCapabilities, event: AgentSessionEvent): string | undefined {
374
- const native = (event.hookInput?.tool_name as string | undefined) ?? event.toolName;
375
- return native != null ? resolveCanonicalToolName(capabilities, native) : undefined;
376
- }
377
-
378
- /** Whether GitLens can actually answer a blocking permission ask from this agent. Two conditions,
379
- * deliberately separate: the agent must support blocking hooks at all, AND the response we build
380
- * must be one it understands. {@link PermissionResponse} is Claude-shaped
381
- * (`hookSpecificOutput.hookEventName: 'PermissionRequest'`), so an agent that supports blocking
382
- * hooks but expects a different envelope stays observe-only until per-agent response builders land
383
- * — at which point the second clause is what gets deleted. */
384
- function canAnswerBlockingPermission(capabilities: AgentCapabilities): boolean {
385
- return capabilities.supportsBlockingPermissions && capabilities.hookClientId === 'claude-code';
386
- }
387
-
388
- /** Synthesizes an unresolvable ask for a `permission_requested` row that has no routable
389
- * `PendingPermission` — a non-blocking `Notification`/`PermissionRequest` tail, or a
390
- * poll-discovered live/revived row. None of these hold a blocking hook entry to route an
391
- * Allow/Deny through, so the card must show what the agent is waiting on without offering
392
- * actions the host can't fulfill. */
393
- function synthesizeUnresolvableAsk(toolName: string | null | undefined, planFilePath?: string): PendingPermission {
394
- if (toolName != null) {
395
- const kind = classifyPermissionKind(toolName);
396
- return {
397
- kind: kind,
398
- toolName: toolName,
399
- toolDescription: toolName,
400
- // A plan ask with no body at least links the plan the agent wrote, so the card can offer
401
- // View Plan instead of a bare tool name.
402
- planFilePath: kind === 'plan' ? planFilePath : undefined,
403
- resolvable: false,
404
- };
405
- }
406
-
407
- return {
408
- kind: 'elicitation',
409
- toolName: 'Input Required',
410
- toolDescription: 'Waiting for input',
411
- resolvable: false,
412
- };
413
- }
414
-
415
- /** JSON.stringify with object keys recursively sorted — array order is preserved. `tool_input`
416
- * can arrive with different key order (or via the top-level fallback field) between deliveries;
417
- * canonicalizing makes input equality reliable across those variations. */
418
- function stableStringify(value: unknown): string {
419
- return JSON.stringify(value, (_key, val: unknown) => {
420
- if (val == null || typeof val !== 'object' || Array.isArray(val)) return val;
421
-
422
- const sorted: Record<string, unknown> = {};
423
- for (const key of Object.keys(val).sort()) {
424
- sorted[key] = (val as Record<string, unknown>)[key];
425
- }
426
- return sorted;
427
- });
428
- }
429
-
430
- /** Field-wise equality for the ask a `permission_requested` row publishes. `updateSessionStatus`
431
- * compares it before short-circuiting so a second ask arriving while the row is already
432
- * `permission_requested` (a non-blocking tail for a different tool) still reaches subscribers. */
433
- function isSameAsk(a: PendingPermission | undefined, b: PendingPermission | undefined): boolean {
434
- if (a === b) return true;
435
- if (a == null || b == null) return false;
436
-
437
- return (
438
- a.kind === b.kind &&
439
- a.toolName === b.toolName &&
440
- a.toolDescription === b.toolDescription &&
441
- a.toolInputDescription === b.toolInputDescription &&
442
- a.resolvable === b.resolvable &&
443
- a.planFilePath === b.planFilePath &&
444
- a.planSummary === b.planSummary &&
445
- a.questionText === b.questionText &&
446
- a.questionCount === b.questionCount
447
- );
448
- }
449
-
450
- interface DiscoveryFile {
451
- token: string;
452
- address: string;
453
- port?: number;
454
- workspacePaths?: string[];
455
- }
456
-
457
- interface SessionContext {
458
- /** The owning agent's descriptor — the source of a created session's `providerId`/`providerName`.
459
- * Omitted only by the deferred-commit paths (the `Stop → idle` timer, a permission resolve),
460
- * which run against a row that already exists. */
461
- capabilities?: AgentCapabilities;
462
- pid?: number;
463
- workspacePath?: string;
464
- isInWorkspace?: boolean;
465
- cwd?: string;
466
- /** CLI-provided launch directory (authoritative when present). */
467
- initialCwd?: string;
468
- planFile?: string;
469
- sessionName?: string;
470
- /** The model the session is running, from the hook event. */
471
- model?: string;
472
- /** CLI-resolved worktree root for the current cwd (authoritative when present). */
473
- worktreePath?: string;
474
- /** Distinct worktree roots observed via this event's cwdTimeline. */
475
- visitedWorktreePaths?: string[];
476
- }
477
-
478
- type SerializedAgentSession = Omit<AgentSession, 'lastActivity' | 'phaseSince' | 'subagents'> & {
479
- lastActivity: string;
480
- phaseSince: string;
481
- subagents?: (Omit<AgentSession, 'lastActivity' | 'phaseSince'> & {
482
- lastActivity: string;
483
- phaseSince: string;
484
- })[];
485
- };
486
-
487
- /** Normalize a workspace path to GitLens canonical form (forward slashes, lower-case drive letter
488
- * on Windows). Comparisons throughout the home view assume this form, but `_workspacePaths` and
489
- * peer-session `workspacePath` values originate from `Uri.fsPath`, which on Windows uses
490
- * backslashes and preserves drive-letter casing. Without this, `session.workspacePath ===
491
- * repository.path` always fails on Windows. */
492
- function normalizeWorkspacePath(value: string | null | undefined): string | undefined {
493
- return value ? normalizePath(value) : undefined;
494
- }
495
-
496
- type FileActivityEntry = NonNullable<AgentSession['fileActivity']>[number];
497
-
498
- /** Structural comparison of two `fileActivity` arrays — same set of paths, same kinds present,
499
- * same `reading`/`editing` flags. Ignores numeric timestamp values because those drift between
500
- * every call (Date.now() advances) even when nothing has actually changed. Used to gate event
501
- * firing: timestamps still get refreshed via the `_sessions[]` write, but a fire only happens
502
- * when the structural shape changed. Treats `undefined` and `[]` as equal. */
503
- function fileActivityStructurallyEqual(
504
- a: readonly FileActivityEntry[] | undefined,
505
- b: readonly FileActivityEntry[] | undefined,
506
- ): boolean {
507
- const aLen = a?.length ?? 0;
508
- const bLen = b?.length ?? 0;
509
- if (aLen !== bLen) return false;
510
- if (aLen === 0) return true;
511
-
512
- const byPath = new Map<string, FileActivityEntry>();
513
- for (const entry of a!) {
514
- byPath.set(entry.path, entry);
515
- }
516
- for (const entry of b!) {
517
- const other = byPath.get(entry.path);
518
- if (other == null) return false;
519
- if ((entry.readAt != null) !== (other.readAt != null)) return false;
520
- if ((entry.editedAt != null) !== (other.editedAt != null)) return false;
521
- if ((entry.reading ?? false) !== (other.reading ?? false)) return false;
522
- if ((entry.editing ?? false) !== (other.editing ?? false)) return false;
523
- }
524
- return true;
525
- }
526
-
527
- /**
528
- * Maps what `claude agents --json` reports for a live session onto `AgentSessionStatus`, per Claude's
529
- * documented agent-view vocabulary.
530
- *
531
- * `kind: 'interactive'` entries carry `status` while their process is alive, plus `waitingFor` when
532
- * that status is `waiting` — `'permission prompt'` is the one value with a distinct status of its own
533
- * here, since a permission request is a different kind of block from an ordinary question.
534
- *
535
- * `kind: 'background'` entries carry `state` instead: `working` / `blocked` / `done` / `failed` /
536
- * `stopped`, where `blocked` IS the documented "needs input" state.
537
- *
538
- * Returns `undefined` for a background session in a TERMINAL state. `--json` lists background
539
- * sessions "still working or blocked even when their process has exited", so a listing is not by
540
- * itself proof of life — without this, a genuinely finished session would be resurrected as `idle`,
541
- * which is the opposite of the bug this mapping exists to fix. Callers must not treat such an entry
542
- * as live: ended-record paths trust the durable record, while active discovery/repair paths skip or
543
- * remove the stale active row.
544
- *
545
- * Anything unrecognized degrades to `'idle'` rather than guessing a working/waiting phase.
546
- */
547
- /** Resolves a non-terminal listing override for an `ended` record. `undefined` means "let the ended
548
- * record stand": either the listing doesn't mention this session, or its background state is
549
- * terminal (see {@link statusFromLiveSession}). Active-record paths inspect the entry separately
550
- * because they must distinguish absence (unknown; keep record status) from terminal (skip/remove). */
551
- function liveOverrideFor(
552
- liveSessions: ReadonlyMap<string, LiveAgentSession> | undefined,
553
- sessionId: string,
554
- ): { live: LiveAgentSession; status: AgentSessionStatus } | undefined {
555
- const live = liveSessions?.get(sessionId);
556
- if (live == null) return undefined;
557
-
558
- const status = statusFromLiveSession(live);
559
- return status != null ? { live: live, status: status } : undefined;
560
- }
561
-
562
- function statusFromLiveSession(live: {
563
- status?: string;
564
- state?: string;
565
- waitingFor?: string;
566
- }): AgentSessionStatus | undefined {
567
- switch (live.status) {
568
- case 'waiting':
569
- return live.waitingFor === 'permission prompt' ? 'permission_requested' : 'waiting';
570
- case 'busy':
571
- return 'thinking';
572
- }
573
-
574
- switch (live.state) {
575
- case 'blocked':
576
- return 'waiting';
577
- case 'working':
578
- return 'thinking';
579
- case 'done':
580
- case 'failed':
581
- case 'stopped':
582
- return undefined;
583
- }
584
-
585
- return 'idle';
586
- }
587
-
588
- /** True when a CLI invocation failed because it didn't recognize a flag/command. `--status` and the
589
- * active-only `list-sessions` default shipped together (gk 3.1.69); an older CLI rejects `--status`
590
- * and returns all sessions unfiltered, so the flagless retry is the correct legacy equivalent. */
591
- function isUnknownFlagError(ex: unknown): boolean {
592
- const message = ex instanceof Error ? ex.message : String(ex);
593
- return /unknown (?:flag|shorthand flag|command)/i.test(message);
594
- }
595
-
596
- export class GkAgentProvider implements AgentSessionProvider {
597
- /** Identifies the PROVIDER, not an agent: this one hosts every `gk ai hook` client GitLens has
598
- * a descriptor for. A session's own agent identity lives on {@link AgentSession.providerId}
599
- * (`claudeCode`, `codex`, …), sourced from {@link AgentCapabilities} — never from here. */
600
- readonly id = 'gkAgents';
601
- readonly name = 'Coding Agents';
602
- readonly icon = 'robot';
603
- readonly agentProviderIds: readonly string[] = agentCapabilities.map(c => c.providerId);
604
-
605
- private readonly _onDidChangeSessions = new Emitter<void>();
606
- readonly onDidChangeSessions = this._onDidChangeSessions.event;
607
-
608
- private _sessions: AgentSession[] = [];
609
- private _ipcStarted = false;
610
- private _disposed = false;
611
- private _handlerDisposables: UnifiedDisposable[] = [];
612
- private readonly _pendingPermissions = new Map<string, PendingPermissionEntry>();
613
- /** `sessionId -> cwd currently being probed`, and the newest cwd requested while that probe runs.
614
- * A probe's answer is only valid for the cwd it was started with, so a session that moves
615
- * mid-flight must supersede rather than dedupe — otherwise the older lookup lands last and
616
- * overwrites the newer location with the directory the session already left. */
617
- private readonly _resolveGitInfoInFlight = new Map<string, string>();
618
- private readonly _resolveGitInfoPending = new Map<string, string>();
619
- /** Short-TTL cache of the machine-global archived-session id list. Holds the in-flight promise so
620
- * concurrent per-worktree `getPastSessions` calls share one CLI spawn; `archiveSession` clears it. */
621
- private _archivedSessionIdsCache: { promise: Promise<string[]>; at: number } | undefined;
622
- private readonly _sessionBookkeeping = new Map<string, SessionBookkeeping>();
623
- /** Per-session timers for the deferred `Stop → idle` commit. The handle is cleared (and the
624
- * transition cancelled) by any non-idle status update arriving before the timer fires. */
625
- private readonly _pendingIdleTimers = new Map<string, NodeJS.Timeout>();
626
- private _workspacePaths: string[] = [];
627
- private _staleCheckTimer: UnifiedDisposable | undefined;
628
- /** When the last poll actually ran (gated or not) — paces the idle cadence above. */
629
- private _lastSyncAt = 0;
630
- /** Whether agent hooks are installed, pushed by the host via {@link setHooksInstalled}.
631
- * Fail-open (`true`) until the first push lands so a fresh window with real hooks isn't
632
- * suppressed on its first ticks. Gates the reconciliation poll in {@link syncSessions}. */
633
- private _hooksInstalled = true;
634
- /** Whether the CLI supports `list-sessions --status` (and with it the durable ended-session
635
- * store). Optimistic until the poll's flagless fallback proves otherwise. When unsupported,
636
- * ended rows are untenable — no poll can ever confirm or archive one — so `SessionEnd`
637
- * reverts to removing the session and reconciliation drops any ended stragglers. */
638
- private _statusFilterSupported = true;
639
- protected _transcriptReader: ClaudeCodeTranscriptReader = new ClaudeCodeTranscriptReader();
640
- private _terminalGeneration = 0;
641
- /** Ended-transcript detail results, cached forever per window — an ended transcript is
642
- * immutable, so once resolved it never needs a second read. Keyed by session id. */
643
- private readonly _sessionDetailsCache = new Map<string, Promise<EndedTranscriptDetails | undefined>>();
644
-
645
- constructor(private readonly callbacks: AgentProviderCallbacks) {}
646
-
647
- get sessions(): readonly AgentSession[] {
648
- return this._sessions;
649
- }
650
-
651
- get terminalGeneration(): number {
652
- return this._terminalGeneration;
653
- }
654
-
655
- /** Single choke point for every terminal transition — a session ending, being removed on end
656
- * (legacy path), being pruned, or a fresh ended record being discovered. Bumps the generation
657
- * the host's history queries validate against (a transition mid-query means the answer may be
658
- * missing a session) and drops the transcript listing caches, whose warm entries may predate
659
- * the transcript the transition just left behind. */
660
- private noteTerminalTransition(): void {
661
- this._terminalGeneration++;
662
- this._transcriptReader.invalidateListings();
663
- }
664
-
665
- start(workspacePaths: string[]): void {
666
- this._workspacePaths = workspacePaths.map(p => normalizePath(p));
667
- void this.ensureIpcServer();
668
- }
669
-
670
- stop(): void {
671
- // IPC server stays up intentionally — hooks fire even when the window is unfocused
672
- }
673
-
674
- sync(): Promise<void> {
675
- return this.syncSessions();
676
- }
677
-
678
- updateWorkspacePaths(workspacePaths: string[]): void {
679
- this._workspacePaths = workspacePaths.map(p => normalizePath(p));
680
-
681
- let changed = false;
682
- for (let i = 0; i < this._sessions.length; i++) {
683
- const s = this._sessions[i];
684
- const nextWorkspacePath = s.cwd != null ? this.resolveWorkspacePath(s.cwd) : undefined;
685
- const nextIsInWorkspace = nextWorkspacePath != null;
686
- let subagentsChanged = false;
687
- const subagents = s.subagents?.map(sub => {
688
- const subNextWorkspacePath = sub.cwd != null ? this.resolveWorkspacePath(sub.cwd) : nextWorkspacePath;
689
- const subNextIsInWorkspace = sub.cwd != null ? subNextWorkspacePath != null : nextIsInWorkspace;
690
- if (sub.isInWorkspace === subNextIsInWorkspace && sub.workspacePath === subNextWorkspacePath) {
691
- return sub;
692
- }
693
-
694
- subagentsChanged = true;
695
- return { ...sub, isInWorkspace: subNextIsInWorkspace, workspacePath: subNextWorkspacePath };
696
- });
697
- if (s.isInWorkspace !== nextIsInWorkspace || s.workspacePath !== nextWorkspacePath || subagentsChanged) {
698
- this._sessions[i] = {
699
- ...s,
700
- isInWorkspace: nextIsInWorkspace,
701
- workspacePath: nextWorkspacePath,
702
- subagents: subagents,
703
- };
704
- changed = true;
705
- }
706
- }
707
- if (changed) {
708
- this._onDidChangeSessions.fire();
709
- }
710
-
711
- if (!this._ipcStarted) {
712
- // IPC wasn't bootstrapped yet — start it. ensureIpcServer publishes with the
713
- // current `_workspacePaths`.
714
- void this.ensureIpcServer();
715
- } else {
716
- // Re-publish so the agents discovery file reflects the new workspacePaths.
717
- this.callbacks.ipc
718
- .publishAgents(this._workspacePaths)
719
- .catch((ex: unknown) => Logger.error(ex, 'GkAgentProvider.updateWorkspacePaths: publishAgents failed'));
720
- }
721
- }
722
-
723
- setHooksInstalled(installed: boolean): void {
724
- const wasOff = !this._hooksInstalled;
725
- this._hooksInstalled = installed;
726
- // On a fresh off→on transition, reconcile immediately rather than waiting up to a full
727
- // interval — picks up any session that was already running before hooks were installed.
728
- // This is a deliberate discovery pass (like the cold-start bootstrap), not a routine tick:
729
- // hooks were just off, so the live path couldn't have seen these sessions and anything we
730
- // find here is expected, not drift. Run it ungated so it neither skips nor reports drift
731
- // telemetry (an ungated `syncSessions()` always polls and never emits `onSyncDiscrepancy`).
732
- if (installed && wasOff) {
733
- void this.syncSessions();
734
- }
735
- }
736
-
737
- /** Reads Claude history from its transcript store and tracked terminal records. The exact project
738
- * directory preserves legacy sessions that predate the CLI's durable records. Recent ended
739
- * records add an authoritative worktree-to-session association, so their ids are also recovered
740
- * from other project directories when Claude left the transcript behind after a cwd/worktree
741
- * move. A tracked record with no readable transcript remains visible and archivable, but does not
742
- * advertise Resume. Archive filtering stays provider-local so ids from another harness cannot
743
- * suppress Claude rows. `requireResume` drops the transcript-less tracked records, leaving only
744
- * rows that carry a resume action — and a `total` counting only those. */
745
- async listSessionHistory(cwd: string, options?: AgentSessionHistoryOptions): Promise<AgentSessionHistoryResult> {
746
- const resumeOnly = options?.requireResume === true;
747
- // No launcher means no row can carry resume, so a resume-only query has nothing to return.
748
- if (resumeOnly && this.callbacks.resumeSession == null) return { sessions: [], total: 0 };
749
-
750
- const archivedSessionIds = new Set(await this.getArchivedSessionIds());
751
- const excludeSessionIds = new Set(options?.excludeSessionIds);
752
- for (const id of archivedSessionIds) {
753
- excludeSessionIds.add(id);
754
- }
755
-
756
- const trackedEndedSessions: AgentSession[] = [];
757
- const endedSessionIds = new Set<string>();
758
- for (const session of this._sessions) {
759
- if (
760
- session.status === 'ended' &&
761
- session.worktreePath != null &&
762
- arePathsEqual(session.worktreePath, cwd) &&
763
- !excludeSessionIds.has(session.id)
764
- ) {
765
- // A tracked record's own row carries archive but no resume; its id still drives transcript
766
- // recovery below, which does produce a resumable row when the transcript moved with it.
767
- if (!resumeOnly) {
768
- trackedEndedSessions.push(session);
769
- }
770
- // Only an agent whose sessions can be resumed feeds transcript recovery. The reader is
771
- // Claude-transcript-specific, so another agent's id could only ever miss — but stating it
772
- // keeps the resume capability the reason, rather than leaving it to a coincidence of which
773
- // store the reader happens to read.
774
- if (getAgentCapabilitiesByProviderId(session.providerId)?.supportsResume !== false) {
775
- endedSessionIds.add(session.id);
776
- }
777
- }
778
- }
779
-
780
- const [direct, recovered] = await Promise.all([
781
- this._transcriptReader.listSessions(cwd, {
782
- limit: options?.limit,
783
- excludeSessionIds: excludeSessionIds,
784
- }),
785
- this._transcriptReader.listSessionsByIds(endedSessionIds, {
786
- limit: options?.limit,
787
- excludeCwd: cwd,
788
- }),
789
- ]);
790
-
791
- const historyById = new Map<string, AgentSessionHistoryItem>();
792
- const addTranscript = (session: (typeof direct.sessions)[number], resumeCwd: string): void => {
793
- if (excludeSessionIds.has(session.sessionId)) return;
794
-
795
- const disposition = archivedSessionIds.has(session.sessionId) ? 'archived' : 'ended';
796
- const resumeActions = this.callbacks.resumeSession != null ? { resume: { cwd: resumeCwd } } : {};
797
- historyById.set(session.sessionId, {
798
- id: session.sessionId,
799
- // The reader only reads Claude Code's transcript store, so every row it produces is
800
- // Claude's by construction.
801
- providerId: claudeCodeCapabilities.providerId,
802
- disposition: disposition,
803
- actions: disposition === 'ended' ? { ...resumeActions, archive: true } : resumeActions,
804
- lastActivity: new Date(session.lastActivityMs),
805
- titles: session.titles,
806
- lastPrompt: session.lastPrompt,
807
- });
808
- };
809
- for (const session of direct.sessions) {
810
- addTranscript(session, cwd);
811
- }
812
- for (const session of recovered.sessions) {
813
- addTranscript(session, session.cwd);
814
- }
815
-
816
- for (const session of trackedEndedSessions) {
817
- if (historyById.has(session.id)) continue;
818
-
819
- historyById.set(session.id, {
820
- id: session.id,
821
- providerId: session.providerId,
822
- disposition: 'ended',
823
- actions: { archive: true },
824
- lastActivity: session.lastActivity,
825
- name: session.name,
826
- titles: session.transcriptTitles,
827
- firstPrompt: session.firstPrompt,
828
- lastPrompt: session.lastPrompt,
829
- });
830
- }
831
-
832
- const history = [...historyById.values()].sort((a, b) => b.lastActivity.getTime() - a.lastActivity.getTime());
833
- const limited = options?.limit != null && options.limit > 0 ? history.slice(0, options.limit) : history;
834
-
835
- return { sessions: limited, total: Math.max(direct.total + recovered.total, history.length) };
836
- }
837
-
838
- resumeSession(
839
- sessionId: string,
840
- cwd: string,
841
- target: AgentSessionResumeTarget,
842
- name?: string,
843
- ): Promise<AgentSessionResumeOutcome | false> {
844
- return this.callbacks.resumeSession?.(sessionId, cwd, target, name) ?? Promise.resolve(false);
845
- }
846
-
847
- dispose(): void {
848
- this._disposed = true;
849
- this.stop();
850
- this._staleCheckTimer?.dispose();
851
- this._staleCheckTimer = undefined;
852
- for (const d of this._handlerDisposables) {
853
- d.dispose();
854
- }
855
- this._handlerDisposables = [];
856
- for (const pending of this._pendingPermissions.values()) {
857
- this.rejectAllPending(pending, new Error('Provider disposed'));
858
- }
859
- this._pendingPermissions.clear();
860
- for (const bk of this._sessionBookkeeping.values()) {
861
- this.cancelPendingFileClears(bk);
862
- this.cancelPendingReadClears(bk);
863
- }
864
- for (const timer of this._pendingIdleTimers.values()) {
865
- clearTimeout(timer);
866
- }
867
- this._pendingIdleTimers.clear();
868
- this._sessionBookkeeping.clear();
869
- if (this._ipcStarted) {
870
- void this.callbacks.ipc.unpublishAgents();
871
- this._ipcStarted = false;
872
- }
873
- this._onDidChangeSessions.dispose();
874
- }
875
-
876
- [Symbol.dispose](): void {
877
- this.dispose();
878
- }
879
-
880
- @gate<typeof GkAgentProvider.prototype.ensureIpcServer>()
881
- private async ensureIpcServer(): Promise<void> {
882
- if (this._ipcStarted || this._disposed) return;
883
-
884
- // Register handlers before any async work so blocking permission requests aren't delayed.
885
- const handlers: UnifiedDisposable[] = [
886
- this.callbacks.ipc.registerHandler('agents/session', (request, searchParams) => {
887
- if (request != null) {
888
- const isBlocking = searchParams.get('blocking') === 'true';
889
- return this.handleSessionEvent(request as AgentSessionEvent, isBlocking);
890
- }
891
- return Promise.resolve();
892
- }),
893
- this.callbacks.ipc.registerHandler('agents/sessions/list', () =>
894
- Promise.resolve(
895
- // Exclude terminal `ended` rows: an older-version peer has no `ended` phase and
896
- // would import them as phantom "idle" sessions. Our own poll surfaces ended sessions
897
- // independently, so peers never need them from here.
898
- this._sessions
899
- .filter(s => s.status !== 'ended')
900
- .map(s => ({
901
- ...s,
902
- lastActivity: s.lastActivity.toISOString(),
903
- phaseSince: s.phaseSince.toISOString(),
904
- subagents: s.subagents?.map(sub => ({
905
- ...sub,
906
- lastActivity: sub.lastActivity.toISOString(),
907
- phaseSince: sub.phaseSince.toISOString(),
908
- })),
909
- })),
910
- ),
911
- ),
912
- this.callbacks.ipc.registerHandler('agents/sessions/open', async request => {
913
- const sessionId = (request as { sessionId?: string } | undefined)?.sessionId;
914
- if (!sessionId || this.callbacks.openSessionInClaudeExtension == null) {
915
- return { opened: false };
916
- }
917
-
918
- try {
919
- await this.callbacks.openSessionInClaudeExtension(sessionId);
920
- return { opened: true };
921
- } catch (ex) {
922
- Logger.warn(
923
- `GkAgentProvider.agents/sessions/open: ${ex instanceof Error ? ex.message : String(ex)}`,
924
- );
925
- return { opened: false };
926
- }
927
- }),
928
- ];
929
-
930
- try {
931
- await this.callbacks.ipc.publishAgents(this._workspacePaths);
932
- } catch (ex) {
933
- // Unwind so a retry can re-register without hitting "already registered".
934
- for (const d of handlers) {
935
- d.dispose();
936
- }
937
- Logger.error(ex, 'GkAgentProvider.ensureIpcServer');
938
- return;
939
- }
940
-
941
- // `publishAgents` no-ops silently if the IPC server failed to start; don't seal
942
- // retries in that case — the next call should re-attempt.
943
- if (this.callbacks.ipc.port == null) {
944
- for (const d of handlers) {
945
- d.dispose();
946
- }
947
- return;
948
- }
949
-
950
- if (this._disposed) {
951
- for (const d of handlers) {
952
- d.dispose();
953
- }
954
- return;
955
- }
956
-
957
- this._handlerDisposables = handlers;
958
- this._ipcStarted = true;
959
-
960
- try {
961
- await this.syncSessions();
962
- if (this._disposed) return;
963
-
964
- void this.querySiblingWindowSessions();
965
-
966
- this._staleCheckTimer?.dispose();
967
- this._staleCheckTimer = disposableInterval(
968
- () => void this.syncSessions({ gate: true }),
969
- staleCheckIntervalMs,
970
- );
971
- } catch (ex) {
972
- Logger.error(ex, 'GkAgentProvider.ensureIpcServer');
973
- }
974
- }
975
-
976
- private handleSessionEvent(event: AgentSessionEvent, isBlocking: boolean): Promise<PermissionResponse | void> {
977
- // The CLI broadcasts every AI host's events to every listener, stamped with its `gk ai hook`
978
- // client id. Absent = `claude-code`: older CLIs don't stamp it, and no other client existed
979
- // then. A client we hold no descriptor for is dropped — the CLI supports more than this table
980
- // does, and we can't interpret an agent whose vocabulary we don't know. A dropped blocking
981
- // request gets no decision, so the CLI waits out its own hook timeout, which is correct: we
982
- // must not answer for an agent we don't track.
983
- const hookClientId = event.providerId ?? 'claude-code';
984
- const capabilities = getAgentCapabilities(hookClientId);
985
- if (capabilities == null) {
986
- Logger.debug(`GkAgentProvider.handleSessionEvent: ignoring ${event.event} from ${hookClientId}`);
987
- return Promise.resolve();
988
- }
989
-
990
- // The CLI relays each agent's native event name verbatim, so translate before doing anything
991
- // else. An unresolvable name must never reach the switch below: it would match no case while
992
- // still having created/updated a row, and for a blocking delivery would answer nothing while
993
- // looking handled.
994
- const hookEvent = resolveCanonicalHookEvent(capabilities, event.event, event.hookInput);
995
- if (hookEvent == null) {
996
- Logger.debug(
997
- `GkAgentProvider.handleSessionEvent: ignoring unmapped event ${event.event} from ${hookClientId}`,
998
- );
999
- return Promise.resolve();
1000
- }
1001
-
1002
- const toolName = canonicalToolNameFromEvent(capabilities, event);
1003
- const workspacePath = this.resolveWorkspacePath(event.cwd);
1004
- const eventContext: SessionContext = {
1005
- capabilities: capabilities,
1006
- pid: event.pid,
1007
- workspacePath: workspacePath,
1008
- isInWorkspace: workspacePath != null,
1009
- cwd: event.cwd,
1010
- initialCwd: event.initialCwd,
1011
- planFile: event.planFile,
1012
- sessionName: event.sessionName,
1013
- model: event.model,
1014
- worktreePath: worktreeFromEvent(event),
1015
- visitedWorktreePaths: visitedFromTimeline(event.cwdTimeline),
1016
- };
1017
- const tag = this.sessionTag(event.sessionId, event.sessionName ?? 'unnamed');
1018
- // Log the native name alongside the canonical one — a mapping mistake is otherwise invisible.
1019
- const eventTag = event.event === hookEvent ? hookEvent : `${hookEvent}(${event.event})`;
1020
-
1021
- if (hookEvent === 'SessionStart' || hookEvent === 'SessionEnd') {
1022
- Logger.info(`GkAgentProvider.handleSessionEvent: ${eventTag} ${tag}`);
1023
- } else {
1024
- Logger.debug(
1025
- `GkAgentProvider.handleSessionEvent: ${eventTag} ${tag}${toolName ? ` tool=${toolName}` : ''}${event.agentId ? ` agent=${event.agentId}` : ''}${event.notificationType ? ` type=${event.notificationType}` : ''}`,
1026
- );
1027
- }
1028
-
1029
- switch (hookEvent) {
1030
- case 'SessionStart': {
1031
- const wasNew = this._sessions.findIndex(s => s.id === event.sessionId) < 0;
1032
- const { index } = this.ensureSession(event.sessionId, eventContext);
1033
-
1034
- if (wasNew) {
1035
- // ensureSession created the session at 'idle' already. Just refresh pid if
1036
- // the event carries one — bookkeeping was implicitly clean.
1037
- this.resetBookkeeping(event.sessionId);
1038
- if (event.pid != null) {
1039
- const prev = this._sessions[index];
1040
- if (prev.pid !== event.pid) {
1041
- this._sessions[index] = { ...prev, pid: event.pid };
1042
- }
1043
- }
1044
- } else {
1045
- const prev = this._sessions[index];
1046
- if (prev.status === 'ended') {
1047
- // Resuming a terminal session reuses its id, so a SessionStart on an ended
1048
- // row IS the resume signal — revive it to a live `idle` row (clearing the
1049
- // ended/archivable state) with clean bookkeeping that endSession tore
1050
- // down. The next real event advances it out of idle. A `SessionStart` arrives on
1051
- // THIS window's hook flow, so the resume is locally owned now: clear the peer
1052
- // ownership carried over from the terminal record, and take the resume's pid
1053
- // (never the old terminal/reused one — retaining it would let dispatch focus a
1054
- // stale or unrelated process).
1055
- this._sessions[index] = {
1056
- ...prev,
1057
- status: 'idle',
1058
- phase: 'idle',
1059
- phaseSince: new Date(),
1060
- lastActivity: new Date(),
1061
- pid: event.pid,
1062
- isPeerOwned: undefined,
1063
- };
1064
- this.resetBookkeeping(event.sessionId);
1065
- } else if (event.pid != null && event.pid !== prev.pid) {
1066
- // SessionStart for a live session we already track is almost always a CLI
1067
- // replay (resume/reconnect/hook re-init). Don't clobber live state — the next
1068
- // real event re-establishes status. Refresh pid only.
1069
- this._sessions[index] = { ...prev, pid: event.pid, lastActivity: new Date() };
1070
- }
1071
- }
1072
-
1073
- this._onDidChangeSessions.fire();
1074
- void this.resolveTranscriptTitles(event.sessionId, event.cwd);
1075
- this.callbacks.onSessionStarted?.(capabilities.providerId);
1076
- break;
1077
- }
1078
-
1079
- case 'SessionEnd': {
1080
- const pending = this._pendingPermissions.get(event.sessionId);
1081
- if (pending != null) {
1082
- this.rejectAllPending(pending, new Error('Session ended'));
1083
- this._pendingPermissions.delete(event.sessionId);
1084
- }
1085
- this.cancelPendingIdleTransition(event.sessionId);
1086
-
1087
- const index = this._sessions.findIndex(s => s.id === event.sessionId);
1088
- if (index >= 0) {
1089
- if (this._statusFilterSupported) {
1090
- // Transition to a terminal `ended` row rather than removing it — ended
1091
- // sessions stay visible (de-emphasized) until archived or 30-day-purged by the CLI.
1092
- this.endSession(index, new Date());
1093
- } else {
1094
- // Legacy CLI (no `--status` durable store): an ended row could never be
1095
- // poll-confirmed nor archived, so keep the pre-ended remove-on-end behavior.
1096
- this._sessions.splice(index, 1);
1097
- const bk = this._sessionBookkeeping.get(event.sessionId);
1098
- if (bk != null) {
1099
- this.cancelPendingFileClears(bk);
1100
- this.cancelPendingReadClears(bk);
1101
- }
1102
- this._sessionBookkeeping.delete(event.sessionId);
1103
- this._transcriptReader.forget(event.sessionId);
1104
- this.noteTerminalTransition();
1105
- }
1106
- this._onDidChangeSessions.fire();
1107
- } else {
1108
- // SessionEnd for a session we no longer track (already pruned/removed, or a duplicate
1109
- // end): still clear any bookkeeping timers + transcript cache the row left behind, so
1110
- // they don't leak. The tracked branches above already tear this down themselves.
1111
- const bk = this._sessionBookkeeping.get(event.sessionId);
1112
- if (bk != null) {
1113
- this.cancelPendingFileClears(bk);
1114
- this.cancelPendingReadClears(bk);
1115
- }
1116
- this._sessionBookkeeping.delete(event.sessionId);
1117
- this._transcriptReader.forget(event.sessionId);
1118
- // Untracked or not, a session just reached a terminal state and finalized its
1119
- // transcript — an in-flight history query must not accept an answer that predates it.
1120
- this.noteTerminalTransition();
1121
- }
1122
- this.callbacks.onSessionEnded?.(capabilities.providerId);
1123
- break;
1124
- }
1125
-
1126
- case 'UserPromptSubmit': {
1127
- const { index, changed } = this.ensureSession(event.sessionId, eventContext);
1128
- // Seated metadata (location, model, name) has to reach subscribers even when the prompt
1129
- // turns out to be synthetic and the status update below never runs.
1130
- if (changed) {
1131
- this._onDidChangeSessions.fire();
1132
- }
1133
-
1134
- const cleaned = prepareStoredPrompt(event.prompt);
1135
- // A `UserPromptSubmit` whose payload sanitizes to nothing is harness-synthetic
1136
- // (e.g. background-bash <task-notification>, slash-command stdout echo). Treat it
1137
- // as informational so it doesn't flip the session to `thinking` or wipe a pending
1138
- // permission when no real user activity occurred.
1139
- if (!cleaned) break;
1140
-
1141
- const existing = this._sessions[index];
1142
- this._sessions[index] = {
1143
- ...existing,
1144
- lastPrompt: cleaned,
1145
- firstPrompt: existing.firstPrompt ?? prepareStoredPrompt(event.firstPrompt),
1146
- };
1147
- this.clearStalePermission(event.sessionId, 'UserPromptSubmit');
1148
- this.updateSessionStatus(event.sessionId, 'thinking', eventContext);
1149
- break;
1150
- }
1151
-
1152
- case 'PreToolUse': {
1153
- const bk = this.getBookkeeping(event.sessionId);
1154
- bk.activeToolCount++;
1155
- const filesChanged = this.trackToolUseFile(event.sessionId, event, capabilities);
1156
- const readsChanged = this.trackToolUseRead(event.sessionId, event, capabilities);
1157
- // Resolve a rich `Bash(grep …)`-style detail via the same tool_input passthrough the
1158
- // PermissionRequest handler uses, so working sessions surface what their tool is
1159
- // actually doing — not just the bare tool name. Falls back to bare name when no
1160
- // toolInput is available (older CLI / unhandled tool).
1161
- const toolInput =
1162
- (event.hookInput?.tool_input as Record<string, unknown> | undefined) ?? event.toolInput;
1163
- const statusDetail =
1164
- toolInput != null && toolName ? describeToolInput(toolName, toolInput) : toolName || undefined;
1165
- // Capture pre-update status so we can tell whether the upcoming updateSessionStatus
1166
- // will fire on its own — needed to avoid dropping fileActivity changes when the
1167
- // session is already in tool_use (back-to-back tool calls).
1168
- const sessionIndex = this._sessions.findIndex(s => s.id === event.sessionId);
1169
- const prevStatus = sessionIndex >= 0 ? this._sessions[sessionIndex].status : undefined;
1170
- const prevDetail = sessionIndex >= 0 ? this._sessions[sessionIndex].statusDetail : undefined;
1171
- const statusWillFire = prevStatus !== 'tool_use' || prevDetail !== statusDetail;
1172
- this.updateSessionStatus(event.sessionId, 'tool_use', {
1173
- ...eventContext,
1174
- statusDetail: statusDetail,
1175
- });
1176
- if ((filesChanged || readsChanged) && !statusWillFire) {
1177
- this._onDidChangeSessions.fire();
1178
- }
1179
- break;
1180
- }
1181
-
1182
- case 'PostToolUse':
1183
- case 'PostToolUseFailure': {
1184
- this.clearStalePermission(event.sessionId, hookEvent, this.settledByFromEvent(event, capabilities));
1185
- const bk = this.getBookkeeping(event.sessionId);
1186
- bk.activeToolCount = Math.max(0, bk.activeToolCount - 1);
1187
- // Cooldown the file-edit decoration instead of dropping it immediately, so a quick
1188
- // follow-up Edit/Write to the same path doesn't blink. The deferred clear fires its
1189
- // own change event when it expires. The immediate re-sync inside each flips
1190
- // `editing`/`reading` off the moment refcount hits zero, though — capture whether that
1191
- // changed the published list so we fire it ourselves when the status update below short-
1192
- // circuits (a parallel tool is still in flight, so activeToolCount stays > 0).
1193
- const filesChanged = this.scheduleClearToolUseFile(event.sessionId, event, capabilities);
1194
- const readsChanged = this.scheduleClearToolUseRead(event.sessionId, event, capabilities);
1195
- if (bk.activeToolCount === 0) {
1196
- // updateSessionStatus short-circuits without firing when status+detail are unchanged
1197
- // (already 'thinking' with no detail) — which would drop the editing/reading flag-flip
1198
- // above and leave a stale live pulse. Capture whether it will fire on its own and, if
1199
- // not, fire explicitly — mirrors the PreToolUse / PermissionDenied guards.
1200
- const idx = this._sessions.findIndex(s => s.id === event.sessionId);
1201
- const statusWillFire =
1202
- idx < 0 ||
1203
- this._sessions[idx].status !== 'thinking' ||
1204
- this._sessions[idx].statusDetail != null;
1205
- this.updateSessionStatus(event.sessionId, 'thinking', eventContext);
1206
- if ((filesChanged || readsChanged) && !statusWillFire) {
1207
- this._onDidChangeSessions.fire();
1208
- }
1209
- } else if (filesChanged || readsChanged) {
1210
- this._onDidChangeSessions.fire();
1211
- }
1212
- break;
1213
- }
1214
-
1215
- case 'Stop':
1216
- case 'StopFailure': {
1217
- const pending = this._pendingPermissions.get(event.sessionId);
1218
- if (pending != null) {
1219
- this.rejectAllPending(pending, new Error('Session stopped'));
1220
- this._pendingPermissions.delete(event.sessionId);
1221
- }
1222
- // Turn ended: stop the "live" pulse but keep the per-operation decay tail fading over
1223
- // the configured window (anchored at each tool's completion, not at the turn end). A full
1224
- // reset here would wipe the tail the instant the agent finishes a response.
1225
- this.settleBookkeeping(event.sessionId);
1226
- // Apply Stop's metadata synchronously so we don't carry stale context through the
1227
- // debounce window — any in-window mutations (e.g. CwdChanged) survive when the
1228
- // timer fires. ensureSession doesn't fire on metadata-only updates of existing
1229
- // sessions, so fire here to match the original Stop-handler's UI-update semantics.
1230
- const { changed } = this.ensureSession(event.sessionId, eventContext);
1231
- if (changed) {
1232
- this._onDidChangeSessions.fire();
1233
- }
1234
- // Defer the status change — if the agent continues within the debounce window
1235
- // (hook-driven re-prompt, IPC reordering, auto-resume), the next non-idle event
1236
- // cancels this and we never flick through idle.
1237
- this.scheduleIdleTransition(event.sessionId);
1238
- break;
1239
- }
1240
-
1241
- case 'PreCompact':
1242
- this.updateSessionStatus(event.sessionId, 'compacting', eventContext);
1243
- break;
1244
-
1245
- case 'PostCompact':
1246
- this.updateSessionStatus(event.sessionId, 'thinking', eventContext);
1247
- break;
1248
-
1249
- case 'Notification':
1250
- switch (event.notificationType) {
1251
- case 'idle_prompt':
1252
- // No-op — session is already idle from the preceding stop/session-start event
1253
- break;
1254
- case 'permission_prompt':
1255
- this.updateSessionStatus(event.sessionId, 'permission_requested', {
1256
- ...eventContext,
1257
- statusDetail: toolName,
1258
- pendingPermission: synthesizeUnresolvableAsk(
1259
- toolName,
1260
- // Plan-kind body: prefer the session's planFile, fall back to the event's
1261
- // — mirrors the blocking path.
1262
- this._sessions.find(s => s.id === event.sessionId)?.planFile ?? event.planFile,
1263
- ),
1264
- });
1265
- break;
1266
- case 'elicitation_dialog':
1267
- // An MCP elicitation, not a tool permission — `toolName` names the elicitation, so
1268
- // classifying it as a tool ask would mislabel the card. Same shape the `Elicitation`
1269
- // hook produces.
1270
- this.updateSessionStatus(event.sessionId, 'permission_requested', {
1271
- ...eventContext,
1272
- statusDetail: toolName,
1273
- pendingPermission: {
1274
- kind: 'elicitation',
1275
- toolName: toolName ?? 'Input Required',
1276
- toolDescription: toolName ?? 'Waiting for input',
1277
- resolvable: false,
1278
- },
1279
- });
1280
- break;
1281
- default:
1282
- // auth_success, unknown, or missing: no status change, just update timestamp
1283
- break;
1284
- }
1285
- break;
1286
-
1287
- case 'PermissionRequest': {
1288
- // hookInput is the raw passthrough; fall back to top-level fields for older CLI versions.
1289
- const hookInput = event.hookInput;
1290
- const toolInput = (hookInput?.tool_input as Record<string, unknown> | undefined) ?? event.toolInput;
1291
- // An agent whose asks we can't answer falls through to the observe-only branch below,
1292
- // which publishes the same card marked `resolvable: false`. Registering a pending entry
1293
- // for it would promise an Allow/Deny this window can never route.
1294
- if (canAnswerBlockingPermission(capabilities) && isBlocking && toolInput != null) {
1295
- const askToolName = toolName ?? '';
1296
- const toolDescription = describeToolInput(askToolName, toolInput);
1297
- const toolInputDescription = (toolInput.description as string | undefined) || undefined;
1298
- const kind: PendingPermissionKind = classifyPermissionKind(askToolName);
1299
- const toolUseId = hookInput?.tool_use_id as string | undefined;
1300
- const toolInputJson = stableStringify(toolInput);
1301
-
1302
- return new Promise<PermissionResponse>((resolve, reject) => {
1303
- const existing = this._pendingPermissions.get(event.sessionId);
1304
- // Same identity as `settledByFromEvent`'s comparisons: `toolUseId` when both sides
1305
- // have one, else toolName + canonical input.
1306
- const isDuplicateDelivery =
1307
- existing != null &&
1308
- ((existing.toolUseId != null && toolUseId != null && existing.toolUseId === toolUseId) ||
1309
- (existing.toolUseId == null &&
1310
- toolUseId == null &&
1311
- existing.toolName === askToolName &&
1312
- existing.toolInputJson === toolInputJson));
1313
-
1314
- if (isDuplicateDelivery) {
1315
- // Duplicate delivery of the same ask (CLI retry after its 60s response-header
1316
- // timeout) — attach instead of replacing so both connections settle together.
1317
- // Nothing about the display changes, so skip rebuilding it below. Accepted
1318
- // residual: two independent, identical, concurrent asks are byte-for-byte
1319
- // indistinguishable on the wire and coalesce the same way, receiving one
1320
- // decision — evicting instead would falsely deny a live ask.
1321
- existing.resolvers.push({ resolve: resolve, reject: reject });
1322
- return;
1323
- }
1324
-
1325
- if (existing != null) {
1326
- Logger.debug(
1327
- `GkAgentProvider.handleSessionEvent: auto-denying stale permission ${tag} tool=${existing.toolName}`,
1328
- );
1329
- this.resolveAllPending(existing, {
1330
- hookSpecificOutput: {
1331
- hookEventName: 'PermissionRequest',
1332
- decision: { behavior: 'deny' },
1333
- },
1334
- });
1335
- }
1336
-
1337
- this._pendingPermissions.set(event.sessionId, {
1338
- resolvers: [{ resolve: resolve, reject: reject }],
1339
- toolName: askToolName,
1340
- toolDescription: toolDescription,
1341
- toolUseId: toolUseId,
1342
- toolInputJson: toolInputJson,
1343
- });
1344
-
1345
- // Plan-kind body: prefer the existing session's planFile (set via SessionStart/
1346
- // PostToolUse) and fall back to the event's planFile for cold-start cases.
1347
- const planFilePath =
1348
- kind === 'plan'
1349
- ? (this._sessions.find(s => s.id === event.sessionId)?.planFile ?? event.planFile)
1350
- : undefined;
1351
- const planSummary = kind === 'plan' ? extractPlanSummary(toolInput) : undefined;
1352
- const questionDetails = kind === 'question' ? extractQuestionDetails(toolInput) : undefined;
1353
-
1354
- const permission: PendingPermission = {
1355
- kind: kind,
1356
- toolName: askToolName,
1357
- toolDescription: toolDescription,
1358
- toolInputDescription: toolInputDescription,
1359
- suggestions:
1360
- (hookInput?.permission_suggestions as PermissionSuggestion[] | undefined) ??
1361
- event.permissionSuggestions,
1362
- planFilePath: planFilePath,
1363
- planSummary: planSummary,
1364
- questionText: questionDetails?.text,
1365
- questionCount: questionDetails?.count,
1366
- };
1367
- this.updateSessionWithPermission(event.sessionId, permission, eventContext);
1368
- });
1369
- }
1370
-
1371
- {
1372
- // Mirror the blocking payload's shape so the ask renders identically either way —
1373
- // just unresolvable, since no blocking hook entry exists to route an answer through.
1374
- const kind = toolName != null ? classifyPermissionKind(toolName) : undefined;
1375
- // Plan-kind body: same session-first preference as the blocking path.
1376
- const planFilePath =
1377
- kind === 'plan'
1378
- ? (this._sessions.find(s => s.id === event.sessionId)?.planFile ?? event.planFile)
1379
- : undefined;
1380
- // Same body extraction as the blocking path — surfaces render `questionText`/
1381
- // `planSummary` in place of the tool description, so omitting them would show a
1382
- // generic "awaiting" card for a question the input actually carries.
1383
- const questionDetails =
1384
- kind === 'question' && toolInput != null ? extractQuestionDetails(toolInput) : undefined;
1385
- const synthesized: PendingPermission =
1386
- kind != null && toolName != null && toolInput != null
1387
- ? {
1388
- kind: kind,
1389
- toolName: toolName,
1390
- toolDescription: describeToolInput(toolName, toolInput),
1391
- toolInputDescription: (toolInput.description as string | undefined) || undefined,
1392
- planFilePath: planFilePath,
1393
- planSummary: kind === 'plan' ? extractPlanSummary(toolInput) : undefined,
1394
- questionText: questionDetails?.text,
1395
- questionCount: questionDetails?.count,
1396
- resolvable: false,
1397
- }
1398
- : synthesizeUnresolvableAsk(toolName, planFilePath);
1399
- this.updateSessionStatus(event.sessionId, 'permission_requested', {
1400
- ...eventContext,
1401
- statusDetail: toolName,
1402
- pendingPermission: synthesized,
1403
- });
1404
- }
1405
- break;
1406
- }
1407
-
1408
- case 'PermissionDenied': {
1409
- this.clearStalePermission(
1410
- event.sessionId,
1411
- 'PermissionDenied',
1412
- this.settledByFromEvent(event, capabilities),
1413
- );
1414
- const bk = this.getBookkeeping(event.sessionId);
1415
- bk.activeToolCount = Math.max(0, bk.activeToolCount - 1);
1416
- const filesChanged = this.untrackToolUseFile(event.sessionId, event, capabilities);
1417
- const readsChanged = this.untrackToolUseRead(event.sessionId, event, capabilities);
1418
- // When a parallel tool is still active the next status equals the current one and
1419
- // updateSessionStatus short-circuits — without an explicit fire, the dropped path
1420
- // would never reach subscribers (treemap activity overlay, WIP decoration).
1421
- const nextStatus = bk.activeToolCount > 0 ? 'tool_use' : 'thinking';
1422
- const sessionIndex = this._sessions.findIndex(s => s.id === event.sessionId);
1423
- const prevStatus = sessionIndex >= 0 ? this._sessions[sessionIndex].status : undefined;
1424
- const statusWillFire = prevStatus !== nextStatus;
1425
- this.updateSessionStatus(event.sessionId, nextStatus, eventContext);
1426
- if ((filesChanged || readsChanged) && !statusWillFire) {
1427
- this._onDidChangeSessions.fire();
1428
- }
1429
- break;
1430
- }
1431
-
1432
- case 'Elicitation': {
1433
- const bk = this.getBookkeeping(event.sessionId);
1434
- bk.pendingPermission = {
1435
- kind: 'elicitation',
1436
- toolName: toolName ?? 'Input Required',
1437
- toolDescription: toolName ?? 'Waiting for input',
1438
- // `Elicitation` arrives on a non-blocking hook, so no entry exists for
1439
- // `resolvePermission` to answer — the user responds in the agent's own session.
1440
- resolvable: false,
1441
- };
1442
- this.updateSessionStatus(event.sessionId, 'permission_requested', {
1443
- ...eventContext,
1444
- statusDetail: toolName,
1445
- });
1446
- break;
1447
- }
1448
-
1449
- case 'ElicitationResult': {
1450
- const bk = this.getBookkeeping(event.sessionId);
1451
- bk.pendingPermission = undefined;
1452
- this.updateSessionStatus(
1453
- event.sessionId,
1454
- bk.activeToolCount > 0 ? 'tool_use' : 'thinking',
1455
- eventContext,
1456
- );
1457
- break;
1458
- }
1459
-
1460
- case 'CwdChanged':
1461
- if (event.cwd) {
1462
- // Route through `ensureSession`: it owns the whole location transition — timeline
1463
- // seat (or handing attribution back to the probe when the CLI didn't explain the
1464
- // move), workspacePath/isInWorkspace recompute, the unresolvable-flag clear, and
1465
- // the probe gating. Writing `cwd` directly here left `cliSeatedWorktree` set, so
1466
- // the probe's answer for the new cwd could never replace the stale seated one.
1467
- const { changed } = this.ensureSession(event.sessionId, eventContext);
1468
- if (changed) {
1469
- this._onDidChangeSessions.fire();
1470
- }
1471
- }
1472
- break;
1473
-
1474
- case 'SubagentStart': {
1475
- const { index: parentIdx } = this.ensureSession(event.sessionId, eventContext);
1476
- const parent = this._sessions[parentIdx];
1477
- if (event.agentId) {
1478
- const now = new Date();
1479
- const subagent: AgentSession = {
1480
- id: event.agentId,
1481
- providerId: capabilities.providerId,
1482
- providerName: capabilities.displayName,
1483
- name: event.agentType ?? 'Subagent',
1484
- status: 'thinking',
1485
- phase: 'working',
1486
- phaseSince: now,
1487
- lastActivity: now,
1488
- isSubagent: true,
1489
- parentId: event.sessionId,
1490
- isInWorkspace: workspacePath != null,
1491
- };
1492
- const existingSubs = parent.subagents ? [...parent.subagents] : [];
1493
- existingSubs.push(subagent);
1494
- this._sessions[parentIdx] = { ...parent, subagents: existingSubs };
1495
- this._onDidChangeSessions.fire();
1496
- }
1497
- break;
1498
- }
1499
-
1500
- case 'SubagentStop': {
1501
- const { index: parentIdx } = this.ensureSession(event.sessionId, eventContext);
1502
- const parentSession = this._sessions[parentIdx];
1503
- if (parentSession.subagents != null && event.agentId) {
1504
- const filtered = parentSession.subagents.filter(s => s.id !== event.agentId);
1505
- if (filtered.length !== parentSession.subagents.length) {
1506
- this._sessions[parentIdx] = {
1507
- ...parentSession,
1508
- subagents: filtered.length > 0 ? filtered : undefined,
1509
- };
1510
- this._onDidChangeSessions.fire();
1511
- }
1512
- }
1513
- break;
1514
- }
1515
-
1516
- case 'TeammateIdle':
1517
- case 'TaskCompleted':
1518
- case 'InstructionsLoaded':
1519
- case 'ConfigChange':
1520
- case 'WorktreeCreate':
1521
- case 'WorktreeRemove':
1522
- // Not yet handled — intentional no-op.
1523
- break;
1524
- }
1525
-
1526
- return Promise.resolve();
1527
- }
1528
-
1529
- private sessionTag(sessionId: string, name?: string): string {
1530
- return name != null
1531
- ? `[session=${sessionId.substring(0, 8)}(${name})]`
1532
- : `[session=${sessionId.substring(0, 8)}]`;
1533
- }
1534
-
1535
- /** The owning agent's descriptor for a tracked session, resolved from the session's own
1536
- * `providerId`. `undefined` when the session isn't tracked (pruned/archived since) or carries a
1537
- * `providerId` the descriptor table doesn't know — callers pick their own fallback rather than
1538
- * silently assuming Claude Code. */
1539
- private getSessionCapabilities(sessionId: string): AgentCapabilities | undefined {
1540
- const session = this._sessions.find(s => s.id === sessionId);
1541
- return session != null ? getAgentCapabilitiesByProviderId(session.providerId) : undefined;
1542
- }
1543
-
1544
- /** Whether the session's agent leaves a transcript on disk for {@link ClaudeCodeTranscriptReader}
1545
- * to tail. Fails OPEN when no descriptor resolves, so an unknown/absent `providerId` can never
1546
- * suppress the Claude path this reader exists for. */
1547
- private supportsTranscripts(sessionId: string): boolean {
1548
- return this.getSessionCapabilities(sessionId)?.supportsTranscripts ?? true;
1549
- }
1550
-
1551
- private getBookkeeping(sessionId: string): SessionBookkeeping {
1552
- let bk = this._sessionBookkeeping.get(sessionId);
1553
- if (bk == null) {
1554
- bk = {
1555
- activeToolCount: 0,
1556
- currentFileCounts: new Map(),
1557
- pendingFileClears: new Map(),
1558
- currentReadCounts: new Map(),
1559
- pendingReadClears: new Map(),
1560
- lastTouchedAt: new Map(),
1561
- gitInfoUnresolvable: false,
1562
- };
1563
- this._sessionBookkeeping.set(sessionId, bk);
1564
- }
1565
- return bk;
1566
- }
1567
-
1568
- /** Returns the active decay window in milliseconds — the cooldown between PostToolUse and
1569
- * dropping the path from the bookkeeping. Reads through the host callback every time so a
1570
- * setting change takes effect on the next Pre/Post pair without re-wiring (existing timers
1571
- * keep their original timeout — acceptable since they're short-lived relative to the setting). */
1572
- private get activityDecayMs(): number {
1573
- return this.callbacks.getActivityDecayMs?.() ?? defaultActivityDecayMs;
1574
- }
1575
-
1576
- /** Locates the *parent* session id that owns the given session id — returns the id itself when
1577
- * it matches a top-level session, otherwise the `id` of the parent that holds it under
1578
- * `subagents[]`. Returns `undefined` when neither match (session no longer exists). Used to
1579
- * route per-session bookkeeping changes to the right top-level `syncSessionFileActivity` call.
1580
- * Tool events are parent-keyed (the CLI sends a sub-agent's tool events under the parent id),
1581
- * so the `subagents[]` branch is a defensive fallback for any other-keyed caller. */
1582
- private findOwningParentId(sessionId: string): string | undefined {
1583
- for (const s of this._sessions) {
1584
- if (s.id === sessionId) return s.id;
1585
-
1586
- if (s.subagents != null) {
1587
- for (const sub of s.subagents) {
1588
- if (sub.id === sessionId) return s.id;
1589
- }
1590
- }
1591
- }
1592
- return undefined;
1593
- }
1594
-
1595
- private resetBookkeeping(sessionId: string): void {
1596
- const bk = this.getBookkeeping(sessionId);
1597
- bk.activeToolCount = 0;
1598
- bk.pendingPermission = undefined;
1599
- this.cancelPendingFileClears(bk);
1600
- bk.currentFileCounts.clear();
1601
- this.cancelPendingReadClears(bk);
1602
- bk.currentReadCounts.clear();
1603
- bk.lastTouchedAt.clear();
1604
- bk.gitInfoUnresolvable = false;
1605
- // `cliSeatedWorktree` deliberately survives: SessionStart seats the worktree via
1606
- // `ensureSession` BEFORE resetting bookkeeping, and an unexplained cwd move clears it anyway.
1607
- const parentId = this.findOwningParentId(sessionId);
1608
- if (parentId != null) {
1609
- this.syncSessionFileActivity(parentId);
1610
- }
1611
- }
1612
-
1613
- /** Turn-end settle (Stop): the agent finished its turn, so nothing is "live" anymore — but
1614
- * the per-operation decay tail must survive. Heat fades over {@link activityDecayMs} measured
1615
- * from when each *tool* finished (the PostToolUse cooldown), NOT from when the turn ends, so a
1616
- * Stop must not cancel those cooldowns or clear `lastTouchedAt`. We only reset turn-scoped
1617
- * state and force-finish any path whose PostToolUse never arrived before Stop: drop its
1618
- * refcount to 0 (flipping `editing`/`reading` off so the pulse stops) and schedule the same
1619
- * decay eviction a normal completion would. Contrast {@link resetBookkeeping}, which hard-
1620
- * wipes everything for SessionStart/SessionEnd (the agent is gone, not merely between turns). */
1621
- private settleBookkeeping(sessionId: string): void {
1622
- const bk = this.getBookkeeping(sessionId);
1623
- bk.activeToolCount = 0;
1624
- bk.pendingPermission = undefined;
1625
- // Allow git re-resolution next turn (matches the prior reset-on-Stop intent).
1626
- bk.gitInfoUnresolvable = false;
1627
- for (const [path, count] of bk.currentFileCounts) {
1628
- if (count > 0) {
1629
- bk.currentFileCounts.set(path, 0);
1630
- this.scheduleFileDecayEviction(bk, sessionId, path);
1631
- }
1632
- }
1633
- for (const [path, count] of bk.currentReadCounts) {
1634
- if (count > 0) {
1635
- bk.currentReadCounts.set(path, 0);
1636
- this.scheduleReadDecayEviction(bk, sessionId, path);
1637
- }
1638
- }
1639
- const parentId = this.findOwningParentId(sessionId);
1640
- if (parentId != null && this.syncSessionFileActivity(parentId)) {
1641
- this._onDidChangeSessions.fire();
1642
- }
1643
- }
1644
-
1645
- private cancelPendingFileClears(bk: SessionBookkeeping): void {
1646
- for (const timer of bk.pendingFileClears.values()) {
1647
- clearTimeout(timer);
1648
- }
1649
- bk.pendingFileClears.clear();
1650
- }
1651
-
1652
- private cancelPendingReadClears(bk: SessionBookkeeping): void {
1653
- for (const timer of bk.pendingReadClears.values()) {
1654
- clearTimeout(timer);
1655
- }
1656
- bk.pendingReadClears.clear();
1657
- }
1658
-
1659
- /** Extracts the targeted file path from a PreToolUse/PostToolUse event. Tries the raw
1660
- * `hookInput.tool_input` passthrough first, falls back to the projected `event.toolInput`
1661
- * for older CLI versions that only fill the top-level fields. Treats an empty-object
1662
- * `hookInput.tool_input` as missing — `??` would have kept it (truthy) and shadowed a
1663
- * populated `event.toolInput`. */
1664
- private getEventFilePath(event: AgentSessionEvent, capabilities: AgentCapabilities): string | undefined {
1665
- const toolName = canonicalToolNameFromEvent(capabilities, event);
1666
- if (toolName == null) return undefined;
1667
-
1668
- const rawHookInput = event.hookInput?.tool_input as Record<string, unknown> | undefined;
1669
- const hookInputPopulated = rawHookInput != null && Object.keys(rawHookInput).length > 0;
1670
- const toolInput = hookInputPopulated ? rawHookInput : event.toolInput;
1671
- return getToolFilePath(toolName, toolInput);
1672
- }
1673
-
1674
- /** Stamps the per-path/per-kind `lastTouchedAt[kind]` to the current epoch ms. Stamped on every
1675
- * PreToolUse for the matching kind so `serializeFileActivity` can emit `readAt`/`editedAt` as
1676
- * a relative `now - timestamp` delta. */
1677
- private stampLastTouched(bk: SessionBookkeeping, filePath: string, kind: 'read' | 'edit'): void {
1678
- const existing = bk.lastTouchedAt.get(filePath);
1679
- const next: { edit?: number; read?: number } = existing != null ? { ...existing } : {};
1680
- next[kind] = Date.now();
1681
- bk.lastTouchedAt.set(filePath, next);
1682
- }
1683
-
1684
- /** Drops the `lastTouchedAt[kind]` slot for `filePath`, and the whole entry when neither kind
1685
- * remains. Mirrors the cooldown-timer eviction in `scheduleClear*` — once the path is gone
1686
- * from the count map for that kind, its timestamp is no longer load-bearing. */
1687
- private clearLastTouched(bk: SessionBookkeeping, filePath: string, kind: 'read' | 'edit'): void {
1688
- const existing = bk.lastTouchedAt.get(filePath);
1689
- if (existing == null) return;
1690
- if (existing[kind] == null) return;
1691
-
1692
- const next: { edit?: number; read?: number } = { ...existing, [kind]: undefined };
1693
- if (next.edit == null && next.read == null) {
1694
- bk.lastTouchedAt.delete(filePath);
1695
- } else {
1696
- bk.lastTouchedAt.set(filePath, next);
1697
- }
1698
- }
1699
-
1700
- /** Records that a file-mutating tool call is in flight against `filePath` so consumers can
1701
- * highlight the path. Refcounts per path — keyed by path (not `tool_use_id`, which the GK CLI
1702
- * doesn't reliably surface) so parallel calls bump the count and Post/Pre order doesn't matter.
1703
- * Returns whether the parent's published `fileActivity` actually changed so callers can fire
1704
- * when the surrounding {@link updateSessionStatus} short-circuits. */
1705
- private trackToolUseFile(sessionId: string, event: AgentSessionEvent, capabilities: AgentCapabilities): boolean {
1706
- const filePath = this.getEventFilePath(event, capabilities);
1707
- if (filePath == null) return false;
1708
-
1709
- const bk = this.getBookkeeping(sessionId);
1710
- // A fresh Pre during a pending Post-cooldown means the file is live again — cancel the
1711
- // scheduled clear so the deferred drop doesn't run after the new tool already finished.
1712
- const pendingClear = bk.pendingFileClears.get(filePath);
1713
- if (pendingClear != null) {
1714
- clearTimeout(pendingClear);
1715
- bk.pendingFileClears.delete(filePath);
1716
- }
1717
- bk.currentFileCounts.set(filePath, (bk.currentFileCounts.get(filePath) ?? 0) + 1);
1718
- this.stampLastTouched(bk, filePath, 'edit');
1719
- return this.syncSessionFileActivityForChild(sessionId);
1720
- }
1721
-
1722
- /** Decrements the in-flight refcount for a finished tool call's file. While the count stays
1723
- * above zero (overlapping parallel calls) the path remains marked. When the count drops to
1724
- * zero, leaves the entry in place at count `0` and schedules a {@link activityDecayMs}
1725
- * cooldown before actually dropping it — so a recently-edited file lingers as a decay tail
1726
- * on the treemap. A Pre arriving during cooldown cancels the timer and bumps the count back
1727
- * above zero. Returns whether the parent's published `fileActivity` changed (i.e. `editing`
1728
- * flipped off) so the PostToolUse caller can fire when its `updateSessionStatus` short-
1729
- * circuits (a parallel tool still in flight keeps activeToolCount > 0). */
1730
- private scheduleClearToolUseFile(
1731
- sessionId: string,
1732
- event: AgentSessionEvent,
1733
- capabilities: AgentCapabilities,
1734
- ): boolean {
1735
- const filePath = this.getEventFilePath(event, capabilities);
1736
- if (filePath == null) return false;
1737
-
1738
- const bk = this.getBookkeeping(sessionId);
1739
- const count = bk.currentFileCounts.get(filePath);
1740
- // A duplicate / out-of-order Post during cooldown (count is already 0) would drive the
1741
- // refcount negative and schedule a second timer — ignore those so the cooldown stays
1742
- // authoritative until either its timer fires or a fresh Pre bumps the count back up.
1743
- if (count == null || count <= 0) return false;
1744
-
1745
- const next = count - 1;
1746
- bk.currentFileCounts.set(filePath, next);
1747
- // Re-sync so `editing` drops from `true` to `undefined` the moment refcount hits zero —
1748
- // the cooldown only governs when the path itself leaves the snapshot, not when the
1749
- // "live" boolean flips off.
1750
- const changed = this.syncSessionFileActivityForChild(sessionId);
1751
- if (next > 0) return changed;
1752
-
1753
- // Refcount just hit zero — keep the path in `currentFileCounts` for the cooldown window,
1754
- // then drop it.
1755
- this.scheduleFileDecayEviction(bk, sessionId, filePath);
1756
- return changed;
1757
- }
1758
-
1759
- /** Schedules (replacing any prior timer) the {@link activityDecayMs} cooldown that finally
1760
- * drops `filePath` from the edit bookkeeping once it has fully decayed. Shared by the
1761
- * PostToolUse cooldown path and the Stop settle ({@link settleBookkeeping}); the path stays
1762
- * in `currentFileCounts` at count 0 meanwhile so its `editedAt` keeps aging on the client. */
1763
- private scheduleFileDecayEviction(bk: SessionBookkeeping, sessionId: string, filePath: string): void {
1764
- const existing = bk.pendingFileClears.get(filePath);
1765
- if (existing != null) {
1766
- clearTimeout(existing);
1767
- }
1768
-
1769
- const timer = setTimeout(() => {
1770
- if (this._disposed) return;
1771
-
1772
- const cur = this._sessionBookkeeping.get(sessionId);
1773
- if (cur == null) return;
1774
-
1775
- cur.pendingFileClears.delete(filePath);
1776
- // Re-check: a Pre during cooldown bumped the count and cancelled this timer, but a
1777
- // concurrent reset could have cleared everything — bail in either case.
1778
- if ((cur.currentFileCounts.get(filePath) ?? 0) > 0) return;
1779
-
1780
- cur.currentFileCounts.delete(filePath);
1781
- this.clearLastTouched(cur, filePath, 'edit');
1782
- if (this.syncSessionFileActivityForChild(sessionId)) {
1783
- this._onDidChangeSessions.fire();
1784
- }
1785
- }, this.activityDecayMs);
1786
- bk.pendingFileClears.set(filePath, timer);
1787
- }
1788
-
1789
- /** Drops a tracked tool call's file immediately (no cooldown). Used for PermissionDenied,
1790
- * where the tool never ran — keeping the "live" badge would be misleading.
1791
- * Returns whether the published list changed. */
1792
- private untrackToolUseFile(sessionId: string, event: AgentSessionEvent, capabilities: AgentCapabilities): boolean {
1793
- const filePath = this.getEventFilePath(event, capabilities);
1794
- if (filePath == null) return false;
1795
-
1796
- const bk = this.getBookkeeping(sessionId);
1797
- const count = bk.currentFileCounts.get(filePath);
1798
- if (count == null) return false;
1799
- if (count > 1) {
1800
- bk.currentFileCounts.set(filePath, count - 1);
1801
- return this.syncSessionFileActivityForChild(sessionId);
1802
- }
1803
-
1804
- const pending = bk.pendingFileClears.get(filePath);
1805
- if (pending != null) {
1806
- clearTimeout(pending);
1807
- bk.pendingFileClears.delete(filePath);
1808
- }
1809
- bk.currentFileCounts.delete(filePath);
1810
- this.clearLastTouched(bk, filePath, 'edit');
1811
- return this.syncSessionFileActivityForChild(sessionId);
1812
- }
1813
-
1814
- /** Mirror of {@link getEventFilePath} for read-only file tools. */
1815
- private getEventReadPath(event: AgentSessionEvent, capabilities: AgentCapabilities): string | undefined {
1816
- const toolName = canonicalToolNameFromEvent(capabilities, event);
1817
- if (toolName == null) return undefined;
1818
-
1819
- const rawHookInput = event.hookInput?.tool_input as Record<string, unknown> | undefined;
1820
- const hookInputPopulated = rawHookInput != null && Object.keys(rawHookInput).length > 0;
1821
- const toolInput = hookInputPopulated ? rawHookInput : event.toolInput;
1822
- return getToolReadPath(toolName, toolInput);
1823
- }
1824
-
1825
- /** Refcount mirror of {@link trackToolUseFile} for read-only file tools. */
1826
- private trackToolUseRead(sessionId: string, event: AgentSessionEvent, capabilities: AgentCapabilities): boolean {
1827
- const filePath = this.getEventReadPath(event, capabilities);
1828
- if (filePath == null) return false;
1829
-
1830
- const bk = this.getBookkeeping(sessionId);
1831
- const pendingClear = bk.pendingReadClears.get(filePath);
1832
- if (pendingClear != null) {
1833
- clearTimeout(pendingClear);
1834
- bk.pendingReadClears.delete(filePath);
1835
- }
1836
- bk.currentReadCounts.set(filePath, (bk.currentReadCounts.get(filePath) ?? 0) + 1);
1837
- this.stampLastTouched(bk, filePath, 'read');
1838
- return this.syncSessionFileActivityForChild(sessionId);
1839
- }
1840
-
1841
- /** Cooldown mirror of {@link scheduleClearToolUseFile} for read-only file tools. Returns
1842
- * whether the parent's published `fileActivity` changed (i.e. `reading` flipped off). */
1843
- private scheduleClearToolUseRead(
1844
- sessionId: string,
1845
- event: AgentSessionEvent,
1846
- capabilities: AgentCapabilities,
1847
- ): boolean {
1848
- const filePath = this.getEventReadPath(event, capabilities);
1849
- if (filePath == null) return false;
1850
-
1851
- const bk = this.getBookkeeping(sessionId);
1852
- const count = bk.currentReadCounts.get(filePath);
1853
- // Same guard as scheduleClearToolUseFile — duplicate / out-of-order Posts during cooldown
1854
- // would otherwise drive the refcount negative and stack up redundant clear timers.
1855
- if (count == null || count <= 0) return false;
1856
-
1857
- const next = count - 1;
1858
- bk.currentReadCounts.set(filePath, next);
1859
- // Re-sync so `reading` drops to `undefined` the moment refcount hits zero.
1860
- const changed = this.syncSessionFileActivityForChild(sessionId);
1861
- if (next > 0) return changed;
1862
-
1863
- this.scheduleReadDecayEviction(bk, sessionId, filePath);
1864
- return changed;
1865
- }
1866
-
1867
- /** Read-class mirror of {@link scheduleFileDecayEviction}. */
1868
- private scheduleReadDecayEviction(bk: SessionBookkeeping, sessionId: string, filePath: string): void {
1869
- const existing = bk.pendingReadClears.get(filePath);
1870
- if (existing != null) {
1871
- clearTimeout(existing);
1872
- }
1873
-
1874
- const timer = setTimeout(() => {
1875
- if (this._disposed) return;
1876
-
1877
- const cur = this._sessionBookkeeping.get(sessionId);
1878
- if (cur == null) return;
1879
-
1880
- cur.pendingReadClears.delete(filePath);
1881
- if ((cur.currentReadCounts.get(filePath) ?? 0) > 0) return;
1882
-
1883
- cur.currentReadCounts.delete(filePath);
1884
- this.clearLastTouched(cur, filePath, 'read');
1885
- if (this.syncSessionFileActivityForChild(sessionId)) {
1886
- this._onDidChangeSessions.fire();
1887
- }
1888
- }, this.activityDecayMs);
1889
- bk.pendingReadClears.set(filePath, timer);
1890
- }
1891
-
1892
- /** Immediate-drop mirror of {@link untrackToolUseFile} for read-only file tools. */
1893
- private untrackToolUseRead(sessionId: string, event: AgentSessionEvent, capabilities: AgentCapabilities): boolean {
1894
- const filePath = this.getEventReadPath(event, capabilities);
1895
- if (filePath == null) return false;
1896
-
1897
- const bk = this.getBookkeeping(sessionId);
1898
- const count = bk.currentReadCounts.get(filePath);
1899
- if (count == null) return false;
1900
- if (count > 1) {
1901
- bk.currentReadCounts.set(filePath, count - 1);
1902
- return this.syncSessionFileActivityForChild(sessionId);
1903
- }
1904
-
1905
- const pending = bk.pendingReadClears.get(filePath);
1906
- if (pending != null) {
1907
- clearTimeout(pending);
1908
- bk.pendingReadClears.delete(filePath);
1909
- }
1910
- bk.currentReadCounts.delete(filePath);
1911
- this.clearLastTouched(bk, filePath, 'read');
1912
- return this.syncSessionFileActivityForChild(sessionId);
1913
- }
1914
-
1915
- /** Convenience wrapper that resolves the parent for a possibly-sub-agent session id, then calls
1916
- * {@link syncSessionFileActivity}. Tool-call handlers call this without knowing whether the
1917
- * session is a top-level or sub-agent session. Returns `false` when the session isn't found
1918
- * (already pruned / unknown peer id). */
1919
- private syncSessionFileActivityForChild(sessionId: string): boolean {
1920
- const parentId = this.findOwningParentId(sessionId);
1921
- if (parentId == null) return false;
1922
- return this.syncSessionFileActivity(parentId);
1923
- }
1924
-
1925
- /** Rebuilds the parent session's `fileActivity` array from its bookkeeping and writes it onto
1926
- * the session in place. Returns whether the *structural* shape changed (paths/kinds/flags) —
1927
- * timestamps are always refreshed regardless, but a fire is gated on structural change so
1928
- * non-meaningful drift doesn't churn the wire.
1929
- *
1930
- * No separate sub-agent rollup is needed: the GK CLI keys a sub-agent's tool events under its
1931
- * PARENT session id (with `agentId` set), so they accumulate in the parent's own bookkeeping
1932
- * directly and are already reflected here. Sub-agent serialized objects carry no `fileActivity`. */
1933
- private syncSessionFileActivity(parentSessionId: string): boolean {
1934
- const index = this._sessions.findIndex(s => s.id === parentSessionId);
1935
- if (index < 0) return false;
1936
-
1937
- const parent = this._sessions[index];
1938
- if (parent.status === 'ended') return false;
1939
-
1940
- const parentBk = this._sessionBookkeeping.get(parentSessionId);
1941
-
1942
- type Contrib = { editCount: number; readCount: number; editAt?: number; readAt?: number };
1943
- const merged = new Map<string, Contrib>();
1944
-
1945
- const contributeFrom = (bk: SessionBookkeeping | undefined): void => {
1946
- if (bk == null) return;
1947
-
1948
- for (const [path, count] of bk.currentFileCounts) {
1949
- let entry = merged.get(path);
1950
- if (entry == null) {
1951
- entry = { editCount: 0, readCount: 0 };
1952
- merged.set(path, entry);
1953
- }
1954
- entry.editCount += count;
1955
- const ts = bk.lastTouchedAt.get(path)?.edit;
1956
- if (ts != null && (entry.editAt == null || ts > entry.editAt)) {
1957
- entry.editAt = ts;
1958
- }
1959
- }
1960
- for (const [path, count] of bk.currentReadCounts) {
1961
- let entry = merged.get(path);
1962
- if (entry == null) {
1963
- entry = { editCount: 0, readCount: 0 };
1964
- merged.set(path, entry);
1965
- }
1966
- entry.readCount += count;
1967
- const ts = bk.lastTouchedAt.get(path)?.read;
1968
- if (ts != null && (entry.readAt == null || ts > entry.readAt)) {
1969
- entry.readAt = ts;
1970
- }
1971
- }
1972
- };
1973
-
1974
- contributeFrom(parentBk);
1975
-
1976
- const now = Date.now();
1977
- let next: FileActivityEntry[] | undefined;
1978
- if (merged.size > 0) {
1979
- next = [];
1980
- for (const [path, contrib] of merged) {
1981
- const entry: FileActivityEntry = { path: path };
1982
- if (contrib.editAt != null) {
1983
- entry.editedAt = Math.max(0, now - contrib.editAt);
1984
- }
1985
- if (contrib.readAt != null) {
1986
- entry.readAt = Math.max(0, now - contrib.readAt);
1987
- }
1988
- if (contrib.editCount > 0) {
1989
- entry.editing = true;
1990
- }
1991
- if (contrib.readCount > 0) {
1992
- entry.reading = true;
1993
- }
1994
- next.push(entry);
1995
- }
1996
- }
1997
-
1998
- const changed = !fileActivityStructurallyEqual(parent.fileActivity, next);
1999
- // Always replace so timestamps reflect the latest serialization, even when the structural
2000
- // shape is unchanged (so a downstream fire from anywhere else carries fresh `sinceMs`).
2001
- this._sessions[index] = { ...parent, fileActivity: next };
2002
- return changed;
2003
- }
2004
-
2005
- /** Defer the `Stop → idle` commit. If the agent produces a non-idle event within
2006
- * `stopToIdleDebounceMs`, `cancelPendingIdleTransition` (called from `updateSessionStatus`)
2007
- * cancels this and the session stays in its prior phase — no working → idle → working flicker.
2008
- *
2009
- * Intentionally takes no context: the caller is expected to apply any event metadata
2010
- * synchronously before scheduling. Carrying Stop-time context into the timer would let
2011
- * in-window metadata mutations (e.g. `CwdChanged` updating `_sessions[i].cwd` directly)
2012
- * get reverted when the deferred commit replays the stale context through `ensureSession`. */
2013
- private scheduleIdleTransition(sessionId: string): void {
2014
- this.cancelPendingIdleTransition(sessionId);
2015
- const timer = setTimeout(() => {
2016
- this._pendingIdleTimers.delete(sessionId);
2017
- // Session may have been removed (SessionEnd, prune) during the window, or ended by a
2018
- // poll/SessionEnd that raced this timer — reviving a terminal row to idle would zombie it.
2019
- const idx = this._sessions.findIndex(s => s.id === sessionId);
2020
- if (idx < 0 || this._sessions[idx].status === 'ended') return;
2021
-
2022
- this.updateSessionStatus(sessionId, 'idle');
2023
- }, stopToIdleDebounceMs);
2024
- this._pendingIdleTimers.set(sessionId, timer);
2025
- }
2026
-
2027
- private cancelPendingIdleTransition(sessionId: string): void {
2028
- const timer = this._pendingIdleTimers.get(sessionId);
2029
- if (timer == null) return;
2030
-
2031
- clearTimeout(timer);
2032
- this._pendingIdleTimers.delete(sessionId);
2033
- }
2034
-
2035
- /** Stable phase-since: if we're oscillating back into the phase we were just in (within a
2036
- * short window), restore its original timestamp so the displayed elapsed time doesn't snap
2037
- * to 0. Otherwise records the current phase as the new "prior" and returns `now`. */
2038
- private resolvePhaseSince(
2039
- sessionId: string,
2040
- prevPhase: AgentSessionPhase,
2041
- prevPhaseSince: Date,
2042
- newPhase: AgentSessionPhase,
2043
- ): Date {
2044
- if (prevPhase === newPhase) return prevPhaseSince;
2045
-
2046
- const bk = this.getBookkeeping(sessionId);
2047
- const now = new Date();
2048
-
2049
- if (bk.priorPhase?.phase === newPhase && now.getTime() - prevPhaseSince.getTime() < phaseSinceRestoreWindowMs) {
2050
- const restored = bk.priorPhase.phaseSince;
2051
- bk.priorPhase = { phase: prevPhase, phaseSince: prevPhaseSince };
2052
- return restored;
2053
- }
2054
-
2055
- bk.priorPhase = { phase: prevPhase, phaseSince: prevPhaseSince };
2056
- return now;
2057
- }
2058
-
2059
- /** Extracts the ask identity a settlement event refers to. `PostToolUse`/`PermissionDenied`
2060
- * carry `hookInput.tool_use_id` and `hookInput.tool_input` in production; the top-level
2061
- * `event.toolName`/`event.toolInput` are the older-CLI fallback. Input is canonicalized so key
2062
- * order (or arriving via the fallback field instead of `hookInput`) doesn't defeat comparison. */
2063
- private settledByFromEvent(
2064
- event: AgentSessionEvent,
2065
- capabilities: AgentCapabilities,
2066
- ): {
2067
- toolUseId?: string;
2068
- toolName?: string;
2069
- toolInputJson?: string;
2070
- } {
2071
- const hookInput = event.hookInput;
2072
- const toolInput = (hookInput?.tool_input as Record<string, unknown> | undefined) ?? event.toolInput;
2073
- return {
2074
- toolUseId: hookInput?.tool_use_id as string | undefined,
2075
- // Canonical, because the registered ask's `toolName` is canonical too — comparing a native
2076
- // name against it would look like an identity mismatch and strand the ask.
2077
- toolName: canonicalToolNameFromEvent(capabilities, event),
2078
- toolInputJson: toolInput != null ? stableStringify(toolInput) : undefined,
2079
- };
2080
- }
2081
-
2082
- private resolveAllPending(entry: PendingPermissionEntry, response: PermissionResponse): void {
2083
- for (const resolver of entry.resolvers) {
2084
- resolver.resolve(response);
2085
- }
2086
- }
2087
-
2088
- private rejectAllPending(entry: PendingPermissionEntry, reason: Error): void {
2089
- for (const resolver of entry.resolvers) {
2090
- resolver.reject(reason);
2091
- }
2092
- }
2093
-
2094
- // Called when a pending permission was resolved outside GitLens (e.g. via the CLI terminal).
2095
- // The 'deny' response is a safe no-op since the tool has already run or been denied upstream.
2096
- // `settledBy` identifies the ask the settling event actually refers to — out-of-order/duplicate
2097
- // hook deliveries (PostToolUse for an unrelated tool, a PermissionDenied for an already-evicted
2098
- // earlier ask) must never deny a still-live blocking ask just because it shares a session id.
2099
- // `PermissionDenied` itself only ever fires after Claude Code's auto-mode classifier denies a
2100
- // tool call — never as an echo of a deny a `PermissionRequest` response sent.
2101
- private clearStalePermission(
2102
- sessionId: string,
2103
- reason: string,
2104
- settledBy?: { toolUseId?: string; toolName?: string; toolInputJson?: string },
2105
- ): void {
2106
- const bk = this.getBookkeeping(sessionId);
2107
- const pending = this._pendingPermissions.get(sessionId);
2108
-
2109
- if (pending != null) {
2110
- if (settledBy != null) {
2111
- let mismatch = false;
2112
-
2113
- if (pending.toolUseId != null && settledBy.toolUseId != null) {
2114
- // Rule 1: both sides identified — the only trustworthy comparison. `PermissionDenied`/
2115
- // `PostToolUse` hookInput DOES carry `tool_use_id`, but `PermissionRequest` never does,
2116
- // so `pending` never actually has one — this branch only engages if a future CLI adds
2117
- // it to requests too. The asymmetry is deliberate, not a gap to close.
2118
- mismatch = pending.toolUseId !== settledBy.toolUseId;
2119
- } else if (pending.toolName === '') {
2120
- // Rule 2: no identity was captured at registration (degraded payload) — nothing to
2121
- // gate on, so clear unconditionally rather than stranding the ask forever.
2122
- mismatch = false;
2123
- } else {
2124
- // Rule 3: name must match; when both sides have input, it must match too. A
2125
- // PostToolUse for a different invocation of the same tool (different input) leaves
2126
- // the ask untouched.
2127
- mismatch =
2128
- settledBy.toolName !== pending.toolName ||
2129
- (pending.toolInputJson != null &&
2130
- settledBy.toolInputJson != null &&
2131
- pending.toolInputJson !== settledBy.toolInputJson);
2132
- }
2133
-
2134
- if (mismatch) {
2135
- Logger.debug(
2136
- `GkAgentProvider.clearStalePermission: skip (identity mismatch) ${reason} ${this.sessionTag(sessionId)} pending=${pending.toolUseId ?? pending.toolName} settledBy=${settledBy.toolUseId ?? settledBy.toolName ?? 'unknown'}`,
2137
- );
2138
- return;
2139
- }
2140
- }
2141
-
2142
- Logger.debug(`GkAgentProvider.clearStalePermission: ${reason} ${this.sessionTag(sessionId)}`);
2143
- this.resolveAllPending(pending, {
2144
- hookSpecificOutput: { hookEventName: 'PermissionRequest', decision: { behavior: 'deny' } },
2145
- });
2146
- this._pendingPermissions.delete(sessionId);
2147
- bk.pendingPermission = undefined;
2148
- return;
2149
- }
2150
-
2151
- if (bk.pendingPermission == null) return;
2152
-
2153
- if (
2154
- settledBy?.toolName != null &&
2155
- bk.pendingPermission.toolName != null &&
2156
- settledBy.toolName !== bk.pendingPermission.toolName
2157
- ) {
2158
- Logger.debug(
2159
- `GkAgentProvider.clearStalePermission: skip (display-only identity mismatch) ${reason} ${this.sessionTag(sessionId)} pending=${bk.pendingPermission.toolName} settledBy=${settledBy.toolName}`,
2160
- );
2161
- return;
2162
- }
2163
-
2164
- Logger.debug(`GkAgentProvider.clearStalePermission: ${reason} ${this.sessionTag(sessionId)}`);
2165
- bk.pendingPermission = undefined;
2166
- }
2167
-
2168
- resolvePermission(
2169
- sessionId: string,
2170
- decision: PermissionDecision,
2171
- updatedPermissions?: PermissionSuggestion[],
2172
- ): boolean {
2173
- // Explicit refusal rather than an incidental one: an agent we can't answer for never registers
2174
- // a pending entry, so the `pending == null` check below would already return `false` — but the
2175
- // caller deserves to know WHY, and the guard has to survive a future entry-registering path.
2176
- const capabilities = this.getSessionCapabilities(sessionId);
2177
- if (capabilities != null && !canAnswerBlockingPermission(capabilities)) {
2178
- Logger.debug(
2179
- `GkAgentProvider.resolvePermission: refusing ${decision} for ${this.sessionTag(sessionId)} — ${capabilities.displayName} asks are observe-only`,
2180
- );
2181
- return false;
2182
- }
2183
-
2184
- const pending = this._pendingPermissions.get(sessionId);
2185
- if (pending == null) return false;
2186
-
2187
- Logger.debug(
2188
- `GkAgentProvider.resolvePermission: ${decision} ${this.sessionTag(sessionId)} tool=${pending.toolName}`,
2189
- );
2190
- this.resolveAllPending(pending, {
2191
- hookSpecificOutput: {
2192
- hookEventName: 'PermissionRequest',
2193
- decision: { behavior: decision, updatedPermissions: updatedPermissions },
2194
- },
2195
- });
2196
- this.callbacks.onPermissionResolved?.({
2197
- provider: capabilities?.providerId ?? claudeCodeCapabilities.providerId,
2198
- tool: pending.toolName,
2199
- decision: decision,
2200
- });
2201
- this._pendingPermissions.delete(sessionId);
2202
-
2203
- const bk = this.getBookkeeping(sessionId);
2204
- bk.pendingPermission = undefined;
2205
-
2206
- const nextStatus = bk.activeToolCount > 0 ? 'tool_use' : 'thinking';
2207
- this.updateSessionStatus(sessionId, nextStatus);
2208
- return true;
2209
- }
2210
-
2211
- async archiveSession(sessionId: string): Promise<boolean> {
2212
- // Authoritative status guard: the CLI's `archive-session` ends an active session first, so a
2213
- // stale webview action (or a row that resumed out of `ended` since the click) must never
2214
- // reach the CLI — archiving is only ever valid on a terminal row. Callers gate the UI too,
2215
- // but this is the last line that owns live process safety. Returns `false` when refused so the
2216
- // host doesn't record a success telemetry event for an archive that never happened.
2217
- const session = this._sessions.find(s => s.id === sessionId);
2218
- if (session != null && session.status !== 'ended') {
2219
- Logger.warn(
2220
- `GkAgentProvider.archiveSession: refusing to archive non-ended ${this.sessionTag(sessionId)} (status=${session.status})`,
2221
- );
2222
- return false;
2223
- }
2224
-
2225
- Logger.debug(`GkAgentProvider.archiveSession: ${this.sessionTag(sessionId)}`);
2226
- await this.callbacks.runCLICommand(['ai', 'hook', 'archive-session', sessionId, '--json']);
2227
- // Invalidate the archived-id cache so the next `getPastSessions` re-fetches and excludes this id.
2228
- this._archivedSessionIdsCache = undefined;
2229
- // Optimistically drop it — archived sessions are excluded from the `active,ended` poll, so the
2230
- // next reconciliation confirms the removal regardless.
2231
- const index = this._sessions.findIndex(s => s.id === sessionId);
2232
- if (index >= 0) {
2233
- this._sessions.splice(index, 1);
2234
- this._sessionBookkeeping.delete(sessionId);
2235
- this._transcriptReader.forget(sessionId);
2236
- this._onDidChangeSessions.fire();
2237
- }
2238
- return true;
2239
- }
2240
-
2241
- /** Lists the ids of sessions the CLI has archived, so callers (the "Past" transcript listing) can
2242
- * exclude them — the tracked row is gone, but the transcript on disk survives and would otherwise
2243
- * resurface there. Resolves to `[]` on any error, including a CLI too old to support the
2244
- * `archived` status filter. */
2245
- async getArchivedSessionIds(): Promise<string[]> {
2246
- const cached = this._archivedSessionIdsCache;
2247
- if (cached != null && Date.now() - cached.at < archivedSessionIdsCacheTtlMs) {
2248
- return cached.promise;
2249
- }
2250
-
2251
- const promise = this.fetchArchivedSessionIds();
2252
- this._archivedSessionIdsCache = { promise: promise, at: Date.now() };
2253
- return promise;
2254
- }
2255
-
2256
- private async fetchArchivedSessionIds(): Promise<string[]> {
2257
- try {
2258
- const output = await this.callbacks.runCLICommand([
2259
- 'ai',
2260
- 'hook',
2261
- 'list-sessions',
2262
- '--status',
2263
- 'archived',
2264
- '--json',
2265
- ]);
2266
- const sessions = JSON.parse(output) as SessionFileData[];
2267
- return sessions.map(s => s.sessionId).filter(id => id.length > 0);
2268
- } catch {
2269
- return [];
2270
- }
2271
- }
2272
-
2273
- resolveEndedSessionDetails(sessionId: string): void {
2274
- const session = this._sessions.find(s => s.id === sessionId);
2275
- // Only ended sessions defer resolution; live sessions resolve eagerly on discovery.
2276
- if (session?.status !== 'ended') return;
2277
-
2278
- // The durable store drops the prompt (and only ever carries an auto-slug name) for most ended
2279
- // sessions, so read the terminal transcript once on demand to recover a real title + the
2280
- // first/last prompt. Both the git probe and transcript read sit under the guard so repeated
2281
- // opens don't re-probe git or re-read a possibly-large file.
2282
- const bk = this.getBookkeeping(sessionId);
2283
- if (bk.endedDetailsResolved) return;
2284
-
2285
- bk.endedDetailsResolved = true;
2286
-
2287
- if (session.cwd) {
2288
- void this.resolveGitInfo(sessionId, session.cwd);
2289
- }
2290
- // Git identity resolves for every agent; the transcript read is Claude-only (see
2291
- // `resolveTranscriptTitles`).
2292
- if (this.supportsTranscripts(sessionId)) {
2293
- void this.applyEndedTranscriptDetails(sessionId, session.cwd);
2294
- }
2295
- }
2296
-
2297
- /** No `_sessions` gate, unlike {@link resolveEndedSessionDetails} — must work for a
2298
- * transcript-only id this window never tracked live (past-session-sheet callers). */
2299
- async resolveSessionDetails(sessionId: string, cwd?: string): Promise<EndedTranscriptDetails | undefined> {
2300
- // The reader only reads Claude Code's store, so an agent that leaves no transcript can only
2301
- // ever miss here. Reachable rather than theoretical: an ended session of ANY agent becomes a
2302
- // Past row, and opening its sheet routes here by `providerId`. `supportsTranscripts` fails
2303
- // open, so the transcript-only ids this method exists for — untracked, therefore capability-
2304
- // less, and Claude's by construction since only its store produces them — still resolve.
2305
- if (!this.supportsTranscripts(sessionId)) return undefined;
2306
-
2307
- let cached = this._sessionDetailsCache.get(sessionId);
2308
- if (cached == null) {
2309
- cached = this._transcriptReader.resolveEndedDetails(sessionId, cwd).catch((ex: unknown) => {
2310
- // A read error (transient I/O, permissions) is not terminal state — drop the cache
2311
- // entry so a later call retries. A resolved `undefined` IS terminal (no transcript on
2312
- // disk) and stays cached.
2313
- this._sessionDetailsCache.delete(sessionId);
2314
- throw ex;
2315
- });
2316
- this._sessionDetailsCache.set(sessionId, cached);
2317
- }
2318
-
2319
- try {
2320
- return await cached;
2321
- } catch {
2322
- return undefined;
2323
- }
2324
- }
2325
-
2326
- private async applyEndedTranscriptDetails(sessionId: string, cwd: string | undefined): Promise<void> {
2327
- let details;
2328
- try {
2329
- details = await this._transcriptReader.resolveEndedDetails(sessionId, cwd);
2330
- } catch {
2331
- // A read error (transient I/O, permissions) is not terminal state — clear the once-only
2332
- // guard so a later open retries. A `undefined` result IS terminal (no transcript on disk),
2333
- // so it keeps the guard and never re-scans.
2334
- const bk = this._sessionBookkeeping.get(sessionId);
2335
- if (bk != null) {
2336
- bk.endedDetailsResolved = false;
2337
- }
2338
- return;
2339
- }
2340
- if (details == null) return;
2341
-
2342
- const index = this._sessions.findIndex(s => s.id === sessionId);
2343
- if (index < 0) return;
2344
-
2345
- const session = this._sessions[index];
2346
- // The session may have been archived/removed while we were reading.
2347
- if (session.status !== 'ended') return;
2348
-
2349
- const { titles } = details;
2350
- const nextTitles =
2351
- titles.custom != null || titles.ai != null || titles.agent != null
2352
- ? { custom: titles.custom, ai: titles.ai, agent: titles.agent }
2353
- : session.transcriptTitles;
2354
- // Durable-store prompts (full text) win over the transcript's (CLI-truncated) copy; only
2355
- // backfill from the transcript when the field is empty.
2356
- const nextFirstPrompt = session.firstPrompt ?? prepareStoredPrompt(details.firstPrompt);
2357
- const nextLastPrompt = session.lastPrompt ?? prepareStoredPrompt(details.lastPrompt);
2358
-
2359
- const titlesChanged =
2360
- nextTitles?.custom !== session.transcriptTitles?.custom ||
2361
- nextTitles?.ai !== session.transcriptTitles?.ai ||
2362
- nextTitles?.agent !== session.transcriptTitles?.agent;
2363
- if (!titlesChanged && nextFirstPrompt === session.firstPrompt && nextLastPrompt === session.lastPrompt) {
2364
- return;
2365
- }
2366
-
2367
- this._sessions[index] = {
2368
- ...session,
2369
- transcriptTitles: nextTitles,
2370
- firstPrompt: nextFirstPrompt,
2371
- lastPrompt: nextLastPrompt,
2372
- };
2373
- this._onDidChangeSessions.fire();
2374
- }
2375
-
2376
- private matchesWorkspace(workspacePath: string | undefined): boolean {
2377
- if (!workspacePath) return false;
2378
-
2379
- // `_workspacePaths` is normalized; normalize the input so prefix comparisons work
2380
- // regardless of whether the caller passed a raw `fsPath` (CLI hook cwd, peer-session
2381
- // workspacePath) or an already-normalized path.
2382
- const normalized = normalizePath(workspacePath);
2383
- return this._workspacePaths.some(
2384
- p => normalized === p || normalized.startsWith(`${p}/`) || p.startsWith(`${normalized}/`),
2385
- );
2386
- }
2387
-
2388
- private resolveWorkspacePath(cwd: string | undefined): string | undefined {
2389
- if (!cwd) return undefined;
2390
-
2391
- const normalized = normalizePath(cwd);
2392
- return this._workspacePaths.find(
2393
- p => normalized === p || normalized.startsWith(`${p}/`) || p.startsWith(`${normalized}/`),
2394
- );
2395
- }
2396
-
2397
- private updateSessionWithPermission(
2398
- sessionId: string,
2399
- permission: PendingPermission,
2400
- context?: SessionContext,
2401
- ): void {
2402
- // A pending permission is a working/waiting transition — cancel any deferred Stop → idle
2403
- // commit so the session doesn't briefly flip to idle before showing the prompt.
2404
- this.cancelPendingIdleTransition(sessionId);
2405
-
2406
- // The full event context, not just the pid: a blocking ask can be the first event of a
2407
- // session, and a row created here with no location would sit unattributed for the whole block.
2408
- const { index } = this.ensureSession(sessionId, context);
2409
-
2410
- const prev = this._sessions[index];
2411
- const newPhase = getPhaseForStatus('permission_requested');
2412
- this.getBookkeeping(sessionId).pendingPermission = permission;
2413
- this._sessions[index] = {
2414
- ...prev,
2415
- status: 'permission_requested',
2416
- phase: newPhase,
2417
- phaseSince: this.resolvePhaseSince(sessionId, prev.phase, prev.phaseSince, newPhase),
2418
- statusDetail: permission.toolDescription,
2419
- pendingPermission: permission,
2420
- lastActivity: new Date(),
2421
- };
2422
- this._onDidChangeSessions.fire();
2423
- }
2424
-
2425
- private updateSessionStatus(
2426
- sessionId: string,
2427
- status: AgentSessionStatus,
2428
- options?: SessionContext & { statusDetail?: string; pendingPermission?: PendingPermission },
2429
- ): void {
2430
- // Any non-idle status update implicitly cancels a deferred Stop → idle commit. The
2431
- // timer is owned here so the schedule/cancel pair is uniformly enforced regardless of
2432
- // which event path produced the next status.
2433
- if (status !== 'idle') {
2434
- this.cancelPendingIdleTransition(sessionId);
2435
- }
2436
-
2437
- const { index, changed: metadataChanged } = this.ensureSession(sessionId, options);
2438
-
2439
- const prev = this._sessions[index];
2440
- const bk = this.getBookkeeping(sessionId);
2441
-
2442
- // If a permission/elicitation is pending, preserve permission_requested state.
2443
- // Tool counts and other bookkeeping are still updated by callers before this
2444
- // method, but the visible status remains locked until the wait is explicitly
2445
- // resolved.
2446
- if (bk.pendingPermission != null && status !== 'permission_requested') {
2447
- this._sessions[index] = { ...prev, lastActivity: new Date() };
2448
- if (metadataChanged) {
2449
- this._onDidChangeSessions.fire();
2450
- }
2451
- return;
2452
- }
2453
-
2454
- const statusDetail = options?.statusDetail;
2455
- // Resolved before the short-circuit: a second ask can arrive while the row already sits at
2456
- // `permission_requested`, and comparing only status+detail would publish the first ask forever.
2457
- const nextPendingPermission =
2458
- status === 'permission_requested' ? (bk.pendingPermission ?? options?.pendingPermission) : undefined;
2459
- if (
2460
- prev.status === status &&
2461
- prev.statusDetail === statusDetail &&
2462
- isSameAsk(prev.pendingPermission, nextPendingPermission)
2463
- ) {
2464
- if (metadataChanged) {
2465
- this._onDidChangeSessions.fire();
2466
- }
2467
- return;
2468
- }
2469
-
2470
- const newPhase = getPhaseForStatus(status);
2471
- this._sessions[index] = {
2472
- ...prev,
2473
- status: status,
2474
- phase: newPhase,
2475
- phaseSince: this.resolvePhaseSince(sessionId, prev.phase, prev.phaseSince, newPhase),
2476
- statusDetail: statusDetail,
2477
- pendingPermission: nextPendingPermission,
2478
- lastActivity: new Date(),
2479
- };
2480
- this._onDidChangeSessions.fire();
2481
-
2482
- if (status === 'thinking' || status === 'tool_use' || status === 'compacting') {
2483
- const sessionCwd = this._sessions[index].cwd;
2484
- if (sessionCwd != null) {
2485
- this.callbacks.onBranchAgentActivity?.(sessionCwd);
2486
- }
2487
- }
2488
-
2489
- // On a fresh transition into idle, recheck transcript titles — Claude typically (re-)writes
2490
- // `ai-title` right after finishing a response, so this is the prime moment to pick it up.
2491
- // Tail-read caching makes repeated checks cheap.
2492
- if (status === 'idle' && prev.status !== 'idle') {
2493
- void this.resolveTranscriptTitles(sessionId, this._sessions[index].cwd);
2494
- }
2495
- }
2496
-
2497
- private ensureSession(sessionId: string, context?: SessionContext): { index: number; changed: boolean } {
2498
- const {
2499
- capabilities,
2500
- pid,
2501
- workspacePath,
2502
- isInWorkspace,
2503
- cwd,
2504
- initialCwd,
2505
- planFile,
2506
- sessionName,
2507
- model,
2508
- worktreePath,
2509
- visitedWorktreePaths,
2510
- } = context ?? {};
2511
-
2512
- let index = this._sessions.findIndex(s => s.id === sessionId);
2513
- if (index < 0) {
2514
- const now = new Date();
2515
- // Same worktree-derived fallback as the update branch below: a session created with a
2516
- // scratch cwd but a CLI-seated worktree still belongs to that worktree's workspace.
2517
- const createdWorkspacePath = workspacePath ?? this.resolveWorkspacePath(worktreePath);
2518
- // Identity comes from the owning agent's descriptor, never from the provider — Claude's
2519
- // `providerId` stays the `claudeCode` the descriptor encodes. The fallback covers the
2520
- // deferred-commit paths (see `SessionContext.capabilities`), which only ever run against a
2521
- // row that already exists; reaching it would mean a row created outside every event path.
2522
- const identity = capabilities ?? claudeCodeCapabilities;
2523
- index = this._sessions.length;
2524
- this._sessions.push({
2525
- id: sessionId,
2526
- providerId: identity.providerId,
2527
- providerName: identity.displayName,
2528
- name: sessionName || undefined,
2529
- status: 'idle',
2530
- phase: getPhaseForStatus('idle'),
2531
- phaseSince: now,
2532
- pid: pid,
2533
- lastActivity: now,
2534
- isSubagent: false,
2535
- workspacePath: createdWorkspacePath,
2536
- cwd: cwd,
2537
- initialCwd: initialCwd ?? cwd,
2538
- planFile: planFile,
2539
- isInWorkspace: createdWorkspacePath != null ? true : (isInWorkspace ?? false),
2540
- model: model,
2541
- worktreePath: worktreePath != null ? normalizePath(worktreePath) : undefined,
2542
- visitedWorktreePaths: unionVisited(undefined, visitedWorktreePaths, worktreePath),
2543
- });
2544
- Logger.debug(
2545
- `GkAgentProvider.ensureSession: implicitly created ${this.sessionTag(sessionId, sessionName ?? 'unnamed')}`,
2546
- );
2547
- if (worktreePath != null) {
2548
- this.getBookkeeping(sessionId).cliSeatedWorktree = true;
2549
- }
2550
- this._onDidChangeSessions.fire();
2551
-
2552
- if (cwd != null) {
2553
- void this.resolveGitInfo(sessionId, cwd);
2554
- }
2555
- void this.resolveTranscriptTitles(sessionId, cwd);
2556
- return { index: index, changed: true };
2557
- }
2558
-
2559
- const existing = this._sessions[index];
2560
- const updatedPid = pid != null && existing.pid == null ? pid : existing.pid;
2561
- const updatedPlanFile = planFile ?? existing.planFile;
2562
- const updatedName = sessionName || existing.name;
2563
- const updatedModel = model || existing.model;
2564
- const updatedCwd = cwd ?? existing.cwd;
2565
- // Prefer the CLI's authoritative `initialCwd` (the true launch dir it captured first-hand,
2566
- // even for sessions that started before this window opened); otherwise keep whatever we
2567
- // already captured, and only as a last resort backfill from the current cwd (older CLIs that
2568
- // don't send `initialCwd`, or a cold-create before any cwd was known). Comparing live `cwd`
2569
- // against `initialCwd` is how consumers detect launch-vs-current drift, so it stays stable.
2570
- const updatedInitialCwd = initialCwd ?? existing.initialCwd ?? cwd;
2571
- // The CLI resolves this at hook time and sends it on every event — authoritative when
2572
- // present, so it seats ahead of the `resolveGitInfo` probe below (which still fills
2573
- // `commonPath` and remains the fallback for older CLIs that don't send `cwdTimeline`).
2574
- const updatedWorktreePath = worktreePath != null ? normalizePath(worktreePath) : existing.worktreePath;
2575
- const updatedVisitedWorktreePaths = unionVisited(
2576
- existing.visitedWorktreePaths,
2577
- visitedWorktreePaths,
2578
- updatedWorktreePath,
2579
- );
2580
- // Workspace membership follows the ATTRIBUTION, not the raw cwd: a scratch-dir excursion
2581
- // (cwd in /tmp, worktree kept) stays in-workspace via its worktree, while a genuine move to
2582
- // a repo outside the workspace honestly drops membership when events carry a cwd.
2583
- const updatedWorkspacePath =
2584
- workspacePath ??
2585
- this.resolveWorkspacePath(updatedWorktreePath) ??
2586
- (cwd == null ? existing.workspacePath : undefined);
2587
- const updatedIsInWorkspace = cwd == null ? existing.isInWorkspace : updatedWorkspacePath != null;
2588
-
2589
- const cwdMoved = cwd != null && updatedCwd !== existing.cwd;
2590
- const worktreeMoved =
2591
- updatedWorktreePath != null &&
2592
- existing.worktreePath != null &&
2593
- updatedWorktreePath !== existing.worktreePath;
2594
-
2595
- // The CLI explaining the location owns attribution; a move it didn't explain hands it back to
2596
- // the probe.
2597
- if (worktreePath != null) {
2598
- this.getBookkeeping(sessionId).cliSeatedWorktree = true;
2599
- } else if (cwdMoved) {
2600
- this.getBookkeeping(sessionId).cliSeatedWorktree = false;
2601
- }
2602
-
2603
- let changed = false;
2604
- if (
2605
- updatedPid !== existing.pid ||
2606
- updatedWorkspacePath !== existing.workspacePath ||
2607
- updatedIsInWorkspace !== existing.isInWorkspace ||
2608
- updatedPlanFile !== existing.planFile ||
2609
- updatedName !== existing.name ||
2610
- updatedModel !== existing.model ||
2611
- updatedCwd !== existing.cwd ||
2612
- updatedInitialCwd !== existing.initialCwd ||
2613
- updatedWorktreePath !== existing.worktreePath ||
2614
- updatedVisitedWorktreePaths !== existing.visitedWorktreePaths
2615
- ) {
2616
- this._sessions[index] = {
2617
- ...existing,
2618
- name: updatedName,
2619
- model: updatedModel,
2620
- pid: updatedPid,
2621
- workspacePath: updatedWorkspacePath,
2622
- cwd: updatedCwd,
2623
- initialCwd: updatedInitialCwd,
2624
- planFile: updatedPlanFile,
2625
- isInWorkspace: updatedIsInWorkspace,
2626
- worktreePath: updatedWorktreePath,
2627
- visitedWorktreePaths: updatedVisitedWorktreePaths,
2628
- // A worktree move can cross repos, and the spread would otherwise carry the old repo
2629
- // identity forward — which consumers prefer over the new worktree, pinning the card to
2630
- // the wrong repo. Drop it; the probe below refills it. Mirrors the poll's ended-record
2631
- // re-seat.
2632
- commonPath: worktreeMoved ? undefined : existing.commonPath,
2633
- };
2634
- changed = true;
2635
- }
2636
-
2637
- // Re-fire `resolveGitInfo` if the session is missing either `worktreePath` or `commonPath`.
2638
- // Peer-synced sessions arrive with `worktreePath` already set (from the peer that owns the
2639
- // hook flow) but may lack `commonPath` if the peer was running pre-commonPath code; this
2640
- // gives us a chance to fill it in locally. Idempotent — `resolveGitInfo` only mutates the
2641
- // session when one of `cwd`/`worktreePath`/`commonPath` actually changes. The
2642
- // `gitInfoUnresolvable` bookkeeping flag (see `SessionBookkeeping`) is set once
2643
- // `resolveGitInfo` confirms the cwd isn't a git repo, preventing a retry storm on every
2644
- // hook event for non-git-repo cwds. The flag is cleared on cwd change (`CwdChanged`) and
2645
- // on `resetBookkeeping` (Stop/SessionStart) so re-resolution can happen when warranted.
2646
- // A cwd move re-resolves too. `worktreePath`/`commonPath` are resolved from whichever cwd was
2647
- // seen first, so an agent that moves between worktrees would otherwise stay pinned to its
2648
- // launch repo — attaching to the wrong WIP row and leaving its real worktree showing no
2649
- // agents at all. `CwdChanged` covers the move the CLI announces; this covers an ordinary
2650
- // event that simply arrives carrying a new cwd.
2651
- //
2652
- // A cwd the CLI already attributed to the same worktree needs no probe at all — that's the
2653
- // common case (an agent `cd`-ing around inside its own tree), and probing it would spawn a git
2654
- // call per hook event for an answer we already have. A first seat (no prior worktree) still
2655
- // counts as a location change: the move may be out of a non-git dir whose probe latched
2656
- // `gitInfoUnresolvable`, and `commonPath` still needs the probe.
2657
- //
2658
- // A worktree move is a location change on its own, even with the SAME cwd (cwdTimeline
2659
- // updated, cwd unchanged) — the `commonPath` drop above (`worktreeMoved ? undefined : …`)
2660
- // needs the probe to refill it, and without `worktreeMoved` here nothing else would fire it.
2661
- const locationNeedsProbe =
2662
- worktreeMoved || (cwdMoved && (worktreePath == null || existing.worktreePath == null));
2663
- if (locationNeedsProbe) {
2664
- this.getBookkeeping(sessionId).gitInfoUnresolvable = false;
2665
- }
2666
-
2667
- if (
2668
- cwd != null &&
2669
- (locationNeedsProbe || existing.worktreePath == null || existing.commonPath == null) &&
2670
- !this.getBookkeeping(sessionId).gitInfoUnresolvable
2671
- ) {
2672
- void this.resolveGitInfo(sessionId, cwd);
2673
- }
2674
-
2675
- return { index: index, changed: changed };
2676
- }
2677
-
2678
- private async resolveGitInfo(sessionId: string, cwd: string): Promise<void> {
2679
- const resolveGitInfo = this.callbacks.resolveGitInfo;
2680
- if (resolveGitInfo == null) return;
2681
-
2682
- // Dedupe concurrent calls for the same session — every hook event (PreToolUse, PostToolUse,
2683
- // UserPromptSubmit, etc.) flows through `ensureSession`, which retries `resolveGitInfo`
2684
- // whenever `commonPath`/`worktreePath` is missing. Without this guard a non-git-repo cwd
2685
- // would spawn a fresh git probe per hook event. The `gitInfoUnresolvable` bookkeeping flag
2686
- // (set below when `info == null`) is the long-lived suppressor across hook events; this
2687
- // in-flight set only dedupes concurrent overlapping probes within a single resolution.
2688
- //
2689
- // Keyed by cwd as well: a probe for the SAME cwd is genuinely redundant, but one for a
2690
- // different cwd (a missed `CwdChanged` corrected off the durable record, say) must not be
2691
- // swallowed — the in-flight answer describes the old directory. Record it as pending so the
2692
- // running probe hands off to it, and discard the stale answer below.
2693
- const inFlightCwd = this._resolveGitInfoInFlight.get(sessionId);
2694
- if (inFlightCwd != null) {
2695
- if (inFlightCwd !== cwd) {
2696
- this._resolveGitInfoPending.set(sessionId, cwd);
2697
- }
2698
- return;
2699
- }
2700
-
2701
- this._resolveGitInfoInFlight.set(sessionId, cwd);
2702
- try {
2703
- let info: Awaited<ReturnType<typeof resolveGitInfo>> | undefined;
2704
- let failed = false;
2705
- try {
2706
- info = await resolveGitInfo(cwd);
2707
- } catch {
2708
- // A transient git/spawn failure, distinct from a confirmed "not a repo" (`info ==
2709
- // null` below). Must not latch `gitInfoUnresolvable` — the next hook event retries
2710
- // via `ensureSession`'s `commonPath == null` check.
2711
- failed = true;
2712
- info = undefined;
2713
- }
2714
-
2715
- const index = this._sessions.findIndex(s => s.id === sessionId);
2716
- if (index < 0) return;
2717
-
2718
- // Superseded while we were probing: this answer describes a directory the session has
2719
- // already left, so applying it would undo the newer location. Drop it — the `finally`
2720
- // below re-runs for the newest cwd.
2721
- if (this._resolveGitInfoPending.has(sessionId)) return;
2722
-
2723
- if (failed) return;
2724
-
2725
- const session = this._sessions[index];
2726
-
2727
- // `workspacePath` stays as the matched workspace folder (or undefined). Repo identity
2728
- // flows through `commonPath`, populated here from `info.repoRoot` at the same step as
2729
- // `worktreePath` — both available together from `resolveGitInfo`, much earlier than the
2730
- // follow-on worktree-name refresh (which needs `getWorktrees()` on the parent repo).
2731
- //
2732
- // When `info == null` (cwd is not inside any git repo), set the `gitInfoUnresolvable`
2733
- // bookkeeping flag so subsequent `ensureSession`/peer-sync retry checks skip re-firing.
2734
- // We deliberately do NOT write a tombstone onto the session DTO — keeping `commonPath`
2735
- // as `undefined` (a) keeps the wire-shape unambiguous for peers, (b) lets consumers
2736
- // safely treat undefined as "no repo identity", and (c) means peers can still attempt
2737
- // their own local resolution. The flag is cleared on cwd change (`CwdChanged`) and on
2738
- // `resetBookkeeping` so re-resolution happens when warranted (e.g. user runs `git init`
2739
- // followed by a new session start).
2740
- if (info == null) {
2741
- this.getBookkeeping(sessionId).gitInfoUnresolvable = true;
2742
-
2743
- // The cwd itself isn't a repo, but a CLI-seated worktree (from `cwdTimeline`, see
2744
- // `worktreeFromEvent`) may still name one — e.g. a scratch cwd like `/tmp/x` whose
2745
- // session the CLI attributed to a real worktree. Probe the worktree root inline, in
2746
- // the same in-flight window, so a scratch cwd doesn't leave `commonPath` permanently
2747
- // unresolved.
2748
- let worktreeInfo: Awaited<ReturnType<typeof resolveGitInfo>> | undefined;
2749
- let liveIndex = index;
2750
- let liveSession = session;
2751
- if (session.worktreePath != null && session.worktreePath !== cwd && session.commonPath == null) {
2752
- try {
2753
- worktreeInfo = await resolveGitInfo(session.worktreePath);
2754
- } catch {
2755
- worktreeInfo = undefined;
2756
- }
2757
-
2758
- // Re-find and re-read after this second await, for both the success and
2759
- // fall-through writes below — the array can have been reassigned/spliced (e.g.
2760
- // `pruneDeadSessions`), or the session's own fields updated, while the probe ran.
2761
- liveIndex = this._sessions.findIndex(s => s.id === sessionId);
2762
- if (liveIndex < 0) return;
2763
-
2764
- if (this._resolveGitInfoPending.has(sessionId)) return;
2765
-
2766
- liveSession = this._sessions[liveIndex];
2767
- }
2768
-
2769
- if (worktreeInfo != null) {
2770
- const worktreeFirstResolve = liveSession.initialCommonPath == null;
2771
- this._sessions[liveIndex] = {
2772
- ...liveSession,
2773
- cwd: cwd,
2774
- commonPath: worktreeInfo.repoRoot,
2775
- initialWorktreePath: worktreeFirstResolve
2776
- ? liveSession.worktreePath
2777
- : liveSession.initialWorktreePath,
2778
- initialCommonPath: worktreeFirstResolve ? worktreeInfo.repoRoot : liveSession.initialCommonPath,
2779
- visitedWorktreePaths: unionVisited(liveSession.visitedWorktreePaths, liveSession.worktreePath),
2780
- };
2781
- this._onDidChangeSessions.fire();
2782
- return;
2783
- }
2784
-
2785
- if (cwd !== liveSession.cwd) {
2786
- this._sessions[liveIndex] = { ...liveSession, cwd: cwd };
2787
- this._onDidChangeSessions.fire();
2788
- }
2789
- return;
2790
- }
2791
-
2792
- // A CLI-seated worktree wins over the probe's answer (see `cliSeatedWorktree`): the CLI
2793
- // resolved it for this exact cwd, while the probe can name the parent worktree for a nested
2794
- // one. `commonPath` always comes from the probe — the CLI doesn't resolve repo identity.
2795
- const effectiveWorktreePath =
2796
- this.getBookkeeping(sessionId).cliSeatedWorktree && session.worktreePath != null
2797
- ? session.worktreePath
2798
- : info.worktreePath;
2799
-
2800
- // Capture the worktree/commonPath at the first successful resolve so consumers can
2801
- // detect drift (e.g. an agent that `cd`'d into a sibling worktree after launch). Gated
2802
- // on `initialCommonPath` — captured together with `initialWorktreePath` so a first
2803
- // resolve where `info.worktreePath` happens to be undefined doesn't leave the latter
2804
- // open to a later overwrite. Once set, never overwritten.
2805
- const firstResolve = session.initialCommonPath == null;
2806
- const updatedInitialWorktreePath = firstResolve ? effectiveWorktreePath : session.initialWorktreePath;
2807
- const updatedInitialCommonPath = firstResolve ? info.repoRoot : session.initialCommonPath;
2808
-
2809
- if (
2810
- cwd !== session.cwd ||
2811
- effectiveWorktreePath !== session.worktreePath ||
2812
- info.repoRoot !== session.commonPath ||
2813
- updatedInitialWorktreePath !== session.initialWorktreePath ||
2814
- updatedInitialCommonPath !== session.initialCommonPath
2815
- ) {
2816
- this._sessions[index] = {
2817
- ...session,
2818
- cwd: cwd,
2819
- worktreePath: effectiveWorktreePath,
2820
- commonPath: info.repoRoot,
2821
- initialWorktreePath: updatedInitialWorktreePath,
2822
- initialCommonPath: updatedInitialCommonPath,
2823
- visitedWorktreePaths: unionVisited(session.visitedWorktreePaths, effectiveWorktreePath),
2824
- };
2825
- this._onDidChangeSessions.fire();
2826
- }
2827
- } finally {
2828
- this._resolveGitInfoInFlight.delete(sessionId);
2829
-
2830
- // Hand off to the newest cwd requested while this probe ran, so a move that arrived
2831
- // mid-flight still resolves instead of being dropped by the dedupe.
2832
- const pendingCwd = this._resolveGitInfoPending.get(sessionId);
2833
- if (pendingCwd != null) {
2834
- this._resolveGitInfoPending.delete(sessionId);
2835
- void this.resolveGitInfo(sessionId, pendingCwd);
2836
- }
2837
- }
2838
- }
2839
-
2840
- private async resolveTranscriptTitles(sessionId: string, cwd: string | undefined): Promise<void> {
2841
- // The reader only understands Claude Code's `~/.claude/projects/**` JSONL store, so for any
2842
- // other agent this would be a guaranteed-miss directory scan on every event and poll.
2843
- if (!this.supportsTranscripts(sessionId)) return;
2844
-
2845
- let titles;
2846
- try {
2847
- titles = await this._transcriptReader.resolve(sessionId, cwd);
2848
- } catch {
2849
- return;
2850
- }
2851
- if (titles == null) return;
2852
-
2853
- const index = this._sessions.findIndex(s => s.id === sessionId);
2854
- if (index < 0) return;
2855
-
2856
- const session = this._sessions[index];
2857
- const prev = session.transcriptTitles;
2858
- if (prev?.custom === titles.custom && prev?.ai === titles.ai && prev?.agent === titles.agent) return;
2859
-
2860
- this._sessions[index] = {
2861
- ...session,
2862
- transcriptTitles: { custom: titles.custom, ai: titles.ai, agent: titles.agent },
2863
- };
2864
- this._onDidChangeSessions.fire();
2865
- }
2866
-
2867
- /** Transitions the session at `index` to the terminal `ended` state in place: keeps the row,
2868
- * stamps `endedAt` as its activity time, clears transient state (statusDetail, pendingPermission,
2869
- * subagents), and tears down its bookkeeping/timers. Shared by the live `SessionEnd` path and the
2870
- * poll backstop for a `SessionEnd` the live path missed. Does not fire change events — the caller
2871
- * batches those. */
2872
- private endSession(index: number, endedAt: Date): void {
2873
- const prev = this._sessions[index];
2874
- this._sessions[index] = {
2875
- ...prev,
2876
- status: 'ended',
2877
- phase: 'ended',
2878
- phaseSince: endedAt,
2879
- lastActivity: endedAt,
2880
- statusDetail: undefined,
2881
- pendingPermission: undefined,
2882
- subagents: undefined,
2883
- };
2884
- // Terminal teardown, owned here so every caller gets it — the live `SessionEnd` path does
2885
- // this before calling us, but the poll's missed-SessionEnd transition doesn't: reject any
2886
- // in-flight permission promise (otherwise a blocking request leaks until dispose) and cancel a
2887
- // pending Stop→idle timer (otherwise it fires inside its window and revives this row to a
2888
- // live zombie).
2889
- const pending = this._pendingPermissions.get(prev.id);
2890
- if (pending != null) {
2891
- this.rejectAllPending(pending, new Error('Session ended'));
2892
- this._pendingPermissions.delete(prev.id);
2893
- }
2894
- this.cancelPendingIdleTransition(prev.id);
2895
-
2896
- const bk = this._sessionBookkeeping.get(prev.id);
2897
- if (bk != null) {
2898
- this.cancelPendingFileClears(bk);
2899
- this.cancelPendingReadClears(bk);
2900
- }
2901
- this._sessionBookkeeping.delete(prev.id);
2902
- this._transcriptReader.forget(prev.id);
2903
- this.noteTerminalTransition();
2904
- }
2905
-
2906
- private pruneDeadSessions(): boolean {
2907
- const kept: AgentSession[] = [];
2908
- const removedIds: string[] = [];
2909
- for (const s of this._sessions) {
2910
- // Ended sessions are terminal and durable — their PID is usually dead by design, so
2911
- // liveness pruning must never drop them (they leave only via poll reconciliation).
2912
- if (s.status === 'ended') {
2913
- kept.push(s);
2914
- continue;
2915
- }
2916
-
2917
- // An agent that multiplexes concurrent sessions in one process (Codex) reports the SAME
2918
- // pid for all of them, so the pid answers "is the host process alive", not "is this
2919
- // session alive" — in both directions. A live shared process would pin every session it
2920
- // ever hosted, and a dead one would reap live siblings along with the finished session.
2921
- // Neither is recoverable here, so pid liveness is not evidence for these rows at all:
2922
- // keep them and let the paths that DO identify a session end them — a `SessionEnd`, or the
2923
- // poll seeing the CLI's durable `ended` record (which the CLI writes for a stale session
2924
- // of its own accord). The CLI takes the same position: for `pidSharingClients` a pid-only
2925
- // match is a guess, so it keeps such an end revivable.
2926
- if (getAgentCapabilitiesByProviderId(s.providerId)?.sharesPids === true) {
2927
- kept.push(s);
2928
- continue;
2929
- }
2930
-
2931
- // A session blocking on us for a permission decision is by definition alive,
2932
- // even if `kill(pid, 0)` says otherwise (e.g. transient EPERM/ESRCH). Dropping
2933
- // it here loses pendingPermission and lastPrompt; the next syncSessions then
2934
- // re-adds it as a stale shell with `permission_requested` status but no detail.
2935
- if (s.pid == null || isProcessAlive(s.pid) || this._pendingPermissions.has(s.id)) {
2936
- kept.push(s);
2937
- } else {
2938
- removedIds.push(s.id);
2939
- }
2940
- }
2941
- if (removedIds.length === 0) return false;
2942
-
2943
- this._sessions = kept;
2944
- for (const id of removedIds) {
2945
- const pending = this._pendingPermissions.get(id);
2946
- if (pending != null) {
2947
- this.rejectAllPending(pending, new Error('Session pruned'));
2948
- this._pendingPermissions.delete(id);
2949
- }
2950
- this.cancelPendingIdleTransition(id);
2951
- // Cancel any in-flight decay-eviction timers before dropping the bookkeeping (mirrors
2952
- // SessionEnd). A Stopped-then-killed session reaches here with armed cooldown timers
2953
- // (settleBookkeeping leaves them running), so without this they'd leak until they fire.
2954
- const bk = this._sessionBookkeeping.get(id);
2955
- if (bk != null) {
2956
- this.cancelPendingFileClears(bk);
2957
- this.cancelPendingReadClears(bk);
2958
- }
2959
- this._sessionBookkeeping.delete(id);
2960
- this._transcriptReader.forget(id);
2961
- }
2962
- this.noteTerminalTransition();
2963
- Logger.debug(
2964
- `GkAgentProvider.syncSessions: removed ${removedIds.length} stale session(s): ${removedIds.map(id => id.substring(0, 8)).join(', ')}`,
2965
- );
2966
- return true;
2967
- }
2968
-
2969
- protected async syncSessions(options?: { gate?: boolean }): Promise<void> {
2970
- // Recurring (gated) calls skip the CLI spawn when there's nothing to reconcile: no tracked
2971
- // sessions AND no installed hooks. Sessions only ever appear via the IPC push path or a peer,
2972
- // so an empty list with hooks off means no agents are reachable here. We still poll whenever
2973
- // sessions exist — that's the only local backstop for pruning agents that die without firing
2974
- // `SessionEnd`, and it keeps us correct even if hook detection is stale/wrong. The bootstrap
2975
- // call in `ensureIpcServer` passes no options, so cold-start discovery always runs.
2976
- // Deliberately counts ALL tracked sessions, ended included. An ended row is something
2977
- // we have to reconcile — it's removed only when the CLI stops listing it (archived from
2978
- // another window, or aged out) — and this poll is the sole mechanism that does so. It's also
2979
- // what triggers the CLI's own 30-day retention sweep, which runs on `list-sessions` and
2980
- // nothing else: no timer, no daemon. Gating it out to save a spawn wouldn't avoid work, it
2981
- // would strand the rows on screen AND the records on disk until the window reloads.
2982
- if (options?.gate && this._sessions.length === 0 && !this._hooksInstalled) return;
2983
-
2984
- // Narrowly: hooks off AND nothing running, i.e. we hold only terminal history. There's no
2985
- // dying process to reap and no live state to correct — just rows to reconcile and the
2986
- // retention sweep to trigger, neither of which needs quarter-hourly resolution. Back the
2987
- // cadence off rather than stopping (window startup polls ungated, so a reload is current).
2988
- // Hooks ON keeps the full cadence deliberately: there the tick is also the backstop for a
2989
- // session whose hook events never reached our IPC server.
2990
- if (
2991
- options?.gate &&
2992
- !this._hooksInstalled &&
2993
- !this._sessions.some(s => s.status !== 'ended') &&
2994
- Date.now() - this._lastSyncAt < idleReconcileIntervalMs
2995
- ) {
2996
- return;
2997
- }
2998
-
2999
- // Stamped BEFORE the CLI call so the reconciliation below can tell "this poll's snapshot was
3000
- // taken after we ended the row" from "an in-flight poll that predates it" — see the
3001
- // ended-removal filter.
3002
- const pollStartedAt = Date.now();
3003
- this._lastSyncAt = pollStartedAt;
3004
-
3005
- let sessions: SessionFileData[];
3006
- try {
3007
- let output: string;
3008
- try {
3009
- // `--status active,ended` surfaces both live sessions and the CLI's durable ended records
3010
- // (excluding `archived`). Newer CLIs default `list-sessions` to active-only, so the flag
3011
- // is required to see ended sessions at all.
3012
- output = await this.callbacks.runCLICommand([
3013
- 'ai',
3014
- 'hook',
3015
- 'list-sessions',
3016
- '--status',
3017
- 'active,ended',
3018
- '--json',
3019
- ]);
3020
- this._statusFilterSupported = true;
3021
- } catch (ex) {
3022
- // Only retry on an unknown-flag error — a pre-3.1.69 CLI rejects `--status` but returns
3023
- // all sessions unfiltered, so the flagless call is the correct legacy equivalent. Real
3024
- // failures (CLI missing, spawn error) rethrow to the prune-and-return handler below.
3025
- if (!isUnknownFlagError(ex)) throw ex;
3026
-
3027
- this._statusFilterSupported = false;
3028
- output = await this.callbacks.runCLICommand(['ai', 'hook', 'list-sessions', '--json']);
3029
- }
3030
- sessions = JSON.parse(output) as SessionFileData[];
3031
- } catch (ex) {
3032
- // Expected whenever the CLI is missing or still installing, so not an error — but log it:
3033
- // every session in the window silently stops reconciling until the next tick, and without
3034
- // this the symptom ("no agents anywhere") is indistinguishable from having no agents.
3035
- Logger.debug(
3036
- `GkAgentProvider.syncSessions: poll skipped — ${ex instanceof Error ? ex.message : String(ex)}`,
3037
- );
3038
- if (this.pruneDeadSessions()) {
3039
- this._onDidChangeSessions.fire();
3040
- }
3041
- return;
3042
- }
3043
-
3044
- // Snapshot what the live IPC path had already tracked, so we can detect drift between it and
3045
- // what the poll returns. Ideally they match exactly — any difference means the live push path
3046
- // missed an add (poll discovers it) or a teardown (poll no longer reports a session we track).
3047
- // Drift is measured over *live* sessions only; ended sessions are durable history, so their
3048
- // absence from `polledAlive` is expected and must not skew the discrepancy signal.
3049
- //
3050
- // Both maps carry the owning agent's `providerId` per session so the report below can be split
3051
- // per agent: the two paths' reliability differs by agent (one may never reach the live path at
3052
- // all), and one pooled number would average that away into noise.
3053
- const trackedLiveBefore = new Map(
3054
- this._sessions.filter(s => s.status !== 'ended').map(s => [s.id, s.providerId] as const),
3055
- );
3056
- const polledAlive = new Map<string, string>();
3057
- // Every id the poll returned (live *and* ended). Drives ended-session reconciliation:
3058
- // a tracked ended session absent from this set was archived/purged by the CLI.
3059
- const polledIds = new Set<string>();
3060
- // Accumulates ids already tracked or added during this poll, so the membership check is O(1)
3061
- // and duplicate ids within a single response are only added once.
3062
- const known = new Set(this._sessions.map(s => s.id));
3063
-
3064
- // The CLI's durable store never revives a resumed session — once it moves a record to its
3065
- // `ended` store, nothing moves it back, so a record reporting `ended` isn't proof the session
3066
- // is over. The listing also repairs the two active-record cases that have no fresher hook state:
3067
- // first discovery and synthesized, unresolvable permission asks. Fetched once per poll (not per
3068
- // row); the host caches the underlying `claude agents --json` invocation behind a short TTL, so
3069
- // this doesn't spawn a process per session.
3070
- const liveSessions = await this.callbacks.getLiveAgentSessions?.();
3071
-
3072
- let changed = false;
3073
- const discoveredByAgent = new Map<string, number>();
3074
- const noteDiscovered = (providerId: string): void => {
3075
- discoveredByAgent.set(providerId, (discoveredByAgent.get(providerId) ?? 0) + 1);
3076
- };
3077
- /** Ids kept alive against a stale `ended` record. Logged below — this path has no other
3078
- * observability, and "the agent isn't showing up" is otherwise indistinguishable from
3079
- * "the listing never ran". */
3080
- const keptAlive: string[] = [];
3081
-
3082
- for (const data of sessions) {
3083
- if (!data.sessionId) continue;
3084
-
3085
- // A record from a client we hold no descriptor for (the CLI supports more than this table
3086
- // does). Must stay ahead of `polledIds`/`polledAlive`: an id in either would skew ended-row
3087
- // reconciliation and the `onSyncDiscrepancy` drift signal below. Absent = `claude-code`,
3088
- // matching the live path — older CLIs don't stamp it.
3089
- const capabilities = getAgentCapabilities(data.providerId ?? 'claude-code');
3090
- if (capabilities == null) continue;
3091
-
3092
- // The record's `event` is the agent's native name. Canonicalize it so `deriveStatusFromEvent`
3093
- // sees a vocabulary it knows — an unmapped native name falls through to `data.event`, which
3094
- // that function's `default` already resolves to `idle`. Deliberately NOT a skip: unlike the
3095
- // live path, nothing here switches exhaustively on the name, and dropping the record would
3096
- // hide a real session whose last event simply has no canonical analogue.
3097
- //
3098
- // `data.hookInput` must be passed: an agent whose event name alone can't determine the
3099
- // canonical event (OpenCode's `session.status`) is resolved from the payload, so withholding
3100
- // it makes those names permanently unresolvable on this path — and the poll is the only way
3101
- // a pre-existing session enters after a window reload.
3102
- const canonicalEvent = resolveCanonicalHookEvent(capabilities, data.event, data.hookInput) ?? data.event;
3103
- // Same translation for the record's tool name — `classifyPermissionKind` and the display
3104
- // detail expect canonical names.
3105
- const polledToolName =
3106
- data.toolName != null ? resolveCanonicalToolName(capabilities, data.toolName) : undefined;
3107
-
3108
- const effectiveStatus = data.status ?? 'active';
3109
-
3110
- // Archived records are excluded by the `active,ended` query; guard anyway.
3111
- if (effectiveStatus === 'archived') continue;
3112
-
3113
- if (effectiveStatus === 'ended') {
3114
- polledIds.add(data.sessionId);
3115
- const endedDate = new Date(data.endedAt ?? data.updatedAt);
3116
- const override = liveOverrideFor(liveSessions, data.sessionId);
3117
- const liveCwd = override?.live.cwd;
3118
- const currentCwd = liveCwd ?? data.cwd;
3119
- const liveCwdMoved = liveCwd != null && liveCwd !== data.cwd;
3120
- const recordWorktreeRoot = worktreeRootFromData(data);
3121
- const currentWorktreeRoot = liveCwdMoved ? undefined : recordWorktreeRoot;
3122
-
3123
- const existingIndex = this._sessions.findIndex(s => s.id === data.sessionId);
3124
- if (existingIndex >= 0) {
3125
- const bookkeeping = this.getBookkeeping(data.sessionId);
3126
- if (liveCwdMoved) {
3127
- bookkeeping.supersededRecordCwd = data.cwd;
3128
- } else if (
3129
- bookkeeping.supersededRecordCwd != null &&
3130
- bookkeeping.supersededRecordCwd !== data.cwd
3131
- ) {
3132
- // The CLI rewrote the record for the resumed run — it describes the session again.
3133
- bookkeeping.supersededRecordCwd = undefined;
3134
- }
3135
-
3136
- // The record still names the cwd a resume superseded, and this poll no longer lists the
3137
- // session live: the resumed process exited (or died) before the CLI rewrote the record,
3138
- // so neither its cwd nor its `endedAt` describe where or when the session last ran.
3139
- const recordLocationStale = override == null && bookkeeping.supersededRecordCwd === data.cwd;
3140
-
3141
- if (override != null) {
3142
- const { live: liveSession, status: liveStatus } = override;
3143
- // `claude agents --json` still lists this exact session id as live — the store's
3144
- // `ended` record is stale. Keep the row alive instead of ending it, and prefer the
3145
- // listing's pid — the record's own may already be dead, or reused. Background sessions
3146
- // carry no pid at all, so fall back to whatever pid the row already has.
3147
- //
3148
- // The listing establishes LIVENESS, not status. It's a poll SAMPLE of a value that flips
3149
- // many times per turn, so adopting it every poll stamps a row back to `idle` moments after
3150
- // a hook event moved it to `tool_use`/`thinking` — the row then reads idle almost always
3151
- // and working almost never. Hook events are strictly fresher, so the sample is only taken
3152
- // when the row has nothing better: it was ended, and this poll is what revives it.
3153
- const trackedSession = this._sessions[existingIndex];
3154
- // The listing's pid, or none. Never fall back to the pid we already hold: the listing
3155
- // omits one only for background sessions, which have no process of their own, and the
3156
- // pid on the row is the dead one whose exit produced the `ended` record. Carrying it
3157
- // onto a live row makes `pruneDeadSessions` reap the row later in this same poll.
3158
- const nextPid = liveSession.pid;
3159
- const revived = trackedSession.status === 'ended';
3160
- const nextStatus = revived ? liveStatus : trackedSession.status;
3161
- if (
3162
- trackedSession.status !== nextStatus ||
3163
- trackedSession.pid !== nextPid ||
3164
- trackedSession.endReason != null ||
3165
- trackedSession.endedAt != null
3166
- ) {
3167
- this._sessions[existingIndex] = {
3168
- ...trackedSession,
3169
- status: nextStatus,
3170
- phase: getPhaseForStatus(nextStatus),
3171
- // Only re-stamp the phase/activity clocks on the revive itself; otherwise a poll
3172
- // keeps resetting them under a row whose status it didn't change.
3173
- phaseSince: revived ? new Date(pollStartedAt) : trackedSession.phaseSince,
3174
- lastActivity: revived ? new Date(pollStartedAt) : trackedSession.lastActivity,
3175
- pid: nextPid,
3176
- statusDetail: revived ? undefined : trackedSession.statusDetail,
3177
- endReason: undefined,
3178
- endedAt: undefined,
3179
- };
3180
- changed = true;
3181
- }
3182
- keptAlive.push(`${data.sessionId.substring(0, 8)}:${nextStatus}`);
3183
- polledAlive.set(data.sessionId, capabilities.providerId);
3184
- } else if (this._sessions[existingIndex].status !== 'ended') {
3185
- // Already tracked, and Claude no longer lists it as live. If the live path missed the
3186
- // SessionEnd (e.g. `/clear` kept the PID alive under a new id), transition it to
3187
- // ended now — otherwise a live-status row would linger as a zombie, since
3188
- // pruneDeadSessions can't reap a live PID.
3189
- // The record's `endedAt` can predate activity this window already observed — a
3190
- // superseded record after a cwd move, or a same-cwd resume the CLI hasn't rewritten
3191
- // the record for yet — so never move the row's clock backward.
3192
- const { lastActivity } = this._sessions[existingIndex];
3193
- this.endSession(
3194
- existingIndex,
3195
- lastActivity.getTime() > endedDate.getTime() ? lastActivity : endedDate,
3196
- );
3197
- changed = true;
3198
- }
3199
-
3200
- // Re-seat the row on its current location. The durable record is authoritative for a
3201
- // terminal session, but an independent Claude listing can prove that record stale and
3202
- // supply the resumed process's current cwd. Keep the prior worktree attribution until
3203
- // the live cwd's git probe confirms a replacement — a resumed session can legitimately
3204
- // sit in scratch space — but never let a later poll move it back to the ended record's
3205
- // directory. A record already known superseded stays ignored until the CLI rewrites it,
3206
- // so the row survives the resumed process exiting before that write lands. Prompts get a
3207
- // hole-fill from the record below when nothing has been set yet; titles keep their own
3208
- // on-demand transcript resolution, which holds richer values than the record does.
3209
- const existing = this._sessions[existingIndex];
3210
- const cwdChanged = !recordLocationStale && currentCwd !== existing.cwd;
3211
- // Pre-`cwdTimeline` records carry no worktree data. If the cwd moved, whatever we hold
3212
- // is no longer trustworthy — it may point into a different repo entirely — so drop it
3213
- // rather than leave the card filed under the wrong branch, and re-resolve below. A cwd
3214
- // supplied by the live listing is different: preserve the old attribution as a fallback
3215
- // until the current cwd's probe succeeds.
3216
- const nextWorktreePath = recordLocationStale
3217
- ? existing.worktreePath
3218
- : currentWorktreeRoot != null
3219
- ? normalizePath(currentWorktreeRoot)
3220
- : cwdChanged && !liveCwdMoved
3221
- ? undefined
3222
- : existing.worktreePath;
3223
- const worktreeMoved = nextWorktreePath !== existing.worktreePath;
3224
- if (cwdChanged || worktreeMoved) {
3225
- const workspacePath = this.resolveWorkspacePath(currentCwd);
3226
- this._sessions[existingIndex] = {
3227
- ...existing,
3228
- cwd: currentCwd,
3229
- initialCwd: existing.initialCwd ?? data.initialCwd ?? data.cwd,
3230
- worktreePath: nextWorktreePath,
3231
- workspacePath: workspacePath,
3232
- isInWorkspace: workspacePath != null,
3233
- // A worktree move can cross repos, and the spread would otherwise carry the old
3234
- // repo identity forward — which consumers PREFER over the freshly-resolved
3235
- // worktree metadata, pinning the card to the wrong repo. Drop it and let the
3236
- // host backfill from the new worktree.
3237
- commonPath: worktreeMoved ? undefined : existing.commonPath,
3238
- visitedWorktreePaths: unionVisited(
3239
- existing.visitedWorktreePaths,
3240
- visitedFromTimeline(data.cwdTimeline),
3241
- data.worktrees,
3242
- nextWorktreePath,
3243
- ),
3244
- };
3245
- changed = true;
3246
-
3247
- // Nothing in the record to re-seat it with (older CLI), or the record names a
3248
- // NEW worktree the drop above just cleared `commonPath` for — either way, resolve
3249
- // the cwd once rather than leaving the row location-less until some later event
3250
- // happens to change `cwd`/`worktreePath` again (a same-worktree poll afterward
3251
- // skips this branch entirely, since `worktreeMoved` goes false).
3252
- if (liveCwdMoved || nextWorktreePath == null || worktreeMoved) {
3253
- this.getBookkeeping(data.sessionId).cliSeatedWorktree = currentWorktreeRoot != null;
3254
- void this.resolveGitInfo(data.sessionId, currentCwd);
3255
- }
3256
- }
3257
-
3258
- // Prompts backfill-only: the record's prompt/firstPrompt sanitized the same way the
3259
- // discovery path does. Never overwrites a value the hook path or a transcript resolve
3260
- // already set.
3261
- const currentSession = this._sessions[existingIndex];
3262
- const nextLastPrompt = currentSession.lastPrompt ?? prepareStoredPrompt(data.prompt ?? undefined);
3263
- const nextFirstPrompt =
3264
- currentSession.firstPrompt ?? prepareStoredPrompt(data.firstPrompt ?? undefined);
3265
- if (
3266
- nextLastPrompt !== currentSession.lastPrompt ||
3267
- nextFirstPrompt !== currentSession.firstPrompt
3268
- ) {
3269
- this._sessions[existingIndex] = {
3270
- ...currentSession,
3271
- lastPrompt: nextLastPrompt,
3272
- firstPrompt: nextFirstPrompt,
3273
- };
3274
- changed = true;
3275
- }
3276
-
3277
- const nextBookkeeping = this.getBookkeeping(data.sessionId);
3278
- nextBookkeeping.polledAtLeastOnce = true;
3279
- // `endSession` above drops the bookkeeping entry, but the latch has to outlive that
3280
- // transition — otherwise the next poll finds none and re-seats the row onto the very
3281
- // record this one already knew was superseded.
3282
- nextBookkeeping.supersededRecordCwd = bookkeeping.supersededRecordCwd;
3283
- continue;
3284
- }
3285
-
3286
- known.add(data.sessionId);
3287
- const workspacePath = this.resolveWorkspacePath(currentCwd);
3288
-
3289
- if (override != null) {
3290
- const { live: liveSession, status: liveStatus } = override;
3291
- // This window is learning about the session for the first time via its `ended`
3292
- // record, but `claude agents --json` still lists it as live — same override as the
3293
- // tracked branch above, just pushing a fresh row instead of updating one in place. The
3294
- // listing's cwd is current; the ended record's worktree remains only as a fallback until
3295
- // the live cwd's git probe resolves it.
3296
- this._sessions.push({
3297
- id: data.sessionId,
3298
- providerId: capabilities.providerId,
3299
- providerName: capabilities.displayName,
3300
- name: data.sessionName || undefined,
3301
- status: liveStatus,
3302
- phase: getPhaseForStatus(liveStatus),
3303
- phaseSince: new Date(pollStartedAt),
3304
- // Listing's pid or none — never the record's, which is the dead process whose exit
3305
- // wrote the `ended` record (see the tracked branch above).
3306
- pid: liveSession.pid,
3307
- lastActivity: new Date(pollStartedAt),
3308
- isSubagent: false,
3309
- workspacePath: workspacePath,
3310
- worktreePath: recordWorktreeRoot != null ? normalizePath(recordWorktreeRoot) : undefined,
3311
- visitedWorktreePaths: unionVisited(
3312
- undefined,
3313
- visitedFromTimeline(data.cwdTimeline),
3314
- data.worktrees,
3315
- recordWorktreeRoot != null ? normalizePath(recordWorktreeRoot) : undefined,
3316
- ),
3317
- cwd: currentCwd,
3318
- initialCwd: data.initialCwd ?? data.cwd,
3319
- planFile: data.planFile ?? undefined,
3320
- isInWorkspace: workspacePath != null,
3321
- lastPrompt: prepareStoredPrompt(data.prompt ?? undefined),
3322
- firstPrompt: prepareStoredPrompt(data.firstPrompt ?? undefined),
3323
- model: data.model ?? undefined,
3324
- });
3325
- this.getBookkeeping(data.sessionId).polledAtLeastOnce = true;
3326
- this.getBookkeeping(data.sessionId).cliSeatedWorktree = currentWorktreeRoot != null;
3327
- if (liveCwdMoved) {
3328
- // Latch the cwd this listing superseded, so a later poll that no longer finds the
3329
- // session live doesn't re-seat the row back onto the not-yet-rewritten record.
3330
- this.getBookkeeping(data.sessionId).supersededRecordCwd = data.cwd;
3331
- }
3332
- changed = true;
3333
- noteDiscovered(capabilities.providerId);
3334
- keptAlive.push(`${data.sessionId.substring(0, 8)}:${liveStatus}`);
3335
- polledAlive.set(data.sessionId, capabilities.providerId);
3336
- if (liveCwdMoved || recordWorktreeRoot == null) {
3337
- void this.resolveGitInfo(data.sessionId, currentCwd);
3338
- }
3339
- void this.resolveTranscriptTitles(data.sessionId, currentCwd);
3340
- continue;
3341
- }
3342
-
3343
- this._sessions.push({
3344
- id: data.sessionId,
3345
- providerId: capabilities.providerId,
3346
- providerName: capabilities.displayName,
3347
- name: data.sessionName || undefined,
3348
- status: 'ended',
3349
- phase: 'ended',
3350
- phaseSince: endedDate,
3351
- pid: data.pid,
3352
- lastActivity: endedDate,
3353
- isSubagent: false,
3354
- workspacePath: workspacePath,
3355
- // Read straight from the CLI's durable record so ended sessions attach to branch
3356
- // cards / WIP rows / the resume picker at any age, no git probe.
3357
- worktreePath: recordWorktreeRoot != null ? normalizePath(recordWorktreeRoot) : undefined,
3358
- visitedWorktreePaths: unionVisited(
3359
- undefined,
3360
- visitedFromTimeline(data.cwdTimeline),
3361
- data.worktrees,
3362
- recordWorktreeRoot != null ? normalizePath(recordWorktreeRoot) : undefined,
3363
- ),
3364
- cwd: data.cwd,
3365
- initialCwd: data.initialCwd ?? data.cwd,
3366
- planFile: data.planFile ?? undefined,
3367
- isInWorkspace: workspacePath != null,
3368
- lastPrompt: prepareStoredPrompt(data.prompt ?? undefined),
3369
- firstPrompt: prepareStoredPrompt(data.firstPrompt ?? undefined),
3370
- model: data.model ?? undefined,
3371
- endReason: data.endReason ?? undefined,
3372
- endedAt: data.endedAt != null ? new Date(data.endedAt).getTime() : undefined,
3373
- // Subagents are meaningless once terminal.
3374
- subagents: undefined,
3375
- });
3376
- this.getBookkeeping(data.sessionId).polledAtLeastOnce = true;
3377
- changed = true;
3378
- this.noteTerminalTransition();
3379
- // Titles/prompts still resolve lazily via resolveEndedSessionDetails (a 30-day
3380
- // cold-start must not fan out transcript reads). worktreePath comes from the record
3381
- // above; only fall back to an eager git probe when the record predates that CLI field,
3382
- // and only for the recent (WIP-window) tail so the fallback stays bounded.
3383
- if (
3384
- recordWorktreeRoot == null &&
3385
- data.cwd &&
3386
- Date.now() - endedDate.getTime() < recentEndedGitResolveThresholdMs
3387
- ) {
3388
- void this.resolveGitInfo(data.sessionId, data.cwd);
3389
- }
3390
- continue;
3391
- }
3392
-
3393
- // Active / legacy (absent-status) path — only genuinely-live processes are discoverable.
3394
- if (!data.pid || !isProcessAlive(data.pid)) continue;
3395
-
3396
- polledAlive.set(data.sessionId, capabilities.providerId);
3397
- polledIds.add(data.sessionId);
3398
-
3399
- if (known.has(data.sessionId)) {
3400
- // Already tracked.
3401
- const existingIndex = this._sessions.findIndex(s => s.id === data.sessionId);
3402
- if (existingIndex >= 0) {
3403
- const existing = this._sessions[existingIndex];
3404
- // The status the CLI's last event implies — a resumed session may already be mid-work
3405
- // (`PreToolUse` → tool_use, `UserPromptSubmit` → thinking), and hardcoding `idle` would
3406
- // mislabel active work and risk a concurrent-write resume.
3407
- const polledStatus = deriveStatusFromEvent(canonicalEvent);
3408
- const isSynthesizedAsk =
3409
- existing.status === 'permission_requested' && existing.pendingPermission?.resolvable === false;
3410
- const liveEntry = isSynthesizedAsk ? liveSessions?.get(data.sessionId) : undefined;
3411
- const liveStatus = liveEntry != null ? statusFromLiveSession(liveEntry) : undefined;
3412
- if (isSynthesizedAsk && liveEntry != null && liveStatus == null) {
3413
- // A terminal listing entry proves this active record is stale, even if its last event
3414
- // advanced after the ask. It carries no authoritative terminal timestamp/details, so
3415
- // remove the false live row and let the durable ended record restore it later. Run the
3416
- // standard terminal teardown first so no timers or caches survive the row.
3417
- this.endSession(existingIndex, new Date(pollStartedAt));
3418
- this._sessions.splice(existingIndex, 1);
3419
- polledAlive.delete(data.sessionId);
3420
- changed = true;
3421
- continue;
3422
- }
3423
-
3424
- if (existing.status === 'ended') {
3425
- // We hold it as `ended` but the CLI now reports it as a live process (a
3426
- // resume the live SessionStart path missed) — revive it, the symmetric backstop
3427
- // to the ended-branch's live→ended transition above. Leave genuinely-live
3428
- // rows to the IPC path.
3429
- const revivedAt = new Date(data.updatedAt);
3430
- // Only when the record is at least as new as the end. `SessionEnd` fires
3431
- // while the process is still winding down, so a poll whose CLI call started
3432
- // BEFORE it returns a snapshot that still says "active" with a live pid — reviving
3433
- // off that stale record would resurrect the row, and `pruneDeadSessions` would
3434
- // then delete it outright once the process exits, losing the ended row.
3435
- if (revivedAt.getTime() < existing.phaseSince.getTime()) continue;
3436
-
3437
- // The poll has no blocking hook entry for this ask (it's discovered off the CLI's
3438
- // durable record, not this window's IPC path) — synthesize an unresolvable one so
3439
- // the row shows what it's waiting on instead of an empty "Needs input" card.
3440
- const revivedPendingPermission =
3441
- polledStatus === 'permission_requested'
3442
- ? synthesizeUnresolvableAsk(polledToolName, data.planFile ?? undefined)
3443
- : undefined;
3444
- // Seat worktreePath from the record, mirroring the ended-record re-seat above — the
3445
- // existing row is otherwise stuck with whatever (or nothing) it had while ended.
3446
- const revivedWorktreeRoot = worktreeRootFromData(data);
3447
- // No record worktree data (older CLI): KEEP the ended row's attribution. A
3448
- // resume whose cwd is scratch space (e.g. /tmp) still belongs to its worktree —
3449
- // re-rooting happens only when git affirmatively resolves a new one (the probe
3450
- // below, on success). Attribution persists until something better is known.
3451
- const revivedWorktreePath =
3452
- revivedWorktreeRoot != null ? normalizePath(revivedWorktreeRoot) : existing.worktreePath;
3453
- // Membership follows the attribution, mirroring `ensureSession`: cwd first, the
3454
- // kept worktree as the fallback for a scratch-dir resume.
3455
- const revivedWorkspacePath =
3456
- this.resolveWorkspacePath(data.cwd) ?? this.resolveWorkspacePath(revivedWorktreePath);
3457
- this._sessions[existingIndex] = {
3458
- ...existing,
3459
- status: polledStatus,
3460
- phase: getPhaseForStatus(polledStatus),
3461
- phaseSince: revivedAt,
3462
- lastActivity: revivedAt,
3463
- // Fresh pid from the CLI, and re-classify ownership on next open rather than
3464
- // trusting the terminal record's `isPeerOwned`.
3465
- pid: data.pid,
3466
- isPeerOwned: undefined,
3467
- pendingPermission: revivedPendingPermission,
3468
- // The resume can be running somewhere else entirely, so take the record's
3469
- // location rather than leaving the row filed under where it ended.
3470
- cwd: data.cwd ?? existing.cwd,
3471
- workspacePath:
3472
- revivedWorkspacePath ?? (data.cwd == null ? existing.workspacePath : undefined),
3473
- isInWorkspace: data.cwd == null ? existing.isInWorkspace : revivedWorkspacePath != null,
3474
- worktreePath: revivedWorktreePath,
3475
- visitedWorktreePaths: unionVisited(
3476
- existing.visitedWorktreePaths,
3477
- visitedFromTimeline(data.cwdTimeline),
3478
- data.worktrees,
3479
- revivedWorktreePath,
3480
- ),
3481
- // A worktree move can cross repos — drop the stale repo identity and let the
3482
- // probe below refill it.
3483
- commonPath: revivedWorktreePath !== existing.worktreePath ? undefined : existing.commonPath,
3484
- // No longer terminal: leaving these set makes the row read as ended to
3485
- // consumers that key off them.
3486
- endReason: undefined,
3487
- endedAt: undefined,
3488
- };
3489
- this.resetBookkeeping(data.sessionId);
3490
- // The record's worktree is the CLI's own resolution; without one the probe owns
3491
- // attribution again.
3492
- this.getBookkeeping(data.sessionId).cliSeatedWorktree = revivedWorktreeRoot != null;
3493
- // A revive whose worktree moved but whose cwd happens to equal the prior one still
3494
- // dropped `commonPath` above and needs the probe to refill it.
3495
- if (
3496
- data.cwd != null &&
3497
- (data.cwd !== existing.cwd || revivedWorktreePath !== existing.worktreePath)
3498
- ) {
3499
- void this.resolveGitInfo(data.sessionId, data.cwd);
3500
- }
3501
- changed = true;
3502
- } else if (
3503
- existing.pendingPermission?.resolvable === false &&
3504
- polledStatus !== 'permission_requested' &&
3505
- new Date(data.updatedAt).getTime() > existing.lastActivity.getTime()
3506
- ) {
3507
- // The record has moved past the ask this row still shows. Only a synthesized
3508
- // (`resolvable: false`) ask is repairable this way — a routable one is bookkeeping-
3509
- // owned and cleared by its own resolution paths, which the record can't see.
3510
- this._sessions[existingIndex] = {
3511
- ...existing,
3512
- status: polledStatus,
3513
- phase: getPhaseForStatus(polledStatus),
3514
- phaseSince: new Date(data.updatedAt),
3515
- // The detail described the ask's tool; carry the record's current tool for
3516
- // tool_use, else clear — keeping it would show the answered ask's tool as
3517
- // the active one.
3518
- statusDetail: polledStatus === 'tool_use' ? polledToolName : undefined,
3519
- pendingPermission: undefined,
3520
- lastActivity: new Date(data.updatedAt),
3521
- };
3522
- changed = true;
3523
- } else if (isSynthesizedAsk) {
3524
- // The record never moved past the ask it froze on — the ask was resolved while no
3525
- // window was listening, so nothing ever advanced it — but Claude's own listing
3526
- // says the session has moved on. Correct the row so it heals on the next poll
3527
- // instead of waiting for a reload or fresh hook events. Only synthesized asks
3528
- // are repairable here: a routable one is bookkeeping-owned and cleared by its
3529
- // own resolution paths, which the listing must never preempt.
3530
- if (liveStatus != null && liveStatus !== 'permission_requested') {
3531
- this._sessions[existingIndex] = {
3532
- ...existing,
3533
- status: liveStatus,
3534
- phase: getPhaseForStatus(liveStatus),
3535
- phaseSince: new Date(pollStartedAt),
3536
- pendingPermission: undefined,
3537
- statusDetail: undefined,
3538
- };
3539
- changed = true;
3540
- }
3541
- } else if (existing.transcriptTitles?.ai == null && existing.transcriptTitles?.custom == null) {
3542
- // A live session we discovered via the poll (another worktree/window owns its
3543
- // hook flow) gets none of the local IPC re-resolves (SessionStart, idle). So one
3544
- // discovered before Claude wrote its `ai-title` would show the repo slug forever.
3545
- // Re-check the transcript each poll until a real title lands — cheap: the reader
3546
- // tail-reads from its mtime cache and no-ops when the file hasn't grown.
3547
- void this.resolveTranscriptTitles(data.sessionId, data.cwd);
3548
- }
3549
- }
3550
- continue;
3551
- }
3552
-
3553
- known.add(data.sessionId);
3554
-
3555
- const workspacePath = this.resolveWorkspacePath(data.cwd);
3556
- const isInWorkspace = workspacePath != null;
3557
- const activityDate = new Date(data.updatedAt);
3558
- let status = deriveStatusFromEvent(canonicalEvent);
3559
- let phaseSince = activityDate;
3560
- // The record's last event can be arbitrarily stale — an ask answered while no window
3561
- // was listening (e.g. mid-reload), or a session stopped right there, never advances it,
3562
- // so every window start would re-seed the frozen state. Claude's own listing is
3563
- // authoritative for what the session is doing right now — same contract as the
3564
- // ended-record revival above. Absence from the listing (including its fail-soft empty
3565
- // map on spawn failure) keeps record-derived status: absence must never mean dead.
3566
- const liveEntry = liveSessions?.get(data.sessionId);
3567
- if (liveEntry != null) {
3568
- const liveStatus = statusFromLiveSession(liveEntry);
3569
- if (liveStatus == null) {
3570
- polledAlive.delete(data.sessionId);
3571
- continue;
3572
- }
3573
-
3574
- if (liveStatus !== status) {
3575
- status = liveStatus;
3576
- phaseSince = new Date(pollStartedAt);
3577
- }
3578
- }
3579
-
3580
- const phase = getPhaseForStatus(status);
3581
- // Same rationale as the revived-session branch above: no blocking hook entry exists for a
3582
- // poll-discovered ask, so synthesize an unresolvable one for the card.
3583
- const pendingPermission =
3584
- status === 'permission_requested'
3585
- ? synthesizeUnresolvableAsk(polledToolName, data.planFile ?? undefined)
3586
- : undefined;
3587
- // Read straight from the CLI's durable record so the row attaches to the right branch
3588
- // card/WIP row immediately, no git probe — mirrors the ended-session push below.
3589
- const worktreeRoot = worktreeRootFromData(data);
3590
-
3591
- const subagents: AgentSession[] | undefined = data.subagents?.map(sub => ({
3592
- id: sub.agentId,
3593
- providerId: capabilities.providerId,
3594
- providerName: capabilities.displayName,
3595
- name: sub.agentType ?? 'Subagent',
3596
- status: 'thinking',
3597
- phase: 'working' as const,
3598
- phaseSince: activityDate,
3599
- lastActivity: activityDate,
3600
- isSubagent: true,
3601
- parentId: data.sessionId,
3602
- isInWorkspace: isInWorkspace,
3603
- }));
3604
-
3605
- this._sessions.push({
3606
- id: data.sessionId,
3607
- providerId: capabilities.providerId,
3608
- providerName: capabilities.displayName,
3609
- name: data.sessionName || undefined,
3610
- status: status,
3611
- phase: phase,
3612
- phaseSince: phaseSince,
3613
- pid: data.pid,
3614
- lastActivity: activityDate,
3615
- isSubagent: false,
3616
- workspacePath: workspacePath,
3617
- cwd: data.cwd,
3618
- // Prefer the CLI's launch dir; fall back to the snapshot's (possibly drifted) cwd on
3619
- // older CLIs that don't record it. Without this, a window that cold-starts after the
3620
- // agent drifted would stamp the drifted cwd as the launch dir.
3621
- initialCwd: data.initialCwd ?? data.cwd,
3622
- planFile: data.planFile ?? undefined,
3623
- isInWorkspace: isInWorkspace,
3624
- lastPrompt: prepareStoredPrompt(data.prompt ?? undefined),
3625
- firstPrompt: prepareStoredPrompt(data.firstPrompt ?? undefined),
3626
- model: data.model ?? undefined,
3627
- subagents: subagents,
3628
- pendingPermission: pendingPermission,
3629
- worktreePath: worktreeRoot != null ? normalizePath(worktreeRoot) : undefined,
3630
- visitedWorktreePaths: unionVisited(
3631
- undefined,
3632
- visitedFromTimeline(data.cwdTimeline),
3633
- data.worktrees,
3634
- worktreeRoot != null ? normalizePath(worktreeRoot) : undefined,
3635
- ),
3636
- });
3637
- changed = true;
3638
- noteDiscovered(capabilities.providerId);
3639
-
3640
- // The record's worktree is the CLI's own resolution — mark it seated so the probe below
3641
- // (still wanted for `commonPath`) can't clobber it for a nested worktree. Mirrors the
3642
- // revival branch above.
3643
- this.getBookkeeping(data.sessionId).cliSeatedWorktree = worktreeRoot != null;
3644
- if (data.cwd) {
3645
- void this.resolveGitInfo(data.sessionId, data.cwd);
3646
- }
3647
- void this.resolveTranscriptTitles(data.sessionId, data.cwd);
3648
- }
3649
-
3650
- // The poll is authoritative for ended-session removal (archived elsewhere or 30-day-purged).
3651
- // Only remove ones a prior poll confirmed present — never a session the live SessionEnd path just
3652
- // transitioned, which an already-in-flight poll can legitimately miss (guarded by polledAtLeastOnce).
3653
- // On a legacy CLI (no `--status`) no poll can ever confirm an ended row, so the guard would
3654
- // instead pin it forever — there, absence from the poll is the confirmation.
3655
- const staleEnded = this._sessions.filter(
3656
- s =>
3657
- s.status === 'ended' &&
3658
- !polledIds.has(s.id) &&
3659
- (!this._statusFilterSupported ||
3660
- this._sessionBookkeeping.get(s.id)?.polledAtLeastOnce === true ||
3661
- // Or this poll's snapshot postdates the end, so its absence is authoritative.
3662
- // `polledAtLeastOnce` alone can never be set for a session whose record disappeared
3663
- // (archived from another window, or purged) before any poll observed it — and
3664
- // `endSession` drops the bookkeeping — so without this the row is unremovable for
3665
- // the window's lifetime. The timestamp answers the same question the flag was proxying:
3666
- // could this poll have seen it? The grace keeps a just-ended row out of reach of
3667
- // an absence that's really a not-yet-visible record.
3668
- s.phaseSince.getTime() < pollStartedAt - endedRemovalGraceMs),
3669
- );
3670
- if (staleEnded.length > 0) {
3671
- const staleIds = new Set(staleEnded.map(s => s.id));
3672
- this._sessions = this._sessions.filter(s => !staleIds.has(s.id));
3673
- for (const s of staleEnded) {
3674
- this._sessionBookkeeping.delete(s.id);
3675
- this._transcriptReader.forget(s.id);
3676
- }
3677
- changed = true;
3678
- }
3679
-
3680
- if (liveSessions != null) {
3681
- Logger.debug(
3682
- `GkAgentProvider.syncSessions: Claude lists ${liveSessions.size} session(s); kept ${keptAlive.length} alive against a stale ended record${
3683
- keptAlive.length > 0 ? `: ${keptAlive.join(', ')}` : ''
3684
- }`,
3685
- );
3686
- }
3687
-
3688
- if (this.pruneDeadSessions()) {
3689
- changed = true;
3690
- }
3691
-
3692
- if (changed) {
3693
- this._onDidChangeSessions.fire();
3694
- }
3695
-
3696
- // Report any drift between the live-synced set and the poll. Only on the recurring (gated)
3697
- // poll — the ungated bootstrap call runs before the live path has had a chance to receive
3698
- // any events, so its discoveries are expected cold-start state, not drift. `missing` counts
3699
- // live sessions we still track that the poll no longer reports alive (a teardown the live path
3700
- // missed, e.g. an agent killed without firing `SessionEnd`).
3701
- if (options?.gate) {
3702
- type Drift = { discovered: number; missing: number; polled: number; tracked: number };
3703
- const driftByAgent = new Map<string, Drift>();
3704
- const driftFor = (providerId: string): Drift => {
3705
- let drift = driftByAgent.get(providerId);
3706
- if (drift == null) {
3707
- drift = { discovered: 0, missing: 0, polled: 0, tracked: 0 };
3708
- driftByAgent.set(providerId, drift);
3709
- }
3710
- return drift;
3711
- };
3712
-
3713
- for (const [providerId, count] of discoveredByAgent) {
3714
- driftFor(providerId).discovered += count;
3715
- }
3716
- for (const [id, providerId] of trackedLiveBefore) {
3717
- const drift = driftFor(providerId);
3718
- drift.tracked++;
3719
- if (!polledAlive.has(id)) {
3720
- drift.missing++;
3721
- }
3722
- }
3723
- for (const providerId of polledAlive.values()) {
3724
- driftFor(providerId).polled++;
3725
- }
3726
-
3727
- // One report per agent, not one pooled report: the counts describe how well the live path
3728
- // and the poll agree, which is a per-agent property. A single agent's numbers are unchanged
3729
- // from the pre-multi-agent shape.
3730
- for (const [providerId, drift] of driftByAgent) {
3731
- if (drift.discovered === 0 && drift.missing === 0) continue;
3732
-
3733
- this.callbacks.onSyncDiscrepancy?.({ provider: providerId, ...drift });
3734
- }
3735
- }
3736
- }
3737
-
3738
- protected async querySiblingWindowSessions(): Promise<void> {
3739
- const discoveryDir = this.callbacks.ipc.agentDiscoveryDir;
3740
- if (discoveryDir == null) return;
3741
-
3742
- let files: string[];
3743
- try {
3744
- files = await readdir(discoveryDir);
3745
- } catch {
3746
- return;
3747
- }
3748
-
3749
- const ownPort = this.callbacks.ipc.port;
3750
-
3751
- const peerBatches = await Promise.all(
3752
- files
3753
- .filter(f => f.startsWith('gitlens-ipc-server-') && f.endsWith('.json'))
3754
- .map(async f => this.fetchPeerSessions(join(discoveryDir, f), ownPort)),
3755
- );
3756
-
3757
- let changed = false;
3758
- // Peer sessions that arrived with `cwd` but no `commonPath` (peer was running pre-
3759
- // commonPath code, OR the peer's own `resolveGitInfo` failed and we get to retry locally).
3760
- // Resolved after the merge loop so the local Repository registry is consulted only once
3761
- // per unique session, off the hot merge path. Keyed by id (value = cwd) so the same
3762
- // session appearing in multiple peer responses (or re-queued within this loop) only
3763
- // triggers a single backfill probe.
3764
- const sessionsNeedingResolve = new Map<string, string>();
3765
-
3766
- for (const peerSessions of peerBatches) {
3767
- if (peerSessions == null) continue;
3768
-
3769
- for (const peerSession of peerSessions) {
3770
- // Skip terminal rows a peer published: ended sessions live in the machine-global CLI
3771
- // store, so our own poll surfaces them with proper reconciliation. Importing them from a
3772
- // peer would add a row with no `polledAtLeastOnce` that our poll can't confirm — a ghost
3773
- // pinned for this window's lifetime if the store later drops it. (The publish endpoint
3774
- // filters ended rows out anyway; this also keeps a version-skewed peer's terminal claim
3775
- // from ending a row we still track live.)
3776
- if (peerSession.status === 'ended') continue;
3777
-
3778
- const existing = this._sessions.find(s => s.id === peerSession.id);
3779
- const peerActivity = new Date(peerSession.lastActivity);
3780
- const peerPhaseSince = new Date(peerSession.phaseSince);
3781
-
3782
- if (existing != null) {
3783
- // If the peer reports a different `cwd` than we last saw, the agent moved
3784
- // (peer fired a `CwdChanged` event we don't receive locally). Pick up the new
3785
- // cwd AND reset our local `gitInfoUnresolvable` flag — the new cwd might be a
3786
- // git repo even if the previous one wasn't.
3787
- const cwdChanged = peerSession.cwd != null && existing.cwd !== peerSession.cwd;
3788
- if (cwdChanged && this._sessionBookkeeping.get(peerSession.id)?.gitInfoUnresolvable) {
3789
- this.getBookkeeping(peerSession.id).gitInfoUnresolvable = false;
3790
- }
3791
-
3792
- if (peerActivity > existing.lastActivity) {
3793
- const idx = this._sessions.indexOf(existing);
3794
- // A terminal row the peer now reports live IS a resume the peer owns. Mirror the
3795
- // poll/SessionStart revive paths: take the peer's pid (retaining the terminal
3796
- // record's dead — and possibly reused — pid would let dispatch focus an unrelated
3797
- // process) and mark it peer-owned so opens route through the peer's IPC.
3798
- const revivedFromEnded = existing.status === 'ended';
3799
- // Peer is the authoritative hook recipient for this session, so wipe any
3800
- // local bookkeeping that survived from a previous local-ownership window.
3801
- // Without this, refcounts/timers from a prior local-hosting stretch would
3802
- // resurface stale paths if ownership ever flips back to us.
3803
- const bk = this._sessionBookkeeping.get(peerSession.id);
3804
- if (bk != null) {
3805
- this.cancelPendingFileClears(bk);
3806
- this.cancelPendingReadClears(bk);
3807
- bk.currentFileCounts.clear();
3808
- bk.currentReadCounts.clear();
3809
- bk.lastTouchedAt.clear();
3810
- }
3811
- this._sessions[idx] = {
3812
- ...existing,
3813
- status: peerSession.status,
3814
- phase: peerSession.phase,
3815
- phaseSince: peerPhaseSince,
3816
- statusDetail: peerSession.statusDetail,
3817
- lastActivity: peerActivity,
3818
- // A locally-routable ask wins — a blocking ask fans out to every window, so we
3819
- // may hold the hook entry the peer lacks. Otherwise mirror the peer's ask marked
3820
- // unresolvable (we have no entry to answer it), and clear it when the peer did.
3821
- pendingPermission:
3822
- this._sessionBookkeeping.get(peerSession.id)?.pendingPermission ??
3823
- (peerSession.pendingPermission != null
3824
- ? { ...peerSession.pendingPermission, resolvable: false }
3825
- : undefined),
3826
- ...(revivedFromEnded ? { pid: peerSession.pid, isPeerOwned: true } : undefined),
3827
- subagents: rehydrateSubagents(peerSession.subagents),
3828
- // Carry the peer's published fileActivity across so peer-window WIP
3829
- // decorations + treemap heatmap follow the agent. Owned by the peer (it's
3830
- // the hook recipient); we never originate this field for a remote session.
3831
- fileActivity: peerSession.fileActivity,
3832
- // Track the peer's resolved repo identity. The peer owns the hook flow and
3833
- // re-resolves both on `CwdChanged`, so a worktree move (e.g. the agent
3834
- // `cd`'d into a sibling worktree of the same repo — `commonPath` unchanged
3835
- // but `worktreePath` changed) only reaches us if we carry BOTH. Carrying
3836
- // `commonPath` alone froze the displayed worktree at first-discovery.
3837
- worktreePath: peerSession.worktreePath ?? existing.worktreePath,
3838
- visitedWorktreePaths: unionVisitedAppendOnly(
3839
- existing.visitedWorktreePaths,
3840
- peerSession.visitedWorktreePaths,
3841
- ),
3842
- commonPath: peerSession.commonPath ?? existing.commonPath,
3843
- // Pick up the peer's latest cwd so our backfill probe (queued below)
3844
- // targets the right path. Stale-cwd locally would just re-probe the
3845
- // non-repo dir and hit the gitInfoUnresolvable retry guard again.
3846
- cwd: cwdChanged ? peerSession.cwd : existing.cwd,
3847
- // Launch-state fields are immutable per session: prefer whichever side
3848
- // already set them (peer that owns the hook flow typically sets first),
3849
- // and never overwrite a populated local value with the peer's.
3850
- initialCwd: existing.initialCwd ?? peerSession.initialCwd,
3851
- initialWorktreePath: existing.initialWorktreePath ?? peerSession.initialWorktreePath,
3852
- initialCommonPath: existing.initialCommonPath ?? peerSession.initialCommonPath,
3853
- // The hook path, when present, carries richer un-truncated prompts than what a
3854
- // peer forwards — prefer our own value over the peer's.
3855
- lastPrompt: existing.lastPrompt ?? peerSession.lastPrompt,
3856
- firstPrompt: existing.firstPrompt ?? peerSession.firstPrompt,
3857
- // Forward peer-discovered transcript titles. Both windows can resolve them
3858
- // independently against the same on-disk transcript, but only the peer's
3859
- // hook flow drives its updates — without this, we'd freeze the snapshot
3860
- // taken at first discovery.
3861
- transcriptTitles: peerSession.transcriptTitles ?? existing.transcriptTitles,
3862
- };
3863
- changed = true;
3864
- }
3865
- } else {
3866
- this._sessions.push({
3867
- ...peerSession,
3868
- lastActivity: peerActivity,
3869
- phaseSince: peerPhaseSince,
3870
- // The peer owns the hook entry, so an imported ask can never be routed from here.
3871
- pendingPermission:
3872
- peerSession.pendingPermission != null
3873
- ? { ...peerSession.pendingPermission, resolvable: false }
3874
- : undefined,
3875
- workspacePath: normalizeWorkspacePath(peerSession.workspacePath),
3876
- isInWorkspace: this.matchesWorkspace(peerSession.workspacePath),
3877
- subagents: rehydrateSubagents(peerSession.subagents),
3878
- // Override whatever the peer published — this is OUR ownership view: the peer
3879
- // (or some other window) hosts the live session, not us. Drives the dispatcher
3880
- // to route opens through the peer's IPC + OS-level window focus instead of
3881
- // calling `claude-vscode.editor.open` locally (which would just open an inert
3882
- // view in our window).
3883
- isPeerOwned: true,
3884
- });
3885
- changed = true;
3886
- }
3887
-
3888
- // Queue a local resolveGitInfo for any peer session that arrived without a
3889
- // commonPath but has a cwd — we can fill it in from our own git registry. Skip
3890
- // if our own bookkeeping already marked this session's cwd unresolvable (we tried
3891
- // locally and the cwd isn't a git repo from our vantage point either).
3892
- const merged = this._sessions.find(s => s.id === peerSession.id);
3893
- if (
3894
- merged?.commonPath == null &&
3895
- merged?.cwd != null &&
3896
- !this.getBookkeeping(merged.id).gitInfoUnresolvable
3897
- ) {
3898
- sessionsNeedingResolve.set(merged.id, merged.cwd);
3899
- }
3900
- }
3901
- }
3902
-
3903
- if (changed) {
3904
- this._onDidChangeSessions.fire();
3905
- }
3906
-
3907
- // Backfill commonPath for peer-synced sessions whose owning peer didn't populate it.
3908
- // `resolveGitInfo` is idempotent — only mutates + fires if it actually finds new info,
3909
- // so the worst case here is a few wasted git probes for unresolvable cwds.
3910
- for (const [id, cwd] of sessionsNeedingResolve) {
3911
- void this.resolveGitInfo(id, cwd);
3912
- }
3913
- }
3914
-
3915
- private async fetchPeerSessions(
3916
- path: string,
3917
- ownPort: number | undefined,
3918
- ): Promise<SerializedAgentSession[] | undefined> {
3919
- let discovery: DiscoveryFile;
3920
- try {
3921
- discovery = JSON.parse(await readFile(path, 'utf8')) as DiscoveryFile;
3922
- } catch {
3923
- return undefined;
3924
- }
3925
- if (ownPort != null && discovery.port === ownPort) return undefined;
3926
-
3927
- try {
3928
- const response = await fetch(`${discovery.address}/agents/sessions/list`, {
3929
- method: 'POST',
3930
- headers: { Authorization: `Bearer ${discovery.token}`, 'Content-Type': 'application/json' },
3931
- body: '{}',
3932
- signal: AbortSignal.timeout(2000),
3933
- });
3934
- if (!response.ok) return undefined;
3935
- return (await response.json()) as SerializedAgentSession[];
3936
- } catch {
3937
- return undefined;
3938
- }
3939
- }
3940
-
3941
- /** Find the peer GitLens window whose discovery file claims `workspacePath`, and POST to its
3942
- * `/agents/sessions/open` route to ask its Claude Code extension to open the session.
3943
- *
3944
- * Resolves to `true` when at least one peer claimed the workspace AND was reachable (the POST
3945
- * completed with any HTTP status). The peer's response shape (`{ opened: boolean }`) is logged
3946
- * for diagnostics but does NOT affect the return value — an `opened: false` peer is still the
3947
- * right window for the caller to focus, since it's the window that owns the session. Resolves
3948
- * to `false` when no peer claimed the workspace OR every claimer was unreachable; the caller
3949
- * treats that as "no peer to focus" and opens a new window instead of replacing the current
3950
- * one. Best-effort: swallows scan, JSON-parse errors. */
3951
- async notifyPeerOpenSession(workspacePath: string, sessionId: string): Promise<boolean> {
3952
- const discoveryDir = this.callbacks.ipc.agentDiscoveryDir;
3953
- if (discoveryDir == null) return false;
3954
-
3955
- const target = normalizePath(workspacePath);
3956
- const ownPort = this.callbacks.ipc.port;
3957
-
3958
- let files: string[];
3959
- try {
3960
- files = await readdir(discoveryDir);
3961
- } catch {
3962
- return false;
3963
- }
3964
-
3965
- const results = await Promise.all(
3966
- files
3967
- .filter(f => f.startsWith('gitlens-ipc-server-') && f.endsWith('.json'))
3968
- .map(async f => {
3969
- let discovery: DiscoveryFile;
3970
- try {
3971
- discovery = JSON.parse(await readFile(join(discoveryDir, f), 'utf8')) as DiscoveryFile;
3972
- } catch {
3973
- return false;
3974
- }
3975
- if (ownPort != null && discovery.port === ownPort) return false;
3976
- // Symmetric prefix containment — same shape as `matchesWorkspace()` above.
3977
- // Strict equality misses the common case where the dispatcher passes a `cwd`
3978
- // inside the peer's workspace folder (or, less commonly, a parent dir that
3979
- // contains the peer's workspace).
3980
- if (
3981
- !discovery.workspacePaths?.some(p => {
3982
- const normalized = normalizePath(p);
3983
- return (
3984
- normalized === target ||
3985
- target.startsWith(`${normalized}/`) ||
3986
- normalized.startsWith(`${target}/`)
3987
- );
3988
- })
3989
- ) {
3990
- return false;
3991
- }
3992
-
3993
- try {
3994
- const response = await fetch(`${discovery.address}/agents/sessions/open`, {
3995
- method: 'POST',
3996
- headers: {
3997
- Authorization: `Bearer ${discovery.token}`,
3998
- 'Content-Type': 'application/json',
3999
- },
4000
- body: JSON.stringify({ sessionId: sessionId }),
4001
- signal: AbortSignal.timeout(2000),
4002
- });
4003
- if (!response.ok) {
4004
- Logger.warn(
4005
- `GkAgentProvider.notifyPeerOpenSession: peer at ${discovery.address} returned ${response.status}`,
4006
- );
4007
- } else {
4008
- // Log a peer that failed to open the specific session, but still treat it
4009
- // as a reachable claimer of the workspace — focusing that window is still
4010
- // what the user wants.
4011
- const body = (await response.json().catch(() => undefined)) as
4012
- | { opened?: boolean }
4013
- | undefined;
4014
- if (body?.opened === false) {
4015
- Logger.warn(
4016
- `GkAgentProvider.notifyPeerOpenSession: peer at ${discovery.address} could not open session ${sessionId}`,
4017
- );
4018
- }
4019
- }
4020
- return true;
4021
- } catch (ex) {
4022
- // Peer advertised but unreachable (timeout, RST, etc.). Treat as no match so
4023
- // the caller opens a new window instead of trying to focus a dead window.
4024
- Logger.warn(
4025
- `GkAgentProvider.notifyPeerOpenSession: peer at ${discovery.address} unreachable: ${
4026
- ex instanceof Error ? ex.message : String(ex)
4027
- }`,
4028
- );
4029
- return false;
4030
- }
4031
- }),
4032
- );
4033
-
4034
- return results.some(Boolean);
4035
- }
4036
- }