@duckmind/dm-linux-x64 0.65.4 → 0.65.6

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 (1208) hide show
  1. package/README.md +0 -8
  2. package/dm +2 -2
  3. package/extensions/.dm-extensions.json +677 -44
  4. package/extensions/dm-lens/clients/actionable-warnings-logger.js +0 -6
  5. package/extensions/dm-lens/clients/actionable-warnings.js +42 -13
  6. package/extensions/dm-lens/clients/advisory-provenance.js +129 -41
  7. package/extensions/dm-lens/clients/agent-behavior-client.js +1 -0
  8. package/extensions/dm-lens/clients/analysed-root.js +0 -0
  9. package/extensions/dm-lens/clients/ast-grep-client.js +38 -3
  10. package/extensions/dm-lens/clients/ast-grep-rule-manager.js +37 -5
  11. package/extensions/dm-lens/clients/ast-grep-tool-logger.js +1 -7
  12. package/extensions/dm-lens/clients/bash-file-access.js +138 -1
  13. package/extensions/dm-lens/clients/biome-client.js +9 -2
  14. package/extensions/dm-lens/clients/blocker-freshness.js +171 -21
  15. package/extensions/dm-lens/clients/bootstrap.js +9 -5
  16. package/extensions/dm-lens/clients/bounded-pid-file-lock.js +1 -1
  17. package/extensions/dm-lens/clients/bounded-telemetry.js +1 -4
  18. package/extensions/dm-lens/clients/bundled-resource-health.js +43 -0
  19. package/extensions/dm-lens/clients/bus-events-logger.js +0 -6
  20. package/extensions/dm-lens/clients/cache/rule-cache.js +11 -3
  21. package/extensions/dm-lens/clients/cache-manager.js +29 -5
  22. package/extensions/dm-lens/clients/cache-observability.js +104 -4
  23. package/extensions/dm-lens/clients/cargo-manifest.js +6 -3
  24. package/extensions/dm-lens/clients/cascade-format.js +87 -5
  25. package/extensions/dm-lens/clients/cascade-logger.js +0 -6
  26. package/extensions/dm-lens/clients/child-unref.js +5 -9
  27. package/extensions/dm-lens/clients/code-quality-warnings.js +4 -2
  28. package/extensions/dm-lens/clients/complexity-client.js +5 -1
  29. package/extensions/dm-lens/clients/config-core/index.js +2 -68
  30. package/extensions/dm-lens/clients/config-core/process-spec.js +1 -1
  31. package/extensions/dm-lens/clients/config-core/provenance.js +1 -2
  32. package/extensions/dm-lens/clients/config-core/records.js +1 -1
  33. package/extensions/dm-lens/clients/config-core/schema.js +3 -6
  34. package/extensions/dm-lens/clients/config-diagnostic-codes.js +5 -3
  35. package/extensions/dm-lens/clients/config-locations.js +125 -5
  36. package/extensions/dm-lens/clients/config-resolve.js +12 -6
  37. package/extensions/dm-lens/clients/config-schema.js +56 -3
  38. package/extensions/dm-lens/clients/config-warn.js +3 -2
  39. package/extensions/dm-lens/clients/dead-code-client.js +4 -1
  40. package/extensions/dm-lens/clients/dead-code-logger.js +0 -3
  41. package/extensions/dm-lens/clients/degradation-ledger.js +29 -4
  42. package/extensions/dm-lens/clients/dependency-checker.js +29 -50
  43. package/extensions/dm-lens/clients/diagnostic-dispositions.js +23 -4
  44. package/extensions/dm-lens/clients/diagnostic-logger.js +1 -1
  45. package/extensions/dm-lens/clients/dispatch/ast-grep-catalog.js +19 -0
  46. package/extensions/dm-lens/clients/dispatch/auxiliary-lsp.js +9 -0
  47. package/extensions/dm-lens/clients/dispatch/collect-later-tier.js +0 -3
  48. package/extensions/dm-lens/clients/dispatch/dispatcher.js +30 -27
  49. package/extensions/dm-lens/clients/dispatch/facts/function-facts.js +1 -1
  50. package/extensions/dm-lens/clients/dispatch/facts/import-facts.js +4 -1
  51. package/extensions/dm-lens/clients/dispatch/finding-policy.js +163 -0
  52. package/extensions/dm-lens/clients/dispatch/indent-detect.js +213 -13
  53. package/extensions/dm-lens/clients/dispatch/inline-suppressions.js +20 -1
  54. package/extensions/dm-lens/clients/dispatch/integration.js +77 -13
  55. package/extensions/dm-lens/clients/dispatch/lazy.js +0 -3
  56. package/extensions/dm-lens/clients/dispatch/pending-runner-findings.js +1 -1
  57. package/extensions/dm-lens/clients/dispatch/plan.js +0 -3
  58. package/extensions/dm-lens/clients/dispatch/rule-ignores.js +21 -0
  59. package/extensions/dm-lens/clients/dispatch/rules/high-complexity.js +2 -2
  60. package/extensions/dm-lens/clients/dispatch/rules/high-fan-out.js +1 -1
  61. package/extensions/dm-lens/clients/dispatch/runners/actionlint.js +25 -29
  62. package/extensions/dm-lens/clients/dispatch/runners/ast-grep-napi.js +8 -32
  63. package/extensions/dm-lens/clients/dispatch/runners/biome-check.js +39 -23
  64. package/extensions/dm-lens/clients/dispatch/runners/cpp-check.js +29 -48
  65. package/extensions/dm-lens/clients/dispatch/runners/credo.js +17 -14
  66. package/extensions/dm-lens/clients/dispatch/runners/cue-vet.js +9 -7
  67. package/extensions/dm-lens/clients/dispatch/runners/dart-analyze.js +21 -43
  68. package/extensions/dm-lens/clients/dispatch/runners/detekt.js +11 -5
  69. package/extensions/dm-lens/clients/dispatch/runners/dotnet-build.js +27 -23
  70. package/extensions/dm-lens/clients/dispatch/runners/elixir-check.js +23 -38
  71. package/extensions/dm-lens/clients/dispatch/runners/eslint.js +22 -35
  72. package/extensions/dm-lens/clients/dispatch/runners/fish-indent.js +2 -1
  73. package/extensions/dm-lens/clients/dispatch/runners/gleam-check.js +35 -12
  74. package/extensions/dm-lens/clients/dispatch/runners/go-vet.js +5 -3
  75. package/extensions/dm-lens/clients/dispatch/runners/golangci-lint.js +21 -22
  76. package/extensions/dm-lens/clients/dispatch/runners/hadolint.js +26 -16
  77. package/extensions/dm-lens/clients/dispatch/runners/helm-lint.js +3 -1
  78. package/extensions/dm-lens/clients/dispatch/runners/helm-render.js +3 -2
  79. package/extensions/dm-lens/clients/dispatch/runners/htmlhint.js +18 -9
  80. package/extensions/dm-lens/clients/dispatch/runners/javac.js +24 -23
  81. package/extensions/dm-lens/clients/dispatch/runners/ktlint.js +23 -54
  82. package/extensions/dm-lens/clients/dispatch/runners/lsp.js +17 -7
  83. package/extensions/dm-lens/clients/dispatch/runners/markdownlint.js +12 -6
  84. package/extensions/dm-lens/clients/dispatch/runners/mypy.js +2 -1
  85. package/extensions/dm-lens/clients/dispatch/runners/oxlint.js +24 -7
  86. package/extensions/dm-lens/clients/dispatch/runners/php-lint.js +26 -17
  87. package/extensions/dm-lens/clients/dispatch/runners/phpstan.js +2 -1
  88. package/extensions/dm-lens/clients/dispatch/runners/prisma-validate.js +2 -1
  89. package/extensions/dm-lens/clients/dispatch/runners/psscriptanalyzer.js +10 -11
  90. package/extensions/dm-lens/clients/dispatch/runners/pyright.js +2 -1
  91. package/extensions/dm-lens/clients/dispatch/runners/rubocop.js +22 -18
  92. package/extensions/dm-lens/clients/dispatch/runners/ruff.js +17 -22
  93. package/extensions/dm-lens/clients/dispatch/runners/rust-clippy.js +8 -11
  94. package/extensions/dm-lens/clients/dispatch/runners/shellcheck.js +9 -4
  95. package/extensions/dm-lens/clients/dispatch/runners/shfmt.js +4 -13
  96. package/extensions/dm-lens/clients/dispatch/runners/spellcheck.js +15 -5
  97. package/extensions/dm-lens/clients/dispatch/runners/spotbugs.js +2 -1
  98. package/extensions/dm-lens/clients/dispatch/runners/sqlfluff.js +10 -4
  99. package/extensions/dm-lens/clients/dispatch/runners/stylelint.js +9 -4
  100. package/extensions/dm-lens/clients/dispatch/runners/swiftlint.js +11 -3
  101. package/extensions/dm-lens/clients/dispatch/runners/taplo.js +13 -7
  102. package/extensions/dm-lens/clients/dispatch/runners/terragrunt.js +2 -1
  103. package/extensions/dm-lens/clients/dispatch/runners/tflint.js +26 -16
  104. package/extensions/dm-lens/clients/dispatch/runners/tree-sitter.js +12 -13
  105. package/extensions/dm-lens/clients/dispatch/runners/trivy-config.js +9 -3
  106. package/extensions/dm-lens/clients/dispatch/runners/utils/availability-policy.js +5 -0
  107. package/extensions/dm-lens/clients/dispatch/runners/utils/candidate-probe.js +2 -2
  108. package/extensions/dm-lens/clients/dispatch/runners/utils/diagnostic-parsers.js +8 -52
  109. package/extensions/dm-lens/clients/dispatch/runners/utils/runner-helpers.js +35 -3
  110. package/extensions/dm-lens/clients/dispatch/runners/utils.js +0 -17
  111. package/extensions/dm-lens/clients/dispatch/runners/vale.js +12 -4
  112. package/extensions/dm-lens/clients/dispatch/runners/yaml-rule-parser.js +2 -20
  113. package/extensions/dm-lens/clients/dispatch/runners/yamllint.js +16 -4
  114. package/extensions/dm-lens/clients/dispatch/runners/zig-check.js +26 -38
  115. package/extensions/dm-lens/clients/dispatch/suppress-writer.js +13 -3
  116. package/extensions/dm-lens/clients/dispatch/types.js +4 -1
  117. package/extensions/dm-lens/clients/dispatch/utils/format-utils.js +2 -2
  118. package/extensions/dm-lens/clients/disposition-logger.js +0 -6
  119. package/extensions/dm-lens/clients/disposition-publish.js +2 -1
  120. package/extensions/dm-lens/clients/effective-config.js +14 -4
  121. package/extensions/dm-lens/clients/event-loop-hold.js +123 -0
  122. package/extensions/dm-lens/clients/event-loop-monitor.js +2 -2
  123. package/extensions/dm-lens/clients/extension-log.js +6 -1
  124. package/extensions/dm-lens/clients/feature-hints.js +2 -1
  125. package/extensions/dm-lens/clients/file-kinds.js +17 -21
  126. package/extensions/dm-lens/clients/file-role.js +11 -2
  127. package/extensions/dm-lens/clients/file-time.js +1 -1
  128. package/extensions/dm-lens/clients/file-utils.js +93 -6
  129. package/extensions/dm-lens/clients/finding-delivery-gate.js +22 -26
  130. package/extensions/dm-lens/clients/format-service.js +33 -16
  131. package/extensions/dm-lens/clients/formatters.js +129 -92
  132. package/extensions/dm-lens/clients/generated-artifacts.js +2 -2
  133. package/extensions/dm-lens/clients/generation-guard.js +4 -0
  134. package/extensions/dm-lens/clients/git-guard.js +80 -26
  135. package/extensions/dm-lens/clients/gitleaks-client.js +26 -0
  136. package/extensions/dm-lens/clients/govulncheck-client.js +34 -6
  137. package/extensions/dm-lens/clients/gradle-ktfmt-style.js +96 -13
  138. package/extensions/dm-lens/clients/grammar-source.js +3 -3
  139. package/extensions/dm-lens/clients/host-edit-normalize.js +1 -1
  140. package/extensions/dm-lens/clients/indent-retarget.js +35 -5
  141. package/extensions/dm-lens/clients/inline-blocker-dispositions.js +107 -0
  142. package/extensions/dm-lens/clients/install-diagnostics.js +8 -6
  143. package/extensions/dm-lens/clients/installer/index.js +695 -183
  144. package/extensions/dm-lens/clients/installer/managed-tool-refresh.js +85 -25
  145. package/extensions/dm-lens/clients/instance-reaper-state.js +3 -0
  146. package/extensions/dm-lens/clients/instance-reaper.js +23 -16
  147. package/extensions/dm-lens/clients/instance-registry.js +34 -2
  148. package/extensions/dm-lens/clients/jscpd-client.js +11 -1
  149. package/extensions/dm-lens/clients/knip-client.js +2 -1
  150. package/extensions/dm-lens/clients/language-profile.js +18 -12
  151. package/extensions/dm-lens/clients/language-registry.js +3 -1
  152. package/extensions/dm-lens/clients/latency-logger.js +14 -4
  153. package/extensions/dm-lens/clients/ledger-bounds.js +13 -1
  154. package/extensions/dm-lens/clients/lens-config.js +74 -7
  155. package/extensions/dm-lens/clients/lens-engine.js +9 -10
  156. package/extensions/dm-lens/clients/lens-events.js +1 -1
  157. package/extensions/dm-lens/clients/lens-flag-registry.js +47 -2
  158. package/extensions/dm-lens/clients/lens-map.js +1 -1
  159. package/extensions/dm-lens/clients/log-cleanup.js +0 -28
  160. package/extensions/dm-lens/clients/lsp/cascade-tier.js +2 -1
  161. package/extensions/dm-lens/clients/lsp/client.js +70 -30
  162. package/extensions/dm-lens/clients/lsp/config.js +41 -15
  163. package/extensions/dm-lens/clients/lsp/content-limits.js +21 -0
  164. package/extensions/dm-lens/clients/lsp/diagnostic-binding.js +8 -0
  165. package/extensions/dm-lens/clients/lsp/document-drift.js +72 -1
  166. package/extensions/dm-lens/clients/lsp/edits.js +3 -3
  167. package/extensions/dm-lens/clients/lsp/index.js +435 -62
  168. package/extensions/dm-lens/clients/lsp/inferred-project.js +1 -1
  169. package/extensions/dm-lens/clients/lsp/language.js +0 -3
  170. package/extensions/dm-lens/clients/lsp/launch.js +12 -5
  171. package/extensions/dm-lens/clients/lsp/path-utils.js +0 -2
  172. package/extensions/dm-lens/clients/lsp/pending-aux-coverage.js +0 -1
  173. package/extensions/dm-lens/clients/lsp/server.js +125 -60
  174. package/extensions/dm-lens/clients/lsp/session-roots.js +26 -5
  175. package/extensions/dm-lens/clients/lsp/sync-kind.js +14 -1
  176. package/extensions/dm-lens/clients/lsp/tsserver-sync.js +5 -7
  177. package/extensions/dm-lens/clients/lsp/wait-policy/classification.js +3 -0
  178. package/extensions/dm-lens/clients/lsp/wait-policy/strategies.js +31 -2
  179. package/extensions/dm-lens/clients/lsp/workspace-diagnostics-cache.js +26 -4
  180. package/extensions/dm-lens/clients/lsp-document-symbols.js +1 -1
  181. package/extensions/dm-lens/clients/map-with-concurrency.js +7 -3
  182. package/extensions/dm-lens/clients/mcp/analyze.js +1 -1
  183. package/extensions/dm-lens/clients/mcp/ipc.js +104 -40
  184. package/extensions/dm-lens/clients/mcp/review.js +39 -7
  185. package/extensions/dm-lens/clients/mcp/session.js +38 -4
  186. package/extensions/dm-lens/clients/metrics-history.js +19 -84
  187. package/extensions/dm-lens/clients/middle-man-analysis.js +2 -4
  188. package/extensions/dm-lens/clients/module-report.js +1 -1
  189. package/extensions/dm-lens/clients/mutating-tool.js +3 -3
  190. package/extensions/dm-lens/clients/mutation-attribution.js +3 -3
  191. package/extensions/dm-lens/clients/ndjson-logger.js +17 -9
  192. package/extensions/dm-lens/clients/observed-mutation.js +42 -11
  193. package/extensions/dm-lens/clients/opaque-mutation-scan.js +17 -7
  194. package/extensions/dm-lens/clients/opengrep-client.js +92 -10
  195. package/extensions/dm-lens/clients/opengrep-config.js +1 -1
  196. package/extensions/dm-lens/clients/package-manager.js +2 -1
  197. package/extensions/dm-lens/clients/path-utils.js +199 -3
  198. package/extensions/dm-lens/clients/performance-report.js +3 -3
  199. package/extensions/dm-lens/clients/persistent-reverify.js +167 -0
  200. package/extensions/dm-lens/clients/php-cs-fixer-config.js +9 -4
  201. package/extensions/dm-lens/clients/pipeline.js +124 -68
  202. package/extensions/dm-lens/clients/project-changes.js +1 -1
  203. package/extensions/dm-lens/clients/project-diagnostics/cache.js +19 -5
  204. package/extensions/dm-lens/clients/project-diagnostics/extractors.js +1 -1
  205. package/extensions/dm-lens/clients/project-diagnostics/fresh-fetch.js +150 -18
  206. package/extensions/dm-lens/clients/project-diagnostics/runner-adapters/dead-code.js +4 -1
  207. package/extensions/dm-lens/clients/project-diagnostics/runner-adapters/gitleaks.js +3 -3
  208. package/extensions/dm-lens/clients/project-diagnostics/runner-adapters/jscpd.js +1 -1
  209. package/extensions/dm-lens/clients/project-diagnostics/runner-adapters/knip.js +1 -1
  210. package/extensions/dm-lens/clients/project-diagnostics/runner-adapters/madge.js +1 -1
  211. package/extensions/dm-lens/clients/project-diagnostics/runner-adapters/opengrep.js +1 -1
  212. package/extensions/dm-lens/clients/project-diagnostics/runner-adapters/runner-findings.js +19 -2
  213. package/extensions/dm-lens/clients/project-diagnostics/scanner.js +35 -4
  214. package/extensions/dm-lens/clients/project-lens-config.js +58 -4
  215. package/extensions/dm-lens/clients/project-scan-policy.js +0 -46
  216. package/extensions/dm-lens/clients/python-environment.js +80 -4
  217. package/extensions/dm-lens/clients/python-provenance.js +551 -0
  218. package/extensions/dm-lens/clients/read-guard-logger.js +3 -1
  219. package/extensions/dm-lens/clients/read-guard.js +29 -6
  220. package/extensions/dm-lens/clients/recent-touches.js +2 -2
  221. package/extensions/dm-lens/clients/resource-sampler.js +59 -9
  222. package/extensions/dm-lens/clients/review-graph/builder.js +39 -34
  223. package/extensions/dm-lens/clients/review-graph/git-identity.js +11 -7
  224. package/extensions/dm-lens/clients/review-graph/import-resolvers.js +1 -1
  225. package/extensions/dm-lens/clients/review-graph-logger.js +3 -1
  226. package/extensions/dm-lens/clients/ruff-client.js +11 -10
  227. package/extensions/dm-lens/clients/runtime-agent-end.js +41 -5
  228. package/extensions/dm-lens/clients/runtime-context.js +5 -2
  229. package/extensions/dm-lens/clients/runtime-coordinator.js +60 -6
  230. package/extensions/dm-lens/clients/runtime-session.js +52 -8
  231. package/extensions/dm-lens/clients/runtime-tool-call.js +27 -15
  232. package/extensions/dm-lens/clients/runtime-tool-result.js +279 -63
  233. package/extensions/dm-lens/clients/runtime-turn.js +453 -185
  234. package/extensions/dm-lens/clients/safe-spawn.js +248 -62
  235. package/extensions/dm-lens/clients/sanitize.js +0 -4
  236. package/extensions/dm-lens/clients/scan-utils.js +1 -39
  237. package/extensions/dm-lens/clients/scratch-tree-policy.js +2 -3
  238. package/extensions/dm-lens/clients/security-scan-client.js +2 -2
  239. package/extensions/dm-lens/clients/session-event-guard.js +59 -2
  240. package/extensions/dm-lens/clients/session-summary.js +0 -25
  241. package/extensions/dm-lens/clients/sg-runner.js +24 -12
  242. package/extensions/dm-lens/clients/sgconfig.js +58 -6
  243. package/extensions/dm-lens/clients/shared-checkout-guard.js +1 -1
  244. package/extensions/dm-lens/clients/situational-tool-telemetry.js +85 -0
  245. package/extensions/dm-lens/clients/skills-resolver.js +42 -0
  246. package/extensions/dm-lens/clients/smells-rollup.js +2 -2
  247. package/extensions/dm-lens/clients/spawn-output-cap.js +29 -0
  248. package/extensions/dm-lens/clients/startup-scan.js +1 -2
  249. package/extensions/dm-lens/clients/string-utils.js +3 -0
  250. package/extensions/dm-lens/clients/test-runner-client.js +118 -57
  251. package/extensions/dm-lens/clients/test-runner-delivery.js +118 -66
  252. package/extensions/dm-lens/clients/tool-agreement.js +403 -0
  253. package/extensions/dm-lens/clients/tool-config.js +218 -0
  254. package/extensions/dm-lens/clients/tool-cwd.js +248 -0
  255. package/extensions/dm-lens/clients/tool-definition.js +18 -1
  256. package/extensions/dm-lens/clients/tool-policy.js +138 -10
  257. package/extensions/dm-lens/clients/tool-probe.js +5 -0
  258. package/extensions/dm-lens/clients/tool-set-policy.js +35 -0
  259. package/extensions/dm-lens/clients/tree-sitter-client.js +201 -80
  260. package/extensions/dm-lens/clients/tree-sitter-query-loader.js +78 -130
  261. package/extensions/dm-lens/clients/trivy-client.js +8 -1
  262. package/extensions/dm-lens/clients/tui-fit.js +0 -3
  263. package/extensions/dm-lens/clients/turn-context.js +48 -0
  264. package/extensions/dm-lens/clients/turn-end/lane.js +0 -0
  265. package/extensions/dm-lens/clients/turn-end/lanes/govulncheck.js +54 -0
  266. package/extensions/dm-lens/clients/turn-end/lanes/secrets.js +140 -0
  267. package/extensions/dm-lens/clients/user-notify.js +0 -3
  268. package/extensions/dm-lens/clients/warm-attach.js +25 -0
  269. package/extensions/dm-lens/clients/widget-state.js +203 -13
  270. package/extensions/dm-lens/clients/word-index-logger.js +0 -6
  271. package/extensions/dm-lens/clients/word-index-store.js +1 -4
  272. package/extensions/dm-lens/clients/word-index.js +9 -5
  273. package/extensions/dm-lens/clients/workspace-topology.js +20 -3
  274. package/extensions/dm-lens/clients/write-ordering-guard.js +6 -0
  275. package/extensions/dm-lens/clients/zizmor-config.js +15 -11
  276. package/extensions/dm-lens/config/dependency-cruiser-eager-allowlist.json +8 -2
  277. package/extensions/dm-lens/dist/clients/actionable-warnings-logger.js +24 -0
  278. package/extensions/dm-lens/dist/clients/actionable-warnings.js +1014 -0
  279. package/extensions/dm-lens/dist/clients/advisory-provenance.js +385 -0
  280. package/extensions/dm-lens/dist/clients/agent-behavior-client.js +94 -0
  281. package/extensions/dm-lens/dist/clients/agent-nudge.js +151 -0
  282. package/extensions/dm-lens/dist/clients/analysed-root.js +1 -0
  283. package/extensions/dm-lens/dist/clients/ast-grep-client.js +576 -0
  284. package/extensions/dm-lens/dist/clients/ast-grep-rule-manager.js +114 -0
  285. package/extensions/dm-lens/dist/clients/ast-grep-tool-logger.js +85 -0
  286. package/extensions/dm-lens/dist/clients/ast-grep-types.js +1 -0
  287. package/extensions/dm-lens/dist/clients/ast-grep-yaml-synth.js +109 -0
  288. package/extensions/dm-lens/dist/clients/atomic-write-staging.js +66 -0
  289. package/extensions/dm-lens/dist/clients/atomic-write.js +37 -0
  290. package/extensions/dm-lens/dist/clients/availability-probe-flight.js +11 -0
  291. package/extensions/dm-lens/dist/clients/bash-file-access.js +830 -0
  292. package/extensions/dm-lens/dist/clients/biome-client.js +221 -0
  293. package/extensions/dm-lens/dist/clients/blocker-freshness.js +385 -0
  294. package/extensions/dm-lens/dist/clients/blocker-past-eof.js +49 -0
  295. package/extensions/dm-lens/dist/clients/bootstrap.js +246 -0
  296. package/extensions/dm-lens/dist/clients/bounded-cache.js +105 -0
  297. package/extensions/dm-lens/dist/clients/bounded-pid-file-lock.js +173 -0
  298. package/extensions/dm-lens/dist/clients/bounded-telemetry.js +104 -0
  299. package/extensions/dm-lens/dist/clients/build-identity.js +45 -0
  300. package/extensions/dm-lens/dist/clients/bundled-resource-health.js +41 -0
  301. package/extensions/dm-lens/dist/clients/bus-events-logger.js +48 -0
  302. package/extensions/dm-lens/dist/clients/bus-publish.js +117 -0
  303. package/extensions/dm-lens/dist/clients/cache/rule-cache.js +103 -0
  304. package/extensions/dm-lens/dist/clients/cache-manager.js +340 -0
  305. package/extensions/dm-lens/dist/clients/cache-observability.js +847 -0
  306. package/extensions/dm-lens/dist/clients/call-graph.js +628 -0
  307. package/extensions/dm-lens/dist/clients/cargo-manifest.js +151 -0
  308. package/extensions/dm-lens/dist/clients/cascade-budget.js +52 -0
  309. package/extensions/dm-lens/dist/clients/cascade-format.js +160 -0
  310. package/extensions/dm-lens/dist/clients/cascade-logger.js +22 -0
  311. package/extensions/dm-lens/dist/clients/cascade-types.js +1 -0
  312. package/extensions/dm-lens/dist/clients/child-unref.js +59 -0
  313. package/extensions/dm-lens/dist/clients/code-quality-warnings.js +192 -0
  314. package/extensions/dm-lens/dist/clients/codebase-model.js +110 -0
  315. package/extensions/dm-lens/dist/clients/collateral-test-role.js +8 -0
  316. package/extensions/dm-lens/dist/clients/complexity-client.js +398 -0
  317. package/extensions/dm-lens/dist/clients/config-core/deny.js +117 -0
  318. package/extensions/dm-lens/dist/clients/config-core/index.js +2 -0
  319. package/extensions/dm-lens/dist/clients/config-core/merge.js +228 -0
  320. package/extensions/dm-lens/dist/clients/config-core/normalize.js +217 -0
  321. package/extensions/dm-lens/dist/clients/config-core/process-spec.js +147 -0
  322. package/extensions/dm-lens/dist/clients/config-core/provenance.js +54 -0
  323. package/extensions/dm-lens/dist/clients/config-core/records.js +79 -0
  324. package/extensions/dm-lens/dist/clients/config-core/resolve.js +48 -0
  325. package/extensions/dm-lens/dist/clients/config-core/safe-object.js +22 -0
  326. package/extensions/dm-lens/dist/clients/config-core/schema.js +90 -0
  327. package/extensions/dm-lens/dist/clients/config-diagnostic-codes.js +90 -0
  328. package/extensions/dm-lens/dist/clients/config-locations.js +175 -0
  329. package/extensions/dm-lens/dist/clients/config-resolve.js +359 -0
  330. package/extensions/dm-lens/dist/clients/config-schema.js +128 -0
  331. package/extensions/dm-lens/dist/clients/config-warn.js +146 -0
  332. package/extensions/dm-lens/dist/clients/console-guard-install.js +4 -0
  333. package/extensions/dm-lens/dist/clients/cooperative-budget.js +36 -0
  334. package/extensions/dm-lens/dist/clients/dead-code-client.js +342 -0
  335. package/extensions/dm-lens/dist/clients/dead-code-logger.js +18 -0
  336. package/extensions/dm-lens/dist/clients/deadline-utils.js +117 -0
  337. package/extensions/dm-lens/dist/clients/debug-handles.js +121 -0
  338. package/extensions/dm-lens/dist/clients/debug-heap.js +80 -0
  339. package/extensions/dm-lens/dist/clients/deferred-lsp-work.js +33 -0
  340. package/extensions/dm-lens/dist/clients/degradation-ledger.js +272 -0
  341. package/extensions/dm-lens/dist/clients/demoted-finding-render.js +61 -0
  342. package/extensions/dm-lens/dist/clients/dependency-checker.js +525 -0
  343. package/extensions/dm-lens/dist/clients/deps/ast-grep-napi.js +17 -0
  344. package/extensions/dm-lens/dist/clients/deps/dm-host-deps.js +10 -0
  345. package/extensions/dm-lens/dist/clients/deps/dm-tui-compat.js +58 -0
  346. package/extensions/dm-lens/dist/clients/deps/js-yaml.js +7 -0
  347. package/extensions/dm-lens/dist/clients/deps/minimatch.js +6 -0
  348. package/extensions/dm-lens/dist/clients/deps/pidusage.js +5 -0
  349. package/extensions/dm-lens/dist/clients/deps/typebox.js +5 -0
  350. package/extensions/dm-lens/dist/clients/deps/vscode-jsonrpc.js +8 -0
  351. package/extensions/dm-lens/dist/clients/deps/web-tree-sitter.js +11 -0
  352. package/extensions/dm-lens/dist/clients/diagnostic-dispositions.js +318 -0
  353. package/extensions/dm-lens/dist/clients/diagnostic-line-freshness.js +144 -0
  354. package/extensions/dm-lens/dist/clients/diagnostic-logger.js +60 -0
  355. package/extensions/dm-lens/dist/clients/diagnostic-tracker.js +89 -0
  356. package/extensions/dm-lens/dist/clients/diagnostics-publish.js +134 -0
  357. package/extensions/dm-lens/dist/clients/dispatch/ast-grep-catalog.js +16 -0
  358. package/extensions/dm-lens/dist/clients/dispatch/auxiliary-lsp.js +158 -0
  359. package/extensions/dm-lens/dist/clients/dispatch/collect-later-tier.js +25 -0
  360. package/extensions/dm-lens/dist/clients/dispatch/diagnostic-taxonomy.js +58 -0
  361. package/extensions/dm-lens/dist/clients/dispatch/dispatcher.js +953 -0
  362. package/extensions/dm-lens/dist/clients/dispatch/fact-provider-types.js +1 -0
  363. package/extensions/dm-lens/dist/clients/dispatch/fact-rule-runner.js +17 -0
  364. package/extensions/dm-lens/dist/clients/dispatch/fact-runner.js +18 -0
  365. package/extensions/dm-lens/dist/clients/dispatch/fact-scheduler.js +52 -0
  366. package/extensions/dm-lens/dist/clients/dispatch/fact-store.js +199 -0
  367. package/extensions/dm-lens/dist/clients/dispatch/facts/comment-facts.js +20 -0
  368. package/extensions/dm-lens/dist/clients/dispatch/facts/file-content.js +18 -0
  369. package/extensions/dm-lens/dist/clients/dispatch/facts/function-facts.js +281 -0
  370. package/extensions/dm-lens/dist/clients/dispatch/facts/import-facts.js +169 -0
  371. package/extensions/dm-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +19 -0
  372. package/extensions/dm-lens/dist/clients/dispatch/facts/try-catch-facts.js +95 -0
  373. package/extensions/dm-lens/dist/clients/dispatch/finding-policy.js +159 -0
  374. package/extensions/dm-lens/dist/clients/dispatch/indent-detect.js +227 -0
  375. package/extensions/dm-lens/dist/clients/dispatch/inline-suppressions.js +52 -0
  376. package/extensions/dm-lens/dist/clients/dispatch/integration.js +1463 -0
  377. package/extensions/dm-lens/dist/clients/dispatch/lazy.js +8 -0
  378. package/extensions/dm-lens/dist/clients/dispatch/pending-runner-findings.js +75 -0
  379. package/extensions/dm-lens/dist/clients/dispatch/plan.js +278 -0
  380. package/extensions/dm-lens/dist/clients/dispatch/priorities.js +14 -0
  381. package/extensions/dm-lens/dist/clients/dispatch/rule-id-normalize.js +42 -0
  382. package/extensions/dm-lens/dist/clients/dispatch/rule-ignores.js +21 -0
  383. package/extensions/dm-lens/dist/clients/dispatch/rule-policy.js +73 -0
  384. package/extensions/dm-lens/dist/clients/dispatch/rules/async-noise.js +31 -0
  385. package/extensions/dm-lens/dist/clients/dispatch/rules/async-unnecessary-wrapper.js +27 -0
  386. package/extensions/dm-lens/dist/clients/dispatch/rules/cors-wildcard.js +35 -0
  387. package/extensions/dm-lens/dist/clients/dispatch/rules/error-obscuring.js +29 -0
  388. package/extensions/dm-lens/dist/clients/dispatch/rules/error-swallowing.js +29 -0
  389. package/extensions/dm-lens/dist/clients/dispatch/rules/framework-call-noise.js +31 -0
  390. package/extensions/dm-lens/dist/clients/dispatch/rules/high-complexity.js +56 -0
  391. package/extensions/dm-lens/dist/clients/dispatch/rules/high-fan-out.js +45 -0
  392. package/extensions/dm-lens/dist/clients/dispatch/rules/high-import-coupling.js +31 -0
  393. package/extensions/dm-lens/dist/clients/dispatch/rules/missing-error-propagation.js +45 -0
  394. package/extensions/dm-lens/dist/clients/dispatch/rules/no-commented-credentials.js +46 -0
  395. package/extensions/dm-lens/dist/clients/dispatch/rules/pass-through-wrappers.js +38 -0
  396. package/extensions/dm-lens/dist/clients/dispatch/rules/placeholder-comments.js +40 -0
  397. package/extensions/dm-lens/dist/clients/dispatch/rules/unsafe-boundary.js +101 -0
  398. package/extensions/dm-lens/dist/clients/dispatch/runner-context.js +46 -0
  399. package/extensions/dm-lens/dist/clients/dispatch/runners/actionlint.js +98 -0
  400. package/extensions/dm-lens/dist/clients/dispatch/runners/ast-grep-napi.js +690 -0
  401. package/extensions/dm-lens/dist/clients/dispatch/runners/biome-check.js +202 -0
  402. package/extensions/dm-lens/dist/clients/dispatch/runners/cpp-check.js +190 -0
  403. package/extensions/dm-lens/dist/clients/dispatch/runners/credo.js +66 -0
  404. package/extensions/dm-lens/dist/clients/dispatch/runners/cue-vet.js +167 -0
  405. package/extensions/dm-lens/dist/clients/dispatch/runners/dart-analyze.js +151 -0
  406. package/extensions/dm-lens/dist/clients/dispatch/runners/detekt.js +157 -0
  407. package/extensions/dm-lens/dist/clients/dispatch/runners/dotnet-build.js +157 -0
  408. package/extensions/dm-lens/dist/clients/dispatch/runners/elixir-check.js +153 -0
  409. package/extensions/dm-lens/dist/clients/dispatch/runners/eslint.js +99 -0
  410. package/extensions/dm-lens/dist/clients/dispatch/runners/fact-rules.js +20 -0
  411. package/extensions/dm-lens/dist/clients/dispatch/runners/fish-indent.js +65 -0
  412. package/extensions/dm-lens/dist/clients/dispatch/runners/gleam-check.js +107 -0
  413. package/extensions/dm-lens/dist/clients/dispatch/runners/go-vet.js +51 -0
  414. package/extensions/dm-lens/dist/clients/dispatch/runners/golangci-lint.js +89 -0
  415. package/extensions/dm-lens/dist/clients/dispatch/runners/hadolint.js +83 -0
  416. package/extensions/dm-lens/dist/clients/dispatch/runners/helm-lint.js +144 -0
  417. package/extensions/dm-lens/dist/clients/dispatch/runners/helm-render.js +593 -0
  418. package/extensions/dm-lens/dist/clients/dispatch/runners/htmlhint.js +103 -0
  419. package/extensions/dm-lens/dist/clients/dispatch/runners/index.js +100 -0
  420. package/extensions/dm-lens/dist/clients/dispatch/runners/javac.js +82 -0
  421. package/extensions/dm-lens/dist/clients/dispatch/runners/ktlint.js +93 -0
  422. package/extensions/dm-lens/dist/clients/dispatch/runners/lsp.js +206 -0
  423. package/extensions/dm-lens/dist/clients/dispatch/runners/markdownlint.js +129 -0
  424. package/extensions/dm-lens/dist/clients/dispatch/runners/mypy.js +70 -0
  425. package/extensions/dm-lens/dist/clients/dispatch/runners/oxlint.js +305 -0
  426. package/extensions/dm-lens/dist/clients/dispatch/runners/php-lint.js +70 -0
  427. package/extensions/dm-lens/dist/clients/dispatch/runners/phpstan.js +90 -0
  428. package/extensions/dm-lens/dist/clients/dispatch/runners/prisma-validate.js +56 -0
  429. package/extensions/dm-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +307 -0
  430. package/extensions/dm-lens/dist/clients/dispatch/runners/pyright.js +118 -0
  431. package/extensions/dm-lens/dist/clients/dispatch/runners/rubocop.js +79 -0
  432. package/extensions/dm-lens/dist/clients/dispatch/runners/ruff.js +69 -0
  433. package/extensions/dm-lens/dist/clients/dispatch/runners/rust-clippy.js +147 -0
  434. package/extensions/dm-lens/dist/clients/dispatch/runners/shellcheck.js +111 -0
  435. package/extensions/dm-lens/dist/clients/dispatch/runners/shfmt.js +81 -0
  436. package/extensions/dm-lens/dist/clients/dispatch/runners/spellcheck.js +74 -0
  437. package/extensions/dm-lens/dist/clients/dispatch/runners/spotbugs.js +208 -0
  438. package/extensions/dm-lens/dist/clients/dispatch/runners/sqlfluff.js +136 -0
  439. package/extensions/dm-lens/dist/clients/dispatch/runners/stylelint.js +118 -0
  440. package/extensions/dm-lens/dist/clients/dispatch/runners/swiftlint.js +143 -0
  441. package/extensions/dm-lens/dist/clients/dispatch/runners/taplo.js +97 -0
  442. package/extensions/dm-lens/dist/clients/dispatch/runners/terragrunt.js +110 -0
  443. package/extensions/dm-lens/dist/clients/dispatch/runners/tflint.js +104 -0
  444. package/extensions/dm-lens/dist/clients/dispatch/runners/tree-sitter.js +516 -0
  445. package/extensions/dm-lens/dist/clients/dispatch/runners/trivy-config.js +155 -0
  446. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/availability-policy.js +285 -0
  447. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +61 -0
  448. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +61 -0
  449. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +130 -0
  450. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +1067 -0
  451. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +40 -0
  452. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/tool-failure.js +117 -0
  453. package/extensions/dm-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +88 -0
  454. package/extensions/dm-lens/dist/clients/dispatch/runners/utils.js +8 -0
  455. package/extensions/dm-lens/dist/clients/dispatch/runners/vale.js +106 -0
  456. package/extensions/dm-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +163 -0
  457. package/extensions/dm-lens/dist/clients/dispatch/runners/yamllint.js +83 -0
  458. package/extensions/dm-lens/dist/clients/dispatch/runners/zig-check.js +72 -0
  459. package/extensions/dm-lens/dist/clients/dispatch/suppress-writer.js +57 -0
  460. package/extensions/dm-lens/dist/clients/dispatch/tool-profile.js +30 -0
  461. package/extensions/dm-lens/dist/clients/dispatch/types.js +7 -0
  462. package/extensions/dm-lens/dist/clients/dispatch/utils/format-utils.js +45 -0
  463. package/extensions/dm-lens/dist/clients/dispatch/utils/lsp-diagnostics.js +31 -0
  464. package/extensions/dm-lens/dist/clients/disposition-logger.js +19 -0
  465. package/extensions/dm-lens/dist/clients/disposition-publish.js +88 -0
  466. package/extensions/dm-lens/dist/clients/durable-store.js +77 -0
  467. package/extensions/dm-lens/dist/clients/effective-config.js +160 -0
  468. package/extensions/dm-lens/dist/clients/env-utils.js +26 -0
  469. package/extensions/dm-lens/dist/clients/error-class.js +3 -0
  470. package/extensions/dm-lens/dist/clients/eval-timestamp.js +2 -0
  471. package/extensions/dm-lens/dist/clients/event-loop-hold.js +119 -0
  472. package/extensions/dm-lens/dist/clients/event-loop-monitor.js +84 -0
  473. package/extensions/dm-lens/dist/clients/extension-log.js +230 -0
  474. package/extensions/dm-lens/dist/clients/extension-mode.js +20 -0
  475. package/extensions/dm-lens/dist/clients/feature-hints.js +88 -0
  476. package/extensions/dm-lens/dist/clients/file-kinds.js +304 -0
  477. package/extensions/dm-lens/dist/clients/file-role.js +50 -0
  478. package/extensions/dm-lens/dist/clients/file-time.js +112 -0
  479. package/extensions/dm-lens/dist/clients/file-utils.js +630 -0
  480. package/extensions/dm-lens/dist/clients/finding-delivery-gate.js +129 -0
  481. package/extensions/dm-lens/dist/clients/finding-identity.js +23 -0
  482. package/extensions/dm-lens/dist/clients/fix-worklog.js +75 -0
  483. package/extensions/dm-lens/dist/clients/format-events-publish.js +230 -0
  484. package/extensions/dm-lens/dist/clients/format-service.js +154 -0
  485. package/extensions/dm-lens/dist/clients/formatters-lazy.js +8 -0
  486. package/extensions/dm-lens/dist/clients/formatters.js +1351 -0
  487. package/extensions/dm-lens/dist/clients/freshness-cadence.js +1 -0
  488. package/extensions/dm-lens/dist/clients/freshness.js +8 -0
  489. package/extensions/dm-lens/dist/clients/generated-artifacts.js +209 -0
  490. package/extensions/dm-lens/dist/clients/generation-guard.js +105 -0
  491. package/extensions/dm-lens/dist/clients/git-guard.js +770 -0
  492. package/extensions/dm-lens/dist/clients/git-tracked-ignore.js +120 -0
  493. package/extensions/dm-lens/dist/clients/gitleaks-client.js +282 -0
  494. package/extensions/dm-lens/dist/clients/go-client.js +47 -0
  495. package/extensions/dm-lens/dist/clients/govulncheck-client.js +411 -0
  496. package/extensions/dm-lens/dist/clients/gradle-ktfmt-style.js +177 -0
  497. package/extensions/dm-lens/dist/clients/grammar-source.js +224 -0
  498. package/extensions/dm-lens/dist/clients/gzip-stage-write.js +85 -0
  499. package/extensions/dm-lens/dist/clients/hashline-anchor.js +222 -0
  500. package/extensions/dm-lens/dist/clients/hook-budgets.js +16 -0
  501. package/extensions/dm-lens/dist/clients/host-edit-normalize.js +87 -0
  502. package/extensions/dm-lens/dist/clients/host-ports.js +35 -0
  503. package/extensions/dm-lens/dist/clients/indent-retarget.js +102 -0
  504. package/extensions/dm-lens/dist/clients/inline-blocker-dispositions.js +103 -0
  505. package/extensions/dm-lens/dist/clients/install-diagnostics.js +123 -0
  506. package/extensions/dm-lens/dist/clients/installer/index.js +3770 -0
  507. package/extensions/dm-lens/dist/clients/installer/managed-tool-refresh-session.js +17 -0
  508. package/extensions/dm-lens/dist/clients/installer/managed-tool-refresh.js +522 -0
  509. package/extensions/dm-lens/dist/clients/instance-reaper-state.js +3 -0
  510. package/extensions/dm-lens/dist/clients/instance-reaper.js +577 -0
  511. package/extensions/dm-lens/dist/clients/instance-registry-lock.js +131 -0
  512. package/extensions/dm-lens/dist/clients/instance-registry.js +452 -0
  513. package/extensions/dm-lens/dist/clients/jscpd-client.js +252 -0
  514. package/extensions/dm-lens/dist/clients/json-cache-read.js +25 -0
  515. package/extensions/dm-lens/dist/clients/knip-client.js +472 -0
  516. package/extensions/dm-lens/dist/clients/language-policy.js +242 -0
  517. package/extensions/dm-lens/dist/clients/language-profile.js +261 -0
  518. package/extensions/dm-lens/dist/clients/language-registry.js +424 -0
  519. package/extensions/dm-lens/dist/clients/latency-logger.js +192 -0
  520. package/extensions/dm-lens/dist/clients/lazy-import.js +14 -0
  521. package/extensions/dm-lens/dist/clients/ledger-bounds.js +21 -0
  522. package/extensions/dm-lens/dist/clients/lens-config.js +237 -0
  523. package/extensions/dm-lens/dist/clients/lens-engine.js +181 -0
  524. package/extensions/dm-lens/dist/clients/lens-events.js +105 -0
  525. package/extensions/dm-lens/dist/clients/lens-flag-registry.js +302 -0
  526. package/extensions/dm-lens/dist/clients/lens-map.js +980 -0
  527. package/extensions/dm-lens/dist/clients/live-bus-emitter.js +53 -0
  528. package/extensions/dm-lens/dist/clients/log-cleanup.js +138 -0
  529. package/extensions/dm-lens/dist/clients/lsp/aggregation.js +88 -0
  530. package/extensions/dm-lens/dist/clients/lsp/cascade-tier.js +203 -0
  531. package/extensions/dm-lens/dist/clients/lsp/client.js +2709 -0
  532. package/extensions/dm-lens/dist/clients/lsp/config.js +252 -0
  533. package/extensions/dm-lens/dist/clients/lsp/content-limits.js +21 -0
  534. package/extensions/dm-lens/dist/clients/lsp/diagnostic-binding.js +79 -0
  535. package/extensions/dm-lens/dist/clients/lsp/document-drift.js +312 -0
  536. package/extensions/dm-lens/dist/clients/lsp/edits.js +930 -0
  537. package/extensions/dm-lens/dist/clients/lsp/index.js +4448 -0
  538. package/extensions/dm-lens/dist/clients/lsp/inferred-project.js +121 -0
  539. package/extensions/dm-lens/dist/clients/lsp/jvm-runtime.js +194 -0
  540. package/extensions/dm-lens/dist/clients/lsp/language.js +23 -0
  541. package/extensions/dm-lens/dist/clients/lsp/launch.js +468 -0
  542. package/extensions/dm-lens/dist/clients/lsp/lombok.js +146 -0
  543. package/extensions/dm-lens/dist/clients/lsp/path-utils.js +1 -0
  544. package/extensions/dm-lens/dist/clients/lsp/pending-aux-coverage.js +90 -0
  545. package/extensions/dm-lens/dist/clients/lsp/position-encoding.js +28 -0
  546. package/extensions/dm-lens/dist/clients/lsp/ruby-drive-dirs.js +42 -0
  547. package/extensions/dm-lens/dist/clients/lsp/server.js +2422 -0
  548. package/extensions/dm-lens/dist/clients/lsp/session-roots.js +52 -0
  549. package/extensions/dm-lens/dist/clients/lsp/spawn-history.js +15 -0
  550. package/extensions/dm-lens/dist/clients/lsp/sync-kind.js +30 -0
  551. package/extensions/dm-lens/dist/clients/lsp/tsserver-sync.js +156 -0
  552. package/extensions/dm-lens/dist/clients/lsp/wait-policy/capability-snapshot.js +1 -0
  553. package/extensions/dm-lens/dist/clients/lsp/wait-policy/classification.js +36 -0
  554. package/extensions/dm-lens/dist/clients/lsp/wait-policy/index.js +3 -0
  555. package/extensions/dm-lens/dist/clients/lsp/wait-policy/strategies.js +175 -0
  556. package/extensions/dm-lens/dist/clients/lsp/watch-queue.js +40 -0
  557. package/extensions/dm-lens/dist/clients/lsp/workspace-diagnostics-cache.js +267 -0
  558. package/extensions/dm-lens/dist/clients/lsp/workspace-diagnostics-session.js +7 -0
  559. package/extensions/dm-lens/dist/clients/lsp/workspace-sweep-hold.js +93 -0
  560. package/extensions/dm-lens/dist/clients/lsp-budget.js +83 -0
  561. package/extensions/dm-lens/dist/clients/lsp-document-symbols.js +93 -0
  562. package/extensions/dm-lens/dist/clients/lsp-lazy.js +8 -0
  563. package/extensions/dm-lens/dist/clients/lsp-mutation.js +273 -0
  564. package/extensions/dm-lens/dist/clients/lsp-status.js +37 -0
  565. package/extensions/dm-lens/dist/clients/map-with-concurrency.js +20 -0
  566. package/extensions/dm-lens/dist/clients/mcp/analyze.js +251 -0
  567. package/extensions/dm-lens/dist/clients/mcp/host-shim.js +14 -0
  568. package/extensions/dm-lens/dist/clients/mcp/ipc.js +302 -0
  569. package/extensions/dm-lens/dist/clients/mcp/review.js +137 -0
  570. package/extensions/dm-lens/dist/clients/mcp/session.js +305 -0
  571. package/extensions/dm-lens/dist/clients/memory-sampler.js +107 -0
  572. package/extensions/dm-lens/dist/clients/message-end-attribution.js +18 -0
  573. package/extensions/dm-lens/dist/clients/metrics-client.js +80 -0
  574. package/extensions/dm-lens/dist/clients/metrics-history.js +255 -0
  575. package/extensions/dm-lens/dist/clients/middle-man-analysis.js +161 -0
  576. package/extensions/dm-lens/dist/clients/model-provider.js +31 -0
  577. package/extensions/dm-lens/dist/clients/module-report-lsp.js +140 -0
  578. package/extensions/dm-lens/dist/clients/module-report.js +1642 -0
  579. package/extensions/dm-lens/dist/clients/mutating-tool.js +377 -0
  580. package/extensions/dm-lens/dist/clients/mutation-attribution.js +168 -0
  581. package/extensions/dm-lens/dist/clients/mutation-bridge.js +102 -0
  582. package/extensions/dm-lens/dist/clients/ndjson-logger.js +471 -0
  583. package/extensions/dm-lens/dist/clients/observed-mutation-sources.js +43 -0
  584. package/extensions/dm-lens/dist/clients/observed-mutation.js +709 -0
  585. package/extensions/dm-lens/dist/clients/oldtext-autopatch.js +100 -0
  586. package/extensions/dm-lens/dist/clients/opaque-mutation-scan.js +313 -0
  587. package/extensions/dm-lens/dist/clients/opengrep-client.js +216 -0
  588. package/extensions/dm-lens/dist/clients/opengrep-config.js +52 -0
  589. package/extensions/dm-lens/dist/clients/package-manager.js +303 -0
  590. package/extensions/dm-lens/dist/clients/package-root.js +25 -0
  591. package/extensions/dm-lens/dist/clients/partial-edit-apply.js +337 -0
  592. package/extensions/dm-lens/dist/clients/path-attribution-telemetry.js +31 -0
  593. package/extensions/dm-lens/dist/clients/path-keyed-map.js +57 -0
  594. package/extensions/dm-lens/dist/clients/path-utils.js +476 -0
  595. package/extensions/dm-lens/dist/clients/performance-report.js +291 -0
  596. package/extensions/dm-lens/dist/clients/persist-debounce.js +37 -0
  597. package/extensions/dm-lens/dist/clients/persistent-reverify.js +165 -0
  598. package/extensions/dm-lens/dist/clients/php-cs-fixer-config.js +15 -0
  599. package/extensions/dm-lens/dist/clients/pipeline.js +1091 -0
  600. package/extensions/dm-lens/dist/clients/post-autofix-notice.js +19 -0
  601. package/extensions/dm-lens/dist/clients/print-mode.js +3 -0
  602. package/extensions/dm-lens/dist/clients/probe-home-state.js +62 -0
  603. package/extensions/dm-lens/dist/clients/process-bridge.js +20 -0
  604. package/extensions/dm-lens/dist/clients/process-singletons.js +69 -0
  605. package/extensions/dm-lens/dist/clients/process-snapshot.js +22 -0
  606. package/extensions/dm-lens/dist/clients/project-changes.js +112 -0
  607. package/extensions/dm-lens/dist/clients/project-conventions.js +152 -0
  608. package/extensions/dm-lens/dist/clients/project-diagnostics/cache.js +91 -0
  609. package/extensions/dm-lens/dist/clients/project-diagnostics/extractors.js +53 -0
  610. package/extensions/dm-lens/dist/clients/project-diagnostics/fresh-fetch.js +408 -0
  611. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +64 -0
  612. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/dead-code.js +38 -0
  613. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +21 -0
  614. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/govulncheck.js +23 -0
  615. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +28 -0
  616. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +45 -0
  617. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +34 -0
  618. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +32 -0
  619. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +79 -0
  620. package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +41 -0
  621. package/extensions/dm-lens/dist/clients/project-diagnostics/scanner.js +349 -0
  622. package/extensions/dm-lens/dist/clients/project-diagnostics/types.js +1 -0
  623. package/extensions/dm-lens/dist/clients/project-lens-config.js +437 -0
  624. package/extensions/dm-lens/dist/clients/project-report.js +527 -0
  625. package/extensions/dm-lens/dist/clients/project-scale.js +60 -0
  626. package/extensions/dm-lens/dist/clients/project-scan-policy.js +4 -0
  627. package/extensions/dm-lens/dist/clients/project-snapshot-fingerprint.js +39 -0
  628. package/extensions/dm-lens/dist/clients/project-snapshot-persist-worker.js +14 -0
  629. package/extensions/dm-lens/dist/clients/project-snapshot.js +1178 -0
  630. package/extensions/dm-lens/dist/clients/project-trust.js +104 -0
  631. package/extensions/dm-lens/dist/clients/python-environment.js +120 -0
  632. package/extensions/dm-lens/dist/clients/python-provenance.js +551 -0
  633. package/extensions/dm-lens/dist/clients/quiet-window-config.js +16 -0
  634. package/extensions/dm-lens/dist/clients/quiet-window.js +126 -0
  635. package/extensions/dm-lens/dist/clients/read-bridge.js +46 -0
  636. package/extensions/dm-lens/dist/clients/read-expansion.js +218 -0
  637. package/extensions/dm-lens/dist/clients/read-guard-logger.js +178 -0
  638. package/extensions/dm-lens/dist/clients/read-guard-tool-lines.js +1042 -0
  639. package/extensions/dm-lens/dist/clients/read-guard.js +1096 -0
  640. package/extensions/dm-lens/dist/clients/recent-touches.js +126 -0
  641. package/extensions/dm-lens/dist/clients/redact/secrets.js +386 -0
  642. package/extensions/dm-lens/dist/clients/resource-sampler.js +344 -0
  643. package/extensions/dm-lens/dist/clients/reverse-deps.js +227 -0
  644. package/extensions/dm-lens/dist/clients/review-graph/builder.js +3635 -0
  645. package/extensions/dm-lens/dist/clients/review-graph/format.js +30 -0
  646. package/extensions/dm-lens/dist/clients/review-graph/git-identity.js +115 -0
  647. package/extensions/dm-lens/dist/clients/review-graph/import-resolvers.js +325 -0
  648. package/extensions/dm-lens/dist/clients/review-graph/persist-worker.js +8 -0
  649. package/extensions/dm-lens/dist/clients/review-graph/query.js +195 -0
  650. package/extensions/dm-lens/dist/clients/review-graph/revision-drift.js +3 -0
  651. package/extensions/dm-lens/dist/clients/review-graph/service.js +60 -0
  652. package/extensions/dm-lens/dist/clients/review-graph/shared-extraction-ir.js +54 -0
  653. package/extensions/dm-lens/dist/clients/review-graph/symbol-id.js +29 -0
  654. package/extensions/dm-lens/dist/clients/review-graph/tsconfig-paths.js +253 -0
  655. package/extensions/dm-lens/dist/clients/review-graph/types.js +1 -0
  656. package/extensions/dm-lens/dist/clients/review-graph/workspace-modules.js +374 -0
  657. package/extensions/dm-lens/dist/clients/review-graph-logger.js +51 -0
  658. package/extensions/dm-lens/dist/clients/ruff-client.js +109 -0
  659. package/extensions/dm-lens/dist/clients/rules-scanner.js +113 -0
  660. package/extensions/dm-lens/dist/clients/run-duration.js +9 -0
  661. package/extensions/dm-lens/dist/clients/runtime-agent-end.js +608 -0
  662. package/extensions/dm-lens/dist/clients/runtime-config.js +39 -0
  663. package/extensions/dm-lens/dist/clients/runtime-context.js +155 -0
  664. package/extensions/dm-lens/dist/clients/runtime-coordinator.js +817 -0
  665. package/extensions/dm-lens/dist/clients/runtime-session.js +1625 -0
  666. package/extensions/dm-lens/dist/clients/runtime-tool-call.js +868 -0
  667. package/extensions/dm-lens/dist/clients/runtime-tool-result.js +1317 -0
  668. package/extensions/dm-lens/dist/clients/runtime-turn.js +2333 -0
  669. package/extensions/dm-lens/dist/clients/rust-client.js +48 -0
  670. package/extensions/dm-lens/dist/clients/safe-spawn.js +1267 -0
  671. package/extensions/dm-lens/dist/clients/sanitize.js +174 -0
  672. package/extensions/dm-lens/dist/clients/scan-utils.js +7 -0
  673. package/extensions/dm-lens/dist/clients/scratch-tree-policy.js +57 -0
  674. package/extensions/dm-lens/dist/clients/search-read-registration.js +59 -0
  675. package/extensions/dm-lens/dist/clients/secret-findings.js +69 -0
  676. package/extensions/dm-lens/dist/clients/security-scan-client.js +208 -0
  677. package/extensions/dm-lens/dist/clients/session-event-guard.js +111 -0
  678. package/extensions/dm-lens/dist/clients/session-lifecycle.js +173 -0
  679. package/extensions/dm-lens/dist/clients/session-start-observability.js +47 -0
  680. package/extensions/dm-lens/dist/clients/session-state-store.js +63 -0
  681. package/extensions/dm-lens/dist/clients/session-summary.js +8 -0
  682. package/extensions/dm-lens/dist/clients/sessionstart-logger.js +22 -0
  683. package/extensions/dm-lens/dist/clients/sg-runner.js +627 -0
  684. package/extensions/dm-lens/dist/clients/sgconfig.js +280 -0
  685. package/extensions/dm-lens/dist/clients/shared-checkout-guard.js +177 -0
  686. package/extensions/dm-lens/dist/clients/single-flight.js +52 -0
  687. package/extensions/dm-lens/dist/clients/situational-tool-telemetry.js +85 -0
  688. package/extensions/dm-lens/dist/clients/skills-resolver.js +38 -0
  689. package/extensions/dm-lens/dist/clients/slow-fs.js +77 -0
  690. package/extensions/dm-lens/dist/clients/smells-rollup.js +118 -0
  691. package/extensions/dm-lens/dist/clients/source-filter.js +320 -0
  692. package/extensions/dm-lens/dist/clients/source-walker.js +96 -0
  693. package/extensions/dm-lens/dist/clients/spawn-output-cap.js +35 -0
  694. package/extensions/dm-lens/dist/clients/spawn-timeout-cooldown.js +51 -0
  695. package/extensions/dm-lens/dist/clients/stale-marker.js +1 -0
  696. package/extensions/dm-lens/dist/clients/startup-scan.js +222 -0
  697. package/extensions/dm-lens/dist/clients/startup-timing.js +67 -0
  698. package/extensions/dm-lens/dist/clients/string-utils.js +6 -0
  699. package/extensions/dm-lens/dist/clients/subagent-mode.js +40 -0
  700. package/extensions/dm-lens/dist/clients/symbol-containment.js +16 -0
  701. package/extensions/dm-lens/dist/clients/symbol-types.js +1 -0
  702. package/extensions/dm-lens/dist/clients/test-runner-client.js +1504 -0
  703. package/extensions/dm-lens/dist/clients/test-runner-delivery.js +260 -0
  704. package/extensions/dm-lens/dist/clients/todo-scanner.js +164 -0
  705. package/extensions/dm-lens/dist/clients/tool-agreement.js +377 -0
  706. package/extensions/dm-lens/dist/clients/tool-config.js +218 -0
  707. package/extensions/dm-lens/dist/clients/tool-cwd.js +235 -0
  708. package/extensions/dm-lens/dist/clients/tool-definition.js +22 -0
  709. package/extensions/dm-lens/dist/clients/tool-event.js +18 -0
  710. package/extensions/dm-lens/dist/clients/tool-policy.js +2393 -0
  711. package/extensions/dm-lens/dist/clients/tool-probe.js +5 -0
  712. package/extensions/dm-lens/dist/clients/tool-render.js +79 -0
  713. package/extensions/dm-lens/dist/clients/tool-set-policy.js +70 -0
  714. package/extensions/dm-lens/dist/clients/tree-sitter-cache.js +206 -0
  715. package/extensions/dm-lens/dist/clients/tree-sitter-client.js +2787 -0
  716. package/extensions/dm-lens/dist/clients/tree-sitter-logger.js +77 -0
  717. package/extensions/dm-lens/dist/clients/tree-sitter-navigator.js +198 -0
  718. package/extensions/dm-lens/dist/clients/tree-sitter-query-loader.js +318 -0
  719. package/extensions/dm-lens/dist/clients/tree-sitter-shared.js +87 -0
  720. package/extensions/dm-lens/dist/clients/tree-sitter-symbol-extractor.js +957 -0
  721. package/extensions/dm-lens/dist/clients/trivy-client.js +305 -0
  722. package/extensions/dm-lens/dist/clients/tui-fit.js +26 -0
  723. package/extensions/dm-lens/dist/clients/turn-context.js +48 -0
  724. package/extensions/dm-lens/dist/clients/turn-end/lane.js +1 -0
  725. package/extensions/dm-lens/dist/clients/turn-end/lanes/govulncheck.js +54 -0
  726. package/extensions/dm-lens/dist/clients/turn-end/lanes/secrets.js +131 -0
  727. package/extensions/dm-lens/dist/clients/turn-summary-render.js +56 -0
  728. package/extensions/dm-lens/dist/clients/turn-summary.js +84 -0
  729. package/extensions/dm-lens/dist/clients/typos-config.js +9 -0
  730. package/extensions/dm-lens/dist/clients/user-notify.js +15 -0
  731. package/extensions/dm-lens/dist/clients/vanished-instance-marker.js +18 -0
  732. package/extensions/dm-lens/dist/clients/warm-attach.js +210 -0
  733. package/extensions/dm-lens/dist/clients/widget-state.js +963 -0
  734. package/extensions/dm-lens/dist/clients/word-index-logger.js +21 -0
  735. package/extensions/dm-lens/dist/clients/word-index-store.js +222 -0
  736. package/extensions/dm-lens/dist/clients/word-index.js +1426 -0
  737. package/extensions/dm-lens/dist/clients/workspace-topology.js +261 -0
  738. package/extensions/dm-lens/dist/clients/write-ordering-guard.js +24 -0
  739. package/extensions/dm-lens/dist/clients/zizmor-config.js +160 -0
  740. package/extensions/dm-lens/dist/i18n.js +67 -0
  741. package/extensions/dm-lens/dist/index.js +1782 -0
  742. package/extensions/dm-lens/dist/mcp/analyze-cli.js +178 -0
  743. package/extensions/dm-lens/dist/mcp/build-staleness.js +55 -0
  744. package/extensions/dm-lens/dist/mcp/cli.js +78 -0
  745. package/extensions/dm-lens/dist/mcp/server.js +1208 -0
  746. package/extensions/dm-lens/dist/mcp/worker.js +33 -0
  747. package/extensions/dm-lens/dist/scripts/lib/process-scan.mjs +256 -0
  748. package/extensions/dm-lens/dist/scripts/lib/skills-predicate.mjs +53 -0
  749. package/extensions/dm-lens/dist/scripts/lib/web-tree-sitter-dir.mjs +45 -0
  750. package/extensions/dm-lens/dist/tools/activate-tools.js +61 -0
  751. package/extensions/dm-lens/dist/tools/ast-grep-outline.js +127 -0
  752. package/extensions/dm-lens/dist/tools/ast-grep-replace.js +226 -0
  753. package/extensions/dm-lens/dist/tools/ast-grep-search.js +687 -0
  754. package/extensions/dm-lens/dist/tools/effective-config.js +59 -0
  755. package/extensions/dm-lens/dist/tools/lens-diagnostic-mark.js +240 -0
  756. package/extensions/dm-lens/dist/tools/lens-diagnostics.js +1484 -0
  757. package/extensions/dm-lens/dist/tools/lsp-diagnostics.js +1088 -0
  758. package/extensions/dm-lens/dist/tools/lsp-navigation.js +1194 -0
  759. package/extensions/dm-lens/dist/tools/lsp-structured-output.js +277 -0
  760. package/extensions/dm-lens/dist/tools/module-report.js +332 -0
  761. package/extensions/dm-lens/dist/tools/project-report.js +72 -0
  762. package/extensions/dm-lens/dist/tools/render-compact.js +272 -0
  763. package/extensions/dm-lens/dist/tools/scan-progress.js +31 -0
  764. package/extensions/dm-lens/dist/tools/shared.js +26 -0
  765. package/extensions/dm-lens/dist/tools/symbol-search.js +133 -0
  766. package/extensions/dm-lens/index.js +278 -101
  767. package/extensions/dm-lens/mcp/analyze-cli.js +3 -2
  768. package/extensions/dm-lens/mcp/build-staleness.js +1 -1
  769. package/extensions/dm-lens/mcp/server.js +144 -73
  770. package/extensions/dm-lens/package.json +4 -4
  771. package/extensions/dm-lens/rules/ast-grep-rules/rule-tests/no-assert-tuple-test.yml +13 -0
  772. package/extensions/dm-lens/rules/ast-grep-rules/rule-tests/no-boolean-in-except-test.yml +26 -0
  773. package/extensions/dm-lens/rules/ast-grep-rules/rule-tests/no-http-headers-bracket-access-test.yml +10 -0
  774. package/extensions/dm-lens/rules/ast-grep-rules/rule-tests/unchecked-numeric-parse-python-test.yml +18 -0
  775. package/extensions/dm-lens/rules/ast-grep-rules/rule-tests/unchecked-throwing-call-python-test.yml +4 -3
  776. package/extensions/dm-lens/rules/ast-grep-rules/rules/go-no-fmt-println.yml +2 -2
  777. package/extensions/dm-lens/rules/ast-grep-rules/rules/no-assert-tuple.yml +23 -13
  778. package/extensions/dm-lens/rules/ast-grep-rules/rules/no-boolean-in-except.yml +7 -3
  779. package/extensions/dm-lens/rules/ast-grep-rules/rules/no-console-except-error-js.yml +2 -2
  780. package/extensions/dm-lens/rules/ast-grep-rules/rules/no-console-except-error.yml +2 -2
  781. package/extensions/dm-lens/rules/ast-grep-rules/rules/no-http-headers-bracket-access.yml +12 -7
  782. package/extensions/dm-lens/rules/ast-grep-rules/rules/unchecked-numeric-parse-python.yml +44 -0
  783. package/extensions/dm-lens/rules/ast-grep-rules/rules/unchecked-throwing-call-python.yml +5 -3
  784. package/extensions/dm-lens/rules/tree-sitter-queries/python/python-cross-language-method.yml +3 -3
  785. package/extensions/dm-lens/rules/tree-sitter-queries/python/python-hallucinated-import.yml +2 -2
  786. package/extensions/dm-lens/rules/tree-sitter-queries/python/python-sql-injection.yml +12 -2
  787. package/extensions/dm-lens/rules/tree-sitter-queries/typescript/console-statement.yml +2 -2
  788. package/extensions/dm-lens/rules/tree-sitter-queries/typescript/sql-injection.yml +23 -7
  789. package/extensions/dm-lens/rules/typos/_typos.toml +4 -2
  790. package/extensions/dm-lens/scripts/lib/process-scan.mjs +1 -1
  791. package/extensions/dm-lens/scripts/lib/skills-predicate.mjs +53 -0
  792. package/extensions/dm-lens/scripts/lib/web-tree-sitter-dir.mjs +45 -0
  793. package/extensions/dm-lens/skills/pi-lens-ast-grep/SKILL.md +23 -6
  794. package/extensions/dm-lens/skills/pi-lens-lsp-navigation/SKILL.md +26 -9
  795. package/extensions/dm-lens/tools/activate-tools.js +10 -7
  796. package/extensions/dm-lens/tools/ast-grep-outline.js +2 -6
  797. package/extensions/dm-lens/tools/ast-grep-replace.js +5 -16
  798. package/extensions/dm-lens/tools/ast-grep-search.js +83 -38
  799. package/extensions/dm-lens/tools/effective-config.js +5 -4
  800. package/extensions/dm-lens/tools/lens-diagnostic-mark.js +5 -3
  801. package/extensions/dm-lens/tools/lens-diagnostics.js +478 -106
  802. package/extensions/dm-lens/tools/lsp-diagnostics.js +317 -172
  803. package/extensions/dm-lens/tools/lsp-navigation.js +26 -56
  804. package/extensions/dm-lens/tools/lsp-structured-output.js +3 -3
  805. package/extensions/dm-lens/tools/module-report.js +5 -5
  806. package/extensions/dm-lens/tools/project-report.js +9 -15
  807. package/extensions/dm-lens/tools/render-compact.js +220 -1
  808. package/extensions/dm-lens/tools/symbol-search.js +3 -5
  809. package/extensions/dm-mcp-adapter/OAUTH.md +368 -2
  810. package/extensions/dm-mcp-adapter/THIRD_PARTY_NOTICES.md +1 -1
  811. package/extensions/dm-mcp-adapter/agent-plugin-loader.js +73 -30
  812. package/extensions/dm-mcp-adapter/agent-plugin-provenance.js +26 -0
  813. package/extensions/dm-mcp-adapter/bearer-command-resolver.js +182 -0
  814. package/extensions/dm-mcp-adapter/claude-plugin-loader.js +350 -0
  815. package/extensions/dm-mcp-adapter/cli.js +82 -5
  816. package/extensions/dm-mcp-adapter/commands.js +89 -15
  817. package/extensions/dm-mcp-adapter/config.js +283 -33
  818. package/extensions/dm-mcp-adapter/consent-manager.js +32 -8
  819. package/extensions/dm-mcp-adapter/direct-tool-surface.js +223 -0
  820. package/extensions/dm-mcp-adapter/direct-tools.js +21 -216
  821. package/extensions/dm-mcp-adapter/dist/abort.js +35 -0
  822. package/extensions/dm-mcp-adapter/dist/agent-plugin-loader.js +73 -30
  823. package/extensions/dm-mcp-adapter/dist/agent-plugin-provenance.js +26 -0
  824. package/extensions/dm-mcp-adapter/dist/bearer-command-resolver.js +182 -0
  825. package/extensions/dm-mcp-adapter/dist/claude-plugin-loader.js +350 -0
  826. package/extensions/dm-mcp-adapter/dist/config.js +283 -33
  827. package/extensions/dm-mcp-adapter/dist/consent-manager.js +84 -0
  828. package/extensions/dm-mcp-adapter/dist/elicitation-handler.js +308 -0
  829. package/extensions/dm-mcp-adapter/dist/errors.js +225 -0
  830. package/extensions/dm-mcp-adapter/dist/http-ca.js +71 -0
  831. package/extensions/dm-mcp-adapter/dist/jev-client.js +359 -0
  832. package/extensions/dm-mcp-adapter/dist/jev-contracts.js +1 -0
  833. package/extensions/dm-mcp-adapter/dist/jev-key-store.js +176 -0
  834. package/extensions/dm-mcp-adapter/dist/json-schema-validator.js +70 -0
  835. package/extensions/dm-mcp-adapter/dist/lifecycle.js +387 -0
  836. package/extensions/dm-mcp-adapter/dist/logger.js +115 -0
  837. package/extensions/dm-mcp-adapter/dist/mcp-auth-fetch.js +86 -0
  838. package/extensions/dm-mcp-adapter/dist/mcp-auth-flow.js +949 -0
  839. package/extensions/dm-mcp-adapter/dist/mcp-auth.js +997 -0
  840. package/extensions/dm-mcp-adapter/dist/mcp-bearer-store.js +7 -136
  841. package/extensions/dm-mcp-adapter/dist/mcp-callback-server.js +381 -0
  842. package/extensions/dm-mcp-adapter/dist/mcp-oauth-provider.js +524 -0
  843. package/extensions/dm-mcp-adapter/dist/mcp-probe.js +151 -0
  844. package/extensions/dm-mcp-adapter/dist/mcp-tasks.js +300 -0
  845. package/extensions/dm-mcp-adapter/dist/mcp-trace.js +215 -0
  846. package/extensions/dm-mcp-adapter/dist/metadata-cache.js +10 -28
  847. package/extensions/dm-mcp-adapter/dist/npx-resolver.js +465 -0
  848. package/extensions/dm-mcp-adapter/dist/package-mcp-loader.js +5 -16
  849. package/extensions/dm-mcp-adapter/dist/request-headers-command.js +294 -0
  850. package/extensions/dm-mcp-adapter/dist/runtime-owner.js +94 -0
  851. package/extensions/dm-mcp-adapter/dist/sampling-handler.js +213 -0
  852. package/extensions/dm-mcp-adapter/dist/secure-keyring.js +191 -0
  853. package/extensions/dm-mcp-adapter/dist/server-manager.js +1514 -0
  854. package/extensions/dm-mcp-adapter/dist/session-approvals.js +127 -0
  855. package/extensions/dm-mcp-adapter/dist/session-recovery.js +82 -0
  856. package/extensions/dm-mcp-adapter/dist/state.js +1 -0
  857. package/extensions/dm-mcp-adapter/dist/types.js +39 -0
  858. package/extensions/dm-mcp-adapter/dist/ui-resource-handler.js +204 -0
  859. package/extensions/dm-mcp-adapter/dist/unix-socket-transport.js +83 -0
  860. package/extensions/dm-mcp-adapter/dist/utils.js +84 -9
  861. package/extensions/dm-mcp-adapter/elicitation-handler.js +27 -22
  862. package/extensions/dm-mcp-adapter/host-html-template.js +17 -18
  863. package/extensions/dm-mcp-adapter/http-ca.js +71 -0
  864. package/extensions/dm-mcp-adapter/index.js +904 -167
  865. package/extensions/dm-mcp-adapter/init.js +53 -42
  866. package/extensions/dm-mcp-adapter/jev-client.js +359 -0
  867. package/extensions/dm-mcp-adapter/jev-contracts.js +0 -0
  868. package/extensions/dm-mcp-adapter/jev-key-store.js +176 -0
  869. package/extensions/dm-mcp-adapter/json-schema-validator.js +19 -2
  870. package/extensions/dm-mcp-adapter/lazy-loader.js +7 -0
  871. package/extensions/dm-mcp-adapter/lifecycle.js +8 -3
  872. package/extensions/dm-mcp-adapter/mcp-auth-fetch.js +86 -0
  873. package/extensions/dm-mcp-adapter/mcp-auth-flow.js +228 -75
  874. package/extensions/dm-mcp-adapter/mcp-auth.js +312 -47
  875. package/extensions/dm-mcp-adapter/mcp-bearer-store.js +7 -136
  876. package/extensions/dm-mcp-adapter/mcp-code.js +151 -20
  877. package/extensions/dm-mcp-adapter/mcp-install.js +40 -0
  878. package/extensions/dm-mcp-adapter/mcp-oauth-provider.js +52 -8
  879. package/extensions/dm-mcp-adapter/mcp-output-guard.js +42 -43
  880. package/extensions/dm-mcp-adapter/mcp-panel-theme.js +79 -0
  881. package/extensions/dm-mcp-adapter/mcp-panel.js +294 -286
  882. package/extensions/dm-mcp-adapter/mcp-references.js +13 -18
  883. package/extensions/dm-mcp-adapter/mcp-script-worker.mjs +5 -1
  884. package/extensions/dm-mcp-adapter/mcp-setup-panel.js +317 -282
  885. package/extensions/dm-mcp-adapter/mcp-status.js +2 -0
  886. package/extensions/dm-mcp-adapter/mcp-tasks.js +309 -0
  887. package/extensions/dm-mcp-adapter/metadata-cache.js +11 -28
  888. package/extensions/dm-mcp-adapter/namespace-tools.js +43 -7
  889. package/extensions/dm-mcp-adapter/oauth.js +1 -1
  890. package/extensions/dm-mcp-adapter/package-mcp-loader.js +5 -16
  891. package/extensions/dm-mcp-adapter/package.json +4 -1
  892. package/extensions/dm-mcp-adapter/prompts.js +31 -4
  893. package/extensions/dm-mcp-adapter/proxy-modes.js +394 -199
  894. package/extensions/dm-mcp-adapter/request-headers-command.js +3 -2
  895. package/extensions/dm-mcp-adapter/runtime-owner.js +16 -1
  896. package/extensions/dm-mcp-adapter/sampling-handler.js +16 -25
  897. package/extensions/dm-mcp-adapter/sandbox-proxy-template.js +14 -90
  898. package/extensions/dm-mcp-adapter/search-ranking.js +31 -6
  899. package/extensions/dm-mcp-adapter/secure-keyring.js +191 -0
  900. package/extensions/dm-mcp-adapter/semantic-search.js +160 -0
  901. package/extensions/dm-mcp-adapter/server-manager.js +406 -117
  902. package/extensions/dm-mcp-adapter/session-approvals.js +127 -0
  903. package/extensions/dm-mcp-adapter/skills/mcp-scripting/SKILL.md +11 -1
  904. package/extensions/dm-mcp-adapter/test-runner.mjs +3 -4
  905. package/extensions/dm-mcp-adapter/tool-approval.js +32 -22
  906. package/extensions/dm-mcp-adapter/tool-metadata.js +18 -11
  907. package/extensions/dm-mcp-adapter/tool-registrar.js +7 -4
  908. package/extensions/dm-mcp-adapter/tool-result-renderer.js +4 -2
  909. package/extensions/dm-mcp-adapter/types.js +39 -0
  910. package/extensions/dm-mcp-adapter/ui-server.js +51 -36
  911. package/extensions/dm-mcp-adapter/ui-session.js +57 -25
  912. package/extensions/dm-mcp-adapter/utils.js +84 -9
  913. package/extensions/dm-ponytail/hooks/cursor-hooks.json +17 -0
  914. package/extensions/dm-ponytail/hooks/ponytail-activate.js +14 -2
  915. package/extensions/dm-ponytail/hooks/ponytail-mode-tracker.js +21 -2
  916. package/extensions/dm-ponytail/hooks/ponytail-runtime.js +23 -0
  917. package/extensions/dm-ponytail/package.json +1 -1
  918. package/extensions/dm-subagents/agents/claude-code-writer.md +1 -0
  919. package/extensions/dm-subagents/agents/codex-exec-writer.md +1 -0
  920. package/extensions/dm-subagents/agents/cursor-agent-writer.md +1 -0
  921. package/extensions/dm-subagents/agents/evidence-auditor.md +34 -0
  922. package/extensions/dm-subagents/agents/researcher.md +23 -13
  923. package/extensions/dm-subagents/agents/reviewer.md +6 -4
  924. package/extensions/dm-subagents/agents/scout.md +1 -1
  925. package/extensions/dm-subagents/agents/worker.md +3 -2
  926. package/extensions/dm-subagents/index.js +3 -2
  927. package/extensions/dm-subagents/inspector-runner.mjs +2 -2
  928. package/extensions/dm-subagents/install.mjs +1 -2
  929. package/extensions/dm-subagents/package.json +35 -1
  930. package/extensions/dm-subagents/prompts/council.md +6 -3
  931. package/extensions/dm-subagents/prompts/parallel-review.md +1 -1
  932. package/extensions/dm-subagents/runner-peer-loader.mjs +18 -0
  933. package/extensions/dm-subagents/runner-peer-preload.mjs +29 -0
  934. package/extensions/dm-subagents/skills/dm-subagents/SKILL.md +35 -22
  935. package/extensions/dm-subagents/skills/dm-subagents/references/constraints-and-recipes.md +9 -5
  936. package/extensions/dm-subagents/skills/dm-subagents/references/execution-controls.md +40 -19
  937. package/extensions/dm-subagents/skills/dm-subagents/references/management-authoring-rpc.md +6 -6
  938. package/extensions/dm-subagents/skills/dm-subagents/references/multi-lane-orchestration.md +11 -1
  939. package/extensions/dm-subagents/skills/dm-subagents/references/prompting-and-roles.md +35 -30
  940. package/extensions/dm-subagents/skills/dm-subagents/references/review-and-validation.md +3 -3
  941. package/extensions/dm-subagents/src/agents/advertised-agent-prompt.js +70 -0
  942. package/extensions/dm-subagents/src/agents/agent-management.js +276 -172
  943. package/extensions/dm-subagents/src/agents/agent-memory.js +92 -3
  944. package/extensions/dm-subagents/src/agents/agent-serializer.js +17 -8
  945. package/extensions/dm-subagents/src/agents/agents.js +389 -207
  946. package/extensions/dm-subagents/src/agents/builtin-names.js +1 -0
  947. package/extensions/dm-subagents/src/agents/chain-serializer.js +7 -0
  948. package/extensions/dm-subagents/src/agents/global-npm-root.js +47 -0
  949. package/extensions/dm-subagents/src/agents/proactive-skills.js +1 -1
  950. package/extensions/dm-subagents/src/agents/runtime-agent-registry.js +17 -13
  951. package/extensions/dm-subagents/src/agents/skills.js +36 -10
  952. package/extensions/dm-subagents/src/api/capability-ceiling.js +0 -1
  953. package/extensions/dm-subagents/src/api/child-tool-plan.js +3 -0
  954. package/extensions/dm-subagents/src/api/preflight.js +75 -64
  955. package/extensions/dm-subagents/src/api/required-child-extensions.js +3 -0
  956. package/extensions/dm-subagents/src/api/workflow-resources.js +1 -0
  957. package/extensions/dm-subagents/src/extension/config.js +27 -18
  958. package/extensions/dm-subagents/src/extension/doctor.js +10 -13
  959. package/extensions/dm-subagents/src/extension/fanout-child.js +91 -14
  960. package/extensions/dm-subagents/src/extension/herdr-dm-bridge.js +258 -0
  961. package/extensions/dm-subagents/src/extension/index.js +202 -66
  962. package/extensions/dm-subagents/src/extension/public-execution.js +55 -4
  963. package/extensions/dm-subagents/src/extension/rpc.js +18 -27
  964. package/extensions/dm-subagents/src/extension/schemas.js +99 -84
  965. package/extensions/dm-subagents/src/extension/subagent-guide.js +14 -3
  966. package/extensions/dm-subagents/src/extension/tool-activation.js +157 -0
  967. package/extensions/dm-subagents/src/extension/tool-description.js +30 -78
  968. package/extensions/dm-subagents/src/inspectors/actions.js +145 -0
  969. package/extensions/dm-subagents/src/inspectors/ghostty/actions.js +60 -0
  970. package/extensions/dm-subagents/src/inspectors/ghostty/plugin.js +17 -0
  971. package/extensions/dm-subagents/src/inspectors/herdr/actions.js +94 -179
  972. package/extensions/dm-subagents/src/inspectors/herdr/plugin.js +13 -0
  973. package/extensions/dm-subagents/src/inspectors/herdr/project-panes.js +1 -1
  974. package/extensions/dm-subagents/src/inspectors/{herdr/inspector-runner.js → inspector-runner.js} +31 -25
  975. package/extensions/dm-subagents/src/inspectors/plugins.js +5 -0
  976. package/extensions/dm-subagents/src/inspectors/{herdr/session-roots-codec.js → session-roots-codec.js} +1 -6
  977. package/extensions/dm-subagents/src/inspectors/types.js +1 -0
  978. package/extensions/dm-subagents/src/integrations/dm-web-session-liveness.js +50 -0
  979. package/extensions/dm-subagents/src/intercom/intercom-bridge.js +39 -5
  980. package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +256 -151
  981. package/extensions/dm-subagents/src/intercom/result-intercom.js +6 -0
  982. package/extensions/dm-subagents/src/intercom/supervisor-ui.js +199 -0
  983. package/extensions/dm-subagents/src/missions/workflow-state.js +30 -17
  984. package/extensions/dm-subagents/src/policy/authority.js +6 -2
  985. package/extensions/dm-subagents/src/profiles/profiles.js +10 -5
  986. package/extensions/dm-subagents/src/runs/background/active-async-capacity.js +28 -35
  987. package/extensions/dm-subagents/src/runs/background/active-run-index.js +17 -0
  988. package/extensions/dm-subagents/src/runs/background/async-execution.js +580 -389
  989. package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +71 -47
  990. package/extensions/dm-subagents/src/runs/background/async-resume.js +61 -26
  991. package/extensions/dm-subagents/src/runs/background/async-retention.js +8 -0
  992. package/extensions/dm-subagents/src/runs/background/async-status-snapshot.js +3 -4
  993. package/extensions/dm-subagents/src/runs/background/async-status.js +90 -13
  994. package/extensions/dm-subagents/src/runs/background/auto-drain.js +57 -33
  995. package/extensions/dm-subagents/src/runs/background/binary-bootstrap.js +54 -0
  996. package/extensions/dm-subagents/src/runs/background/chain-append.js +1 -1
  997. package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +11 -27
  998. package/extensions/dm-subagents/src/runs/background/control-channel.js +105 -256
  999. package/extensions/dm-subagents/src/runs/background/fleet-view.js +67 -10
  1000. package/extensions/dm-subagents/src/runs/background/notify.js +252 -17
  1001. package/extensions/dm-subagents/src/runs/background/owned-process-tree.js +33 -2
  1002. package/extensions/dm-subagents/src/runs/background/process-terminal-candidate.js +88 -0
  1003. package/extensions/dm-subagents/src/runs/background/process-terminal.js +29 -87
  1004. package/extensions/dm-subagents/src/runs/background/result-files.js +8 -4
  1005. package/extensions/dm-subagents/src/runs/background/result-watcher.js +15 -3
  1006. package/extensions/dm-subagents/src/runs/background/retained-children.js +3 -2
  1007. package/extensions/dm-subagents/src/runs/background/retained-nested-route-tracker.js +86 -0
  1008. package/extensions/dm-subagents/src/runs/background/run-child-session.js +574 -0
  1009. package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +9 -5
  1010. package/extensions/dm-subagents/src/runs/background/run-status.js +114 -11
  1011. package/extensions/dm-subagents/src/runs/background/runner-aliases.js +129 -0
  1012. package/extensions/dm-subagents/src/runs/background/runner-child-launch.js +62 -0
  1013. package/extensions/dm-subagents/src/runs/background/runner-child-sessions.js +16 -0
  1014. package/extensions/dm-subagents/src/runs/background/runner-http-dispatcher.js +84 -0
  1015. package/extensions/dm-subagents/src/runs/background/runner-startup-failure.js +43 -0
  1016. package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +119 -20
  1017. package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +33 -10
  1018. package/extensions/dm-subagents/src/runs/background/steering.js +24 -3
  1019. package/extensions/dm-subagents/src/runs/background/subagent-runner-bootstrap.js +238 -0
  1020. package/extensions/dm-subagents/src/runs/background/subagent-runner.js +1008 -1344
  1021. package/extensions/dm-subagents/src/runs/background/subagent-wait.js +156 -26
  1022. package/extensions/dm-subagents/src/runs/background/wait-completions.js +91 -23
  1023. package/extensions/dm-subagents/src/runs/background/wait-subscriptions.js +10 -3
  1024. package/extensions/dm-subagents/src/runs/background/wait-tool.js +8 -12
  1025. package/extensions/dm-subagents/src/runs/background/workflow-terminal-proof.js +60 -0
  1026. package/extensions/dm-subagents/src/runs/foreground/async-steering-action.js +29 -26
  1027. package/extensions/dm-subagents/src/runs/foreground/async-stop-action.js +94 -2
  1028. package/extensions/dm-subagents/src/runs/foreground/execution.js +446 -648
  1029. package/extensions/dm-subagents/src/runs/foreground/foreground-control.js +4 -0
  1030. package/extensions/dm-subagents/src/runs/foreground/foreground-history.js +5 -2
  1031. package/extensions/dm-subagents/src/runs/foreground/prompt-audit.js +8 -4
  1032. package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +1337 -445
  1033. package/extensions/dm-subagents/src/runs/foreground/workflow-detach-reconcile.js +10 -5
  1034. package/extensions/dm-subagents/src/runs/foreground/workflow-foreground-steering.js +65 -90
  1035. package/extensions/dm-subagents/src/runs/shared/acceptance.js +265 -68
  1036. package/extensions/dm-subagents/src/runs/shared/agent-contract.js +1 -1
  1037. package/extensions/dm-subagents/src/runs/shared/async-status-projection.js +133 -76
  1038. package/extensions/dm-subagents/src/runs/shared/capability-ceiling.js +4 -2
  1039. package/extensions/dm-subagents/src/runs/shared/child-hooks.js +166 -0
  1040. package/extensions/dm-subagents/src/runs/shared/child-identity.js +10 -2
  1041. package/extensions/dm-subagents/src/runs/shared/child-launch-plan.js +19 -2
  1042. package/extensions/dm-subagents/src/runs/shared/child-launch.js +212 -0
  1043. package/extensions/dm-subagents/src/runs/shared/child-lifecycle.js +22 -0
  1044. package/extensions/dm-subagents/src/runs/shared/child-runtime-config.js +31 -0
  1045. package/extensions/dm-subagents/src/runs/shared/child-session.js +378 -0
  1046. package/extensions/dm-subagents/src/runs/shared/child-tool-plan.js +299 -0
  1047. package/extensions/dm-subagents/src/runs/shared/claude-code-adapter.js +1 -1
  1048. package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +148 -32
  1049. package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +6 -4
  1050. package/extensions/dm-subagents/src/runs/shared/effective-system-prompt.js +19 -0
  1051. package/extensions/dm-subagents/src/runs/shared/external-cli-contract.js +6 -1
  1052. package/extensions/dm-subagents/src/runs/shared/external-cli-preflight.js +14 -2
  1053. package/extensions/dm-subagents/src/runs/shared/external-cli-runner.js +6 -7
  1054. package/extensions/dm-subagents/src/runs/shared/external-job-bridge.js +60 -0
  1055. package/extensions/dm-subagents/src/runs/shared/git-environment.js +25 -0
  1056. package/extensions/dm-subagents/src/runs/shared/herdr-connection.js +341 -0
  1057. package/extensions/dm-subagents/src/runs/shared/herdr-dm-protocol.js +60 -0
  1058. package/extensions/dm-subagents/src/runs/shared/herdr-external-adapters.js +555 -0
  1059. package/extensions/dm-subagents/src/runs/shared/herdr-machine.js +239 -0
  1060. package/extensions/dm-subagents/src/runs/shared/herdr-placed-run.js +987 -0
  1061. package/extensions/dm-subagents/src/runs/shared/host-step-status.js +2 -0
  1062. package/extensions/dm-subagents/src/runs/shared/lane-metadata.js +23 -3
  1063. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +4 -3
  1064. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-grant.js +15 -5
  1065. package/extensions/dm-subagents/src/runs/shared/model-resolution-diagnostic.js +43 -0
  1066. package/extensions/dm-subagents/src/runs/shared/{model-fallback.js → model-resolution.js} +44 -128
  1067. package/extensions/dm-subagents/src/runs/shared/nested-events.js +39 -49
  1068. package/extensions/dm-subagents/src/runs/shared/nested-path.js +0 -13
  1069. package/extensions/dm-subagents/src/runs/shared/orca-progress-tabs.js +22 -12
  1070. package/extensions/dm-subagents/src/runs/shared/parallel-handoff.js +78 -2
  1071. package/extensions/dm-subagents/src/runs/shared/permissions.js +1 -16
  1072. package/extensions/dm-subagents/src/runs/shared/process-signal.js +3 -0
  1073. package/extensions/dm-subagents/src/runs/shared/readonly-drain-observation.js +51 -0
  1074. package/extensions/dm-subagents/src/runs/shared/run-fanout-budget.js +0 -13
  1075. package/extensions/dm-subagents/src/runs/shared/runtime-acknowledged-extensions.js +0 -26
  1076. package/extensions/dm-subagents/src/runs/shared/single-output.js +20 -2
  1077. package/extensions/dm-subagents/src/runs/shared/structured-output.js +263 -10
  1078. package/extensions/dm-subagents/src/runs/shared/subagent-control.js +6 -14
  1079. package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +117 -370
  1080. package/extensions/dm-subagents/src/runs/shared/tool-availability.js +12 -50
  1081. package/extensions/dm-subagents/src/runs/shared/tool-budget.js +10 -13
  1082. package/extensions/dm-subagents/src/runs/shared/tool-timeout.js +1 -1
  1083. package/extensions/dm-subagents/src/runs/shared/usage-reconciliation.js +69 -0
  1084. package/extensions/dm-subagents/src/runs/shared/workflow-async-child-guidance.js +11 -0
  1085. package/extensions/dm-subagents/src/runs/shared/worktree-cleanup-plan.js +30 -10
  1086. package/extensions/dm-subagents/src/runs/shared/worktree-setup-command.js +195 -0
  1087. package/extensions/dm-subagents/src/runs/shared/worktree.js +814 -141
  1088. package/extensions/dm-subagents/src/shared/atomic-json.js +2 -1
  1089. package/extensions/dm-subagents/src/shared/child-cache-retention.js +19 -0
  1090. package/extensions/dm-subagents/src/shared/fork-context.js +40 -77
  1091. package/extensions/dm-subagents/src/shared/fork-session-cwd.js +24 -0
  1092. package/extensions/dm-subagents/src/shared/jsonl-writer.js +8 -0
  1093. package/extensions/dm-subagents/src/shared/launch-contract.js +37 -6
  1094. package/extensions/dm-subagents/src/shared/model-response-aliases.js +15 -0
  1095. package/extensions/dm-subagents/src/shared/opencode-session-headers.js +15 -0
  1096. package/extensions/dm-subagents/src/shared/pruned-fork.js +83 -76
  1097. package/extensions/dm-subagents/src/shared/required-child-extensions.js +72 -0
  1098. package/extensions/dm-subagents/src/shared/shortcuts.js +0 -2
  1099. package/extensions/dm-subagents/src/shared/thinking-ceiling.js +0 -6
  1100. package/extensions/dm-subagents/src/shared/types.js +16 -12
  1101. package/extensions/dm-subagents/src/shared/utils.js +22 -7
  1102. package/extensions/dm-subagents/src/shared/watch-strategy.js +2 -0
  1103. package/extensions/dm-subagents/src/shared/workflow-child-permit.js +70 -0
  1104. package/extensions/dm-subagents/src/slash/delegation-adapters.js +15 -1
  1105. package/extensions/dm-subagents/src/slash/delegation-request.js +13 -0
  1106. package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +28 -1
  1107. package/extensions/dm-subagents/src/slash/prompt-workflows.js +15 -2
  1108. package/extensions/dm-subagents/src/slash/slash-commands.js +12 -269
  1109. package/extensions/dm-subagents/src/slash/subagent-cost.js +255 -0
  1110. package/extensions/dm-subagents/src/slash/subagents-admin.js +49 -25
  1111. package/extensions/dm-subagents/src/tui/fleet-status.js +224 -23
  1112. package/extensions/dm-subagents/src/tui/fleet.js +38 -25
  1113. package/extensions/dm-subagents/src/tui/render.js +636 -63
  1114. package/extensions/dm-subagents/src/watchdog/child-status.js +42 -23
  1115. package/extensions/dm-subagents/src/watchdog/diff-tool.js +86 -0
  1116. package/extensions/dm-subagents/src/watchdog/emission-guard.js +4 -3
  1117. package/extensions/dm-subagents/src/watchdog/guidance.js +19 -0
  1118. package/extensions/dm-subagents/src/watchdog/lsp-diagnostics.js +1 -1
  1119. package/extensions/dm-subagents/src/watchdog/model-selection.js +18 -1
  1120. package/extensions/dm-subagents/src/watchdog/permission-arbiter.js +16 -10
  1121. package/extensions/dm-subagents/src/watchdog/register-child.js +39 -26
  1122. package/extensions/dm-subagents/src/watchdog/register-main.js +51 -43
  1123. package/extensions/dm-subagents/src/watchdog/render.js +5 -7
  1124. package/extensions/dm-subagents/src/watchdog/review.js +142 -67
  1125. package/extensions/dm-subagents/src/watchdog/rules.js +55 -0
  1126. package/extensions/dm-subagents/src/watchdog/runtime.js +167 -109
  1127. package/extensions/dm-subagents/src/watchdog/scope.js +1 -9
  1128. package/extensions/dm-subagents/src/watchdog/settings.js +55 -99
  1129. package/extensions/dm-subagents/src/watchdog/tool-actions.js +11 -12
  1130. package/extensions/dm-subagents/src/watchdog/turn-delta.js +30 -5
  1131. package/extensions/dm-subagents/src/watchdog/types.js +2 -4
  1132. package/extensions/dm-subagents/src/watchdog/warning-format.js +2 -3
  1133. package/extensions/dm-subagents/src/workflows/chat-progress.js +4 -4
  1134. package/extensions/dm-subagents/src/workflows/scripted-workflow.js +482 -36
  1135. package/extensions/dm-subagents/src/workflows/workflow-checklist.js +303 -0
  1136. package/extensions/dm-subagents/src/workflows/workflow-child-summary.js +57 -5
  1137. package/extensions/dm-subagents/src/workflows/workflow-preflight.js +27 -1
  1138. package/extensions/dm-subagents/src/workflows/workflow-receipt.js +51 -3
  1139. package/extensions/dm-subagents/src/workflows/workflow-resources.js +226 -0
  1140. package/extensions/dm-subagents/src/workflows/workflow-settlement.js +5 -0
  1141. package/extensions/dm-web-access/abortable.js +19 -0
  1142. package/extensions/dm-web-access/anysearch.js +4 -13
  1143. package/extensions/dm-web-access/baizhi.js +123 -0
  1144. package/extensions/dm-web-access/bocha.js +3 -24
  1145. package/extensions/dm-web-access/brave.js +3 -23
  1146. package/extensions/dm-web-access/brightdata.js +5 -30
  1147. package/extensions/dm-web-access/content-find.js +181 -47
  1148. package/extensions/dm-web-access/crawl4ai.js +166 -0
  1149. package/extensions/dm-web-access/credential-source.js +1 -0
  1150. package/extensions/dm-web-access/curator-page.js +27 -2
  1151. package/extensions/dm-web-access/curator-run.js +36 -0
  1152. package/extensions/dm-web-access/curator-server.js +11 -2
  1153. package/extensions/dm-web-access/domain-filter-normalization.js +17 -0
  1154. package/extensions/dm-web-access/duckduckgo.js +4 -24
  1155. package/extensions/dm-web-access/extract.js +54 -12
  1156. package/extensions/dm-web-access/fetch-params.js +1 -1
  1157. package/extensions/dm-web-access/firecrawl.js +7 -45
  1158. package/extensions/dm-web-access/gemini-search.js +101 -46
  1159. package/extensions/dm-web-access/github-extract.js +2 -11
  1160. package/extensions/dm-web-access/index.js +382 -247
  1161. package/extensions/dm-web-access/jina-search.js +4 -13
  1162. package/extensions/dm-web-access/kagi.js +4 -13
  1163. package/extensions/dm-web-access/kimi-search.js +5 -30
  1164. package/extensions/dm-web-access/mistral-search.js +251 -0
  1165. package/extensions/dm-web-access/ollama.js +2 -7
  1166. package/extensions/dm-web-access/openai-search.js +204 -46
  1167. package/extensions/dm-web-access/opencode-session-headers.js +15 -0
  1168. package/extensions/dm-web-access/package.json +1 -1
  1169. package/extensions/dm-web-access/page-query.js +14 -4
  1170. package/extensions/dm-web-access/parallel.js +1 -17
  1171. package/extensions/dm-web-access/pdf-extract.js +1 -0
  1172. package/extensions/dm-web-access/perplexity.js +10 -1
  1173. package/extensions/dm-web-access/querit.js +5 -34
  1174. package/extensions/dm-web-access/query-rewrite.js +6 -2
  1175. package/extensions/dm-web-access/search-answer-formatting.js +6 -0
  1176. package/extensions/dm-web-access/search-result-count-normalization.js +5 -0
  1177. package/extensions/dm-web-access/search1api.js +5 -34
  1178. package/extensions/dm-web-access/searchinfinity.js +5 -34
  1179. package/extensions/dm-web-access/searxng.js +3 -23
  1180. package/extensions/dm-web-access/serpapi.js +183 -0
  1181. package/extensions/dm-web-access/serpbase.js +3 -24
  1182. package/extensions/dm-web-access/serpdive.js +3 -23
  1183. package/extensions/dm-web-access/serper.js +5 -30
  1184. package/extensions/dm-web-access/serply.js +184 -0
  1185. package/extensions/dm-web-access/source-check.js +10 -46
  1186. package/extensions/dm-web-access/ssrf-protection.js +3 -1
  1187. package/extensions/dm-web-access/storage.js +9 -0
  1188. package/extensions/dm-web-access/summary-review.js +36 -25
  1189. package/extensions/dm-web-access/tavily.js +3 -23
  1190. package/extensions/dm-web-access/tinyfish.js +5 -34
  1191. package/extensions/dm-web-access/tool-activation.js +112 -0
  1192. package/extensions/dm-web-access/utils.js +27 -10
  1193. package/extensions/dm-web-access/valyu.js +5 -30
  1194. package/extensions/dm-web-access/xai-search.js +78 -42
  1195. package/extensions/dm-web-access/xcrawl.js +5 -30
  1196. package/package.json +7 -4
  1197. package/extensions/dm-lens/tools/ast-dump.js +0 -101
  1198. package/extensions/dm-subagents/src/api/dm-args.js +0 -3
  1199. package/extensions/dm-subagents/src/runs/shared/child-protocol.js +0 -447
  1200. package/extensions/dm-subagents/src/runs/shared/completion-evidence.js +0 -40
  1201. package/extensions/dm-subagents/src/runs/shared/completion-guard.js +0 -173
  1202. package/extensions/dm-subagents/src/runs/shared/dm-args.js +0 -581
  1203. package/extensions/dm-subagents/src/runs/shared/llm-intent-arbiter.js +0 -190
  1204. package/extensions/dm-subagents/src/runs/shared/model-exclusions.js +0 -207
  1205. package/extensions/dm-subagents/src/runs/shared/subagent-startup-retry.js +0 -50
  1206. package/extensions/dm-subagents/src/runs/shared/task-intent.js +0 -130
  1207. package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.js +0 -68
  1208. /package/extensions/dm-subagents/src/inspectors/{herdr/shell-command.js → shell-command.js} +0 -0
@@ -0,0 +1,4448 @@
1
+ import { CASCADE_DIAGNOSTICS_TTL_MS } from "../cascade-types.js";
2
+ import * as nodeFs from "node:fs";
3
+ import fs from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { pathToFileURL, URL } from "node:url";
6
+ import { BoundedFifoMap } from "../bounded-cache.js";
7
+ import { getProjectIgnoreMatcher, isExcludedDirName } from "../file-utils.js";
8
+ import { recordLsp } from "../widget-state.js";
9
+ import { applyAuxiliarySuppressions } from "../dispatch/auxiliary-lsp.js";
10
+ import { detectFileRole } from "../file-role.js";
11
+ import { emitBounded } from "../bounded-telemetry.js";
12
+ import { logLatency, phaseFinished, phaseStarted } from "../latency-logger.js";
13
+ import { logSessionStart } from "../sessionstart-logger.js";
14
+ import { incrementDegradationCount, recordDegradation, recordDegradationOnce } from "../degradation-ledger.js";
15
+ import { isLspSpawnAllowedByTrust, assertInstallAllowed, projectTrustDenialReason } from "../project-trust.js";
16
+ import { shouldPreferPullOnlyDiagnostics } from "../lsp-budget.js";
17
+ import { sampleProcessTreeCpuPercent } from "../resource-sampler.js";
18
+ import { bounded, withDeadline, withTimeout } from "../deadline-utils.js";
19
+ import { HOOK_WALL_BUDGET_MS } from "../hook-budgets.js";
20
+ import { getAmbientAbortSignal } from "../safe-spawn.js";
21
+ import { abortDeferredLspWork } from "../deferred-lsp-work.js";
22
+ import { acquireWorkspaceSweepHold, clearWorkspaceSweepHoldForSessionStart } from "./workspace-sweep-hold.js";
23
+ import { DocumentDriftTracker, fingerprintDocumentContent } from "./document-drift.js";
24
+ import { markPendingAuxiliaryCoverage, napiFallbackCoveredSince } from "./pending-aux-coverage.js";
25
+ import { isAtOrAboveHomeDir, isWindowsPath, normalizeMapKey, uriToPath } from "../path-utils.js";
26
+ import { recordLspMutation } from "../lsp-mutation.js";
27
+ import { createLSPClient } from "./client.js";
28
+ import { auxiliaryCoverageGap, auxiliaryPublicationEvidence, bindingStateLabel, composeBoundToCurrentDisk, createDiskBindingCache, hashDiagnosticContent, resolveTouchVerdict, touchCoverageGap } from "./diagnostic-binding.js";
29
+ import { getServersForFileWithConfig, getServerInitOverride } from "./config.js";
30
+ import { getSessionRootsForTelemetry, isOutsideAllSessionRoots } from "./session-roots.js";
31
+ import { getProcessSingleton } from "../process-singletons.js";
32
+ import { getLanguageId } from "./language.js";
33
+ import { getReverseDepsFromIndex, loadReverseDependencyIndexFromSnapshot } from "../reverse-deps.js";
34
+ import { LSP_SERVERS, enforceLspRootCeiling, hasProjectBoundaryMarker, isDirectLspCommandTemporarilyUnavailable, resetClassicTsRepairGuard, resetLspLaunchAvailabilityGeneration, resolveLspServerCwd } from "./server.js";
35
+ import { classifyCascadeWaitTier, classifyServerWaitTier, getStrategy } from "./wait-policy/index.js";
36
+ const WORKSPACE_ATTRIBUTION_CLIENT_CAP = 16;
37
+ const AUX_WAIT_DEMOTION_THRESHOLD = 5;
38
+ const AUX_WAIT_DEMOTION_RATIO = 0.9;
39
+ const AUX_WAIT_REPROMOTION_THRESHOLD = 5;
40
+ const AUX_WAIT_REPROMOTION_RATIO = 0.5;
41
+ import { raceToCompletion } from "./aggregation.js";
42
+ import { applyWorkspaceEdit, mergeWorkspaceTextEditsByPriority, summarizeWorkspaceEdit, validateWorkspaceEdit } from "./edits.js";
43
+ import { buildScopeKey, createWorkspaceDiagnosticsCacheContext } from "./workspace-diagnostics-cache.js";
44
+ import { attemptTsserverSyncDiagnostics } from "./tsserver-sync.js";
45
+ import { isWarmAttached, tryWarmAttachedDiagnostics } from "../warm-attach.js";
46
+ import { getSuccessfulLspSpawnDurationMs, recordSuccessfulLspSpawn } from "./spawn-history.js";
47
+ function destinationUriPreservingSpelling(oldUri, oldFilePath, newFilePath) {
48
+ const canonical = pathToFileURL(newFilePath);
49
+ try {
50
+ const original = new URL(oldUri);
51
+ const authority = /^file:\/\/([^/]*)/i.exec(oldUri)?.[1] ?? "";
52
+ if (original.protocol !== "file:" || original.host !== "" && original.hostname !== "localhost") {
53
+ return canonical.href;
54
+ }
55
+ const canonicalDestination = () => `${original.protocol}//${authority}${canonical.pathname}`;
56
+ const oldParent = path.resolve(path.dirname(oldFilePath));
57
+ const newParent = path.resolve(path.dirname(newFilePath));
58
+ if (normalizeMapKey(oldParent) !== normalizeMapKey(newParent)) {
59
+ return canonicalDestination();
60
+ }
61
+ const slash = original.pathname.lastIndexOf("/");
62
+ if (slash < 0)
63
+ return canonicalDestination();
64
+ let rawName = canonical.pathname.slice(canonical.pathname.lastIndexOf("/") + 1);
65
+ const oldRawName = original.pathname.slice(slash + 1);
66
+ const oldName = decodeURIComponent(oldRawName);
67
+ const expectedOldName = path.basename(oldFilePath);
68
+ if (oldName.toLowerCase() !== expectedOldName.toLowerCase()) {
69
+ return canonicalDestination();
70
+ }
71
+ for (const match of oldRawName.matchAll(/%([0-9a-f]{2})/gi)) {
72
+ const encoded = match[0];
73
+ const decoded = String.fromCharCode(Number.parseInt(match[1], 16));
74
+ rawName = rawName.split(decoded).join(encoded);
75
+ }
76
+ return `${original.protocol}//${authority}${original.pathname.slice(0, slash + 1)}${rawName}`;
77
+ } catch {
78
+ return canonical.href;
79
+ }
80
+ }
81
+ function deepMergeObjects(base, override) {
82
+ const result = { ...base };
83
+ for (const [key, val] of Object.entries(override)) {
84
+ if (val !== null && typeof val === "object" && !Array.isArray(val) && result[key] !== null && typeof result[key] === "object" && !Array.isArray(result[key])) {
85
+ result[key] = deepMergeObjects(result[key], val);
86
+ } else {
87
+ result[key] = val;
88
+ }
89
+ }
90
+ return result;
91
+ }
92
+ export function mergeInitializationOptions(base, override) {
93
+ if (!override)
94
+ return base;
95
+ if (!base)
96
+ return override;
97
+ return deepMergeObjects(base, override);
98
+ }
99
+ const BROKEN_BASE_COOLDOWN_MS = 15000;
100
+ const BROKEN_MAX_COOLDOWN_MS = 5 * 60000;
101
+ const BROKEN_PERMANENT_AFTER = 5;
102
+ const RUNTIME_EXIT_UPTIME_THRESHOLD_MS = 60000;
103
+ const RUNTIME_EXIT_WINDOW_MS = 15 * 60000;
104
+ const RUNTIME_EXIT_WINDOW_TRIP_COUNT = BROKEN_PERMANENT_AFTER;
105
+ const RUNTIME_EXIT_WINDOW_UPTIME_CEILING_MS = 10 * 60000;
106
+ const NOTIFY_BACKPRESSURE_BROKEN_AFTER = 3;
107
+ const OPTIONAL_LSP_RETRY_COOLDOWN_MS = 5 * 60000;
108
+ const OPTIONAL_LSP_SERVER_IDS = new Set;
109
+ const NAV_CLIENT_WAIT_TIMEOUT_MS = Math.max(0, Number.parseInt(process.env.DM_LENS_LSP_NAV_CLIENT_WAIT_MS ?? "1500", 10) || 1500);
110
+ const TOUCH_DEBOUNCE_MS = Math.max(0, Number.parseInt(process.env.DM_LENS_LSP_TOUCH_DEBOUNCE_MS ?? "1500", 10) || 1500);
111
+ const SCOPED_DEPENDENCY_TOUCH_MAX = 32;
112
+ const RENAME_NOTIFY_TIMEOUT_MS = Math.max(50, Number.parseInt(process.env.DM_LENS_LSP_RENAME_NOTIFY_TIMEOUT_MS ?? "1500", 10) || 1500);
113
+ async function runRenameNotify(send, timeoutMs) {
114
+ try {
115
+ await withTimeout(send(), timeoutMs);
116
+ return { ok: true };
117
+ } catch (err) {
118
+ const timedOut = err instanceof Error && err.message.startsWith("Timeout after");
119
+ return {
120
+ ok: false,
121
+ error: err instanceof Error ? err.message : String(err),
122
+ disposition: timedOut ? "timedOut" : "rejected"
123
+ };
124
+ }
125
+ }
126
+ const DEFAULT_LSP_CLIENT_CEILING = 24;
127
+ const DEFAULT_TS_IDLE_EVICT_MS = 20 * 60000;
128
+ export function getTypeScriptIdleEvictMs() {
129
+ const parsed = Number.parseInt(process.env.DM_LENS_TS_IDLE_EVICT_MS ?? "", 10);
130
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : DEFAULT_TS_IDLE_EVICT_MS;
131
+ }
132
+ export function getLspClientCeiling() {
133
+ const parsed = Number.parseInt(process.env.DM_LENS_LSP_CLIENT_CEILING ?? "", 10);
134
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : DEFAULT_LSP_CLIENT_CEILING;
135
+ }
136
+ function documentIsOpenOn(client, filePath) {
137
+ const probe = client.isDocumentOpen;
138
+ if (typeof probe !== "function")
139
+ return false;
140
+ try {
141
+ return probe.call(client, filePath) === true;
142
+ } catch {
143
+ return false;
144
+ }
145
+ }
146
+ function warmupTimeoutMs() {
147
+ const raw = Number.parseInt(process.env.DM_LENS_LSP_WARMUP_TIMEOUT_MS ?? "", 10);
148
+ return Number.isFinite(raw) && raw > 0 ? raw : 20000;
149
+ }
150
+ function warmupRetryBackoffMs() {
151
+ const raw = Number.parseInt(process.env.DM_LENS_LSP_WARMUP_RETRY_BACKOFF_MS ?? "", 10);
152
+ return Number.isFinite(raw) && raw >= 0 ? raw : 500;
153
+ }
154
+ function readEnvDiagnosticsWaitMs() {
155
+ const raw = process.env.DM_LENS_LSP_DIAGNOSTICS_MAX_WAIT_MS;
156
+ if (raw === undefined)
157
+ return;
158
+ const parsed = Number.parseInt(raw, 10);
159
+ if (!Number.isFinite(parsed) || parsed <= 0)
160
+ return;
161
+ return parsed;
162
+ }
163
+ function readTsserverSyncGraceMs() {
164
+ const raw = process.env.DM_LENS_TSSERVER_SYNC_GRACE_MS;
165
+ if (raw === undefined)
166
+ return 300;
167
+ const parsed = Number.parseInt(raw, 10);
168
+ if (!Number.isFinite(parsed) || parsed < 0)
169
+ return 300;
170
+ return parsed;
171
+ }
172
+ function readEnvAuxGraceMs() {
173
+ const raw = process.env.DM_LENS_AUX_GRACE_MS;
174
+ if (raw === undefined)
175
+ return;
176
+ const parsed = Number.parseInt(raw, 10);
177
+ if (!Number.isFinite(parsed) || parsed < 0)
178
+ return;
179
+ return parsed;
180
+ }
181
+ const DEFAULT_AUX_GRACE_CEILING_MS = 2000;
182
+ const MAX_ADAPTIVE_AUX_GRACE_CEILING_MS = 8000;
183
+ const ADAPTIVE_AUX_GRACE_MARGIN_MS = 500;
184
+ export function auxWaitBudgetMs(serverId, isCold, configuredCeilingMs, declaredWaitMs) {
185
+ if (configuredCeilingMs !== undefined || !isCold) {
186
+ return Math.min(declaredWaitMs, configuredCeilingMs ?? DEFAULT_AUX_GRACE_CEILING_MS);
187
+ }
188
+ const observedSpawnMs = getSuccessfulLspSpawnDurationMs(serverId);
189
+ if (observedSpawnMs === undefined || observedSpawnMs <= 0) {
190
+ return Math.min(declaredWaitMs, DEFAULT_AUX_GRACE_CEILING_MS);
191
+ }
192
+ return Math.min(MAX_ADAPTIVE_AUX_GRACE_CEILING_MS, Math.max(declaredWaitMs, observedSpawnMs + ADAPTIVE_AUX_GRACE_MARGIN_MS));
193
+ }
194
+ const DIAGNOSTICS_SEMANTIC_SETTLE_THRESHOLD_MS = Math.max(0, Number.parseInt(process.env.DM_LENS_LSP_DIAGNOSTICS_SEMANTIC_THRESHOLD_MS ?? "250", 10) || 250);
195
+ const DIAGNOSTICS_SEMANTIC_SETTLE_WAIT_MS = Math.max(0, Number.parseInt(process.env.DM_LENS_LSP_DIAGNOSTICS_SEMANTIC_SETTLE_MS ?? "400", 10) || 400);
196
+ const EARLY_UNBLOCK_GRACE_MS = Math.max(0, Number.parseInt(process.env.DM_LENS_LSP_EARLY_UNBLOCK_GRACE_MS ?? "400", 10) || 400);
197
+ function mergeLspDiagnostics(diagnostics) {
198
+ const merged = [];
199
+ const seen = new Set;
200
+ for (const diagnostic of diagnostics) {
201
+ const key = [
202
+ diagnostic.range.start.line,
203
+ diagnostic.range.start.character,
204
+ diagnostic.message
205
+ ].join(":");
206
+ if (seen.has(key))
207
+ continue;
208
+ seen.add(key);
209
+ merged.push(diagnostic);
210
+ }
211
+ return merged;
212
+ }
213
+ function attributeDiagnostics(serverId, diagnostics) {
214
+ return diagnostics.map((diagnostic) => ({ ...diagnostic, serverId }));
215
+ }
216
+ export function groupFilesByPrimaryServer(files) {
217
+ const byServer = new Map;
218
+ for (const filePath of files) {
219
+ const servers = getServersForFileWithConfig(filePath);
220
+ const primary = servers[0]?.id ?? "none";
221
+ const group = byServer.get(primary);
222
+ if (group) {
223
+ group.files.push(filePath);
224
+ if (servers.length > 1)
225
+ group.multiServer = true;
226
+ } else {
227
+ byServer.set(primary, {
228
+ files: [filePath],
229
+ multiServer: servers.length > 1
230
+ });
231
+ }
232
+ }
233
+ return [...byServer.values()];
234
+ }
235
+ export function createSweepIndexGate() {
236
+ const seen = new Set;
237
+ return {
238
+ consumeFirstTouch(serverId) {
239
+ if (seen.has(serverId))
240
+ return false;
241
+ seen.add(serverId);
242
+ return true;
243
+ }
244
+ };
245
+ }
246
+ export async function runPerServerGroups(groups, concurrency, processGroup, signal) {
247
+ let nextGroup = 0;
248
+ const workers = Math.min(Math.max(1, concurrency), groups.length);
249
+ await Promise.all(Array.from({ length: workers }, async () => {
250
+ while (!signal?.aborted) {
251
+ const gi = nextGroup;
252
+ nextGroup += 1;
253
+ if (gi >= groups.length)
254
+ break;
255
+ await processGroup(groups[gi]);
256
+ }
257
+ return true;
258
+ }));
259
+ }
260
+ const WORKSPACE_DIAGNOSTICS_CONCURRENCY = 8;
261
+ const WORKSPACE_SWEEP_PREOPEN_CHUNK_SIZE = (() => {
262
+ const raw = Number(process.env.DM_LENS_LSP_WORKSPACE_PREOPEN_CHUNK);
263
+ return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 8;
264
+ })();
265
+ const WORKSPACE_SWEEP_EXCLUDED_SERVER_IDS = new Set([
266
+ "opengrep"
267
+ ]);
268
+ function notifyWriteBudgetMs() {
269
+ const raw = Number(process.env.DM_LENS_LSP_NOTIFY_BUDGET_MS);
270
+ return Number.isFinite(raw) && raw > 0 ? raw : 2000;
271
+ }
272
+ function primaryServerWaitFloorMs(filePath, maxWaitMs) {
273
+ const serverWaitOverrideMs = getServersForFileWithConfig(filePath).filter((s) => s.role !== "auxiliary").reduce((max, server) => Math.max(max, server.clientWaitTimeoutMs ?? 0), 0);
274
+ return Math.max(maxWaitMs ?? 0, serverWaitOverrideMs);
275
+ }
276
+ const NOTIFY_WEDGED_BUDGET_MULTIPLIER = 5;
277
+ function notifyWedgedMs() {
278
+ return notifyWriteBudgetMs() * NOTIFY_WEDGED_BUDGET_MULTIPLIER;
279
+ }
280
+ const NOTIFY_WEDGED_EWMA_MULTIPLIER = 2;
281
+ const NOTIFY_WEDGED_CAP_MS = 60000;
282
+ function notifyWedgedCapMs() {
283
+ const raw = Number(process.env.DM_LENS_LSP_NOTIFY_WEDGED_CAP_MS);
284
+ return Number.isFinite(raw) && raw > 0 ? raw : NOTIFY_WEDGED_CAP_MS;
285
+ }
286
+ function notifyStallCpuSampleMs() {
287
+ const raw = Number(process.env.DM_LENS_LSP_NOTIFY_STALL_CPU_SAMPLE_MS);
288
+ return Number.isFinite(raw) && raw > 0 ? raw : 1200;
289
+ }
290
+ const NOTIFY_STALL_CPU_BUSY_FLOOR_PERCENT = 10;
291
+ const AUX_NOTIFY_INFLIGHT_DEFAULT = 8;
292
+ function auxNotifyInflightLimit(info) {
293
+ const perServer = info.notifyInflightLimit;
294
+ if (typeof perServer === "number" && Number.isFinite(perServer) && perServer > 0) {
295
+ return Math.floor(perServer);
296
+ }
297
+ const raw = Number(process.env.DM_LENS_LSP_AUX_NOTIFY_INFLIGHT);
298
+ return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : AUX_NOTIFY_INFLIGHT_DEFAULT;
299
+ }
300
+ function workspacePullBudgetMs() {
301
+ const raw = Number(process.env.DM_LENS_LSP_WORKSPACE_PULL_MS);
302
+ return Number.isFinite(raw) && raw > 0 ? raw : 30000;
303
+ }
304
+ const DEFAULT_MAX_WORKSPACE_DIAGNOSTIC_FILES = 5000;
305
+ function getMaxWorkspaceDiagnosticFiles() {
306
+ const override = Number.parseInt(process.env.DM_LENS_LSP_WORKSPACE_MAX_FILES ?? "", 10);
307
+ return Number.isFinite(override) && override > 0 ? override : DEFAULT_MAX_WORKSPACE_DIAGNOSTIC_FILES;
308
+ }
309
+ async function collectWorkspaceDiagnosticFiles(root, maxFiles = getMaxWorkspaceDiagnosticFiles(), signal, homeDir) {
310
+ const files = [];
311
+ if (isAtOrAboveHomeDir(root, homeDir))
312
+ return files;
313
+ const ignoreMatcher = getProjectIgnoreMatcher(root);
314
+ await ignoreMatcher.ensureTrackedIndex();
315
+ async function walk(current) {
316
+ if (signal?.aborted || files.length >= maxFiles)
317
+ return;
318
+ let entries;
319
+ try {
320
+ entries = await nodeFs.promises.readdir(current, {
321
+ withFileTypes: true
322
+ });
323
+ } catch {
324
+ return;
325
+ }
326
+ for (const entry of entries) {
327
+ if (signal?.aborted || files.length >= maxFiles)
328
+ return;
329
+ if (entry.isSymbolicLink())
330
+ continue;
331
+ const full = path.join(current, entry.name);
332
+ if (entry.isDirectory()) {
333
+ if (isExcludedDirName(entry.name))
334
+ continue;
335
+ if (ignoreMatcher.isIgnored(full, true))
336
+ continue;
337
+ await walk(full);
338
+ } else if (entry.isFile() && getServersForFileWithConfig(full).length > 0 && !ignoreMatcher.isIgnored(full, false)) {
339
+ files.push(full);
340
+ }
341
+ }
342
+ }
343
+ await walk(root);
344
+ return files;
345
+ }
346
+
347
+ export class LSPService {
348
+ sessionCwd;
349
+ state;
350
+ workspaceProbeLogged = new Set;
351
+ projectBoundaryCache = new Map;
352
+ warmStartLogged = new Set;
353
+ optionalFailureLogged = new Set;
354
+ unavailableLogged = new Set;
355
+ optionalDisabled = new Set;
356
+ firstContactWaits = new Map;
357
+ auxWaitPressureStreak = new Map;
358
+ auxWaitFastAnswerStreak = new Map;
359
+ demotedAuxiliaryServerIds = new Set;
360
+ demotedAuxiliaryBudgets = new Map;
361
+ lastSpawnVerdict = new Map;
362
+ failureCounts = new Map;
363
+ runtimeExitCounts = new Map;
364
+ runtimeExitWindow = new Map;
365
+ permanentlyBroken = new Set;
366
+ lastKnownDiagnostics = new Map;
367
+ lastKnownContentHash = new Map;
368
+ documentDrift = new DocumentDriftTracker;
369
+ diskBindingCache = createDiskBindingCache();
370
+ lastDiagnosticsHealth = new Map;
371
+ recentTouches = new Map;
372
+ notifyWriteBackpressureStreak = new Map;
373
+ auxNotifyDrainLatencyEwma = new Map;
374
+ auxNotifyInflight = new Map;
375
+ outstandingAuxNotifyWrites = new Map;
376
+ static MAX_NOTIFY_STALL_DEMOTIONS = 50;
377
+ notifyStallDemotions = new BoundedFifoMap(LSPService.MAX_NOTIFY_STALL_DEMOTIONS);
378
+ clientLastUsedAt = new Map;
379
+ clientLeases = new Map;
380
+ typeScriptIdleTimers = new Map;
381
+ clientSpawnGate = Promise.resolve();
382
+ isDestroyed = false;
383
+ shutdownController = new AbortController;
384
+ generationHandoff;
385
+ constructor(generationHandoff, sessionCwd) {
386
+ this.generationHandoff = generationHandoff;
387
+ this.sessionCwd = sessionCwd;
388
+ this.state = {
389
+ clients: new Map,
390
+ servers: new Map,
391
+ broken: new Map,
392
+ inFlight: new Map,
393
+ clientSpawnedAt: new Map,
394
+ demonstratedReady: new Set,
395
+ demonstratedCold: new Set,
396
+ diagnosticsPublished: new Set,
397
+ diagnosticsUnsupported: new Set
398
+ };
399
+ }
400
+ isAuxiliaryWaitDemoted(serverId, root) {
401
+ return this.demotedAuxiliaryServerIds.has(`${serverId}:${normalizeMapKey(root ?? "")}`);
402
+ }
403
+ noteAuxiliaryWait(key, budgetMs, elapsedMs) {
404
+ if (this.demotedAuxiliaryServerIds.has(key))
405
+ return;
406
+ if (elapsedMs < AUX_WAIT_DEMOTION_RATIO * budgetMs) {
407
+ this.auxWaitPressureStreak.delete(key);
408
+ return;
409
+ }
410
+ const streak = (this.auxWaitPressureStreak.get(key) ?? 0) + 1;
411
+ if (streak >= AUX_WAIT_DEMOTION_THRESHOLD) {
412
+ this.demotedAuxiliaryServerIds.add(key);
413
+ this.demotedAuxiliaryBudgets.set(key, budgetMs);
414
+ this.auxWaitPressureStreak.delete(key);
415
+ this.auxWaitFastAnswerStreak.delete(key);
416
+ recordDegradationOnce({
417
+ kind: "aux_wait_demoted",
418
+ subject: key,
419
+ reason: "auxiliary reached at least 90% of its declared wait budget for five consecutive dispatches"
420
+ });
421
+ return;
422
+ }
423
+ this.auxWaitPressureStreak.set(key, streak);
424
+ }
425
+ noteAuxiliaryLateAnswer(key, elapsedMs) {
426
+ if (!this.demotedAuxiliaryServerIds.has(key))
427
+ return;
428
+ const budgetMs = this.demotedAuxiliaryBudgets.get(key);
429
+ if (budgetMs === undefined || elapsedMs >= AUX_WAIT_REPROMOTION_RATIO * budgetMs) {
430
+ this.auxWaitFastAnswerStreak.delete(key);
431
+ return;
432
+ }
433
+ const streak = (this.auxWaitFastAnswerStreak.get(key) ?? 0) + 1;
434
+ if (streak < AUX_WAIT_REPROMOTION_THRESHOLD) {
435
+ this.auxWaitFastAnswerStreak.set(key, streak);
436
+ return;
437
+ }
438
+ this.demotedAuxiliaryServerIds.delete(key);
439
+ this.demotedAuxiliaryBudgets.delete(key);
440
+ this.auxWaitFastAnswerStreak.delete(key);
441
+ recordDegradationOnce({
442
+ kind: "aux_wait_repromoted",
443
+ subject: key,
444
+ reason: "auxiliary produced five consecutive late answers below half its declared wait budget"
445
+ });
446
+ }
447
+ async resolveServerRoot(server, filePath, onRootFailure) {
448
+ const candidate = await resolveLspServerCwd(server, filePath, this.sessionCwd ?? process.cwd(), onRootFailure);
449
+ if (!candidate)
450
+ return;
451
+ if (isOutsideAllSessionRoots(filePath))
452
+ return;
453
+ const root = enforceLspRootCeiling(candidate, process.cwd(), filePath);
454
+ if (normalizeMapKey(root) === normalizeMapKey(process.cwd()))
455
+ return root;
456
+ const rootKey = normalizeMapKey(root);
457
+ const prefix = `${server.id}:`;
458
+ let nearestAncestor;
459
+ for (const key of new Set([
460
+ ...this.state.clients.keys(),
461
+ ...this.state.inFlight.keys()
462
+ ])) {
463
+ if (!key.startsWith(prefix))
464
+ continue;
465
+ const ancestorKey = key.slice(prefix.length);
466
+ const pathApi = isWindowsPath(ancestorKey) || isWindowsPath(rootKey) ? path.win32 : path;
467
+ const relative = pathApi.relative(ancestorKey, rootKey);
468
+ if (relative === "" || relative.startsWith("..") || pathApi.isAbsolute(relative)) {
469
+ continue;
470
+ }
471
+ if (!nearestAncestor || ancestorKey.length > nearestAncestor.length) {
472
+ nearestAncestor = ancestorKey;
473
+ }
474
+ }
475
+ if (!nearestAncestor)
476
+ return root;
477
+ let boundary = this.projectBoundaryCache.get(rootKey);
478
+ if (!boundary) {
479
+ boundary = hasProjectBoundaryMarker(root);
480
+ this.projectBoundaryCache.set(rootKey, boundary);
481
+ }
482
+ if (await boundary)
483
+ return root;
484
+ return this.state.clients.get(`${server.id}:${nearestAncestor}`)?.root ?? nearestAncestor;
485
+ }
486
+ checkDestroyed() {
487
+ return this.isDestroyed;
488
+ }
489
+ async withClientSpawnGate(operation) {
490
+ const previous = this.clientSpawnGate;
491
+ let release;
492
+ this.clientSpawnGate = new Promise((resolve) => {
493
+ release = resolve;
494
+ });
495
+ await previous;
496
+ try {
497
+ return await operation();
498
+ } finally {
499
+ release();
500
+ }
501
+ }
502
+ async clientKeyFor(entry, filePath) {
503
+ const root = await this.resolveServerRoot(entry.info, filePath);
504
+ return root ? `${entry.info.id}:${normalizeMapKey(root)}` : undefined;
505
+ }
506
+ async acquireClientLease(entry, filePath) {
507
+ const key = await this.clientKeyFor(entry, filePath);
508
+ if (!key)
509
+ return;
510
+ return this.withClientSpawnGate(async () => {
511
+ if (this.isDestroyed || this.state.clients.get(key) !== entry.client) {
512
+ return;
513
+ }
514
+ this.clientLeases.set(key, (this.clientLeases.get(key) ?? 0) + 1);
515
+ return key;
516
+ });
517
+ }
518
+ async acquireClientLeases(entries, filePath) {
519
+ const keys = await Promise.all(entries.map((entry) => this.clientKeyFor(entry, filePath)));
520
+ if (keys.some((key) => key === undefined))
521
+ return;
522
+ const keyed = entries.map((entry, index) => [keys[index], entry]);
523
+ return this.withClientSpawnGate(async () => {
524
+ if (this.isDestroyed || keyed.some(([key, entry]) => this.state.clients.get(key) !== entry.client)) {
525
+ return;
526
+ }
527
+ for (const [key] of keyed) {
528
+ this.clientLeases.set(key, (this.clientLeases.get(key) ?? 0) + 1);
529
+ }
530
+ return keyed.map(([key]) => key);
531
+ });
532
+ }
533
+ releaseClientLease(key) {
534
+ const remaining = (this.clientLeases.get(key) ?? 1) - 1;
535
+ if (remaining > 0) {
536
+ this.clientLeases.set(key, remaining);
537
+ return;
538
+ }
539
+ this.clientLeases.delete(key);
540
+ if (this.state.clients.has(key)) {
541
+ this.clientLastUsedAt.set(key, Date.now());
542
+ this.scheduleTypeScriptIdleEviction(key);
543
+ }
544
+ }
545
+ async withClientForFileUse(filePath, maxWaitMs, hardCapMs, use) {
546
+ while (!this.isDestroyed) {
547
+ const entry = await this.getClientForFile(filePath, maxWaitMs, hardCapMs);
548
+ if (!entry)
549
+ return;
550
+ const leaseKey = await this.acquireClientLease(entry, filePath);
551
+ if (!leaseKey)
552
+ continue;
553
+ try {
554
+ return await use(entry);
555
+ } finally {
556
+ this.releaseClientLease(leaseKey);
557
+ }
558
+ }
559
+ return;
560
+ }
561
+ async makeCapacityForClient(key) {
562
+ const occupiedKeys = new Set(this.state.inFlight.keys());
563
+ for (const [clientKey, client] of this.state.clients) {
564
+ if (client.isAlive())
565
+ occupiedKeys.add(clientKey);
566
+ }
567
+ const occupied = occupiedKeys.size;
568
+ if (occupied < getLspClientCeiling())
569
+ return true;
570
+ const idle = [...this.state.clients.entries()].filter(([candidateKey, client]) => candidateKey !== key && client.isAlive() && client.isBusy?.() !== true && (this.clientLeases.get(candidateKey) ?? 0) === 0).sort(([a], [b]) => (this.clientLastUsedAt.get(a) ?? this.state.clientSpawnedAt.get(a) ?? 0) - (this.clientLastUsedAt.get(b) ?? this.state.clientSpawnedAt.get(b) ?? 0));
571
+ const victim = idle[0];
572
+ if (!victim) {
573
+ logSessionStart(`lsp client ceiling ${getLspClientCeiling()}: spawn declined; all clients are in use`);
574
+ return false;
575
+ }
576
+ const [victimKey, victimClient] = victim;
577
+ this.releaseOutstandingAuxNotifyWrite(victimKey);
578
+ await victimClient.shutdown({ reason: "client_ceiling_lru" });
579
+ this.state.clients.delete(victimKey);
580
+ this.state.clientSpawnedAt.delete(victimKey);
581
+ this.state.demonstratedReady.delete(victimKey);
582
+ this.state.demonstratedCold.delete(victimKey);
583
+ this.clientLastUsedAt.delete(victimKey);
584
+ this.clearTypeScriptIdleTimer(victimKey);
585
+ logSessionStart(`lsp client ceiling ${getLspClientCeiling()}: evicted idle LRU ${victimKey}`);
586
+ return true;
587
+ }
588
+ clearTypeScriptIdleTimer(key) {
589
+ const timer = this.typeScriptIdleTimers.get(key);
590
+ if (timer)
591
+ clearTimeout(timer);
592
+ this.typeScriptIdleTimers.delete(key);
593
+ }
594
+ releaseOutstandingAuxNotifyWrite(key, token) {
595
+ const current = this.outstandingAuxNotifyWrites.get(key);
596
+ if (!current || token !== undefined && current !== token)
597
+ return;
598
+ clearTimeout(current.wedgeTimer);
599
+ this.outstandingAuxNotifyWrites.delete(key);
600
+ current.resolveSettled();
601
+ }
602
+ scheduleTypeScriptIdleEviction(key) {
603
+ if (!key.startsWith("typescript:"))
604
+ return;
605
+ this.clearTypeScriptIdleTimer(key);
606
+ const lastUsedAt = this.clientLastUsedAt.get(key) ?? Date.now();
607
+ const timer = setTimeout(() => {
608
+ this.typeScriptIdleTimers.delete(key);
609
+ this.withClientSpawnGate(async () => {
610
+ if (this.isDestroyed)
611
+ return;
612
+ const client = this.state.clients.get(key);
613
+ if (!client?.isAlive())
614
+ return;
615
+ if (client.isBusy?.() === true || (this.clientLeases.get(key) ?? 0) > 0 || (this.clientLastUsedAt.get(key) ?? 0) !== lastUsedAt) {
616
+ this.scheduleTypeScriptIdleEviction(key);
617
+ return;
618
+ }
619
+ this.releaseOutstandingAuxNotifyWrite(key);
620
+ this.state.clients.delete(key);
621
+ this.state.clientSpawnedAt.delete(key);
622
+ this.state.demonstratedReady.delete(key);
623
+ this.state.demonstratedCold.delete(key);
624
+ this.clientLastUsedAt.delete(key);
625
+ try {
626
+ await client.shutdown({ reason: "typescript_idle_eviction" });
627
+ } catch {}
628
+ logSessionStart(`lsp typescript idle eviction: released ${key}`);
629
+ recordDegradation({
630
+ kind: "ts-idle-eviction",
631
+ subject: key,
632
+ reason: "idle TypeScript client released to bound memory"
633
+ });
634
+ }).catch(() => {});
635
+ }, getTypeScriptIdleEvictMs());
636
+ timer.unref?.();
637
+ this.typeScriptIdleTimers.set(key, timer);
638
+ }
639
+ fingerprintContent(content) {
640
+ if (content.length <= 96) {
641
+ return `${content.length}:${content}`;
642
+ }
643
+ return `${content.length}:${content.slice(0, 48)}:${content.slice(-48)}`;
644
+ }
645
+ shouldSkipTouch(filePath, content, clientScope, waitForDiagnostics, serverIds) {
646
+ if (waitForDiagnostics)
647
+ return false;
648
+ if (serverIds.length === 0)
649
+ return false;
650
+ return serverIds.every((serverId) => this.shouldSkipNotify(filePath, content, clientScope, serverId));
651
+ }
652
+ shouldSkipNotify(filePath, content, clientScope, serverId) {
653
+ if (TOUCH_DEBOUNCE_MS <= 0)
654
+ return false;
655
+ const previous = this.recentTouches.get(this.recentTouchKey(filePath, clientScope, serverId));
656
+ if (!previous)
657
+ return false;
658
+ const now = Date.now();
659
+ if (now - previous.touchedAt > TOUCH_DEBOUNCE_MS)
660
+ return false;
661
+ return previous.fingerprint === this.fingerprintContent(content);
662
+ }
663
+ recentTouchKey(filePath, clientScope, serverId) {
664
+ return `${normalizeMapKey(filePath)}:${clientScope}:${serverId}`;
665
+ }
666
+ markTouched(filePath, content, clientScope, serverId) {
667
+ const key = this.recentTouchKey(filePath, clientScope, serverId);
668
+ const now = Date.now();
669
+ this.recentTouches.set(key, {
670
+ fingerprint: this.fingerprintContent(content),
671
+ touchedAt: now,
672
+ clientScope
673
+ });
674
+ if (this.recentTouches.size > 800) {
675
+ for (const [k, v] of this.recentTouches) {
676
+ if (now - v.touchedAt > TOUCH_DEBOUNCE_MS) {
677
+ this.recentTouches.delete(k);
678
+ }
679
+ }
680
+ }
681
+ }
682
+ async demonstratedReadyKeyFor(server, filePath) {
683
+ const root = await this.resolveServerRoot(server, filePath);
684
+ if (!root)
685
+ return;
686
+ return `${server.id}:${normalizeMapKey(root)}`;
687
+ }
688
+ markDemonstratedReadyKey(key) {
689
+ this.state.demonstratedReady.add(key);
690
+ this.state.demonstratedCold.delete(key);
691
+ }
692
+ recordBreaker(key, reason) {
693
+ recordDegradationOnce({ kind: "lsp-breaker", subject: key, reason });
694
+ }
695
+ recordNotifyWriteBackpressure(key, entry, filePath) {
696
+ if (!key)
697
+ return;
698
+ const streak = (this.notifyWriteBackpressureStreak.get(key) ?? 0) + 1;
699
+ if (streak < NOTIFY_BACKPRESSURE_BROKEN_AFTER) {
700
+ this.notifyWriteBackpressureStreak.set(key, streak);
701
+ return;
702
+ }
703
+ this.notifyWriteBackpressureStreak.delete(key);
704
+ this.demoteNotifyStallCpuGuarded(key, entry, filePath, {
705
+ consecutiveTimeouts: NOTIFY_BACKPRESSURE_BROKEN_AFTER
706
+ });
707
+ }
708
+ async demoteNotifyStallCpuGuarded(key, entry, filePath, reason) {
709
+ if (this.state.clients.get(key) !== entry.client)
710
+ return;
711
+ this.clearTypeScriptIdleTimer(key);
712
+ const verdict = await this.notifyStallCpuVerdict(entry);
713
+ if (verdict.cpuVerdict === "busy") {
714
+ if (this.state.clients.get(key) === entry.client) {
715
+ this.scheduleTypeScriptIdleEviction(key);
716
+ }
717
+ this.logNotifyStallCpuBusy(key, entry, filePath, 0, verdict);
718
+ return;
719
+ }
720
+ if (this.state.clients.get(key) !== entry.client)
721
+ return;
722
+ this.demoteForNotifyStall(key, entry, filePath, {
723
+ ...reason,
724
+ discriminator: "consecutive-timeouts",
725
+ cpuVerdict: verdict.cpuVerdict
726
+ });
727
+ }
728
+ async notifyStallCpuVerdict(entry) {
729
+ const pid = entry.client.getProcessPid?.();
730
+ if (pid === undefined || !(Number.isFinite(pid) && pid > 0)) {
731
+ return { cpuVerdict: "unmeasured", cpuPercent: null };
732
+ }
733
+ const sample = await sampleProcessTreeCpuPercent(pid, notifyStallCpuSampleMs(), NOTIFY_STALL_CPU_BUSY_FLOOR_PERCENT);
734
+ if (!sample.measured) {
735
+ return { cpuVerdict: "unmeasured", cpuPercent: null };
736
+ }
737
+ if (sample.busy) {
738
+ return { cpuVerdict: "busy", cpuPercent: sample.cpuPercent };
739
+ }
740
+ return { cpuVerdict: "flat", cpuPercent: sample.cpuPercent };
741
+ }
742
+ logNotifyStallCpuBusy(key, entry, filePath, outstandingMs, verdict, extra = {}) {
743
+ emitBounded("lsp_notify_stall_cpu_busy", `${key}:${normalizeMapKey(filePath)}`, {
744
+ type: "phase",
745
+ durationMs: outstandingMs,
746
+ metadata: {
747
+ serverId: entry.info.id,
748
+ clientKey: key,
749
+ outstandingMs,
750
+ cpuPercent: verdict.cpuPercent ?? null,
751
+ ...extra
752
+ }
753
+ }, {
754
+ ledgerKind: "lsp-notify-stall-cpu-busy",
755
+ risingEdgePer: "identity"
756
+ });
757
+ }
758
+ auxNotifyWedgeBudgetMs(key) {
759
+ let budgetMs = notifyWedgedMs();
760
+ const ewma = this.auxNotifyDrainLatencyEwma.get(key);
761
+ if (ewma !== undefined && ewma > 0) {
762
+ const unacked = this.auxNotifyInflight.get(key)?.unacked ?? 0;
763
+ budgetMs = Math.max(budgetMs, NOTIFY_WEDGED_EWMA_MULTIPLIER * ewma * unacked);
764
+ }
765
+ return Math.min(budgetMs, notifyWedgedCapMs());
766
+ }
767
+ demoteForNotifyStall(key, entry, filePath, reason) {
768
+ if (this.state.clients.get(key) !== entry.client)
769
+ return;
770
+ this.notifyWriteBackpressureStreak.delete(key);
771
+ this.releaseOutstandingAuxNotifyWrite(key);
772
+ this.auxNotifyInflight.delete(key);
773
+ this.state.broken.set(key, Date.now() + BROKEN_BASE_COOLDOWN_MS);
774
+ this.notifyStallDemotions.set(key, Date.now());
775
+ entry.client.shutdown().catch(() => {});
776
+ this.state.clients.delete(key);
777
+ this.state.clientSpawnedAt.delete(key);
778
+ this.state.demonstratedReady.delete(key);
779
+ this.clientLastUsedAt.delete(key);
780
+ this.clearTypeScriptIdleTimer(key);
781
+ logLatency({
782
+ type: "phase",
783
+ phase: "lsp_notify_backpressure_broken",
784
+ filePath: normalizeMapKey(filePath),
785
+ durationMs: 0,
786
+ metadata: {
787
+ serverId: entry.info.id,
788
+ cooldownMs: BROKEN_BASE_COOLDOWN_MS,
789
+ ...reason
790
+ }
791
+ });
792
+ }
793
+ retractNotifyWriteBackpressure(key, serverId, filePath, outstandingMs, client) {
794
+ if (this.state.clients.get(key) !== client)
795
+ return;
796
+ const streak = this.notifyWriteBackpressureStreak.get(key);
797
+ if (!streak)
798
+ return;
799
+ const streakAfter = streak - 1;
800
+ if (streakAfter <= 0)
801
+ this.notifyWriteBackpressureStreak.delete(key);
802
+ else
803
+ this.notifyWriteBackpressureStreak.set(key, streakAfter);
804
+ logLatency({
805
+ type: "phase",
806
+ phase: "lsp_notify_write_late_landed",
807
+ filePath: normalizeMapKey(filePath),
808
+ durationMs: outstandingMs,
809
+ metadata: { serverId, outstandingMs, streakAfter }
810
+ });
811
+ }
812
+ noteAuxNotifyIssued(key, client) {
813
+ const record = this.auxNotifyInflight.get(key);
814
+ if (record && record.client === client) {
815
+ record.unacked += 1;
816
+ return;
817
+ }
818
+ this.auxNotifyInflight.set(key, { client, unacked: 1 });
819
+ }
820
+ noteAuxNotifyDrainLatency(key, durationMs, outstanding, record) {
821
+ if (!(outstanding > 0 && durationMs > 0))
822
+ return;
823
+ if (record && this.auxNotifyInflight.get(key) !== record)
824
+ return;
825
+ const perWriteMs = durationMs / outstanding;
826
+ const prev = this.auxNotifyDrainLatencyEwma.get(key);
827
+ this.auxNotifyDrainLatencyEwma.set(key, prev === undefined ? perWriteMs : 0.5 * perWriteMs + 0.5 * prev);
828
+ }
829
+ async decideNotifyStallTeardown(key, entry, filePath, token) {
830
+ if (this.state.clients.get(key) !== entry.client || this.outstandingAuxNotifyWrites.get(key) !== token) {
831
+ throw new Error("stale notify generation");
832
+ }
833
+ const outstandingMs = Date.now() - token.startedAt;
834
+ const armedBudgetMs = token.armedBudgetMs;
835
+ if (outstandingMs >= notifyWedgedCapMs()) {
836
+ return {
837
+ action: "demote",
838
+ reason: {
839
+ outstandingMs,
840
+ discriminator: "cap-exceeded",
841
+ budgetMs: armedBudgetMs
842
+ }
843
+ };
844
+ }
845
+ const verdict = await this.notifyStallCpuVerdict(entry);
846
+ if (this.state.clients.get(key) !== entry.client || this.outstandingAuxNotifyWrites.get(key) !== token) {
847
+ throw new Error("stale notify generation");
848
+ }
849
+ if (verdict.cpuVerdict === "busy") {
850
+ this.logNotifyStallCpuBusy(key, entry, filePath, outstandingMs, verdict, {
851
+ budgetMs: armedBudgetMs
852
+ });
853
+ return { action: "rearm", budgetMs: this.auxNotifyWedgeBudgetMs(key) };
854
+ }
855
+ return {
856
+ action: "demote",
857
+ reason: {
858
+ outstandingMs,
859
+ discriminator: verdict.cpuVerdict === "unmeasured" ? "budget-exceeded" : "budget-exceeded-cpu-flat",
860
+ budgetMs: armedBudgetMs,
861
+ ewmaInputMs: this.auxNotifyDrainLatencyEwma.get(key),
862
+ unackedDepth: this.auxNotifyInflight.get(key)?.unacked ?? 0,
863
+ cpuVerdict: verdict.cpuVerdict,
864
+ cpuPercent: verdict.cpuPercent ?? null
865
+ }
866
+ };
867
+ }
868
+ auxNotifyBacklogAtCeiling(key, entry) {
869
+ const record = this.auxNotifyInflight.get(key);
870
+ if (!record || record.client !== entry.client)
871
+ return false;
872
+ if (record.gateOpen)
873
+ return false;
874
+ return record.unacked >= auxNotifyInflightLimit(entry.info);
875
+ }
876
+ async paceAuxNotify(key, entry, filePath, waitMs, context) {
877
+ const record = this.auxNotifyInflight.get(key);
878
+ if (!record)
879
+ return;
880
+ if (record.client !== entry.client) {
881
+ this.auxNotifyInflight.delete(key);
882
+ return;
883
+ }
884
+ if (record.gateOpen)
885
+ return;
886
+ const limit = auxNotifyInflightLimit(entry.info);
887
+ if (record.unacked < limit)
888
+ return;
889
+ const ping = entry.client.pingLiveness;
890
+ if (!ping) {
891
+ record.unacked = 0;
892
+ return;
893
+ }
894
+ if (waitMs <= 0) {
895
+ this.openAuxNotifyGate(key, entry, filePath, context, {
896
+ unacked: record.unacked,
897
+ limit,
898
+ waitMs,
899
+ durationMs: 0
900
+ });
901
+ return;
902
+ }
903
+ if (!record.drain) {
904
+ const startedAt = Date.now();
905
+ const outstanding = record.unacked;
906
+ const client = entry.client;
907
+ const barrier = (async () => {
908
+ let drained = false;
909
+ try {
910
+ drained = await ping.call(client, waitMs);
911
+ } catch {
912
+ drained = false;
913
+ }
914
+ const current = this.auxNotifyInflight.get(key);
915
+ if (current === record && current.client === client) {
916
+ current.drain = undefined;
917
+ if (drained)
918
+ current.unacked = Math.max(0, current.unacked - outstanding);
919
+ }
920
+ const durationMs = Date.now() - startedAt;
921
+ if (drained)
922
+ this.noteAuxNotifyDrainLatency(key, durationMs, outstanding, record);
923
+ this.noteDrainBarrierOutcome(key, entry, filePath, context, {
924
+ unacked: outstanding,
925
+ limit,
926
+ waitMs,
927
+ durationMs,
928
+ outcome: drained ? "drained" : "stalled"
929
+ });
930
+ return drained;
931
+ })();
932
+ barrier.catch(() => {});
933
+ record.drain = barrier;
934
+ record.stallLogged = false;
935
+ }
936
+ const waitStartedAt = Date.now();
937
+ const drained = await withDeadline(record.drain, {
938
+ ms: waitMs,
939
+ onTimeout: "undefined",
940
+ onReject: "undefined"
941
+ });
942
+ if (drained !== true) {
943
+ this.openAuxNotifyGate(key, entry, filePath, context, {
944
+ unacked: record.unacked,
945
+ limit,
946
+ waitMs,
947
+ durationMs: Date.now() - waitStartedAt
948
+ });
949
+ }
950
+ }
951
+ openAuxNotifyGate(key, entry, filePath, context, detail) {
952
+ const record = this.auxNotifyInflight.get(key);
953
+ if (record && record.client === entry.client) {
954
+ record.gateOpen = true;
955
+ }
956
+ this.noteDrainBarrierOutcome(key, entry, filePath, context, {
957
+ ...detail,
958
+ outcome: "stalled"
959
+ });
960
+ }
961
+ noteDrainBarrierOutcome(key, entry, filePath, context, detail) {
962
+ const record = this.auxNotifyInflight.get(key);
963
+ if (detail.outcome === "stalled" && record) {
964
+ if (record.stallLogged)
965
+ return;
966
+ record.stallLogged = true;
967
+ }
968
+ this.logDrainBarrier(key, entry, filePath, context, detail);
969
+ }
970
+ logDrainBarrier(key, entry, filePath, context, detail) {
971
+ if (detail.outcome === "stalled") {
972
+ incrementDegradationCount({
973
+ kind: "lsp-notify-inflight-stall",
974
+ subject: `${entry.info.id}:${normalizeMapKey(filePath)}`,
975
+ reason: `notify barrier stalled with ${detail.unacked} unacknowledged writes`
976
+ });
977
+ }
978
+ logLatency({
979
+ type: "phase",
980
+ phase: "lsp_notify_inflight_barrier",
981
+ filePath: normalizeMapKey(filePath),
982
+ durationMs: detail.durationMs,
983
+ metadata: {
984
+ serverId: entry.info.id,
985
+ clientKey: key,
986
+ source: context.source,
987
+ clientScope: context.clientScope,
988
+ ...detail
989
+ }
990
+ });
991
+ }
992
+ async claimAuxNotifySlot(clientKey, entry, filePath, budgetMs) {
993
+ const deadline = Date.now() + budgetMs;
994
+ for (;; ) {
995
+ const outstanding = this.outstandingAuxNotifyWrites.get(clientKey);
996
+ if (outstanding && outstanding.client !== entry.client) {
997
+ this.releaseOutstandingAuxNotifyWrite(clientKey, outstanding);
998
+ } else if (outstanding) {
999
+ const outstandingMs = Date.now() - outstanding.startedAt;
1000
+ const remainingMs = deadline - Date.now();
1001
+ if (remainingMs <= 0)
1002
+ return { outstandingMs };
1003
+ await withDeadline(outstanding.settled, {
1004
+ ms: remainingMs,
1005
+ onTimeout: "undefined",
1006
+ onReject: "undefined"
1007
+ });
1008
+ continue;
1009
+ }
1010
+ const current = this.state.clients.get(clientKey);
1011
+ if (current !== entry.client) {
1012
+ return { outstandingMs: 0 };
1013
+ }
1014
+ let resolveSettled;
1015
+ const settled = new Promise((resolve) => {
1016
+ resolveSettled = resolve;
1017
+ });
1018
+ const token = {
1019
+ startedAt: Date.now(),
1020
+ client: entry.client,
1021
+ settled,
1022
+ armedBudgetMs: 0,
1023
+ wedgeTimer: undefined,
1024
+ resolveSettled: () => resolveSettled?.()
1025
+ };
1026
+ const fireWedge = async () => {
1027
+ if (this.outstandingAuxNotifyWrites.get(clientKey) !== token || this.state.clients.get(clientKey) !== entry.client) {
1028
+ this.releaseOutstandingAuxNotifyWrite(clientKey, token);
1029
+ return;
1030
+ }
1031
+ let decision;
1032
+ try {
1033
+ decision = await this.decideNotifyStallTeardown(clientKey, entry, filePath, token);
1034
+ } catch {
1035
+ decision = {
1036
+ action: "demote",
1037
+ reason: {
1038
+ outstandingMs: Date.now() - token.startedAt,
1039
+ discriminator: "budget-exceeded",
1040
+ budgetMs: token.armedBudgetMs
1041
+ }
1042
+ };
1043
+ }
1044
+ if (this.outstandingAuxNotifyWrites.get(clientKey) !== token || this.state.clients.get(clientKey) !== entry.client) {
1045
+ this.releaseOutstandingAuxNotifyWrite(clientKey, token);
1046
+ return;
1047
+ }
1048
+ if (decision.action === "demote") {
1049
+ this.demoteForNotifyStall(clientKey, entry, filePath, decision.reason);
1050
+ resolveSettled?.();
1051
+ return;
1052
+ }
1053
+ token.wedgeTimer = setTimeout(() => void fireWedge(), decision.budgetMs);
1054
+ token.wedgeTimer.unref?.();
1055
+ };
1056
+ token.armedBudgetMs = this.auxNotifyWedgeBudgetMs(clientKey);
1057
+ token.wedgeTimer = setTimeout(() => void fireWedge(), token.armedBudgetMs);
1058
+ token.wedgeTimer.unref?.();
1059
+ this.outstandingAuxNotifyWrites.set(clientKey, token);
1060
+ return {
1061
+ release: () => {
1062
+ this.releaseOutstandingAuxNotifyWrite(clientKey, token);
1063
+ }
1064
+ };
1065
+ }
1066
+ }
1067
+ async brokenSkippedAuxiliaryServerIds(filePath, clientScope, options, spawned) {
1068
+ if (clientScope === "primary")
1069
+ return [];
1070
+ const enabledAuxiliaries = clientScope === "with-auxiliary" ? new Set(options.auxiliaryServerIds ?? []) : undefined;
1071
+ const attached = new Set(spawned.map((entry) => entry.info.id));
1072
+ const skipped = [];
1073
+ for (const server of getServersForFileWithConfig(filePath)) {
1074
+ if (server.role !== "auxiliary")
1075
+ continue;
1076
+ if (attached.has(server.id))
1077
+ continue;
1078
+ if (options.excludeServerIds?.has(server.id))
1079
+ continue;
1080
+ if (enabledAuxiliaries && !enabledAuxiliaries.has(server.id))
1081
+ continue;
1082
+ const key = await this.demonstratedReadyKeyFor(server, filePath);
1083
+ if (!key)
1084
+ continue;
1085
+ const brokenUntil = this.state.broken.get(key);
1086
+ if (this.permanentlyBroken.has(key) || typeof brokenUntil === "number" && brokenUntil > Date.now()) {
1087
+ skipped.push(server.id);
1088
+ }
1089
+ }
1090
+ return skipped;
1091
+ }
1092
+ activeClientsForCwd(cwd, priorityServerIds = []) {
1093
+ const normalizedCwd = normalizeMapKey(cwd);
1094
+ const priority = new Map(priorityServerIds.map((serverId, index) => [serverId, index]));
1095
+ const entries = [];
1096
+ for (const [key, client] of this.state.clients) {
1097
+ if (!client.isAlive())
1098
+ continue;
1099
+ const separator = key.indexOf(":");
1100
+ const serverId = separator >= 0 ? key.slice(0, separator) : key;
1101
+ const root = normalizeMapKey(client.root);
1102
+ const sameOrNested = root === normalizedCwd || root.startsWith(`${normalizedCwd}/`) || normalizedCwd.startsWith(`${root}/`);
1103
+ if (!sameOrNested)
1104
+ continue;
1105
+ entries.push({ serverId, client });
1106
+ }
1107
+ return entries.sort((a, b) => (priority.get(a.serverId) ?? Number.MAX_SAFE_INTEGER) - (priority.get(b.serverId) ?? Number.MAX_SAFE_INTEGER));
1108
+ }
1109
+ getOpenDocumentPaths(cwd) {
1110
+ const paths = new Set;
1111
+ for (const { client } of this.activeClientsForCwd(cwd)) {
1112
+ try {
1113
+ for (const filePath of client.openDocumentPaths?.() ?? [])
1114
+ paths.add(filePath);
1115
+ } catch {}
1116
+ }
1117
+ return [...paths];
1118
+ }
1119
+ async getClientForFile(filePath, maxWaitMs, hardCapMs, resolvedRoots, waitSkipReasons) {
1120
+ if (this.checkDestroyed())
1121
+ return;
1122
+ const servers = getServersForFileWithConfig(filePath).filter((s) => s.role !== "auxiliary");
1123
+ const rootMemo = new Map;
1124
+ const partitionCandidates = async () => {
1125
+ const resolved = await Promise.all(servers.map(async (server) => {
1126
+ let rootFailureReason;
1127
+ try {
1128
+ return {
1129
+ server,
1130
+ root: await this.resolveServerRootOnce(server, filePath, rootMemo, (reason) => {
1131
+ rootFailureReason = reason;
1132
+ }),
1133
+ reason: rootFailureReason
1134
+ };
1135
+ } catch (error) {
1136
+ return {
1137
+ server,
1138
+ root: undefined,
1139
+ reason: error instanceof Error ? error.message : String(error)
1140
+ };
1141
+ }
1142
+ }));
1143
+ const unrootedReasons = {};
1144
+ for (const entry of resolved) {
1145
+ if (entry.root === undefined && entry.reason !== undefined) {
1146
+ unrootedReasons[entry.server.id] = entry.reason;
1147
+ }
1148
+ }
1149
+ return {
1150
+ rooted: resolved.filter((entry) => entry.root !== undefined).map(({ server, root }) => ({ server, root })),
1151
+ unrootedIds: resolved.filter((entry) => entry.root === undefined).map((entry) => entry.server.id),
1152
+ unrootedReasons
1153
+ };
1154
+ };
1155
+ const serverBaseMs = primaryServerWaitFloorMs(filePath, maxWaitMs);
1156
+ const effectiveMaxWaitMs = hardCapMs !== undefined ? serverBaseMs > 0 ? Math.min(serverBaseMs, hardCapMs) : hardCapMs : serverBaseMs;
1157
+ let knownSlowResolve;
1158
+ const knownSlowSentinel = Symbol("lsp-client-wait-known-slow");
1159
+ const knownSlow = new Promise((resolve) => {
1160
+ knownSlowResolve = () => resolve(knownSlowSentinel);
1161
+ });
1162
+ const noteSpawnInFlight = (serverId) => {
1163
+ const knownDurationMs = getSuccessfulLspSpawnDurationMs(serverId);
1164
+ if (knownDurationMs !== undefined && knownDurationMs > effectiveMaxWaitMs * 2) {
1165
+ queueMicrotask(() => knownSlowResolve?.());
1166
+ }
1167
+ };
1168
+ const withBudget = async () => {
1169
+ if (servers.length === 0)
1170
+ return;
1171
+ let erroredServerId;
1172
+ let erroredOutcome;
1173
+ for (const server of servers) {
1174
+ const acquisition = {
1175
+ outcome: "declined"
1176
+ };
1177
+ const spawned = await this.ensureClientForServer(filePath, server, resolvedRoots, noteSpawnInFlight, (reported) => {
1178
+ acquisition.outcome = reported;
1179
+ }, rootMemo);
1180
+ if (spawned) {
1181
+ logLatency({
1182
+ type: "phase",
1183
+ phase: "lsp_client_selected",
1184
+ filePath,
1185
+ durationMs: 0,
1186
+ metadata: {
1187
+ serverId: server.id,
1188
+ candidateCount: servers.length,
1189
+ outcome: acquisition.outcome
1190
+ }
1191
+ });
1192
+ return spawned;
1193
+ }
1194
+ if (acquisition.outcome === "spawn-failure" || acquisition.outcome === "spawn-failure-joined") {
1195
+ if (erroredServerId === undefined) {
1196
+ erroredServerId = server.id;
1197
+ erroredOutcome = acquisition.outcome;
1198
+ }
1199
+ }
1200
+ }
1201
+ if (erroredServerId !== undefined) {
1202
+ logLatency({
1203
+ type: "phase",
1204
+ phase: "lsp_client_selected",
1205
+ filePath,
1206
+ durationMs: 0,
1207
+ metadata: {
1208
+ serverId: erroredServerId,
1209
+ candidateCount: servers.length,
1210
+ outcome: erroredOutcome ?? "spawn-failure"
1211
+ }
1212
+ });
1213
+ }
1214
+ const unavailable = (await Promise.all(servers.map(async (server) => {
1215
+ const root = await this.resolveServerRootOnce(server, filePath, rootMemo);
1216
+ return {
1217
+ server,
1218
+ key: `${server.id}:${root ? normalizeMapKey(root) : "<unresolved>"}`
1219
+ };
1220
+ }))).filter(({ key }) => !this.unavailableLogged.has(key));
1221
+ for (const { key } of unavailable)
1222
+ this.unavailableLogged.add(key);
1223
+ if (unavailable.length === 0)
1224
+ return;
1225
+ logLatency({
1226
+ type: "phase",
1227
+ phase: "lsp_client_unavailable",
1228
+ filePath,
1229
+ durationMs: 0,
1230
+ metadata: {
1231
+ candidateCount: unavailable.length,
1232
+ servers: unavailable.map(({ server }) => server.id)
1233
+ }
1234
+ });
1235
+ return;
1236
+ };
1237
+ if (!effectiveMaxWaitMs || effectiveMaxWaitMs <= 0) {
1238
+ return withBudget();
1239
+ }
1240
+ const timeoutSentinel = Symbol("lsp-client-wait-timeout");
1241
+ let waitTimer;
1242
+ let waitResult;
1243
+ try {
1244
+ waitResult = await Promise.race([
1245
+ withBudget(),
1246
+ knownSlow,
1247
+ new Promise((resolve) => {
1248
+ waitTimer = setTimeout(() => resolve(timeoutSentinel), effectiveMaxWaitMs);
1249
+ })
1250
+ ]);
1251
+ } finally {
1252
+ if (waitTimer)
1253
+ clearTimeout(waitTimer);
1254
+ }
1255
+ if (waitResult === knownSlowSentinel) {
1256
+ const { rooted, unrootedIds, unrootedReasons } = await partitionCandidates();
1257
+ for (const { server, root } of rooted) {
1258
+ const client = this.state.clients.get(`${server.id}:${normalizeMapKey(root)}`);
1259
+ if (client?.isAlive())
1260
+ return { client, info: server };
1261
+ }
1262
+ waitSkipReasons?.add("budget_skipped_known_slow");
1263
+ logLatency({
1264
+ type: "phase",
1265
+ phase: "lsp_client_wait_skipped",
1266
+ filePath,
1267
+ durationMs: 0,
1268
+ metadata: {
1269
+ maxWaitMs: effectiveMaxWaitMs,
1270
+ serverIds: rooted.map(({ server }) => server.id),
1271
+ unrootedCandidates: {
1272
+ count: unrootedIds.length,
1273
+ ids: unrootedIds,
1274
+ ...Object.keys(unrootedReasons).length > 0 ? { reasons: unrootedReasons } : {}
1275
+ },
1276
+ reason: "budget_skipped_known_slow"
1277
+ }
1278
+ });
1279
+ return;
1280
+ }
1281
+ if (waitResult === timeoutSentinel) {
1282
+ const partitionBudgetMs = hardCapMs !== undefined ? Math.min(250, hardCapMs / 4) : 250;
1283
+ const partitioned = await withDeadline(partitionCandidates(), {
1284
+ ms: partitionBudgetMs,
1285
+ onTimeout: "undefined"
1286
+ });
1287
+ const { rooted, unrootedIds, unrootedReasons } = partitioned ?? {
1288
+ rooted: [],
1289
+ unrootedIds: [],
1290
+ unrootedReasons: {}
1291
+ };
1292
+ const knownHealth = [...this.state.clients.entries()].filter(([k]) => rooted.some(({ server }) => k.startsWith(`${server.id}:`))).map(([k, c]) => ({
1293
+ serverId: k.split(":")[0],
1294
+ alive: c.isAlive(),
1295
+ spawnedAt: this.state.clientSpawnedAt.get(k) ?? null
1296
+ }));
1297
+ logLatency({
1298
+ type: "phase",
1299
+ phase: "lsp_client_wait_timeout",
1300
+ filePath,
1301
+ durationMs: effectiveMaxWaitMs,
1302
+ metadata: {
1303
+ maxWaitMs: effectiveMaxWaitMs,
1304
+ serverIds: rooted.map(({ server }) => server.id),
1305
+ unrootedCandidates: {
1306
+ count: unrootedIds.length,
1307
+ ids: unrootedIds,
1308
+ ...Object.keys(unrootedReasons).length > 0 ? { reasons: unrootedReasons } : {}
1309
+ },
1310
+ knownClientHealth: knownHealth,
1311
+ ...partitioned === undefined ? { partition: "timed-out" } : {}
1312
+ }
1313
+ });
1314
+ return;
1315
+ }
1316
+ return waitResult;
1317
+ }
1318
+ async getClientsForFile(filePath, excludeServerIds, resolvedRoots) {
1319
+ const allServers = getServersForFileWithConfig(filePath);
1320
+ const servers = excludeServerIds && excludeServerIds.size > 0 ? allServers.filter((s) => !excludeServerIds.has(s.id)) : allServers;
1321
+ if (servers.length === 0)
1322
+ return { clients: [], serverCountAttempted: 0 };
1323
+ const rootedServers = (await Promise.all(servers.map(async (server) => ({
1324
+ server,
1325
+ root: await this.resolveServerRoot(server, filePath)
1326
+ })))).filter((entry) => entry.root !== undefined);
1327
+ let serverCountAttempted = 0;
1328
+ const acquisitions = new Map;
1329
+ const acquire = (server) => {
1330
+ serverCountAttempted += 1;
1331
+ return this.ensureClientForServer(filePath, server, resolvedRoots);
1332
+ };
1333
+ for (const { server } of rootedServers) {
1334
+ const preferred = server.fallbackFor ? acquisitions.get(server.fallbackFor) : undefined;
1335
+ const acquisition = preferred ? preferred.then((entry) => entry === undefined ? acquire(server) : undefined) : acquire(server);
1336
+ acquisitions.set(server.id, acquisition);
1337
+ }
1338
+ const results = await Promise.all(acquisitions.values());
1339
+ const clients = results.filter((entry) => entry !== undefined);
1340
+ return { clients, serverCountAttempted };
1341
+ }
1342
+ async getAuxiliaryClientsForFile(filePath, enabledIds, onOutcome, maxWaitMs, signal, hook) {
1343
+ if (this.checkDestroyed() || enabledIds.size === 0)
1344
+ return [];
1345
+ const servers = getServersForFileWithConfig(filePath).filter((s) => s.role === "auxiliary" && enabledIds.has(s.id));
1346
+ if (servers.length === 0)
1347
+ return [];
1348
+ const rootMemo = new Map;
1349
+ const effectiveSignal = signal ?? getAmbientAbortSignal();
1350
+ const effectiveHook = hook ?? "tool_result_edit";
1351
+ const spawned = await Promise.all(servers.map(async (server) => {
1352
+ const acquisition = this.ensureClientForServer(filePath, server, undefined, undefined, (reported) => onOutcome?.(server.id, reported), rootMemo);
1353
+ if (!maxWaitMs || maxWaitMs <= 0) {
1354
+ return acquisition;
1355
+ }
1356
+ return bounded(acquisition, {
1357
+ ms: maxWaitMs,
1358
+ signal: effectiveSignal,
1359
+ hook: effectiveHook,
1360
+ label: `auxiliary-spawn:${server.id}`
1361
+ });
1362
+ }));
1363
+ return spawned.filter((entry) => Boolean(entry));
1364
+ }
1365
+ async getWarmClientForFile(filePath) {
1366
+ if (this.checkDestroyed())
1367
+ return;
1368
+ const servers = getServersForFileWithConfig(filePath);
1369
+ const missed = [];
1370
+ for (const server of servers) {
1371
+ const root = await this.resolveServerRoot(server, filePath);
1372
+ if (!root)
1373
+ continue;
1374
+ const key = `${server.id}:${normalizeMapKey(root)}`;
1375
+ const existing = this.state.clients.get(key);
1376
+ if (existing?.isAlive()) {
1377
+ return { client: existing, info: server };
1378
+ }
1379
+ missed.push(key);
1380
+ }
1381
+ if (missed.length > 0) {
1382
+ emitBounded("lsp_warm_client_missing", missed.join(","), {
1383
+ filePath,
1384
+ durationMs: 0,
1385
+ metadata: {
1386
+ serverIds: missed.map((key) => key.slice(0, key.indexOf(":"))),
1387
+ roots: missed.map((key) => key.slice(key.indexOf(":") + 1))
1388
+ }
1389
+ }, {
1390
+ ledgerKind: "lsp-warm-client-missing",
1391
+ risingEdgePer: "identity",
1392
+ reason: `no warm client for ${missed.join(",")}`
1393
+ });
1394
+ }
1395
+ return;
1396
+ }
1397
+ async hasServerPublishedForFileRoot(serverId, filePath) {
1398
+ if (this.checkDestroyed())
1399
+ return false;
1400
+ for (const server of getServersForFileWithConfig(filePath)) {
1401
+ if (server.id !== serverId)
1402
+ continue;
1403
+ const root = await this.resolveServerRoot(server, filePath);
1404
+ if (!root)
1405
+ continue;
1406
+ const key = `${server.id}:${normalizeMapKey(root)}`;
1407
+ const client = this.state.clients.get(key);
1408
+ if (client?.isAlive() && client.getDiagnosticsVersionForPath(filePath) > 0) {
1409
+ return true;
1410
+ }
1411
+ }
1412
+ return false;
1413
+ }
1414
+ async readCachedDiagnosticsForServers(filePath, serverIds) {
1415
+ const out = new Map;
1416
+ if (this.checkDestroyed() || serverIds.size === 0)
1417
+ return out;
1418
+ for (const server of getServersForFileWithConfig(filePath)) {
1419
+ if (!serverIds.has(server.id) || out.has(server.id))
1420
+ continue;
1421
+ const root = await this.resolveServerRoot(server, filePath);
1422
+ if (!root)
1423
+ continue;
1424
+ const key = `${server.id}:${normalizeMapKey(root)}`;
1425
+ const client = this.state.clients.get(key);
1426
+ if (!client?.isAlive()) {
1427
+ const demotedAt = this.notifyStallDemotions.get(key);
1428
+ if (demotedAt !== undefined) {
1429
+ out.set(server.id, {
1430
+ diags: [],
1431
+ notifyStallDemoted: true,
1432
+ demotedAt
1433
+ });
1434
+ }
1435
+ continue;
1436
+ }
1437
+ this.notifyStallDemotions.delete(key);
1438
+ const entry = client.getAllDiagnostics().get(normalizeMapKey(filePath));
1439
+ out.set(server.id, {
1440
+ diags: entry?.diags ?? [],
1441
+ publishedAt: entry?.ts
1442
+ });
1443
+ }
1444
+ return out;
1445
+ }
1446
+ async observeLateAuxiliaryAnswer(filePath, serverId, elapsedMs) {
1447
+ for (const server of getServersForFileWithConfig(filePath)) {
1448
+ if (server.id !== serverId)
1449
+ continue;
1450
+ const root = await this.resolveServerRoot(server, filePath);
1451
+ if (root === undefined)
1452
+ return;
1453
+ this.noteAuxiliaryLateAnswer(`${serverId}:${normalizeMapKey(root)}`, elapsedMs);
1454
+ return;
1455
+ }
1456
+ }
1457
+ async notifyExternalFileChange(filePath, type) {
1458
+ if (this.checkDestroyed())
1459
+ return;
1460
+ for (const server of getServersForFileWithConfig(filePath)) {
1461
+ const root = await this.resolveServerRoot(server, filePath);
1462
+ if (!root)
1463
+ continue;
1464
+ const key = `${server.id}:${normalizeMapKey(root)}`;
1465
+ const existing = this.state.clients.get(key);
1466
+ if (existing?.isAlive()) {
1467
+ existing.notify.watchedFileChange(filePath, type);
1468
+ }
1469
+ }
1470
+ }
1471
+ async resyncGitChangedFiles(changedPaths) {
1472
+ if (this.checkDestroyed() || changedPaths.length === 0)
1473
+ return;
1474
+ const targets = new Set;
1475
+ for (const changedPath of changedPaths) {
1476
+ const resolved = path.resolve(changedPath);
1477
+ for (const server of getServersForFileWithConfig(resolved)) {
1478
+ const root = await this.resolveServerRoot(server, resolved);
1479
+ if (!root)
1480
+ continue;
1481
+ const index = loadReverseDependencyIndexFromSnapshot({ cwd: root });
1482
+ for (const importer of index ? getReverseDepsFromIndex(index, resolved) : []) {
1483
+ targets.add(importer);
1484
+ }
1485
+ }
1486
+ targets.add(resolved);
1487
+ }
1488
+ const openTargets = [...targets].filter((filePath) => this.hasLiveClientHoldingDocument(filePath));
1489
+ this.documentDrift.enqueueResync(openTargets);
1490
+ const pass = await this.sweepDocumentDrift({ force: true });
1491
+ logLatency({
1492
+ type: "phase",
1493
+ phase: "lsp_external_change_resync",
1494
+ filePath: changedPaths[0] ?? "",
1495
+ durationMs: 0,
1496
+ metadata: {
1497
+ changedCount: changedPaths.length,
1498
+ enqueued: openTargets.length,
1499
+ deferred: pass?.deferred ?? this.documentDrift.pendingResyncCount
1500
+ }
1501
+ });
1502
+ }
1503
+ async sweepDocumentDrift(options = {}) {
1504
+ if (this.checkDestroyed())
1505
+ return;
1506
+ return this.documentDrift.sweep({
1507
+ resync: async (filePath, content) => {
1508
+ await this.touchFile(filePath, content, {
1509
+ diagnostics: "none",
1510
+ source: "drift_resync",
1511
+ clientScope: "all",
1512
+ excludeServerIds: await this.serverIdsNotHoldingDocument(filePath)
1513
+ });
1514
+ return this.documentDrift.peek(filePath)?.fingerprint === fingerprintDocumentContent(content);
1515
+ },
1516
+ holdsDocument: (filePath) => this.hasLiveClientHoldingDocument(filePath),
1517
+ onDrift: (event) => {
1518
+ if (event.disposition === "unchanged" || event.disposition === "vanished" || event.disposition === "unheld") {
1519
+ return;
1520
+ }
1521
+ incrementDegradationCount({
1522
+ kind: "lsp-document-drift",
1523
+ subject: event.filePath,
1524
+ reason: event.disposition === "deferred" ? `disk drift deferred by resync pacing after ${event.driftAgeMs}ms` : event.disposition === "failed" ? `resync FAILED after ${event.driftAgeMs}ms of untracked disk drift; view still stale (${event.syncedSize}->${event.diskSize} bytes)` : `resynced after ${event.driftAgeMs}ms of untracked disk drift (${event.syncedSize}->${event.diskSize} bytes)`
1525
+ });
1526
+ logLatency({
1527
+ type: "phase",
1528
+ phase: "lsp_document_drift",
1529
+ filePath: event.filePath,
1530
+ durationMs: event.driftAgeMs,
1531
+ metadata: {
1532
+ disposition: event.disposition,
1533
+ driftAgeMs: event.driftAgeMs,
1534
+ syncedSize: event.syncedSize,
1535
+ diskSize: event.diskSize
1536
+ }
1537
+ });
1538
+ }
1539
+ }, options);
1540
+ }
1541
+ _driftTrackedCountForTests() {
1542
+ return this.documentDrift.size;
1543
+ }
1544
+ hasLiveClientHoldingDocument(filePath) {
1545
+ for (const client of this.state.clients.values()) {
1546
+ if (!client.isAlive())
1547
+ continue;
1548
+ if (documentIsOpenOn(client, filePath))
1549
+ return true;
1550
+ }
1551
+ return false;
1552
+ }
1553
+ async serverIdsNotHoldingDocument(filePath) {
1554
+ const exclude = new Set;
1555
+ for (const server of getServersForFileWithConfig(filePath)) {
1556
+ const root = await this.resolveServerRoot(server, filePath);
1557
+ const existing = root ? this.state.clients.get(`${server.id}:${normalizeMapKey(root)}`) : undefined;
1558
+ if (existing?.isAlive() && documentIsOpenOn(existing, filePath))
1559
+ continue;
1560
+ exclude.add(server.id);
1561
+ }
1562
+ return exclude;
1563
+ }
1564
+ recordFullyCoveredSync(filePath, content, targeted, allWritesLanded, at) {
1565
+ if (!allWritesLanded || targeted.length === 0)
1566
+ return;
1567
+ const targetedClients = new Set(targeted.map((entry) => entry.client));
1568
+ for (const client of this.state.clients.values()) {
1569
+ if (targetedClients.has(client))
1570
+ continue;
1571
+ if (!client.isAlive())
1572
+ continue;
1573
+ if (documentIsOpenOn(client, filePath))
1574
+ return;
1575
+ }
1576
+ this.documentDrift.recordSynced(filePath, content, at);
1577
+ }
1578
+ noteSpawnVerdict(key, verdict) {
1579
+ this.lastSpawnVerdict.set(key, verdict);
1580
+ }
1581
+ resolveServerRootOnce(server, filePath, memo, onRootFailure) {
1582
+ if (!memo)
1583
+ return this.resolveServerRoot(server, filePath, onRootFailure);
1584
+ const pending = memo.get(server.id);
1585
+ if (pending !== undefined)
1586
+ return pending;
1587
+ const started = this.resolveServerRoot(server, filePath, onRootFailure);
1588
+ memo.set(server.id, started);
1589
+ return started;
1590
+ }
1591
+ async ensureClientForServer(filePath, server, resolvedRoots, onSpawnInFlight, onOutcome, rootMemo) {
1592
+ const handoff = this.generationHandoff;
1593
+ if (handoff) {
1594
+ await handoff;
1595
+ if (this.generationHandoff === handoff) {
1596
+ this.generationHandoff = undefined;
1597
+ }
1598
+ if (this.checkDestroyed())
1599
+ return;
1600
+ }
1601
+ const root = await this.resolveServerRootOnce(server, filePath, rootMemo);
1602
+ if (!root || this.checkDestroyed())
1603
+ return;
1604
+ if (server.role !== "auxiliary") {
1605
+ resolvedRoots?.set(server.id, normalizeMapKey(root));
1606
+ }
1607
+ const allowInstall = this.shouldAllowInstall(server.id);
1608
+ const normalizedRoot = normalizeMapKey(root);
1609
+ const key = `${server.id}:${normalizedRoot}`;
1610
+ const isOptionalServer = OPTIONAL_LSP_SERVER_IDS.has(server.id);
1611
+ if (server.availabilityKey && isDirectLspCommandTemporarilyUnavailable(server.availabilityKey)) {
1612
+ emitBounded("lsp_client_skipped_unavailable_command", `${server.availabilityKey}:${normalizeMapKey(filePath)}`, {
1613
+ filePath,
1614
+ durationMs: 0,
1615
+ metadata: {
1616
+ serverId: server.id,
1617
+ command: server.availabilityKey
1618
+ }
1619
+ }, {
1620
+ ledgerKind: "lsp-client-skipped-unavailable-command",
1621
+ risingEdgePer: "identity",
1622
+ reason: `command ${server.availabilityKey} temporarily unavailable`
1623
+ });
1624
+ return;
1625
+ }
1626
+ if (isOptionalServer && this.optionalDisabled.has(key)) {
1627
+ return;
1628
+ }
1629
+ if (this.permanentlyBroken.has(key)) {
1630
+ emitBounded("lsp_client_skipped_broken", `${server.id}:${normalizeMapKey(filePath)}`, {
1631
+ filePath,
1632
+ durationMs: 0,
1633
+ metadata: {
1634
+ serverId: server.id,
1635
+ permanent: true
1636
+ }
1637
+ }, {
1638
+ ledgerKind: "lsp-client-skipped-broken",
1639
+ risingEdgePer: "identity",
1640
+ reason: `${server.id} latched permanently broken`
1641
+ });
1642
+ return;
1643
+ }
1644
+ const existing = this.state.clients.get(key);
1645
+ if (existing) {
1646
+ if (existing.isAlive()) {
1647
+ this.unavailableLogged.delete(key);
1648
+ this.clientLastUsedAt.set(key, Date.now());
1649
+ this.scheduleTypeScriptIdleEviction(key);
1650
+ if (!this.warmStartLogged.has(key)) {
1651
+ logSessionStart(`lsp warm-start ${server.id}: reused root=${root} file=${filePath}`);
1652
+ this.warmStartLogged.add(key);
1653
+ }
1654
+ onOutcome?.("warm-reuse");
1655
+ return { client: existing, info: server };
1656
+ }
1657
+ const spawnedAt = this.state.clientSpawnedAt.get(key);
1658
+ const wasIntentional = existing.wasShutdownIntentional();
1659
+ const exitedAt = existing.getExitedAt();
1660
+ const uptimeMs = exitedAt != null && spawnedAt != null ? exitedAt - spawnedAt : spawnedAt != null ? Date.now() - spawnedAt : null;
1661
+ logLatency({
1662
+ type: "phase",
1663
+ phase: "lsp_server_respawn",
1664
+ filePath,
1665
+ durationMs: 0,
1666
+ metadata: {
1667
+ serverId: server.id,
1668
+ root,
1669
+ uptimeMs,
1670
+ intentional: wasIntentional
1671
+ }
1672
+ });
1673
+ this.releaseOutstandingAuxNotifyWrite(key);
1674
+ try {
1675
+ await existing.shutdown();
1676
+ } catch {}
1677
+ this.state.clients.delete(key);
1678
+ this.state.clientSpawnedAt.delete(key);
1679
+ this.clientLastUsedAt.delete(key);
1680
+ this.clearTypeScriptIdleTimer(key);
1681
+ this.state.broken.delete(key);
1682
+ if (wasIntentional) {} else {
1683
+ if (this.recordRuntimeExitWindow(key, exitedAt, uptimeMs)) {
1684
+ const rate = `${RUNTIME_EXIT_WINDOW_TRIP_COUNT} deaths within ${Math.round(RUNTIME_EXIT_WINDOW_MS / 60000)}m`;
1685
+ this.state.broken.set(key, Math.max(this.state.broken.get(key) ?? 0, Date.now() + (isOptionalServer ? OPTIONAL_LSP_RETRY_COOLDOWN_MS : BROKEN_MAX_COOLDOWN_MS)));
1686
+ if (isOptionalServer) {
1687
+ this.optionalDisabled.add(key);
1688
+ this.runtimeExitWindow.delete(key);
1689
+ logSessionStart(`lsp respawn ${server.id}: optional server cooled down (windowed-rate trip: ${rate})`);
1690
+ } else {
1691
+ this.permanentlyBroken.add(key);
1692
+ this.recordBreaker(key, `windowed runtime-exit trip: ${rate}`);
1693
+ logSessionStart(`lsp respawn ${server.id}: permanently disabled (windowed-rate trip: ${rate}, uptimeMs=${uptimeMs})`);
1694
+ }
1695
+ }
1696
+ if (uptimeMs != null && uptimeMs < RUNTIME_EXIT_UPTIME_THRESHOLD_MS) {
1697
+ const rCount = (this.runtimeExitCounts.get(key) ?? 0) + 1;
1698
+ this.runtimeExitCounts.set(key, rCount);
1699
+ const rCooldown = Math.min(BROKEN_BASE_COOLDOWN_MS * 2 ** (rCount - 1), BROKEN_MAX_COOLDOWN_MS);
1700
+ this.state.broken.set(key, Date.now() + rCooldown);
1701
+ if (!isOptionalServer && rCount >= BROKEN_PERMANENT_AFTER) {
1702
+ this.permanentlyBroken.add(key);
1703
+ this.recordBreaker(key, `permanently disabled after ${rCount} early post-init exits`);
1704
+ logSessionStart(`lsp respawn ${server.id}: permanently disabled after ${rCount} early post-init exits (uptimeMs=${uptimeMs})`);
1705
+ } else {
1706
+ logSessionStart(`lsp respawn ${server.id}: early post-init exit ${rCount}/${BROKEN_PERMANENT_AFTER} (uptimeMs=${uptimeMs}), cooldown=${rCooldown}ms`);
1707
+ }
1708
+ } else {
1709
+ this.runtimeExitCounts.delete(key);
1710
+ const windowDeaths = this.runtimeExitWindow.get(key);
1711
+ if (windowDeaths) {
1712
+ const windowNow = exitedAt ?? Date.now();
1713
+ if (!windowDeaths.some((t) => t >= windowNow - RUNTIME_EXIT_WINDOW_MS)) {
1714
+ this.runtimeExitWindow.delete(key);
1715
+ }
1716
+ }
1717
+ }
1718
+ }
1719
+ }
1720
+ const brokenUntil = this.state.broken.get(key);
1721
+ if (typeof brokenUntil === "number" && brokenUntil > Date.now()) {
1722
+ emitBounded("lsp_client_skipped_broken", `${server.id}:${normalizeMapKey(filePath)}`, {
1723
+ filePath,
1724
+ durationMs: 0,
1725
+ metadata: {
1726
+ serverId: server.id,
1727
+ retryInMs: Math.max(0, brokenUntil - Date.now())
1728
+ }
1729
+ }, {
1730
+ ledgerKind: "lsp-client-skipped-broken",
1731
+ risingEdgePer: "identity",
1732
+ reason: `${server.id} in breaker cooldown`
1733
+ });
1734
+ return;
1735
+ }
1736
+ if (typeof brokenUntil === "number" && brokenUntil <= Date.now()) {
1737
+ this.state.broken.delete(key);
1738
+ if (isOptionalServer)
1739
+ this.optionalDisabled.delete(key);
1740
+ }
1741
+ let startedSpawn = false;
1742
+ let spawnPromise = this.state.inFlight.get(key);
1743
+ if (!spawnPromise) {
1744
+ const started = await this.withClientSpawnGate(async () => {
1745
+ const raced = this.state.inFlight.get(key);
1746
+ if (raced)
1747
+ return { promise: raced, startedSpawn: false };
1748
+ if (this.checkDestroyed())
1749
+ return;
1750
+ if (!await this.makeCapacityForClient(key))
1751
+ return;
1752
+ const promise = this.spawnClient(server, root, key, filePath, allowInstall);
1753
+ this.state.inFlight.set(key, promise);
1754
+ return { promise, startedSpawn: true };
1755
+ });
1756
+ if (!started)
1757
+ return;
1758
+ spawnPromise = started.promise;
1759
+ startedSpawn = started.startedSpawn;
1760
+ }
1761
+ onSpawnInFlight?.(server.id);
1762
+ try {
1763
+ const spawned = await spawnPromise;
1764
+ onOutcome?.(spawned ? startedSpawn ? "cold-spawn" : "cold-spawn-joined" : this.lastSpawnVerdict.get(key) === "failed" ? startedSpawn ? "spawn-failure" : "spawn-failure-joined" : "declined");
1765
+ return spawned;
1766
+ } catch (err) {
1767
+ onOutcome?.("spawn-failure");
1768
+ throw err;
1769
+ } finally {
1770
+ if (this.state.inFlight.get(key) === spawnPromise) {
1771
+ this.state.inFlight.delete(key);
1772
+ }
1773
+ }
1774
+ }
1775
+ shouldAllowInstall(serverId) {
1776
+ if (!assertInstallAllowed(`lsp install: ${serverId}`))
1777
+ return false;
1778
+ return process.env.DM_LENS_DISABLE_LSP_INSTALL !== "1";
1779
+ }
1780
+ async spawnClient(server, root, key, filePath, allowInstall) {
1781
+ if (!isLspSpawnAllowedByTrust()) {
1782
+ logSessionStart(`lsp spawn ${server.id}: refused — ${projectTrustDenialReason()}`);
1783
+ this.noteSpawnVerdict(key, "declined");
1784
+ return;
1785
+ }
1786
+ const isOptionalServer = OPTIONAL_LSP_SERVER_IDS.has(server.id);
1787
+ const startedAt = Date.now();
1788
+ logSessionStart(`lsp spawn ${server.id}: start root=${root} install=${allowInstall ? "enabled" : "disabled"} file=${filePath}`);
1789
+ recordLsp(server.id, root, "spawn_start");
1790
+ try {
1791
+ const spawned = await server.spawn(root, { allowInstall });
1792
+ if (this.isDestroyed) {
1793
+ try {
1794
+ spawned?.process?.process?.kill();
1795
+ } catch {}
1796
+ logSessionStart(`lsp spawn ${server.id}: aborted (service shut down mid-spawn)`);
1797
+ this.noteSpawnVerdict(key, "declined");
1798
+ return;
1799
+ }
1800
+ if (!spawned) {
1801
+ logSessionStart(`lsp spawn ${server.id}: unavailable (${Date.now() - startedAt}ms)`);
1802
+ recordLsp(server.id, root, "spawn_failed", Date.now() - startedAt);
1803
+ if (!allowInstall) {
1804
+ logSessionStart(`lsp spawn ${server.id}: unavailable with install disabled; temporary cooldown only`);
1805
+ this.state.broken.set(key, Date.now() + BROKEN_BASE_COOLDOWN_MS);
1806
+ this.noteSpawnVerdict(key, "declined");
1807
+ return;
1808
+ }
1809
+ const uCount = (this.failureCounts.get(key) ?? 0) + 1;
1810
+ this.failureCounts.set(key, uCount);
1811
+ const uCooldown = Math.min(BROKEN_BASE_COOLDOWN_MS * 2 ** (uCount - 1), BROKEN_MAX_COOLDOWN_MS);
1812
+ this.state.broken.set(key, Date.now() + uCooldown);
1813
+ if (uCount >= BROKEN_PERMANENT_AFTER) {
1814
+ this.permanentlyBroken.add(key);
1815
+ this.recordBreaker(key, `permanently disabled after ${uCount} unavailable spawns`);
1816
+ logSessionStart(`lsp spawn ${server.id}: permanently disabled after ${uCount} failures`);
1817
+ }
1818
+ this.noteSpawnVerdict(key, "failed");
1819
+ return;
1820
+ }
1821
+ const override = getServerInitOverride(server.id, filePath);
1822
+ const mergedInit = mergeInitializationOptions(spawned.initialization, override?.initializationOptions);
1823
+ this.state.diagnosticsPublished.delete(server.id);
1824
+ this.state.diagnosticsUnsupported.delete(server.id);
1825
+ this.firstContactWaits.delete(server.id);
1826
+ const client = await createLSPClient({
1827
+ serverId: server.id,
1828
+ process: spawned.process,
1829
+ root,
1830
+ sessionCwd: this.sessionCwd,
1831
+ initialization: mergedInit,
1832
+ initializeTimeoutMs: server.initializeTimeoutMs,
1833
+ launchVariant: spawned.launchVariant,
1834
+ onDiagnosticsPublished: (serverId) => {
1835
+ this.state.diagnosticsPublished.add(serverId);
1836
+ }
1837
+ });
1838
+ if (this.isDestroyed) {
1839
+ client.shutdown({ fast: true }).catch(() => {});
1840
+ logSessionStart(`lsp spawn ${server.id}: aborted (service shut down mid-initialize)`);
1841
+ this.noteSpawnVerdict(key, "declined");
1842
+ return;
1843
+ }
1844
+ const wsDiag = typeof client.getWorkspaceDiagnosticsSupport === "function" ? client.getWorkspaceDiagnosticsSupport() : {
1845
+ advertised: false,
1846
+ mode: "push-only",
1847
+ diagnosticProviderKind: "unavailable"
1848
+ };
1849
+ this.state.clients.set(key, client);
1850
+ this.notifyStallDemotions.delete(key);
1851
+ this.unavailableLogged.delete(key);
1852
+ this.lastSpawnVerdict.delete(key);
1853
+ this.state.clientSpawnedAt.set(key, Date.now());
1854
+ this.clientLastUsedAt.set(key, Date.now());
1855
+ this.scheduleTypeScriptIdleEviction(key);
1856
+ this.failureCounts.delete(key);
1857
+ if (isOptionalServer) {
1858
+ this.optionalDisabled.delete(key);
1859
+ this.optionalFailureLogged.delete(key);
1860
+ }
1861
+ logSessionStart(`lsp spawn ${server.id}: success source=${spawned.source ?? "unknown"} (${Date.now() - startedAt}ms)`);
1862
+ const spawnDurationMs = Date.now() - startedAt;
1863
+ recordLsp(server.id, root, "spawn_success", spawnDurationMs);
1864
+ recordSuccessfulLspSpawn(server.id, spawnDurationMs);
1865
+ logLatency({
1866
+ type: "phase",
1867
+ phase: "lsp_server_spawned",
1868
+ filePath: root,
1869
+ durationMs: spawnDurationMs,
1870
+ metadata: {
1871
+ serverId: server.id,
1872
+ source: spawned.source ?? "unknown",
1873
+ launchVariant: spawned.launchVariant ?? "default",
1874
+ triggerFilePath: filePath
1875
+ }
1876
+ });
1877
+ if (!this.workspaceProbeLogged.has(key)) {
1878
+ logSessionStart(`lsp workspace-diag probe ${server.id}: advertised=${wsDiag.advertised} mode=${wsDiag.mode} provider=${wsDiag.diagnosticProviderKind}`);
1879
+ this.workspaceProbeLogged.add(key);
1880
+ }
1881
+ return { client, info: server };
1882
+ } catch (err) {
1883
+ recordLsp(server.id, root, "spawn_failed", Date.now() - startedAt);
1884
+ if (!isOptionalServer || !this.optionalFailureLogged.has(key)) {
1885
+ logSessionStart(`lsp spawn ${server.id}: failed (${Date.now() - startedAt}ms) error=${err instanceof Error ? err.message : String(err)}`);
1886
+ if (isOptionalServer) {
1887
+ this.optionalFailureLogged.add(key);
1888
+ }
1889
+ }
1890
+ const eCount = (this.failureCounts.get(key) ?? 0) + 1;
1891
+ this.failureCounts.set(key, eCount);
1892
+ const eCooldown = isOptionalServer ? OPTIONAL_LSP_RETRY_COOLDOWN_MS : Math.min(BROKEN_BASE_COOLDOWN_MS * 2 ** (eCount - 1), BROKEN_MAX_COOLDOWN_MS);
1893
+ this.state.broken.set(key, Date.now() + eCooldown);
1894
+ if (!isOptionalServer && eCount >= BROKEN_PERMANENT_AFTER) {
1895
+ this.permanentlyBroken.add(key);
1896
+ this.recordBreaker(key, `permanently disabled after ${eCount} spawn/initialize failures`);
1897
+ logSessionStart(`lsp spawn ${server.id}: permanently disabled after ${eCount} failures`);
1898
+ }
1899
+ if (isOptionalServer) {
1900
+ this.optionalDisabled.add(key);
1901
+ }
1902
+ this.noteSpawnVerdict(key, "failed");
1903
+ return;
1904
+ }
1905
+ }
1906
+ async openFile(filePath, content, options) {
1907
+ if (this.checkDestroyed())
1908
+ return;
1909
+ const startedAt = Date.now();
1910
+ await this.withClientForFileUse(filePath, undefined, options?.spawnBudgetMs, async (spawned) => {
1911
+ const languageId = getLanguageId(filePath) ?? "plaintext";
1912
+ await spawned.client.notify.open(filePath, content, languageId, options?.preserveDiagnostics);
1913
+ this.recordFullyCoveredSync(filePath, content, [spawned], true, startedAt);
1914
+ });
1915
+ }
1916
+ async updateFile(filePath, content) {
1917
+ if (this.checkDestroyed())
1918
+ return;
1919
+ await this.withClientForFileUse(filePath, undefined, undefined, (spawned) => spawned.client.notify.change(filePath, content));
1920
+ }
1921
+ async touchFile(filePath, content, options = {}) {
1922
+ if (this.checkDestroyed()) {
1923
+ const destroyedDiagnosticsMode = options.collectDiagnostics ? options.diagnostics ?? "document" : options.diagnostics ?? "none";
1924
+ logLatency({
1925
+ type: "phase",
1926
+ phase: "lsp_touch_file",
1927
+ filePath: normalizeMapKey(filePath),
1928
+ durationMs: 0,
1929
+ metadata: {
1930
+ serverCountAttempted: 0,
1931
+ serverCountReady: 0,
1932
+ clientScope: options.clientScope ?? (destroyedDiagnosticsMode === "full" ? "all" : "primary"),
1933
+ diagnosticsMode: destroyedDiagnosticsMode,
1934
+ source: options.source ?? "unknown",
1935
+ failureKind: "destroyed"
1936
+ }
1937
+ });
1938
+ return;
1939
+ }
1940
+ const startedAt = Date.now();
1941
+ const hookDeadlineAt = options.hook !== undefined && Object.hasOwn(HOOK_WALL_BUDGET_MS, options.hook) ? startedAt + HOOK_WALL_BUDGET_MS[options.hook] : undefined;
1942
+ const normalizedPath = normalizeMapKey(filePath);
1943
+ const outsideRoot = await this.findOutsideProjectRoot(filePath);
1944
+ if (outsideRoot) {
1945
+ this.recordOutsideRootDecline(filePath, outsideRoot, "touchFile");
1946
+ return { diags: [], skipReason: "outside-project-root" };
1947
+ }
1948
+ this.sweepDocumentDrift().catch(() => {});
1949
+ const diagnosticsMode = options.collectDiagnostics ? options.diagnostics ?? "document" : options.diagnostics ?? "none";
1950
+ const source = options.source ?? "unknown";
1951
+ const clientScope = options.clientScope ?? (diagnosticsMode === "full" ? "all" : "primary");
1952
+ const useAllClients = clientScope === "all";
1953
+ const resolvedPrimaryRoots = new Map;
1954
+ const waitSkipReasons = new Set;
1955
+ const coldAuxiliaryServerIds = new Set;
1956
+ const noteColdAuxiliary = (serverId, outcome) => {
1957
+ if (outcome === "cold-spawn" || outcome === "cold-spawn-joined") {
1958
+ coldAuxiliaryServerIds.add(serverId);
1959
+ }
1960
+ };
1961
+ let spawned;
1962
+ let serverCountAttempted;
1963
+ if (useAllClients) {
1964
+ const result = await this.getClientsForFile(filePath, options.excludeServerIds, resolvedPrimaryRoots);
1965
+ spawned = result.clients;
1966
+ serverCountAttempted = result.serverCountAttempted;
1967
+ } else if (clientScope === "with-auxiliary") {
1968
+ const auxStartedAt = Date.now();
1969
+ const [entry, aux] = await Promise.all([
1970
+ this.getClientForFile(filePath, options.maxClientWaitMs, undefined, resolvedPrimaryRoots, waitSkipReasons),
1971
+ this.getAuxiliaryClientsForFile(filePath, new Set(options.auxiliaryServerIds ?? []), noteColdAuxiliary, options.maxClientWaitMs, undefined, options.hook)
1972
+ ]);
1973
+ const auxDurationMs = Date.now() - auxStartedAt;
1974
+ if (options.auxiliaryServerIds && options.auxiliaryServerIds.length > 0) {
1975
+ logLatency({
1976
+ type: "phase",
1977
+ phase: "auxiliary_readiness",
1978
+ filePath: normalizedPath,
1979
+ durationMs: auxDurationMs,
1980
+ metadata: {
1981
+ serverIds: aux.map((s) => s.info.id),
1982
+ attemptedCount: options.auxiliaryServerIds.length,
1983
+ readyCount: aux.length,
1984
+ coldServerIds: [...coldAuxiliaryServerIds],
1985
+ source
1986
+ }
1987
+ });
1988
+ }
1989
+ spawned = entry ? [entry, ...aux] : aux;
1990
+ serverCountAttempted = spawned.length;
1991
+ } else {
1992
+ const entry = await this.getClientForFile(filePath, options.maxClientWaitMs, undefined, resolvedPrimaryRoots, waitSkipReasons);
1993
+ spawned = entry ? [entry] : [];
1994
+ serverCountAttempted = spawned.length > 0 ? 1 : getServersForFileWithConfig(filePath).length > 0 ? 1 : 0;
1995
+ }
1996
+ if (spawned.length === 0) {
1997
+ const failureKind = this.isSpawnInFlight(filePath, resolvedPrimaryRoots) ? "spawn_in_flight_budget_elapsed" : "no_clients_none_spawning";
1998
+ logLatency({
1999
+ type: "phase",
2000
+ phase: "lsp_touch_file",
2001
+ filePath: normalizedPath,
2002
+ durationMs: Date.now() - startedAt,
2003
+ metadata: {
2004
+ serverCountAttempted,
2005
+ serverCountReady: 0,
2006
+ clientScope,
2007
+ diagnosticsMode,
2008
+ source,
2009
+ maxClientWaitMs: options.maxClientWaitMs,
2010
+ failureKind,
2011
+ ...waitSkipReasons.size > 0 ? { reason: [...waitSkipReasons][0] } : {}
2012
+ }
2013
+ });
2014
+ return;
2015
+ }
2016
+ const leaseKeys = await this.acquireClientLeases(spawned, filePath);
2017
+ if (!leaseKeys) {
2018
+ if (this.checkDestroyed()) {
2019
+ const primaryServerIds = spawned.filter((entry) => entry.info.role !== "auxiliary").map((entry) => entry.info.id);
2020
+ return {
2021
+ diags: [],
2022
+ inconclusive: true,
2023
+ ...primaryServerIds.length > 0 && {
2024
+ inconclusiveServerIds: primaryServerIds
2025
+ },
2026
+ inconclusiveReason: "diagnostics-wait"
2027
+ };
2028
+ }
2029
+ return this.touchFile(filePath, content, options);
2030
+ }
2031
+ try {
2032
+ const spawnedServerIds = spawned.map((entry) => entry.info.id);
2033
+ if (this.shouldSkipTouch(filePath, content, clientScope, diagnosticsMode !== "none", spawnedServerIds)) {
2034
+ logLatency({
2035
+ type: "phase",
2036
+ phase: "lsp_touch_file",
2037
+ filePath: normalizedPath,
2038
+ durationMs: Date.now() - startedAt,
2039
+ metadata: {
2040
+ serverCountReady: spawned.length,
2041
+ clientScope,
2042
+ diagnosticsMode,
2043
+ source,
2044
+ failureKind: "success",
2045
+ skipped: true,
2046
+ reason: "debounced_unchanged_content"
2047
+ }
2048
+ });
2049
+ return { diags: [] };
2050
+ }
2051
+ const languageId = getLanguageId(filePath) ?? "plaintext";
2052
+ const silent = options.silent ?? false;
2053
+ const notifySkippedServerIds = new Set(spawnedServerIds.filter((serverId) => this.shouldSkipNotify(filePath, content, clientScope, serverId)));
2054
+ const notifySkipped = spawned.length > 0 && notifySkippedServerIds.size === spawned.length;
2055
+ const readPathVersion = (client) => client.getDiagnosticsVersionForPath?.(filePath);
2056
+ const diagnosticBaselines = new Map(spawned.map((entry) => [entry.client, readPathVersion(entry.client)]));
2057
+ const markedAtMs = Date.now();
2058
+ const touchContentHash = this.hashContent(content);
2059
+ const bindingMatchesTouchContent = (binding) => binding?.contentHash === touchContentHash;
2060
+ const carriedAuxiliary = options.collectDiagnostics ? spawned.flatMap((entry) => {
2061
+ if (entry.info.role !== "auxiliary")
2062
+ return [];
2063
+ const binding = entry.client.getDiagnosticBinding?.(filePath);
2064
+ if (!bindingMatchesTouchContent(binding))
2065
+ return [];
2066
+ const diags = entry.client.getDiagnostics(filePath);
2067
+ return diags.length > 0 ? [{ serverId: entry.info.id, diags, binding }] : [];
2068
+ }) : [];
2069
+ const auxPublishedThisContent = new Set(spawned.flatMap((entry) => entry.info.role === "auxiliary" && bindingMatchesTouchContent(entry.client.getDiagnosticBinding?.(filePath)) ? [entry.info.id] : []));
2070
+ const spawnedByServerId = new Map(spawned.map((entry) => [entry.info.id, entry]));
2071
+ const auxCoversThisContent = (serverId) => {
2072
+ const entry = spawnedByServerId.get(serverId);
2073
+ if (entry?.info.role !== "auxiliary")
2074
+ return false;
2075
+ return auxPublishedThisContent.has(serverId) || bindingMatchesTouchContent(entry.client.getDiagnosticBinding?.(filePath));
2076
+ };
2077
+ const notifyWriteTimedOutServerIds = [];
2078
+ const notifyDeferredServerIds = [];
2079
+ if (!notifySkipped) {
2080
+ const budget = notifyWriteBudgetMs();
2081
+ const queueWaitMs = options.maxClientWaitMs !== undefined ? Math.min(budget, Math.max(0, primaryServerWaitFloorMs(filePath, options.maxClientWaitMs) - (Date.now() - startedAt))) : budget;
2082
+ await Promise.all(spawned.map(async (entry) => {
2083
+ if (notifySkippedServerIds.has(entry.info.id))
2084
+ return;
2085
+ const clientKey = await this.demonstratedReadyKeyFor(entry.info, filePath);
2086
+ const gated = entry.info.role === "auxiliary" && clientKey !== undefined;
2087
+ let slot;
2088
+ if (gated && clientKey) {
2089
+ const barrierStartedAt = Date.now();
2090
+ await this.paceAuxNotify(clientKey, entry, filePath, queueWaitMs, {
2091
+ source,
2092
+ clientScope
2093
+ });
2094
+ const slotWaitMs = Math.max(0, queueWaitMs - (Date.now() - barrierStartedAt));
2095
+ const claim = await this.claimAuxNotifySlot(clientKey, entry, filePath, slotWaitMs);
2096
+ if ("outstandingMs" in claim) {
2097
+ notifyDeferredServerIds.push(entry.info.id);
2098
+ logLatency({
2099
+ type: "phase",
2100
+ phase: "lsp_notify_resync_deferred",
2101
+ filePath: normalizedPath,
2102
+ durationMs: claim.outstandingMs,
2103
+ metadata: {
2104
+ serverId: entry.info.id,
2105
+ source,
2106
+ clientScope,
2107
+ reason: "outstanding_write",
2108
+ outstandingMs: claim.outstandingMs,
2109
+ queueWaitMs
2110
+ }
2111
+ });
2112
+ return;
2113
+ }
2114
+ slot = claim;
2115
+ }
2116
+ let wrote;
2117
+ let rejected = false;
2118
+ try {
2119
+ const writeStartedAt = Date.now();
2120
+ const writePromise = entry.client.notify.open(filePath, content, languageId, undefined, silent, options.saved === true).then(() => true);
2121
+ if (gated && clientKey) {
2122
+ this.noteAuxNotifyIssued(clientKey, entry.client);
2123
+ }
2124
+ if (slot && clientKey) {
2125
+ const client = entry.client;
2126
+ const release = slot.release;
2127
+ writePromise.then(() => {
2128
+ release();
2129
+ const outstandingMs = Date.now() - writeStartedAt;
2130
+ if (outstandingMs > budget && outstandingMs <= this.auxNotifyWedgeBudgetMs(clientKey)) {
2131
+ this.retractNotifyWriteBackpressure(clientKey, entry.info.id, filePath, outstandingMs, client);
2132
+ }
2133
+ }, release);
2134
+ }
2135
+ wrote = await withDeadline(writePromise, {
2136
+ ms: budget,
2137
+ onTimeout: "undefined",
2138
+ onReject: "propagate"
2139
+ });
2140
+ } catch {
2141
+ rejected = true;
2142
+ slot?.release();
2143
+ }
2144
+ if (wrote === true) {
2145
+ if (clientKey)
2146
+ this.notifyWriteBackpressureStreak.delete(clientKey);
2147
+ this.markTouched(filePath, content, clientScope, entry.info.id);
2148
+ } else {
2149
+ notifyWriteTimedOutServerIds.push(entry.info.id);
2150
+ if (!rejected) {
2151
+ this.recordNotifyWriteBackpressure(clientKey, entry, filePath);
2152
+ }
2153
+ }
2154
+ }));
2155
+ this.recordFullyCoveredSync(filePath, content, spawned, notifyWriteTimedOutServerIds.length === 0 && notifyDeferredServerIds.length === 0, startedAt);
2156
+ if (notifyWriteTimedOutServerIds.length > 0) {
2157
+ logLatency({
2158
+ type: "phase",
2159
+ phase: "lsp_notify_timeout",
2160
+ filePath: normalizedPath,
2161
+ durationMs: Date.now() - startedAt,
2162
+ metadata: {
2163
+ source,
2164
+ clientScope,
2165
+ serverCount: spawned.length,
2166
+ timedOutServerIds: notifyWriteTimedOutServerIds
2167
+ }
2168
+ });
2169
+ }
2170
+ }
2171
+ const notifyWriteTimedOut = notifyWriteTimedOutServerIds.length > 0;
2172
+ const primaryEntries = spawned.filter((entry) => entry.info.role !== "auxiliary");
2173
+ const primaryServerIds = new Set(primaryEntries.map((e) => e.info.id));
2174
+ const primaryNotifyWriteTimedOutServerIds = notifyWriteTimedOutServerIds.filter((id) => primaryServerIds.has(id));
2175
+ const primaryNotifyWriteTimedOut = primaryNotifyWriteTimedOutServerIds.length > 0;
2176
+ const deferredResyncServerIds = new Set(notifyDeferredServerIds);
2177
+ let diagnosticsTimedOut = false;
2178
+ let diagnosticsUnansweredServerIds = [];
2179
+ let diagnosticsUnansweredPrimaryServerIds = [];
2180
+ const retractPrimaryTimeoutAttribution = () => {
2181
+ diagnosticsUnansweredPrimaryServerIds = [];
2182
+ diagnosticsUnansweredServerIds = diagnosticsUnansweredServerIds.filter((id) => !primaryServerIds.has(id));
2183
+ };
2184
+ let auxCutOffServerIds;
2185
+ let auxUnconfirmedServerIds;
2186
+ let lateDeliveryServerIds;
2187
+ let tsserverSyncEligible = false;
2188
+ const isWarmupTouch = source === "lsp_sweep_warmup";
2189
+ let tsserverSyncConfirmed;
2190
+ let diagnosticsUnsupportedServerIds = [];
2191
+ if (diagnosticsMode !== "none") {
2192
+ const envWait = readEnvDiagnosticsWaitMs();
2193
+ const callerCap = options.maxDiagnosticsWaitMs ?? options.maxClientWaitMs;
2194
+ const modeFloor = diagnosticsMode === "full" ? 3000 : 1200;
2195
+ const sweepFirstTouch = new Map;
2196
+ if (options.sweepIndexGate) {
2197
+ for (const entry of spawned) {
2198
+ const strategy = getStrategy(entry.client.serverId, entry.client.getLaunchVariant?.());
2199
+ if (strategy.workspaceIndexing) {
2200
+ sweepFirstTouch.set(entry.client.serverId, options.sweepIndexGate.consumeFirstTouch(entry.client.serverId));
2201
+ }
2202
+ }
2203
+ }
2204
+ const silentCleanWarmupServers = new Set;
2205
+ if (options.warmupOverride && (options.warmupAttempt ?? 1) <= 1) {
2206
+ for (const entry of spawned) {
2207
+ const strategy = getStrategy(entry.client.serverId, entry.client.getLaunchVariant?.());
2208
+ if (strategy.workspaceIndexing !== true)
2209
+ continue;
2210
+ try {
2211
+ const snapshot = {
2212
+ serverId: entry.client.serverId,
2213
+ root: entry.client.root,
2214
+ operationSupport: entry.client.getOperationSupport(),
2215
+ workspaceDiagnosticsSupport: entry.client.getWorkspaceDiagnosticsSupport(),
2216
+ customServer: entry.info.custom,
2217
+ diagnosticsPublished: this.state.diagnosticsPublished.has(entry.info.id),
2218
+ diagnosticsUnsupported: this.state.diagnosticsUnsupported.has(entry.info.id),
2219
+ advertisedCommands: entry.client.getAdvertisedCommands(),
2220
+ rawCapabilityKeys: entry.client.getRawCapabilityKeys?.() ?? [],
2221
+ launchVariant: entry.client.getLaunchVariant?.()
2222
+ };
2223
+ if (classifyServerWaitTier(entry.client.serverId, snapshot) === "tier3-silent") {
2224
+ silentCleanWarmupServers.add(entry.client.serverId);
2225
+ }
2226
+ } catch {}
2227
+ }
2228
+ }
2229
+ const perServerTimeout = (serverId) => {
2230
+ const launchVariant = spawned.find((entry) => entry.client.serverId === serverId)?.client.getLaunchVariant?.();
2231
+ const strategy = getStrategy(serverId, launchVariant);
2232
+ let strategyWait = strategy.aggregateWaitMs;
2233
+ const isFirstTouch = sweepFirstTouch.get(serverId);
2234
+ if (isFirstTouch === false && strategy.workspaceIndexing) {
2235
+ strategyWait = strategy.workspaceIndexingWarmWaitMs ?? Math.min(300, strategyWait);
2236
+ }
2237
+ if (callerCap !== undefined) {
2238
+ if (options.warmupOverride) {
2239
+ if (silentCleanWarmupServers.has(serverId)) {
2240
+ return Math.min(callerCap, strategyWait > 0 ? strategyWait : callerCap);
2241
+ }
2242
+ if ((options.warmupAttempt ?? 1) > 1) {
2243
+ return Math.min(callerCap, strategyWait > 0 ? strategyWait : callerCap);
2244
+ }
2245
+ return Math.max(callerCap, strategyWait > 0 ? strategyWait : 0);
2246
+ }
2247
+ return Math.min(callerCap, strategyWait > 0 ? strategyWait : callerCap);
2248
+ }
2249
+ return strategyWait > 0 ? strategyWait : modeFloor;
2250
+ };
2251
+ let timeoutFor;
2252
+ if (envWait !== undefined) {
2253
+ timeoutFor = () => envWait;
2254
+ } else if (!useAllClients && spawned.length === 1 || clientScope === "with-auxiliary" || clientScope === "all") {
2255
+ timeoutFor = perServerTimeout;
2256
+ } else {
2257
+ timeoutFor = () => callerCap ?? modeFloor;
2258
+ }
2259
+ const timeoutMs = Math.max(0, ...spawned.filter((e) => !deferredResyncServerIds.has(e.info.id)).map((e) => timeoutFor(e.client.serverId)));
2260
+ if (!notifyWriteTimedOut && (options.collectDiagnostics === true || isWarmupTouch) && clientScope === "primary" && spawned.length === 1 && spawned[0].client.serverId === "typescript" && getStrategy(spawned[0].client.serverId, spawned[0].client.getLaunchVariant?.()).silentOnClean === true) {
2261
+ try {
2262
+ const snapshots = await this.getCapabilitySnapshots(filePath);
2263
+ tsserverSyncEligible = classifyCascadeWaitTier(this, filePath, snapshots) === "tier3-silent";
2264
+ } catch {}
2265
+ }
2266
+ const waitStartedAt = Date.now();
2267
+ const hasTouchAuxiliaries = clientScope === "with-auxiliary" && options.collectDiagnostics === true && spawned.some((e) => e.info.role === "auxiliary");
2268
+ if (spawned.some((entry) => entry.info.custom === true)) {
2269
+ try {
2270
+ const snapshotHook = options.hook ?? "tool_result_edit";
2271
+ const snapshotBudget = hookDeadlineAt === undefined ? HOOK_WALL_BUDGET_MS.tool_result_edit : Math.max(0, hookDeadlineAt - Date.now());
2272
+ const snapshots = await bounded(this.getCapabilitySnapshots(filePath), {
2273
+ ms: snapshotBudget,
2274
+ signal: getAmbientAbortSignal(),
2275
+ shutdownSignal: this.shutdownController.signal,
2276
+ hook: snapshotHook,
2277
+ label: `getCapabilitySnapshots:${filePath}`
2278
+ });
2279
+ if (snapshots !== undefined) {
2280
+ diagnosticsUnsupportedServerIds = spawned.filter((entry) => classifyServerWaitTier(entry.info.id, snapshots.find((s) => s.serverId === entry.info.id)) === "diagnostics-unsupported").map((entry) => entry.info.id);
2281
+ }
2282
+ } catch {}
2283
+ }
2284
+ let pressureSnapshots = [];
2285
+ if (shouldPreferPullOnlyDiagnostics()) {
2286
+ try {
2287
+ pressureSnapshots = await this.getCapabilitySnapshots(filePath);
2288
+ } catch {}
2289
+ }
2290
+ const configuredAuxCeilingMs = readEnvAuxGraceMs();
2291
+ const perServerDeclaredTimeouts = spawned.map((entry) => timeoutFor(entry.client.serverId));
2292
+ const perServerWaitTimeouts = spawned.map((entry, entryIndex) => {
2293
+ const declaredServerTimeout = perServerDeclaredTimeouts[entryIndex];
2294
+ const observedSpawnMs = getSuccessfulLspSpawnDurationMs(entry.client.serverId);
2295
+ return hasTouchAuxiliaries && entry.info.role === "auxiliary" && coldAuxiliaryServerIds.has(entry.client.serverId) && (configuredAuxCeilingMs !== undefined || observedSpawnMs !== undefined && observedSpawnMs > 0) ? auxWaitBudgetMs(entry.client.serverId, true, configuredAuxCeilingMs, declaredServerTimeout) : perServerDeclaredTimeouts[entryIndex];
2296
+ });
2297
+ const firstContactCutOffServerIds = new Set;
2298
+ const firstContactErroredServerIds = new Set;
2299
+ const answeredForThisTouch = (entry) => {
2300
+ const baseline = diagnosticBaselines.get(entry.client);
2301
+ const currentPathVersion = readPathVersion(entry.client);
2302
+ if (auxiliaryPublicationEvidence({
2303
+ bindingMatchesContent: false,
2304
+ baseline,
2305
+ currentPathVersion
2306
+ })) {
2307
+ return true;
2308
+ }
2309
+ try {
2310
+ return entry.client.getAllDiagnostics?.().has(normalizedPath) === true;
2311
+ } catch {
2312
+ return false;
2313
+ }
2314
+ };
2315
+ const perServerRaceBudgets = spawned.map((entry, entryIndex) => {
2316
+ return hasTouchAuxiliaries && entry.info.role === "auxiliary" ? auxWaitBudgetMs(entry.client.serverId, coldAuxiliaryServerIds.has(entry.client.serverId), configuredAuxCeilingMs, perServerDeclaredTimeouts[entryIndex]) : perServerDeclaredTimeouts[entryIndex];
2317
+ });
2318
+ const perServerWaits = spawned.map((entry, entryIndex) => {
2319
+ if (hasTouchAuxiliaries && entry.info.role === "auxiliary" && this.isAuxiliaryWaitDemoted(entry.info.id, entry.client.root ?? filePath)) {
2320
+ return Promise.resolve(undefined);
2321
+ }
2322
+ if (this.state.diagnosticsUnsupported.has(entry.info.id) || diagnosticsUnsupportedServerIds.includes(entry.info.id)) {
2323
+ return Promise.resolve(undefined);
2324
+ }
2325
+ if (deferredResyncServerIds.has(entry.info.id)) {
2326
+ return Promise.resolve(undefined);
2327
+ }
2328
+ const serverTimeout = perServerWaitTimeouts[entryIndex];
2329
+ const baseline = diagnosticBaselines.get(entry.client);
2330
+ const pullOnly = classifyServerWaitTier(entry.client.serverId, pressureSnapshots.find((snapshot) => snapshot.serverId === entry.client.serverId)) === "pull-capable";
2331
+ const waitForDiagnostics = () => !notifySkippedServerIds.has(entry.info.id) && Number.isFinite(baseline) ? entry.client.waitForDiagnostics(filePath, serverTimeout, {
2332
+ minVersion: baseline,
2333
+ ...pullOnly && { pullOnly: true },
2334
+ ...isWarmupTouch && { pullSettleSource: "pull-warmup" }
2335
+ }) : pullOnly ? entry.client.waitForDiagnostics(filePath, serverTimeout, {
2336
+ pullOnly: true,
2337
+ ...isWarmupTouch && { pullSettleSource: "pull-warmup" }
2338
+ }) : isWarmupTouch ? entry.client.waitForDiagnostics(filePath, serverTimeout, {
2339
+ pullSettleSource: "pull-warmup"
2340
+ }) : entry.client.waitForDiagnostics(filePath, serverTimeout);
2341
+ const firstContact = entry.info.custom === true && !this.state.diagnosticsPublished.has(entry.info.id) && !this.state.diagnosticsUnsupported.has(entry.info.id) && entry.client.getWorkspaceDiagnosticsSupport().mode !== "pull";
2342
+ if (!firstContact)
2343
+ return waitForDiagnostics().catch(() => {
2344
+ return;
2345
+ });
2346
+ let shared = this.firstContactWaits.get(entry.info.id);
2347
+ if (!shared) {
2348
+ let removeShutdownListener;
2349
+ const shutdown = new Promise((resolve) => {
2350
+ const onShutdown = () => resolve("shutdown");
2351
+ if (this.shutdownController.signal.aborted) {
2352
+ onShutdown();
2353
+ return;
2354
+ }
2355
+ this.shutdownController.signal.addEventListener("abort", onShutdown, {
2356
+ once: true
2357
+ });
2358
+ removeShutdownListener = () => this.shutdownController.signal.removeEventListener("abort", onShutdown);
2359
+ });
2360
+ const probe = Promise.race([
2361
+ waitForDiagnostics().then(() => "completed", () => "errored"),
2362
+ shutdown
2363
+ ]);
2364
+ const completed = withDeadline(probe, {
2365
+ ms: serverTimeout,
2366
+ onTimeout: "undefined"
2367
+ });
2368
+ shared = completed.then((outcome) => {
2369
+ if (outcome === "errored" || outcome === "shutdown") {
2370
+ return "errored";
2371
+ }
2372
+ if (answeredForThisTouch(entry))
2373
+ return "answered";
2374
+ this.state.diagnosticsUnsupported.add(entry.info.id);
2375
+ recordDegradationOnce({
2376
+ kind: "lsp-diagnostics-unsupported",
2377
+ subject: entry.info.id,
2378
+ reason: "custom server has no pull provider and no publish evidence after first contact wait"
2379
+ });
2380
+ return "silent";
2381
+ }).catch(() => "errored").finally(() => {
2382
+ removeShutdownListener?.();
2383
+ if (this.firstContactWaits.get(entry.info.id) === shared) {
2384
+ this.firstContactWaits.delete(entry.info.id);
2385
+ }
2386
+ });
2387
+ this.firstContactWaits.set(entry.info.id, shared);
2388
+ }
2389
+ const callerWait = hookDeadlineAt === undefined ? shared : withDeadline(shared, {
2390
+ ms: Math.max(0, hookDeadlineAt - Date.now()),
2391
+ onTimeout: "undefined"
2392
+ });
2393
+ return callerWait.then((outcome) => {
2394
+ if (outcome === undefined) {
2395
+ firstContactCutOffServerIds.add(entry.info.id);
2396
+ } else if (outcome === "errored") {
2397
+ firstContactErroredServerIds.add(entry.info.id);
2398
+ }
2399
+ return;
2400
+ });
2401
+ });
2402
+ let pushWaitSettled = false;
2403
+ const pushWait = hasTouchAuxiliaries ? (() => {
2404
+ const primaryWaits = perServerWaits.filter((_, i) => spawned[i].info.role !== "auxiliary");
2405
+ const auxWaits = perServerWaits.map((p, i) => spawned[i].info.role === "auxiliary" ? {
2406
+ promise: p,
2407
+ serverId: spawned[i].info.id,
2408
+ client: spawned[i].client,
2409
+ baseline: diagnosticBaselines.get(spawned[i].client),
2410
+ budgetMs: perServerRaceBudgets[i],
2411
+ demoted: this.isAuxiliaryWaitDemoted(spawned[i].info.id, spawned[i].client.root ?? filePath),
2412
+ root: spawned[i].client.root ?? filePath
2413
+ } : null).filter((x) => x !== null);
2414
+ return Promise.all(primaryWaits).then(async () => {
2415
+ if (auxWaits.length === 0)
2416
+ return;
2417
+ const auxWaitStartedAt = Date.now();
2418
+ const outcomes = await Promise.all(auxWaits.map(async (aux) => {
2419
+ const { budgetMs } = aux;
2420
+ if (aux.demoted) {
2421
+ return {
2422
+ serverId: aux.serverId,
2423
+ outcome: deferredResyncServerIds.has(aux.serverId) ? "deferred" : "demoted",
2424
+ publishedThisContent: auxiliaryPublicationEvidence({
2425
+ bindingMatchesContent: auxCoversThisContent(aux.serverId),
2426
+ baseline: aux.baseline,
2427
+ currentPathVersion: readPathVersion(aux.client)
2428
+ }),
2429
+ budgetMs,
2430
+ elapsedMs: 0,
2431
+ elapsedSinceNotifyMs: 0
2432
+ };
2433
+ }
2434
+ let timer;
2435
+ const timeout = new Promise((resolve) => {
2436
+ timer = setTimeout(() => resolve(false), budgetMs);
2437
+ if (typeof timer === "object" && "unref" in timer) {
2438
+ timer.unref?.();
2439
+ }
2440
+ });
2441
+ const raced = await Promise.race([
2442
+ aux.promise.then(() => true),
2443
+ timeout
2444
+ ]);
2445
+ if (timer)
2446
+ clearTimeout(timer);
2447
+ const currentPathVersion = readPathVersion(aux.client);
2448
+ const publishedEvidence = auxiliaryPublicationEvidence({
2449
+ bindingMatchesContent: false,
2450
+ baseline: aux.baseline,
2451
+ currentPathVersion,
2452
+ raced
2453
+ });
2454
+ const outcome = deferredResyncServerIds.has(aux.serverId) ? "deferred" : !raced ? "cut_off" : publishedEvidence ? "answered" : "silent";
2455
+ const elapsedMs = Date.now() - auxWaitStartedAt;
2456
+ if (outcome !== "deferred") {
2457
+ this.noteAuxiliaryWait(`${aux.serverId}:${normalizeMapKey(aux.root)}`, budgetMs, elapsedMs);
2458
+ }
2459
+ return {
2460
+ serverId: aux.serverId,
2461
+ outcome,
2462
+ publishedThisContent: auxCoversThisContent(aux.serverId),
2463
+ budgetMs,
2464
+ elapsedMs,
2465
+ elapsedSinceNotifyMs: Date.now() - waitStartedAt
2466
+ };
2467
+ }));
2468
+ const unfinished = outcomes.filter((outcome) => outcome.outcome === "cut_off").map((outcome) => outcome.serverId);
2469
+ if (unfinished.length > 0)
2470
+ auxCutOffServerIds = unfinished;
2471
+ const uncovered = auxiliaryCoverageGap(outcomes);
2472
+ if (uncovered.length > 0)
2473
+ auxUnconfirmedServerIds = uncovered;
2474
+ const collectLaterServerIds = outcomes.filter((o) => !o.publishedThisContent && (o.outcome === "cut_off" || o.outcome === "silent" || o.outcome === "demoted") && !(o.serverId === "ast-grep" && napiFallbackCoveredSince(filePath, startedAt))).map((o) => o.serverId);
2475
+ if (collectLaterServerIds.length > 0) {
2476
+ lateDeliveryServerIds = collectLaterServerIds;
2477
+ markPendingAuxiliaryCoverage(filePath, collectLaterServerIds, Date.now());
2478
+ }
2479
+ logLatency({
2480
+ type: "phase",
2481
+ phase: "lsp_aux_wait_outcome",
2482
+ filePath: normalizedPath,
2483
+ durationMs: Date.now() - auxWaitStartedAt,
2484
+ metadata: { clientScope, waitShape: "aux_grace", outcomes }
2485
+ });
2486
+ });
2487
+ })() : Promise.all(perServerWaits).then(() => {});
2488
+ const markPushWaitSettled = () => {
2489
+ pushWaitSettled = true;
2490
+ };
2491
+ pushWait.then(markPushWaitSettled, markPushWaitSettled);
2492
+ if (tsserverSyncEligible) {
2493
+ const graceMs = readTsserverSyncGraceMs();
2494
+ const primaryClient = spawned[0].client;
2495
+ const syncRacer = (async () => {
2496
+ await new Promise((resolve) => {
2497
+ const timer = setTimeout(resolve, graceMs);
2498
+ timer.unref?.();
2499
+ });
2500
+ const publishedAt = primaryClient.getAllDiagnostics?.().get(normalizedPath)?.ts;
2501
+ if (pushWaitSettled || publishedAt !== undefined && publishedAt > markedAtMs) {
2502
+ return new Promise(() => {});
2503
+ }
2504
+ try {
2505
+ const result = await attemptTsserverSyncDiagnostics(filePath, this);
2506
+ if (result === undefined || pushWaitSettled) {
2507
+ return new Promise(() => {});
2508
+ }
2509
+ return result;
2510
+ } catch {
2511
+ return new Promise(() => {});
2512
+ }
2513
+ })();
2514
+ const raceOutcome = await Promise.race([
2515
+ pushWait.then(() => {
2516
+ return;
2517
+ }),
2518
+ syncRacer
2519
+ ]);
2520
+ if (raceOutcome !== undefined) {
2521
+ tsserverSyncConfirmed = raceOutcome;
2522
+ }
2523
+ } else {
2524
+ await pushWait;
2525
+ }
2526
+ const waitedMs = Date.now() - waitStartedAt;
2527
+ const firstContactCustomServerIds = spawned.filter((entry) => entry.info.custom === true && !this.state.diagnosticsPublished.has(entry.info.id) && !this.state.diagnosticsUnsupported.has(entry.info.id) && !firstContactCutOffServerIds.has(entry.info.id) && !firstContactErroredServerIds.has(entry.info.id) && entry.client.getWorkspaceDiagnosticsSupport().mode !== "pull").map((entry) => entry.info.id);
2528
+ for (const serverId of firstContactCustomServerIds) {
2529
+ const entry = spawned.find((candidate) => candidate.info.id === serverId);
2530
+ if (!entry || answeredForThisTouch(entry))
2531
+ continue;
2532
+ this.state.diagnosticsUnsupported.add(serverId);
2533
+ diagnosticsUnsupportedServerIds.push(serverId);
2534
+ recordDegradationOnce({
2535
+ kind: "lsp-diagnostics-unsupported",
2536
+ subject: serverId,
2537
+ reason: "custom server has no pull provider and no publish evidence after first contact wait"
2538
+ });
2539
+ }
2540
+ for (const entry of spawned) {
2541
+ if (entry.info.custom === true && this.state.diagnosticsUnsupported.has(entry.info.id)) {
2542
+ diagnosticsUnsupportedServerIds.push(entry.info.id);
2543
+ }
2544
+ }
2545
+ diagnosticsUnsupportedServerIds = [
2546
+ ...new Set(diagnosticsUnsupportedServerIds)
2547
+ ];
2548
+ if (!hasTouchAuxiliaries && options.collectDiagnostics === true) {
2549
+ const auxEntries = spawned.filter((entry) => entry.info.role === "auxiliary");
2550
+ if (auxEntries.length > 0) {
2551
+ const outcomes = auxEntries.map((entry) => {
2552
+ const baseline = diagnosticBaselines.get(entry.client);
2553
+ const currentPathVersion = readPathVersion(entry.client);
2554
+ const publishedEvidence = auxiliaryPublicationEvidence({
2555
+ bindingMatchesContent: false,
2556
+ baseline,
2557
+ currentPathVersion
2558
+ });
2559
+ return {
2560
+ serverId: entry.info.id,
2561
+ outcome: deferredResyncServerIds.has(entry.info.id) ? "deferred" : publishedEvidence ? "answered" : "silent",
2562
+ publishedThisContent: auxCoversThisContent(entry.info.id),
2563
+ budgetMs: timeoutFor(entry.client.serverId),
2564
+ elapsedMs: waitedMs,
2565
+ elapsedSinceNotifyMs: waitedMs
2566
+ };
2567
+ });
2568
+ const uncovered = auxiliaryCoverageGap(outcomes);
2569
+ if (uncovered.length > 0)
2570
+ auxUnconfirmedServerIds = uncovered;
2571
+ logLatency({
2572
+ type: "phase",
2573
+ phase: "lsp_aux_wait_outcome",
2574
+ filePath: normalizedPath,
2575
+ durationMs: waitedMs,
2576
+ metadata: { clientScope, waitShape: "aggregate", outcomes }
2577
+ });
2578
+ }
2579
+ }
2580
+ if (tsserverSyncConfirmed !== undefined) {
2581
+ logLatency({
2582
+ type: "phase",
2583
+ phase: "lsp_tsserver_sync_confirm",
2584
+ filePath: normalizedPath,
2585
+ durationMs: waitedMs,
2586
+ metadata: {
2587
+ source,
2588
+ serverId: spawned[0]?.client.serverId,
2589
+ clientScope,
2590
+ diagnosticsMode,
2591
+ mode: "race",
2592
+ confirmedDiagnosticCount: tsserverSyncConfirmed.length,
2593
+ budgetMs: timeoutMs,
2594
+ savedVsBudgetMs: Math.max(0, timeoutMs - waitedMs)
2595
+ }
2596
+ });
2597
+ } else if (waitedMs + 20 >= timeoutMs || firstContactErroredServerIds.size > 0) {
2598
+ const waited = spawned.filter((entry) => !deferredResyncServerIds.has(entry.info.id) && !diagnosticsUnsupportedServerIds.includes(entry.info.id));
2599
+ const unanswered = waited.filter((entry) => !answeredForThisTouch(entry));
2600
+ diagnosticsUnansweredServerIds = unanswered.map((e) => e.info.id);
2601
+ diagnosticsUnansweredPrimaryServerIds = unanswered.filter((entry) => entry.info.role !== "auxiliary").map((e) => e.info.id);
2602
+ const hasWaitedPrimary = waited.some((entry) => entry.info.role !== "auxiliary");
2603
+ const hasUnsupportedPrimary = diagnosticsUnsupportedServerIds.some((serverId) => spawned.some((entry) => entry.info.id === serverId && entry.info.role !== "auxiliary"));
2604
+ diagnosticsTimedOut = !hasWaitedPrimary && !hasUnsupportedPrimary || diagnosticsUnansweredPrimaryServerIds.length > 0;
2605
+ for (const entry of unanswered) {
2606
+ incrementDegradationCount({
2607
+ kind: "lsp-diagnostics-timeout",
2608
+ subject: entry.info.id,
2609
+ reason: "diagnostics wait timed out"
2610
+ });
2611
+ }
2612
+ logLatency({
2613
+ type: "phase",
2614
+ phase: "lsp_diagnostics_timeout",
2615
+ filePath: normalizedPath,
2616
+ durationMs: waitedMs,
2617
+ metadata: {
2618
+ source,
2619
+ serverIds: spawned.map((e) => e.info.id),
2620
+ clientScope,
2621
+ diagnosticsMode,
2622
+ timeoutMs,
2623
+ unansweredServerIds: diagnosticsUnansweredServerIds,
2624
+ attributedToPrimary: diagnosticsTimedOut
2625
+ }
2626
+ });
2627
+ }
2628
+ if (diagnosticsTimedOut && !primaryNotifyWriteTimedOut && clientScope === "all") {
2629
+ try {
2630
+ const outstanding = primaryEntries.filter((entry) => !notifyWriteTimedOutServerIds.includes(entry.info.id) && !entry.client.getAllDiagnostics().has(normalizedPath));
2631
+ if (outstanding.length > 0) {
2632
+ const snapshots = await this.getCapabilitySnapshots(filePath);
2633
+ const allSilent = outstanding.every((entry) => classifyServerWaitTier(entry.client.serverId, snapshots.find((s) => s.serverId === entry.client.serverId)) === "tier3-silent");
2634
+ if (allSilent) {
2635
+ const liveness = await Promise.all(outstanding.map((entry) => (entry.client.pingLiveness?.() ?? Promise.resolve(true)).catch(() => false)));
2636
+ if (liveness.every(Boolean)) {
2637
+ diagnosticsTimedOut = false;
2638
+ retractPrimaryTimeoutAttribution();
2639
+ logLatency({
2640
+ type: "phase",
2641
+ phase: "lsp_silent_clean_confirm",
2642
+ filePath: normalizedPath,
2643
+ durationMs: Date.now() - startedAt,
2644
+ metadata: {
2645
+ source,
2646
+ clientScope,
2647
+ diagnosticsMode,
2648
+ aggregate: true,
2649
+ serverIds: outstanding.map((entry) => entry.client.serverId)
2650
+ }
2651
+ });
2652
+ }
2653
+ }
2654
+ }
2655
+ } catch {}
2656
+ }
2657
+ }
2658
+ const auxCoveredAtMerge = new Set(spawned.filter((entry) => auxCoversThisContent(entry.info.id)).map((entry) => entry.info.id));
2659
+ const uncoveredDeferredServerIds = notifyDeferredServerIds.filter((serverId) => !auxCoveredAtMerge.has(serverId));
2660
+ const staleWriteAuxiliaryServerIds = spawned.filter((entry) => entry.info.role === "auxiliary" && notifyWriteTimedOutServerIds.includes(entry.info.id) && !auxCoveredAtMerge.has(entry.info.id)).map((entry) => entry.info.id);
2661
+ const droppedAuxiliaryServerIds = new Set([
2662
+ ...uncoveredDeferredServerIds,
2663
+ ...staleWriteAuxiliaryServerIds
2664
+ ]);
2665
+ let collected = options.collectDiagnostics ? tsserverSyncConfirmed !== undefined ? mergeLspDiagnostics(attributeDiagnostics(spawned[0]?.info.id ?? "unknown", tsserverSyncConfirmed)) : mergeLspDiagnostics([
2666
+ ...spawned.flatMap((entry) => droppedAuxiliaryServerIds.has(entry.info.id) ? [] : attributeDiagnostics(entry.info.id, entry.client.getDiagnostics(filePath))),
2667
+ ...carriedAuxiliary.flatMap((entry) => attributeDiagnostics(entry.serverId, entry.diags))
2668
+ ]) : undefined;
2669
+ let syncConfirmed = tsserverSyncConfirmed !== undefined;
2670
+ if (diagnosticsTimedOut && tsserverSyncEligible && collected !== undefined && collected.length === 0) {
2671
+ try {
2672
+ const syncResult = await attemptTsserverSyncDiagnostics(filePath, this);
2673
+ if (syncResult !== undefined) {
2674
+ diagnosticsTimedOut = false;
2675
+ retractPrimaryTimeoutAttribution();
2676
+ syncConfirmed = true;
2677
+ collected = syncResult.length > 0 ? mergeLspDiagnostics(attributeDiagnostics(spawned[0]?.info.id ?? "unknown", syncResult)) : [];
2678
+ logLatency({
2679
+ type: "phase",
2680
+ phase: "lsp_tsserver_sync_confirm",
2681
+ filePath: normalizedPath,
2682
+ durationMs: Date.now() - startedAt,
2683
+ metadata: {
2684
+ source,
2685
+ clientScope,
2686
+ diagnosticsMode,
2687
+ mode: "end-of-wait",
2688
+ confirmedDiagnosticCount: collected.length
2689
+ }
2690
+ });
2691
+ }
2692
+ } catch {}
2693
+ }
2694
+ if (diagnosticsTimedOut && !primaryNotifyWriteTimedOut && (!tsserverSyncEligible || isWarmupTouch) && clientScope === "primary" && spawned.length === 1 && getStrategy(spawned[0].client.serverId, spawned[0].client.getLaunchVariant?.()).silentOnClean === true) {
2695
+ try {
2696
+ const snapshots = await this.getCapabilitySnapshots(filePath);
2697
+ if (classifyCascadeWaitTier(this, filePath, snapshots) === "tier3-silent") {
2698
+ const alive = await (spawned[0].client.pingLiveness?.() ?? Promise.resolve(true)).catch(() => false);
2699
+ if (alive) {
2700
+ diagnosticsTimedOut = false;
2701
+ retractPrimaryTimeoutAttribution();
2702
+ if (collected !== undefined)
2703
+ collected = mergeLspDiagnostics([]);
2704
+ logLatency({
2705
+ type: "phase",
2706
+ phase: "lsp_silent_clean_confirm",
2707
+ filePath: normalizedPath,
2708
+ durationMs: Date.now() - startedAt,
2709
+ metadata: {
2710
+ source,
2711
+ clientScope,
2712
+ diagnosticsMode,
2713
+ serverId: spawned[0].client.serverId
2714
+ }
2715
+ });
2716
+ }
2717
+ }
2718
+ } catch {}
2719
+ }
2720
+ const verdict = resolveTouchVerdict({
2721
+ primaryNotifyWriteTimedOutServerIds,
2722
+ diagnosticsTimedOut,
2723
+ diagnosticsUnansweredServerIds: diagnosticsUnansweredPrimaryServerIds
2724
+ });
2725
+ const inconclusive = verdict.inconclusive;
2726
+ const brokenSkippedServerIds = collected !== undefined ? await this.brokenSkippedAuxiliaryServerIds(filePath, clientScope, options, spawned) : [];
2727
+ const auxNoAnswerServerIds = spawned.filter((entry) => entry.info.role === "auxiliary" && (diagnosticsUnansweredServerIds.includes(entry.info.id) || notifyWriteTimedOutServerIds.includes(entry.info.id)) && !auxCoveredAtMerge.has(entry.info.id)).map((entry) => entry.info.id);
2728
+ const unconfirmedServerIds = [
2729
+ ...new Set([
2730
+ ...auxUnconfirmedServerIds ?? [],
2731
+ ...auxNoAnswerServerIds,
2732
+ ...uncoveredDeferredServerIds,
2733
+ ...brokenSkippedServerIds
2734
+ ])
2735
+ ].filter((serverId) => !auxCoveredAtMerge.has(serverId));
2736
+ const coverageGap = unconfirmedServerIds.length > 0;
2737
+ if (brokenSkippedServerIds.length > 0 || uncoveredDeferredServerIds.length > 0 || auxNoAnswerServerIds.length > 0) {
2738
+ for (const serverId of unconfirmedServerIds) {
2739
+ const reasons = [
2740
+ brokenSkippedServerIds.includes(serverId) && "breaker skip",
2741
+ uncoveredDeferredServerIds.includes(serverId) && "deferred resync",
2742
+ auxNoAnswerServerIds.includes(serverId) && "no diagnostics answer"
2743
+ ].filter(Boolean);
2744
+ incrementDegradationCount({
2745
+ kind: "lsp-scanner-coverage-gap",
2746
+ subject: `${serverId}:${normalizedPath}`,
2747
+ reason: reasons.join(", ") || "scanner coverage gap"
2748
+ });
2749
+ }
2750
+ emitBounded("lsp_scanner_coverage_gap", `${source}:${normalizedPath}`, {
2751
+ filePath: normalizedPath,
2752
+ durationMs: Date.now() - startedAt,
2753
+ metadata: {
2754
+ source,
2755
+ clientScope,
2756
+ ...brokenSkippedServerIds.length > 0 && {
2757
+ brokenSkippedServerIds
2758
+ },
2759
+ ...uncoveredDeferredServerIds.length > 0 && {
2760
+ deferredResyncServerIds: uncoveredDeferredServerIds
2761
+ },
2762
+ ...auxNoAnswerServerIds.length > 0 && { auxNoAnswerServerIds }
2763
+ }
2764
+ });
2765
+ }
2766
+ const notifyTimedOutServerIds = new Set(notifyWriteTimedOutServerIds);
2767
+ const uncoveredServerIds = new Set(unconfirmedServerIds);
2768
+ if (diagnosticsMode !== "none" && !diagnosticsTimedOut) {
2769
+ for (const entry of spawned) {
2770
+ if (notifyTimedOutServerIds.has(entry.info.id))
2771
+ continue;
2772
+ if (uncoveredServerIds.has(entry.info.id))
2773
+ continue;
2774
+ const key = await this.demonstratedReadyKeyFor(entry.info, filePath);
2775
+ if (key)
2776
+ this.markDemonstratedReadyKey(key);
2777
+ }
2778
+ }
2779
+ if (collected !== undefined && !inconclusive && !coverageGap) {
2780
+ const normalizedKey = normalizeMapKey(filePath);
2781
+ if (collected.length > 0) {
2782
+ this.lastKnownDiagnostics.set(normalizedKey, collected);
2783
+ this.lastKnownContentHash.set(normalizedKey, this.hashContent(content));
2784
+ } else {
2785
+ this.lastKnownDiagnostics.delete(normalizedKey);
2786
+ this.lastKnownContentHash.delete(normalizedKey);
2787
+ }
2788
+ }
2789
+ const result = { diags: collected ?? [] };
2790
+ const primaryDiagnosticsUnsupported = diagnosticsUnsupportedServerIds.some((serverId) => spawned.some((entry) => entry.info.id === serverId && entry.info.role !== "auxiliary"));
2791
+ if (diagnosticsUnsupportedServerIds.length > 0) {
2792
+ result.diagnosticsUnsupportedServerIds = diagnosticsUnsupportedServerIds;
2793
+ }
2794
+ if (collected !== undefined && inconclusive) {
2795
+ result.inconclusive = true;
2796
+ if (verdict.inconclusiveServerIds) {
2797
+ result.inconclusiveServerIds = verdict.inconclusiveServerIds;
2798
+ }
2799
+ result.inconclusiveReason = verdict.inconclusiveReason;
2800
+ } else if (collected !== undefined && primaryDiagnosticsUnsupported) {
2801
+ if (unconfirmedServerIds.length > 0) {
2802
+ result.unconfirmedServerIds = [...unconfirmedServerIds];
2803
+ }
2804
+ } else if (collected !== undefined && coverageGap) {
2805
+ result.confirmation = "partial";
2806
+ result.unconfirmedServerIds = [...unconfirmedServerIds];
2807
+ } else if (collected !== undefined && !primaryDiagnosticsUnsupported) {
2808
+ result.confirmation = "confirmed";
2809
+ }
2810
+ let binding;
2811
+ if (collected !== undefined) {
2812
+ binding = syncConfirmed ? { boundToCurrentDisk: "unknown" } : this.mergeBinding(filePath, [
2813
+ ...spawned.filter((entry) => !droppedAuxiliaryServerIds.has(entry.info.id)).map((entry) => entry.client.getDiagnosticBinding?.(filePath)),
2814
+ ...carriedAuxiliary.map((entry) => entry.binding)
2815
+ ]);
2816
+ result.binding = binding;
2817
+ }
2818
+ const lateDeliveryPending = unconfirmedServerIds.filter((serverId) => (lateDeliveryServerIds ?? []).includes(serverId));
2819
+ if (lateDeliveryPending.length > 0) {
2820
+ result.deferredServerIds = lateDeliveryPending;
2821
+ }
2822
+ logLatency({
2823
+ type: "phase",
2824
+ phase: "lsp_touch_file",
2825
+ filePath: normalizedPath,
2826
+ durationMs: Date.now() - startedAt,
2827
+ metadata: {
2828
+ serverCountReady: spawned.length,
2829
+ clientScope,
2830
+ diagnosticsMode,
2831
+ source,
2832
+ failureKind: "success",
2833
+ collectedDiagnostics: collected?.length,
2834
+ ...binding !== undefined && {
2835
+ bindingState: bindingStateLabel(binding.boundToCurrentDisk)
2836
+ },
2837
+ notifySkipped,
2838
+ notifyWriteTimedOut,
2839
+ ...notifyWriteTimedOutServerIds.length > 0 && {
2840
+ notifyWriteTimedOutServerIds
2841
+ },
2842
+ diagnosticsTimedOut,
2843
+ inconclusive,
2844
+ ...verdict.inconclusiveServerIds && {
2845
+ inconclusiveServerIds: verdict.inconclusiveServerIds
2846
+ },
2847
+ ...verdict.inconclusiveReason && {
2848
+ inconclusiveReason: verdict.inconclusiveReason
2849
+ },
2850
+ ...result.confirmation !== undefined && {
2851
+ confirmation: result.confirmation
2852
+ },
2853
+ ...auxCutOffServerIds !== undefined && { auxCutOffServerIds },
2854
+ ...auxUnconfirmedServerIds !== undefined && {
2855
+ auxUnconfirmedServerIds
2856
+ },
2857
+ ...brokenSkippedServerIds.length > 0 && { brokenSkippedServerIds },
2858
+ ...uncoveredDeferredServerIds.length > 0 && {
2859
+ deferredResyncServerIds: uncoveredDeferredServerIds
2860
+ },
2861
+ ...lateDeliveryPending.length > 0 && {
2862
+ lateDeliveryServerIds: lateDeliveryPending
2863
+ },
2864
+ ...auxNoAnswerServerIds.length > 0 && { auxNoAnswerServerIds }
2865
+ }
2866
+ });
2867
+ return result;
2868
+ } finally {
2869
+ for (const key of leaseKeys)
2870
+ this.releaseClientLease(key);
2871
+ }
2872
+ }
2873
+ async findOutsideProjectRoot(filePath) {
2874
+ if (!isOutsideAllSessionRoots(filePath))
2875
+ return;
2876
+ const candidates = await Promise.all(getServersForFileWithConfig(filePath).map((server) => server.root(filePath)));
2877
+ return candidates.find((candidate) => Boolean(candidate));
2878
+ }
2879
+ recordOutsideRootDecline(filePath, nearestRoot, operation) {
2880
+ const root = normalizeMapKey(nearestRoot);
2881
+ const sessionRoots = getSessionRootsForTelemetry().map((sessionRoot) => normalizeMapKey(sessionRoot)).join(", ");
2882
+ emitBounded("lsp_capability_skip", root, {
2883
+ filePath: normalizeMapKey(filePath),
2884
+ durationMs: 0,
2885
+ metadata: {
2886
+ operation,
2887
+ nearestMarkerRoot: root,
2888
+ sessionRoots,
2889
+ identity: root
2890
+ }
2891
+ }, {
2892
+ ledgerKind: "lsp-capability-skip",
2893
+ risingEdgePer: "identity",
2894
+ reason: `declined LSP root outside session cwd: ${root}`
2895
+ });
2896
+ }
2897
+ getDiagnosticsHealth(filePath) {
2898
+ return this.lastDiagnosticsHealth.get(normalizeMapKey(filePath));
2899
+ }
2900
+ getLastKnownDiagnostics(filePath, expectedContentHash) {
2901
+ const normalizedKey = normalizeMapKey(filePath);
2902
+ if (expectedContentHash !== undefined) {
2903
+ const knownHash = this.lastKnownContentHash.get(normalizedKey);
2904
+ if (knownHash === undefined || knownHash !== expectedContentHash) {
2905
+ return;
2906
+ }
2907
+ }
2908
+ return this.lastKnownDiagnostics.get(normalizedKey);
2909
+ }
2910
+ async getDiagnostics(filePath, diagnosticsMode = "full") {
2911
+ const normalizedPath = normalizeMapKey(filePath);
2912
+ const outsideRoot = await this.findOutsideProjectRoot(filePath);
2913
+ if (outsideRoot) {
2914
+ this.recordOutsideRootDecline(filePath, outsideRoot, "getDiagnostics");
2915
+ return [];
2916
+ }
2917
+ if (this.checkDestroyed()) {
2918
+ this.lastDiagnosticsHealth.set(normalizedPath, {
2919
+ health: "destroyed",
2920
+ failureKind: "destroyed",
2921
+ serverCountAttempted: 0,
2922
+ serverCountReady: 0,
2923
+ candidateServerIds: getServersForFileWithConfig(filePath).map((s) => s.id),
2924
+ mergedCount: 0,
2925
+ dedupDroppedCount: 0,
2926
+ checkedAt: new Date().toISOString()
2927
+ });
2928
+ return [];
2929
+ }
2930
+ const startedAt = Date.now();
2931
+ const candidateServerIds = getServersForFileWithConfig(filePath).map((s) => s.id);
2932
+ const { clients: spawned, serverCountAttempted } = await this.getClientsForFile(filePath);
2933
+ if (spawned.length === 0) {
2934
+ const stale = this.lastKnownDiagnostics.get(normalizedPath);
2935
+ const failureKind = stale?.length ? "no_clients_stale" : "no_clients";
2936
+ this.lastDiagnosticsHealth.set(normalizedPath, {
2937
+ health: failureKind,
2938
+ failureKind,
2939
+ serverCountAttempted,
2940
+ serverCountReady: 0,
2941
+ candidateServerIds,
2942
+ mergedCount: stale?.length ?? 0,
2943
+ dedupDroppedCount: 0,
2944
+ checkedAt: new Date().toISOString()
2945
+ });
2946
+ logLatency({
2947
+ type: "phase",
2948
+ phase: "lsp_diagnostics_aggregate",
2949
+ filePath: normalizedPath,
2950
+ durationMs: Date.now() - startedAt,
2951
+ metadata: {
2952
+ serverCountAttempted,
2953
+ serverCountReady: 0,
2954
+ mergedCount: stale?.length ?? 0,
2955
+ dedupDroppedCount: 0,
2956
+ failureKind,
2957
+ health: failureKind,
2958
+ servers: []
2959
+ }
2960
+ });
2961
+ return stale ?? [];
2962
+ }
2963
+ const clientWaits = spawned.map(async (entry) => {
2964
+ const waitStart = Date.now();
2965
+ const launchVariant = entry.client.getLaunchVariant?.();
2966
+ const strategy = getStrategy(entry.info.id, launchVariant);
2967
+ await entry.client.waitForDiagnostics(filePath, strategy.aggregateWaitMs);
2968
+ let diagnostics = entry.client.getDiagnostics(filePath);
2969
+ const firstWaitMs = Date.now() - waitStart;
2970
+ if (strategy.expectSemanticSecondPush && diagnostics.length === 0 && firstWaitMs < DIAGNOSTICS_SEMANTIC_SETTLE_THRESHOLD_MS) {
2971
+ await entry.client.waitForDiagnostics(filePath, DIAGNOSTICS_SEMANTIC_SETTLE_WAIT_MS);
2972
+ diagnostics = entry.client.getDiagnostics(filePath);
2973
+ }
2974
+ return {
2975
+ serverId: entry.info.id,
2976
+ launchVariant,
2977
+ waitMs: Date.now() - waitStart,
2978
+ diagnosticCount: diagnostics.length,
2979
+ diagnostics
2980
+ };
2981
+ });
2982
+ const graceMs = diagnosticsMode === "document" ? 0 : EARLY_UNBLOCK_GRACE_MS;
2983
+ const diagDescriptors = spawned.map((entry) => {
2984
+ if (entry.info.role !== "auxiliary")
2985
+ return { role: "primary" };
2986
+ const strategy = getStrategy(entry.info.id, entry.client.getLaunchVariant?.());
2987
+ return { role: "auxiliary", budgetMs: strategy.aggregateWaitMs };
2988
+ });
2989
+ const perServer = await raceToCompletion(clientWaits, (results) => results.some((r) => r.diagnosticCount > 0 || getStrategy(r.serverId, r.launchVariant).seedFirstPush), {
2990
+ timeoutMs: Math.max(...spawned.map((entry) => getStrategy(entry.info.id, entry.client.getLaunchVariant?.()).aggregateWaitMs)),
2991
+ graceMs,
2992
+ descriptors: diagDescriptors,
2993
+ auxGraceMs: readEnvAuxGraceMs() ?? 2000
2994
+ });
2995
+ const earlyUnblockedCount = spawned.length - perServer.length;
2996
+ const perServerFull = spawned.map((entry) => {
2997
+ const found = perServer.find((r) => r.serverId === entry.info.id);
2998
+ return found ?? {
2999
+ serverId: entry.info.id,
3000
+ launchVariant: entry.client.getLaunchVariant?.(),
3001
+ waitMs: getStrategy(entry.info.id, entry.client.getLaunchVariant?.()).aggregateWaitMs,
3002
+ diagnosticCount: 0,
3003
+ diagnostics: []
3004
+ };
3005
+ });
3006
+ const merged = [];
3007
+ const seen = new Set;
3008
+ for (const entry of perServerFull) {
3009
+ for (const diagnostic of entry.diagnostics) {
3010
+ const key = [
3011
+ diagnostic.range.start.line,
3012
+ diagnostic.range.start.character,
3013
+ diagnostic.message
3014
+ ].join(":");
3015
+ if (seen.has(key))
3016
+ continue;
3017
+ seen.add(key);
3018
+ merged.push({ ...diagnostic, serverId: entry.serverId });
3019
+ }
3020
+ }
3021
+ const rawCount = perServerFull.reduce((sum, entry) => sum + entry.diagnosticCount, 0);
3022
+ const serversWithDiagnostics = perServerFull.filter((entry) => entry.diagnosticCount > 0).length;
3023
+ const failureKind = merged.length === 0 ? "ok_empty" : "success";
3024
+ this.lastDiagnosticsHealth.set(normalizedPath, {
3025
+ health: failureKind === "success" ? "ok" : "ok_empty",
3026
+ failureKind,
3027
+ serverCountAttempted,
3028
+ serverCountReady: perServerFull.length,
3029
+ candidateServerIds,
3030
+ mergedCount: merged.length,
3031
+ dedupDroppedCount: rawCount - merged.length,
3032
+ checkedAt: new Date().toISOString()
3033
+ });
3034
+ logLatency({
3035
+ type: "phase",
3036
+ phase: "lsp_diagnostics_aggregate",
3037
+ filePath: normalizedPath,
3038
+ durationMs: Date.now() - startedAt,
3039
+ metadata: {
3040
+ serverCountAttempted,
3041
+ serverCountReady: perServerFull.length,
3042
+ serverCountWithDiagnostics: serversWithDiagnostics,
3043
+ mergedCount: merged.length,
3044
+ dedupDroppedCount: rawCount - merged.length,
3045
+ earlyUnblockedCount,
3046
+ diagnosticsMode,
3047
+ failureKind,
3048
+ health: failureKind === "success" ? "ok" : "ok_empty",
3049
+ servers: perServerFull.map((entry) => ({
3050
+ id: entry.serverId,
3051
+ waitMs: entry.waitMs,
3052
+ diagnosticCount: entry.diagnosticCount
3053
+ }))
3054
+ }
3055
+ });
3056
+ if (merged.length > 0) {
3057
+ this.lastKnownDiagnostics.set(normalizedPath, merged);
3058
+ } else {
3059
+ this.lastKnownDiagnostics.delete(normalizedPath);
3060
+ }
3061
+ this.lastKnownContentHash.delete(normalizedPath);
3062
+ return merged;
3063
+ }
3064
+ hashContent(content) {
3065
+ return hashDiagnosticContent(content);
3066
+ }
3067
+ mergeBinding(filePath, stored) {
3068
+ const verdicts = [];
3069
+ let version;
3070
+ let contentHash;
3071
+ for (const entry of stored) {
3072
+ verdicts.push(this.diskBindingCache.boundToCurrentDisk(filePath, entry ?? {}));
3073
+ if (version === undefined && entry?.version !== undefined) {
3074
+ version = entry.version;
3075
+ }
3076
+ if (contentHash === undefined && entry?.contentHash !== undefined) {
3077
+ contentHash = entry.contentHash;
3078
+ }
3079
+ }
3080
+ return {
3081
+ version,
3082
+ contentHash,
3083
+ boundToCurrentDisk: composeBoundToCurrentDisk(verdicts)
3084
+ };
3085
+ }
3086
+ async definition(filePath, line, character) {
3087
+ const spawned = await this.getClientForFile(filePath, NAV_CLIENT_WAIT_TIMEOUT_MS);
3088
+ if (!spawned)
3089
+ return [];
3090
+ if (!spawned.client.getOperationSupport().definition) {
3091
+ throw new Error("__UNSUPPORTED__ Active LSP server does not advertise support for definition");
3092
+ }
3093
+ return spawned.client.definition(filePath, line, character);
3094
+ }
3095
+ async typeDefinition(filePath, line, character) {
3096
+ const spawned = await this.getClientForFile(filePath, NAV_CLIENT_WAIT_TIMEOUT_MS);
3097
+ if (!spawned)
3098
+ return [];
3099
+ if (!spawned.client.getOperationSupport().typeDefinition) {
3100
+ throw new Error("__UNSUPPORTED__ Active LSP server does not advertise support for typeDefinition");
3101
+ }
3102
+ return spawned.client.typeDefinition(filePath, line, character);
3103
+ }
3104
+ async declaration(filePath, line, character) {
3105
+ const spawned = await this.getClientForFile(filePath, NAV_CLIENT_WAIT_TIMEOUT_MS);
3106
+ if (!spawned)
3107
+ return [];
3108
+ if (!spawned.client.getOperationSupport().declaration) {
3109
+ throw new Error("__UNSUPPORTED__ Active LSP server does not advertise support for declaration");
3110
+ }
3111
+ return spawned.client.declaration(filePath, line, character);
3112
+ }
3113
+ async references(filePath, line, character, includeDeclaration = true) {
3114
+ const spawned = await this.getClientForFile(filePath, NAV_CLIENT_WAIT_TIMEOUT_MS);
3115
+ if (!spawned)
3116
+ return [];
3117
+ if (!spawned.client.getOperationSupport().references) {
3118
+ throw new Error("__UNSUPPORTED__ Active LSP server does not advertise support for references");
3119
+ }
3120
+ return spawned.client.references(filePath, line, character, includeDeclaration);
3121
+ }
3122
+ async hover(filePath, line, character) {
3123
+ const spawned = await this.getClientForFile(filePath, NAV_CLIENT_WAIT_TIMEOUT_MS);
3124
+ if (!spawned)
3125
+ return null;
3126
+ if (!spawned.client.getOperationSupport().hover) {
3127
+ throw new Error("__UNSUPPORTED__ Active LSP server does not advertise support for hover");
3128
+ }
3129
+ return spawned.client.hover(filePath, line, character);
3130
+ }
3131
+ async signatureHelp(filePath, line, character) {
3132
+ const spawned = await this.getClientForFile(filePath, NAV_CLIENT_WAIT_TIMEOUT_MS);
3133
+ if (!spawned)
3134
+ return null;
3135
+ if (!spawned.client.getOperationSupport().signatureHelp) {
3136
+ throw new Error("__UNSUPPORTED__ Active LSP server does not advertise support for signatureHelp");
3137
+ }
3138
+ return spawned.client.signatureHelp(filePath, line, character);
3139
+ }
3140
+ async documentSymbol(filePath) {
3141
+ const spawned = await this.getClientForFile(filePath, NAV_CLIENT_WAIT_TIMEOUT_MS);
3142
+ if (!spawned)
3143
+ return [];
3144
+ if (!spawned.client.getOperationSupport().documentSymbol) {
3145
+ throw new Error("__UNSUPPORTED__ Active LSP server does not advertise support for documentSymbol");
3146
+ }
3147
+ return spawned.client.documentSymbol(filePath);
3148
+ }
3149
+ selectWorkspaceScopeClient(predicate) {
3150
+ let auxFallback;
3151
+ for (const [key, client] of this.state.clients) {
3152
+ if (!client.isAlive())
3153
+ continue;
3154
+ if (predicate && !predicate(client))
3155
+ continue;
3156
+ const separator = key.indexOf(":");
3157
+ const serverId = separator >= 0 ? key.slice(0, separator) : key;
3158
+ const role = LSP_SERVERS.find((s) => s.id === serverId)?.role;
3159
+ if (role === "auxiliary") {
3160
+ if (!auxFallback)
3161
+ auxFallback = { client, serverId };
3162
+ continue;
3163
+ }
3164
+ return { client, serverId };
3165
+ }
3166
+ return auxFallback;
3167
+ }
3168
+ async workspaceSymbol(query, filePath, attribution) {
3169
+ if (filePath) {
3170
+ const spawned = await this.getClientForFile(filePath, NAV_CLIENT_WAIT_TIMEOUT_MS);
3171
+ if (!spawned)
3172
+ return [];
3173
+ if (!spawned.client.getOperationSupport().workspaceSymbol)
3174
+ return [];
3175
+ return spawned.client.workspaceSymbol(query);
3176
+ }
3177
+ const target = this.selectWorkspaceScopeClient((client) => client.getOperationSupport().workspaceSymbol);
3178
+ if (!target)
3179
+ return [];
3180
+ if (attribution)
3181
+ attribution.workspaceSymbol = target.serverId;
3182
+ return target.client.workspaceSymbol(query);
3183
+ }
3184
+ async getAdvertisedCommands(filePath, attribution) {
3185
+ if (filePath) {
3186
+ const spawned = await this.getClientForFile(filePath, NAV_CLIENT_WAIT_TIMEOUT_MS);
3187
+ if (!spawned)
3188
+ return [];
3189
+ return spawned.client.getAdvertisedCommands();
3190
+ }
3191
+ const first = this.selectWorkspaceScopeClient();
3192
+ if (!first)
3193
+ return [];
3194
+ if (attribution)
3195
+ attribution.getAdvertisedCommands = first.serverId;
3196
+ return first.client.getAdvertisedCommands();
3197
+ }
3198
+ async executeCommand(filePath, command, args, mutationContext, attribution) {
3199
+ if (filePath) {
3200
+ const spawned = await this.getClientForFile(filePath, NAV_CLIENT_WAIT_TIMEOUT_MS);
3201
+ if (!spawned) {
3202
+ return { executed: false, reason: "no LSP server for file" };
3203
+ }
3204
+ return spawned.client.executeCommand(command, args, mutationContext);
3205
+ }
3206
+ const first = this.selectWorkspaceScopeClient();
3207
+ if (!first)
3208
+ return { executed: false, reason: "no active LSP server" };
3209
+ if (attribution)
3210
+ attribution.executeCommand = first.serverId;
3211
+ return first.client.executeCommand(command, args, mutationContext);
3212
+ }
3213
+ async executeReadOnlyCommandOnLiveClient(filePath, command, args) {
3214
+ if (this.checkDestroyed()) {
3215
+ return { executed: false, reason: "lsp service destroyed" };
3216
+ }
3217
+ for (const server of getServersForFileWithConfig(filePath)) {
3218
+ const root = await this.resolveServerRoot(server, filePath);
3219
+ if (!root)
3220
+ continue;
3221
+ const entry = this.state.clients.get(`${server.id}:${normalizeMapKey(root)}`);
3222
+ if (!entry?.isAlive())
3223
+ continue;
3224
+ const run = entry.executeReadOnlyCommand;
3225
+ if (typeof run !== "function")
3226
+ continue;
3227
+ return run.call(entry, command, args);
3228
+ }
3229
+ return { executed: false, reason: "no live LSP server for file" };
3230
+ }
3231
+ async getOperationSupport(filePath, attribution) {
3232
+ if (filePath) {
3233
+ const spawned = await this.getClientForFile(filePath);
3234
+ if (!spawned)
3235
+ return null;
3236
+ const getter = spawned.client.getOperationSupport;
3237
+ if (typeof getter !== "function")
3238
+ return null;
3239
+ return getter();
3240
+ }
3241
+ const readable = (client) => typeof client.getOperationSupport === "function";
3242
+ const first = this.selectWorkspaceScopeClient(readable);
3243
+ if (!first)
3244
+ return null;
3245
+ const aggregate = { ...first.client.getOperationSupport() };
3246
+ const contributors = {};
3247
+ for (const capability of Object.keys(aggregate)) {
3248
+ if (aggregate[capability]) {
3249
+ contributors[capability] = first.serverId;
3250
+ continue;
3251
+ }
3252
+ const supporter = this.selectWorkspaceScopeClient((client) => readable(client) && Boolean(client.getOperationSupport()[capability]));
3253
+ if (supporter) {
3254
+ aggregate[capability] = true;
3255
+ contributors[capability] = supporter.serverId;
3256
+ }
3257
+ }
3258
+ if (attribution) {
3259
+ attribution.getOperationSupport = {
3260
+ baseClientId: first.serverId,
3261
+ contributors
3262
+ };
3263
+ }
3264
+ return aggregate;
3265
+ }
3266
+ async getCapabilitySnapshots(filePath, attribution) {
3267
+ if (this.checkDestroyed())
3268
+ return [];
3269
+ const snapshots = [];
3270
+ if (filePath) {
3271
+ const servers = getServersForFileWithConfig(filePath);
3272
+ for (const server of servers) {
3273
+ const root = await this.resolveServerRoot(server, filePath);
3274
+ if (!root)
3275
+ continue;
3276
+ const client = this.state.clients.get(`${server.id}:${normalizeMapKey(root)}`);
3277
+ if (!client?.isAlive())
3278
+ continue;
3279
+ snapshots.push({
3280
+ serverId: server.id,
3281
+ root,
3282
+ operationSupport: client.getOperationSupport(),
3283
+ workspaceDiagnosticsSupport: client.getWorkspaceDiagnosticsSupport(),
3284
+ customServer: server.custom,
3285
+ diagnosticsPublished: this.state.diagnosticsPublished.has(server.id),
3286
+ diagnosticsUnsupported: this.state.diagnosticsUnsupported.has(server.id),
3287
+ advertisedCommands: client.getAdvertisedCommands(),
3288
+ rawCapabilityKeys: client.getRawCapabilityKeys?.() ?? [],
3289
+ launchVariant: client.getLaunchVariant?.()
3290
+ });
3291
+ }
3292
+ return snapshots;
3293
+ }
3294
+ for (const [key, client] of this.state.clients) {
3295
+ if (!client.isAlive())
3296
+ continue;
3297
+ const separator = key.indexOf(":");
3298
+ const serverId = separator >= 0 ? key.slice(0, separator) : key;
3299
+ snapshots.push({
3300
+ serverId,
3301
+ root: client.root,
3302
+ operationSupport: client.getOperationSupport(),
3303
+ workspaceDiagnosticsSupport: client.getWorkspaceDiagnosticsSupport(),
3304
+ customServer: this.state.servers.get(serverId)?.custom,
3305
+ diagnosticsPublished: this.state.diagnosticsPublished.has(serverId),
3306
+ diagnosticsUnsupported: this.state.diagnosticsUnsupported.has(serverId),
3307
+ advertisedCommands: client.getAdvertisedCommands(),
3308
+ rawCapabilityKeys: client.getRawCapabilityKeys?.() ?? [],
3309
+ launchVariant: client.getLaunchVariant?.()
3310
+ });
3311
+ }
3312
+ if (attribution) {
3313
+ attribution.getCapabilitySnapshots = {
3314
+ clientIds: snapshots.slice(0, WORKSPACE_ATTRIBUTION_CLIENT_CAP).map((snapshot) => snapshot.serverId),
3315
+ clientCount: snapshots.length
3316
+ };
3317
+ }
3318
+ return snapshots;
3319
+ }
3320
+ async getWorkspaceDiagnosticsSupport(filePath, attribution) {
3321
+ if (filePath) {
3322
+ const spawned = await this.getClientForFile(filePath);
3323
+ if (!spawned)
3324
+ return null;
3325
+ const getter = spawned.client.getWorkspaceDiagnosticsSupport;
3326
+ if (typeof getter !== "function")
3327
+ return null;
3328
+ return getter();
3329
+ }
3330
+ const first = this.selectWorkspaceScopeClient();
3331
+ if (!first)
3332
+ return null;
3333
+ const getter = first.client.getWorkspaceDiagnosticsSupport;
3334
+ if (typeof getter !== "function")
3335
+ return null;
3336
+ if (attribution)
3337
+ attribution.getWorkspaceDiagnosticsSupport = first.serverId;
3338
+ return getter.call(first.client);
3339
+ }
3340
+ async codeAction(filePath, line, character, endLine, endCharacter) {
3341
+ const spawned = await this.getClientForFile(filePath, NAV_CLIENT_WAIT_TIMEOUT_MS);
3342
+ if (!spawned)
3343
+ return [];
3344
+ if (!spawned.client.getOperationSupport().codeAction) {
3345
+ throw new Error("__UNSUPPORTED__ Active LSP server does not advertise support for codeAction");
3346
+ }
3347
+ return spawned.client.codeAction(filePath, line, character, endLine, endCharacter);
3348
+ }
3349
+ async rename(filePath, line, character, newName) {
3350
+ const spawned = await this.getClientForFile(filePath, NAV_CLIENT_WAIT_TIMEOUT_MS);
3351
+ if (!spawned)
3352
+ return null;
3353
+ if (!spawned.client.getOperationSupport().rename) {
3354
+ throw new Error("__UNSUPPORTED__ Active LSP server does not advertise support for rename");
3355
+ }
3356
+ return spawned.client.rename(filePath, line, character, newName);
3357
+ }
3358
+ async renameFile(oldFilePath, newFilePath, options) {
3359
+ const cwd = options.cwd;
3360
+ const apply = options.apply ?? false;
3361
+ await validateWorkspaceEdit({
3362
+ documentChanges: [
3363
+ {
3364
+ kind: "rename",
3365
+ oldUri: pathToFileURL(oldFilePath).href,
3366
+ newUri: pathToFileURL(newFilePath).href
3367
+ }
3368
+ ]
3369
+ }, cwd);
3370
+ const priorityServerIds = getServersForFileWithConfig(oldFilePath).map((server) => server.id);
3371
+ const activeClients = this.activeClientsForCwd(cwd, priorityServerIds);
3372
+ const willRenameFailures = [];
3373
+ const didRenameFailures = [];
3374
+ const willRenameClients = activeClients.filter(({ serverId, client }) => {
3375
+ if (client.getOperationSupport().willRenameFiles === true)
3376
+ return true;
3377
+ recordDegradationOnce({
3378
+ kind: "lsp-capability-skip",
3379
+ subject: `${serverId}:workspace/willRenameFiles`,
3380
+ reason: client.getMalformedFileOperationRegistrations().has("willRename") ? "malformed-registration" : "no-registration"
3381
+ });
3382
+ return false;
3383
+ });
3384
+ const willResults = await Promise.all(willRenameClients.map(async ({ serverId, client }) => {
3385
+ try {
3386
+ return {
3387
+ serverId,
3388
+ edit: await client.willRenameFiles(oldFilePath, newFilePath)
3389
+ };
3390
+ } catch (err) {
3391
+ willRenameFailures.push({
3392
+ serverId,
3393
+ error: err instanceof Error ? err.message : String(err)
3394
+ });
3395
+ return { serverId, edit: null };
3396
+ }
3397
+ }));
3398
+ const successfulWillResults = willResults.filter((result) => !willRenameFailures.some((failure) => failure.serverId === result.serverId));
3399
+ if (willRenameClients.length > 0 && successfulWillResults.length === 0) {
3400
+ throw new Error(`workspace/willRenameFiles failed for all active LSP servers: ${willRenameFailures.map((failure) => `${failure.serverId}: ${failure.error}`).join("; ")}`);
3401
+ }
3402
+ const merged = mergeWorkspaceTextEditsByPriority(successfulWillResults);
3403
+ const summary = summarizeWorkspaceEdit(merged.edit, cwd);
3404
+ if (!apply) {
3405
+ return {
3406
+ applied: false,
3407
+ serverIds: activeClients.map((entry) => entry.serverId),
3408
+ willRenameFailures,
3409
+ didRenameFailures,
3410
+ droppedConflicts: merged.droppedConflicts,
3411
+ inputEditCount: merged.inputEditCount,
3412
+ summary
3413
+ };
3414
+ }
3415
+ let applied;
3416
+ try {
3417
+ applied = await applyWorkspaceEdit(merged.edit, cwd, {
3418
+ mutationContext: options.mutationContext,
3419
+ observe: false
3420
+ });
3421
+ } catch (err) {
3422
+ if (options.mutationContext) {
3423
+ const partial = err.appliedWorkspaceEdit;
3424
+ if (partial) {
3425
+ recordLspMutation(options.mutationContext, {
3426
+ results: [partial],
3427
+ status: "failed"
3428
+ });
3429
+ }
3430
+ }
3431
+ throw err;
3432
+ }
3433
+ const openDocuments = activeClients.filter(({ client }) => client.isDocumentOpen(oldFilePath)).map(({ serverId, client }) => ({
3434
+ serverId,
3435
+ client,
3436
+ oldUri: client.getDocumentUri(oldFilePath)
3437
+ }));
3438
+ const closeFailures = [];
3439
+ await Promise.all(openDocuments.map(async ({ serverId, client }) => {
3440
+ const result = await runRenameNotify(() => client.closeDocument(oldFilePath), RENAME_NOTIFY_TIMEOUT_MS);
3441
+ if (!result.ok) {
3442
+ closeFailures.push({
3443
+ serverId,
3444
+ error: result.error,
3445
+ disposition: result.disposition
3446
+ });
3447
+ }
3448
+ }));
3449
+ if (closeFailures.length > 0) {
3450
+ if (options.mutationContext) {
3451
+ recordLspMutation(options.mutationContext, {
3452
+ results: [applied],
3453
+ status: "failed"
3454
+ });
3455
+ }
3456
+ const content = await fs.readFile(oldFilePath, "utf-8");
3457
+ const languageId = getLanguageId(oldFilePath) ?? "plaintext";
3458
+ const resyncFailures = [];
3459
+ await Promise.all(openDocuments.map(async ({ serverId, client }) => {
3460
+ const resyncResult = await runRenameNotify(() => client.notify.open(oldFilePath, content, languageId, true, true), RENAME_NOTIFY_TIMEOUT_MS);
3461
+ if (!resyncResult.ok) {
3462
+ resyncFailures.push({
3463
+ serverId,
3464
+ error: resyncResult.error,
3465
+ disposition: resyncResult.disposition
3466
+ });
3467
+ }
3468
+ }));
3469
+ if (resyncFailures.length > 0) {
3470
+ logLatency({
3471
+ type: "phase",
3472
+ phase: "lsp_rename_resync_failed",
3473
+ filePath: oldFilePath,
3474
+ durationMs: 0,
3475
+ metadata: {
3476
+ failures: resyncFailures.map((failure) => ({
3477
+ serverId: failure.serverId,
3478
+ disposition: failure.disposition,
3479
+ error: failure.error
3480
+ }))
3481
+ }
3482
+ });
3483
+ }
3484
+ const closeFailureSummary = closeFailures.map((failure) => `${failure.serverId} (${failure.disposition}): ${failure.error}`).join("; ");
3485
+ const resyncFailureSummary = resyncFailures.length > 0 ? ` (resync also failed: ${resyncFailures.map((failure) => `${failure.serverId} (${failure.disposition}): ${failure.error}`).join("; ")})` : "";
3486
+ throw new Error(`workspace/didClose failed; rename aborted: ${closeFailureSummary}${resyncFailureSummary}`);
3487
+ }
3488
+ let renameApplied;
3489
+ try {
3490
+ renameApplied = await applyWorkspaceEdit({
3491
+ documentChanges: [
3492
+ {
3493
+ kind: "rename",
3494
+ oldUri: pathToFileURL(oldFilePath).href,
3495
+ newUri: pathToFileURL(newFilePath).href
3496
+ }
3497
+ ]
3498
+ }, cwd, { observe: false });
3499
+ } catch (err) {
3500
+ if (options.mutationContext) {
3501
+ recordLspMutation(options.mutationContext, {
3502
+ results: [applied],
3503
+ status: "failed"
3504
+ });
3505
+ }
3506
+ throw err;
3507
+ }
3508
+ const relOld = path.relative(cwd, oldFilePath).replace(/\\/g, "/") || path.basename(oldFilePath);
3509
+ const relNew = path.relative(cwd, newFilePath).replace(/\\/g, "/") || path.basename(newFilePath);
3510
+ const renameDescription = `Renamed ${relOld} → ${relNew}`;
3511
+ await Promise.all(activeClients.map(async ({ serverId, client }) => {
3512
+ if (client.getOperationSupport().didRenameFiles !== true) {
3513
+ recordDegradationOnce({
3514
+ kind: "lsp-capability-skip",
3515
+ subject: `${serverId}:workspace/didRenameFiles`,
3516
+ reason: client.getMalformedFileOperationRegistrations().has("didRename") ? "malformed-registration" : "no-registration"
3517
+ });
3518
+ return;
3519
+ }
3520
+ const opened = openDocuments.find((entry) => entry.serverId === serverId);
3521
+ const result = await runRenameNotify(() => opened?.oldUri ? client.didRenameFiles(oldFilePath, newFilePath, opened.oldUri, destinationUriPreservingSpelling(opened.oldUri, oldFilePath, newFilePath)) : client.didRenameFiles(oldFilePath, newFilePath), RENAME_NOTIFY_TIMEOUT_MS);
3522
+ if (!result.ok) {
3523
+ didRenameFailures.push({
3524
+ serverId,
3525
+ error: result.error,
3526
+ disposition: result.disposition
3527
+ });
3528
+ }
3529
+ }));
3530
+ const files = [...new Set([...applied.files, oldFilePath, newFilePath])];
3531
+ if (options.mutationContext) {
3532
+ recordLspMutation(options.mutationContext, {
3533
+ results: [
3534
+ {
3535
+ ...applied,
3536
+ files,
3537
+ operationTotal: applied.operationTotal + renameApplied.operationTotal,
3538
+ appliedOperationTotal: applied.appliedOperationTotal + renameApplied.appliedOperationTotal,
3539
+ appliedOperationIndexes: [
3540
+ ...applied.appliedOperationIndexes,
3541
+ ...renameApplied.appliedOperationIndexes.map((index) => applied.operationTotal + index)
3542
+ ],
3543
+ operationCounts: {
3544
+ textEdits: applied.operationCounts.textEdits + renameApplied.operationCounts.textEdits,
3545
+ create: applied.operationCounts.create + renameApplied.operationCounts.create,
3546
+ rename: applied.operationCounts.rename + renameApplied.operationCounts.rename,
3547
+ delete: applied.operationCounts.delete + renameApplied.operationCounts.delete
3548
+ },
3549
+ fileDetails: [...applied.fileDetails, ...renameApplied.fileDetails]
3550
+ }
3551
+ ],
3552
+ status: "success"
3553
+ });
3554
+ }
3555
+ return {
3556
+ applied: true,
3557
+ serverIds: activeClients.map((entry) => entry.serverId),
3558
+ willRenameFailures,
3559
+ didRenameFailures,
3560
+ droppedConflicts: merged.droppedConflicts,
3561
+ inputEditCount: merged.inputEditCount,
3562
+ summary,
3563
+ descriptions: [...applied.descriptions, renameDescription],
3564
+ files
3565
+ };
3566
+ }
3567
+ async implementation(filePath, line, character) {
3568
+ const spawned = await this.getClientForFile(filePath, NAV_CLIENT_WAIT_TIMEOUT_MS);
3569
+ if (!spawned)
3570
+ return [];
3571
+ if (!spawned.client.getOperationSupport().implementation) {
3572
+ throw new Error("__UNSUPPORTED__ Active LSP server does not advertise support for implementation");
3573
+ }
3574
+ return spawned.client.implementation(filePath, line, character);
3575
+ }
3576
+ async prepareCallHierarchy(filePath, line, character) {
3577
+ const spawned = await this.getClientForFile(filePath, NAV_CLIENT_WAIT_TIMEOUT_MS);
3578
+ if (!spawned)
3579
+ return [];
3580
+ if (!spawned.client.getOperationSupport().callHierarchy) {
3581
+ throw new Error("__UNSUPPORTED__ Active LSP server does not advertise support for prepareCallHierarchy");
3582
+ }
3583
+ return spawned.client.prepareCallHierarchy(filePath, line, character);
3584
+ }
3585
+ async incomingCalls(item) {
3586
+ const spawned = await this.getClientForFile(uriToPath(item.uri), NAV_CLIENT_WAIT_TIMEOUT_MS);
3587
+ if (!spawned)
3588
+ return [];
3589
+ if (!spawned.client.getOperationSupport().callHierarchy)
3590
+ return [];
3591
+ return spawned.client.incomingCalls(item);
3592
+ }
3593
+ async outgoingCalls(item) {
3594
+ const spawned = await this.getClientForFile(uriToPath(item.uri), NAV_CLIENT_WAIT_TIMEOUT_MS);
3595
+ if (!spawned)
3596
+ return [];
3597
+ if (!spawned.client.getOperationSupport().callHierarchy)
3598
+ return [];
3599
+ return spawned.client.outgoingCalls(item);
3600
+ }
3601
+ async ensureWarmForSweep(representativeFile, options = {}) {
3602
+ if (this.checkDestroyed() || options.signal?.aborted) {
3603
+ return { performedWarmup: false, failedServerIds: [] };
3604
+ }
3605
+ const servers = getServersForFileWithConfig(representativeFile).filter((s) => s.role !== "auxiliary");
3606
+ if (servers.length === 0) {
3607
+ return { performedWarmup: false, failedServerIds: [] };
3608
+ }
3609
+ const keys = await Promise.all(servers.map((server) => this.demonstratedReadyKeyFor(server, representativeFile)));
3610
+ const alreadyWarm = keys.every((key) => key === undefined || this.state.demonstratedReady.has(key));
3611
+ if (alreadyWarm)
3612
+ return { performedWarmup: false, failedServerIds: [] };
3613
+ const cachedColdServerIds = [];
3614
+ let allNonWarmCached = true;
3615
+ for (let i = 0;i < servers.length; i++) {
3616
+ const key = keys[i];
3617
+ if (key === undefined || this.state.demonstratedReady.has(key))
3618
+ continue;
3619
+ if (this.state.demonstratedCold.has(key)) {
3620
+ cachedColdServerIds.push(servers[i].id);
3621
+ } else {
3622
+ allNonWarmCached = false;
3623
+ }
3624
+ }
3625
+ if (allNonWarmCached && cachedColdServerIds.length > 0) {
3626
+ logLatency({
3627
+ type: "phase",
3628
+ phase: "lsp_sweep_warmup_cached_cold",
3629
+ filePath: representativeFile,
3630
+ durationMs: 0,
3631
+ metadata: { serverIds: cachedColdServerIds }
3632
+ });
3633
+ return {
3634
+ performedWarmup: false,
3635
+ failedServerIds: cachedColdServerIds,
3636
+ skippedFromCache: true
3637
+ };
3638
+ }
3639
+ let content;
3640
+ try {
3641
+ content = await nodeFs.promises.readFile(representativeFile, "utf-8");
3642
+ } catch {
3643
+ return { performedWarmup: false, failedServerIds: [] };
3644
+ }
3645
+ if (options.signal?.aborted) {
3646
+ return { performedWarmup: false, failedServerIds: [] };
3647
+ }
3648
+ const timeoutMs = options.timeoutMs ?? warmupTimeoutMs();
3649
+ const rootedServerIds = [];
3650
+ const unrootedServerIds = [];
3651
+ for (let i = 0;i < servers.length; i++) {
3652
+ if (keys[i] !== undefined)
3653
+ rootedServerIds.push(servers[i].id);
3654
+ else
3655
+ unrootedServerIds.push(servers[i].id);
3656
+ }
3657
+ const unrootedCandidates = {
3658
+ count: unrootedServerIds.length,
3659
+ ids: unrootedServerIds
3660
+ };
3661
+ const stillColdServerIds = () => {
3662
+ const cold = [];
3663
+ for (let i = 0;i < servers.length; i++) {
3664
+ const key = keys[i];
3665
+ if (key !== undefined && !this.state.demonstratedReady.has(key)) {
3666
+ cold.push(servers[i].id);
3667
+ }
3668
+ }
3669
+ return cold;
3670
+ };
3671
+ const runWarmupTouch = async (attempt) => {
3672
+ if (options.signal?.aborted)
3673
+ return;
3674
+ const startedAt = Date.now();
3675
+ logLatency({
3676
+ type: "phase",
3677
+ phase: "lsp_sweep_warmup_start",
3678
+ filePath: representativeFile,
3679
+ durationMs: 0,
3680
+ metadata: {
3681
+ serverIds: rootedServerIds,
3682
+ unrootedCandidates,
3683
+ timeoutMs,
3684
+ attempt
3685
+ }
3686
+ });
3687
+ const warmupAttempt = this.touchFile(representativeFile, content, {
3688
+ diagnostics: "document",
3689
+ collectDiagnostics: false,
3690
+ clientScope: "primary",
3691
+ source: "lsp_sweep_warmup",
3692
+ maxClientWaitMs: timeoutMs,
3693
+ maxDiagnosticsWaitMs: timeoutMs,
3694
+ warmupOverride: true,
3695
+ warmupAttempt: attempt
3696
+ });
3697
+ await (options.signal ? Promise.race([
3698
+ withDeadline(warmupAttempt, {
3699
+ ms: timeoutMs,
3700
+ onTimeout: "undefined"
3701
+ }),
3702
+ new Promise((resolve) => {
3703
+ if (options.signal.aborted) {
3704
+ resolve();
3705
+ return;
3706
+ }
3707
+ options.signal.addEventListener("abort", () => resolve(), {
3708
+ once: true
3709
+ });
3710
+ })
3711
+ ]) : withDeadline(warmupAttempt, {
3712
+ ms: timeoutMs,
3713
+ onTimeout: "undefined"
3714
+ }));
3715
+ const coldServerIds = stillColdServerIds();
3716
+ logLatency({
3717
+ type: "phase",
3718
+ phase: options.signal?.aborted ? "lsp_sweep_warmup_aborted" : coldServerIds.length > 0 ? "lsp_sweep_warmup_failed" : "lsp_sweep_warmup_done",
3719
+ filePath: representativeFile,
3720
+ durationMs: Date.now() - startedAt,
3721
+ metadata: {
3722
+ serverIds: rootedServerIds,
3723
+ unrootedCandidates,
3724
+ timeoutMs,
3725
+ attempt,
3726
+ coldServerIds
3727
+ }
3728
+ });
3729
+ };
3730
+ await runWarmupTouch(1);
3731
+ let failedServerIds = stillColdServerIds();
3732
+ if (failedServerIds.length > 0 && !options.signal?.aborted) {
3733
+ const backoffMs = warmupRetryBackoffMs();
3734
+ if (backoffMs > 0) {
3735
+ await new Promise((resolve) => {
3736
+ const timer = setTimeout(resolve, backoffMs);
3737
+ timer.unref?.();
3738
+ options.signal?.addEventListener("abort", () => resolve(), {
3739
+ once: true
3740
+ });
3741
+ });
3742
+ }
3743
+ if (!options.signal?.aborted) {
3744
+ await runWarmupTouch(2);
3745
+ failedServerIds = stillColdServerIds();
3746
+ }
3747
+ }
3748
+ if (failedServerIds.length > 0) {
3749
+ for (let i = 0;i < servers.length; i++) {
3750
+ const key = keys[i];
3751
+ if (key !== undefined && failedServerIds.includes(servers[i].id)) {
3752
+ this.state.demonstratedCold.add(key);
3753
+ }
3754
+ }
3755
+ }
3756
+ return { performedWarmup: true, failedServerIds };
3757
+ }
3758
+ async runWorkspaceDiagnostics(cwd, options = {}) {
3759
+ const releaseSweepHold = acquireWorkspaceSweepHold();
3760
+ try {
3761
+ return await this.runWorkspaceDiagnosticsSwept(cwd, options);
3762
+ } finally {
3763
+ releaseSweepHold();
3764
+ }
3765
+ }
3766
+ async runWorkspaceDiagnosticsSwept(cwd, options = {}) {
3767
+ const startedAt = Date.now();
3768
+ const root = path.resolve(cwd);
3769
+ const { signal } = options;
3770
+ this.sweepDocumentDrift().catch(() => {});
3771
+ const maxFiles = typeof options.maxFiles === "number" && Number.isFinite(options.maxFiles) && options.maxFiles > 0 ? Math.floor(options.maxFiles) : getMaxWorkspaceDiagnosticFiles();
3772
+ const files = options.files ? options.files.slice(0, maxFiles) : await collectWorkspaceDiagnosticFiles(root, maxFiles, signal);
3773
+ const perFileMs = (() => {
3774
+ const raw = Number(process.env.DM_LENS_LSP_WORKSPACE_PER_FILE_MS);
3775
+ return Number.isFinite(raw) && raw > 0 ? raw : 15000;
3776
+ })();
3777
+ const results = [];
3778
+ let completed = 0;
3779
+ let timedOutFiles = 0;
3780
+ let lastHeartbeat = Date.now();
3781
+ const workspaceDiagnosticsCacheCtx = createWorkspaceDiagnosticsCacheContext(root);
3782
+ const workspaceSweepScopeKey = buildScopeKey("all", [
3783
+ ...WORKSPACE_SWEEP_EXCLUDED_SERVER_IDS
3784
+ ]);
3785
+ const touchScopedOpenDependencies = async (filePath) => {
3786
+ if (options.files === undefined)
3787
+ return true;
3788
+ const imports = workspaceDiagnosticsCacheCtx.importsFor(filePath);
3789
+ if (!imports)
3790
+ return false;
3791
+ const openImports = imports.filter((dependency) => this.hasLiveClientHoldingDocument(dependency));
3792
+ const capped = openImports.length > SCOPED_DEPENDENCY_TOUCH_MAX;
3793
+ if (capped) {
3794
+ recordDegradationOnce({
3795
+ kind: "lsp_dependency_touch_capped",
3796
+ subject: filePath,
3797
+ reason: `scoped full scan limited open import touches to ${SCOPED_DEPENDENCY_TOUCH_MAX}`
3798
+ });
3799
+ }
3800
+ await Promise.all(openImports.slice(0, SCOPED_DEPENDENCY_TOUCH_MAX).map(async (dependency) => {
3801
+ let content;
3802
+ try {
3803
+ content = await fs.readFile(dependency, "utf8");
3804
+ } catch {
3805
+ return;
3806
+ }
3807
+ await this.touchFile(dependency, content, {
3808
+ diagnostics: "none",
3809
+ source: "lens_diagnostics_full_dependency",
3810
+ clientScope: "all",
3811
+ excludeServerIds: await this.serverIdsNotHoldingDocument(dependency)
3812
+ });
3813
+ }));
3814
+ return !capped;
3815
+ };
3816
+ const cachedResults = [];
3817
+ const filesToTouch = [];
3818
+ const writeIndexByPath = new Map;
3819
+ for (const filePath of files) {
3820
+ writeIndexByPath.set(normalizeMapKey(filePath), options.nextWriteIndex?.());
3821
+ const cached = workspaceDiagnosticsCacheCtx.lookup(filePath, workspaceSweepScopeKey);
3822
+ if (cached && cached.binding.boundToCurrentDisk !== false) {
3823
+ const dependenciesConfirmed = await touchScopedOpenDependencies(filePath);
3824
+ if (dependenciesConfirmed)
3825
+ cachedResults.push({
3826
+ filePath,
3827
+ diagnostics: cached.diagnostics,
3828
+ count: cached.count,
3829
+ observedAt: cached.scannedAt,
3830
+ contentHash: cached.binding.contentHash,
3831
+ boundToCurrentDisk: cached.binding.boundToCurrentDisk,
3832
+ writeIndex: writeIndexByPath.get(normalizeMapKey(filePath))
3833
+ });
3834
+ else
3835
+ filesToTouch.push(filePath);
3836
+ } else {
3837
+ filesToTouch.push(filePath);
3838
+ }
3839
+ }
3840
+ completed = cachedResults.length;
3841
+ if (cachedResults.length > 0) {
3842
+ options.onProgress?.(completed, files.length);
3843
+ }
3844
+ const cacheServedKeys = new Set(cachedResults.map((result) => normalizeMapKey(result.filePath)));
3845
+ const supersededCacheKeys = new Set;
3846
+ const scannedMtimeByFile = new Map;
3847
+ const scannedSizeByFile = new Map;
3848
+ const groups = groupFilesByPrimaryServer(filesToTouch);
3849
+ const sweepIndexGate = createSweepIndexGate();
3850
+ const workspacePullEnabled = process.env.DM_LENS_LSP_WORKSPACE_PULL === "1";
3851
+ logLatency({
3852
+ type: "phase",
3853
+ phase: "lsp_workspace_diagnostics_start",
3854
+ filePath: root,
3855
+ durationMs: 0,
3856
+ metadata: {
3857
+ fileCount: files.length,
3858
+ cacheHits: cachedResults.length,
3859
+ filesToTouch: filesToTouch.length,
3860
+ maxFiles,
3861
+ perFileMs,
3862
+ serverGroups: groups.length
3863
+ }
3864
+ });
3865
+ const contentCache = new Map;
3866
+ const preOpenGroupFiles = async (groupFiles) => {
3867
+ if (isWarmAttached())
3868
+ return;
3869
+ for (const filePath of groupFiles) {
3870
+ if (signal?.aborted)
3871
+ return;
3872
+ let content;
3873
+ try {
3874
+ content = await nodeFs.promises.readFile(filePath, "utf-8");
3875
+ } catch {
3876
+ continue;
3877
+ }
3878
+ contentCache.set(filePath, content);
3879
+ const languageId = getLanguageId(filePath) ?? "plaintext";
3880
+ const preOpenAttempt = withDeadline((async () => {
3881
+ const { clients } = await this.getClientsForFile(filePath, WORKSPACE_SWEEP_EXCLUDED_SERVER_IDS);
3882
+ for (const entry of clients) {
3883
+ let auxKey;
3884
+ if (entry.info.role === "auxiliary") {
3885
+ auxKey = await this.demonstratedReadyKeyFor(entry.info, filePath);
3886
+ if (auxKey && this.auxNotifyBacklogAtCeiling(auxKey, entry)) {
3887
+ continue;
3888
+ }
3889
+ }
3890
+ try {
3891
+ await entry.client.notify.open(filePath, content, languageId);
3892
+ if (auxKey)
3893
+ this.noteAuxNotifyIssued(auxKey, entry.client);
3894
+ } catch {}
3895
+ }
3896
+ })(), { ms: perFileMs, onTimeout: "undefined" });
3897
+ await (signal ? Promise.race([
3898
+ preOpenAttempt,
3899
+ new Promise((resolve) => {
3900
+ if (signal.aborted) {
3901
+ resolve();
3902
+ return;
3903
+ }
3904
+ signal.addEventListener("abort", () => resolve(), {
3905
+ once: true
3906
+ });
3907
+ })
3908
+ ]) : preOpenAttempt);
3909
+ }
3910
+ };
3911
+ const markServiceDestroyed = (remainingFiles) => {
3912
+ for (const filePath of remainingFiles) {
3913
+ results.push({
3914
+ filePath,
3915
+ diagnostics: [],
3916
+ count: 0,
3917
+ timedOut: true,
3918
+ unconfirmedReason: "service_destroyed",
3919
+ writeIndex: writeIndexByPath.get(normalizeMapKey(filePath))
3920
+ });
3921
+ timedOutFiles += 1;
3922
+ completed += 1;
3923
+ }
3924
+ options.onProgress?.(completed, files.length);
3925
+ };
3926
+ const processFile = async (filePath) => {
3927
+ try {
3928
+ const content = contentCache.get(filePath) ?? await nodeFs.promises.readFile(filePath, "utf-8");
3929
+ try {
3930
+ const scanStat = nodeFs.statSync(filePath);
3931
+ scannedMtimeByFile.set(filePath, scanStat.mtimeMs);
3932
+ scannedSizeByFile.set(filePath, scanStat.size);
3933
+ } catch {}
3934
+ const attached = isWarmAttached() ? await tryWarmAttachedDiagnostics(filePath, content, perFileMs, "sweep") : undefined;
3935
+ if (attached && !attached.available) {
3936
+ await this.ensureWarmForSweep(filePath, { signal });
3937
+ }
3938
+ const touchResult = attached?.available ? {
3939
+ diags: attached.response.diagnostics,
3940
+ ...attached.response.unconfirmedServerIds !== undefined && {
3941
+ unconfirmedServerIds: attached.response.unconfirmedServerIds
3942
+ }
3943
+ } : await withDeadline(this.touchFile(filePath, content, {
3944
+ diagnostics: "document",
3945
+ collectDiagnostics: true,
3946
+ clientScope: "all",
3947
+ source: "lens_diagnostics_full",
3948
+ excludeServerIds: WORKSPACE_SWEEP_EXCLUDED_SERVER_IDS,
3949
+ sweepIndexGate
3950
+ }), { ms: perFileMs, onTimeout: "undefined" });
3951
+ const diagnostics = touchResult?.diags;
3952
+ const inconclusive = touchResult?.inconclusive === true;
3953
+ const unconfirmedServerIds = touchCoverageGap(touchResult);
3954
+ const coverageGap = unconfirmedServerIds.length > 0;
3955
+ const declinedOutsideRoot = touchResult?.skipReason === "outside-project-root";
3956
+ const timedOut = touchResult === undefined || inconclusive || declinedOutsideRoot;
3957
+ if (timedOut)
3958
+ timedOutFiles += 1;
3959
+ const unconfirmedReason = touchResult === undefined ? "budget" : declinedOutsideRoot ? "outside_project_root" : inconclusive ? "inconclusive" : undefined;
3960
+ const rawBinding = touchResult?.binding;
3961
+ const filteredDiagnostics = diagnostics ? applyAuxiliarySuppressions(diagnostics, content, {
3962
+ fileRole: detectFileRole(filePath, content),
3963
+ filePath,
3964
+ scanRoot: root
3965
+ }) : diagnostics;
3966
+ results.push({
3967
+ filePath,
3968
+ diagnostics: filteredDiagnostics ?? [],
3969
+ count: filteredDiagnostics?.length ?? 0,
3970
+ ...timedOut && { timedOut: true, unconfirmedReason },
3971
+ ...coverageGap && {
3972
+ unconfirmedServerIds: [...unconfirmedServerIds]
3973
+ },
3974
+ contentHash: rawBinding?.contentHash,
3975
+ boundToCurrentDisk: rawBinding?.boundToCurrentDisk,
3976
+ writeIndex: writeIndexByPath.get(normalizeMapKey(filePath))
3977
+ });
3978
+ } catch (err) {
3979
+ results.push({
3980
+ filePath,
3981
+ diagnostics: [],
3982
+ count: 0,
3983
+ error: err instanceof Error ? err.message : String(err),
3984
+ timedOut: true,
3985
+ unconfirmedReason: "error",
3986
+ writeIndex: writeIndexByPath.get(normalizeMapKey(filePath))
3987
+ });
3988
+ }
3989
+ completed += 1;
3990
+ options.onProgress?.(completed, files.length);
3991
+ if (Date.now() - lastHeartbeat >= 1e4) {
3992
+ lastHeartbeat = Date.now();
3993
+ logLatency({
3994
+ type: "phase",
3995
+ phase: "lsp_workspace_diagnostics_progress",
3996
+ filePath: root,
3997
+ durationMs: Date.now() - startedAt,
3998
+ metadata: {
3999
+ completed,
4000
+ total: files.length,
4001
+ timedOutFiles,
4002
+ aborted: signal?.aborted ?? false
4003
+ }
4004
+ });
4005
+ }
4006
+ };
4007
+ const groupWorkers = Math.min(WORKSPACE_DIAGNOSTICS_CONCURRENCY, groups.length);
4008
+ const sweepPhase = phaseStarted("lsp_workspace_diagnostics_touch");
4009
+ try {
4010
+ await runPerServerGroups(groups, groupWorkers, async (group) => {
4011
+ if (signal?.aborted)
4012
+ return;
4013
+ if (this.checkDestroyed()) {
4014
+ markServiceDestroyed(group.files);
4015
+ return;
4016
+ }
4017
+ if (!isWarmAttached() && workspacePullEnabled && !group.multiServer) {
4018
+ const pulled = await this.tryWorkspacePull(group.files, perFileMs, cacheServedKeys);
4019
+ if (pulled) {
4020
+ for (const clean of pulled.extraClean) {
4021
+ supersededCacheKeys.add(normalizeMapKey(clean.filePath));
4022
+ }
4023
+ for (const result of [...pulled.results, ...pulled.extraClean]) {
4024
+ results.push({
4025
+ ...result,
4026
+ writeIndex: writeIndexByPath.get(normalizeMapKey(result.filePath))
4027
+ });
4028
+ try {
4029
+ const pullStat = nodeFs.statSync(result.filePath);
4030
+ scannedMtimeByFile.set(result.filePath, pullStat.mtimeMs);
4031
+ scannedSizeByFile.set(result.filePath, pullStat.size);
4032
+ } catch {}
4033
+ }
4034
+ completed += group.files.length;
4035
+ options.onProgress?.(completed, files.length);
4036
+ return;
4037
+ }
4038
+ }
4039
+ const first = group.files[0];
4040
+ if (first && !isWarmAttached()) {
4041
+ const warmup = await this.ensureWarmForSweep(first, { signal });
4042
+ if (signal?.aborted)
4043
+ return;
4044
+ if (warmup.failedServerIds.length > 0) {
4045
+ logLatency({
4046
+ type: "phase",
4047
+ phase: "lsp_sweep_group_skipped_warmup",
4048
+ filePath: first,
4049
+ durationMs: 0,
4050
+ metadata: {
4051
+ failedServerIds: warmup.failedServerIds,
4052
+ skippedFromCache: warmup.skippedFromCache ?? false,
4053
+ skippedFiles: group.files.length
4054
+ }
4055
+ });
4056
+ for (const filePath of group.files) {
4057
+ results.push({
4058
+ filePath,
4059
+ diagnostics: [],
4060
+ count: 0,
4061
+ timedOut: true,
4062
+ unconfirmedReason: "inconclusive",
4063
+ skippedWarmupFailure: true
4064
+ });
4065
+ timedOutFiles += 1;
4066
+ completed += 1;
4067
+ }
4068
+ options.onProgress?.(completed, files.length);
4069
+ return;
4070
+ }
4071
+ if (warmup.performedWarmup)
4072
+ options.onServerReady?.();
4073
+ }
4074
+ for (let chunkStart = 0;chunkStart < group.files.length; chunkStart += WORKSPACE_SWEEP_PREOPEN_CHUNK_SIZE) {
4075
+ if (signal?.aborted)
4076
+ return;
4077
+ if (this.checkDestroyed()) {
4078
+ markServiceDestroyed(group.files.slice(chunkStart));
4079
+ return;
4080
+ }
4081
+ const chunk = group.files.slice(chunkStart, chunkStart + WORKSPACE_SWEEP_PREOPEN_CHUNK_SIZE);
4082
+ await preOpenGroupFiles(chunk);
4083
+ for (const filePath of chunk) {
4084
+ if (signal?.aborted)
4085
+ return;
4086
+ if (this.checkDestroyed()) {
4087
+ markServiceDestroyed(group.files.slice(group.files.indexOf(filePath)));
4088
+ return;
4089
+ }
4090
+ await processFile(filePath);
4091
+ }
4092
+ }
4093
+ }, signal);
4094
+ } finally {
4095
+ phaseFinished(sweepPhase);
4096
+ }
4097
+ const unconfirmedByReason = {};
4098
+ for (const result of results) {
4099
+ if (!result.timedOut)
4100
+ continue;
4101
+ const reason = result.unconfirmedReason ?? "budget";
4102
+ unconfirmedByReason[reason] = (unconfirmedByReason[reason] ?? 0) + 1;
4103
+ }
4104
+ const partiallyCoveredFiles = results.filter((result) => (result.unconfirmedServerIds?.length ?? 0) > 0).length;
4105
+ const unconfirmedServerIds = [
4106
+ ...new Set(results.flatMap((result) => result.unconfirmedServerIds ?? []))
4107
+ ].sort((a, b) => Number(a > b) - Number(a < b));
4108
+ logLatency({
4109
+ type: "phase",
4110
+ phase: "lsp_workspace_diagnostics",
4111
+ filePath: root,
4112
+ durationMs: Date.now() - startedAt,
4113
+ metadata: {
4114
+ filesChecked: files.length,
4115
+ cacheHits: cachedResults.length,
4116
+ diagnosticCount: results.reduce((sum, result) => sum + (result?.count ?? 0), 0),
4117
+ serverGroups: groups.length,
4118
+ concurrency: groupWorkers,
4119
+ maxFiles,
4120
+ timedOutFiles,
4121
+ unconfirmedByReason,
4122
+ partiallyCoveredFiles,
4123
+ ...unconfirmedServerIds.length > 0 && { unconfirmedServerIds },
4124
+ aborted: signal?.aborted ?? false
4125
+ }
4126
+ });
4127
+ for (const result of results) {
4128
+ const scannedAt = scannedMtimeByFile.get(result.filePath);
4129
+ if (result.error || result.timedOut || (result.unconfirmedServerIds?.length ?? 0) > 0 || scannedAt === undefined) {
4130
+ continue;
4131
+ }
4132
+ workspaceDiagnosticsCacheCtx.record(result.filePath, workspaceSweepScopeKey, result.diagnostics, scannedAt, result.contentHash, scannedSizeByFile.get(result.filePath));
4133
+ }
4134
+ workspaceDiagnosticsCacheCtx.persist();
4135
+ const servedCacheResults = supersededCacheKeys.size === 0 ? cachedResults : cachedResults.filter((result) => !supersededCacheKeys.has(normalizeMapKey(result.filePath)));
4136
+ return [...servedCacheResults, ...results].filter(Boolean);
4137
+ }
4138
+ async tryWorkspacePull(groupFiles, perFileMs, reanswerFor) {
4139
+ try {
4140
+ const first = groupFiles[0];
4141
+ if (!first)
4142
+ return;
4143
+ const spawned = await this.getClientForFile(first, perFileMs);
4144
+ if (!spawned)
4145
+ return;
4146
+ if (!spawned.client.getWorkspaceDiagnosticsSupport().workspaceDiagnostics) {
4147
+ return;
4148
+ }
4149
+ const report = await spawned.client.requestWorkspaceDiagnostics(Math.max(perFileMs, workspacePullBudgetMs()));
4150
+ if (!report)
4151
+ return;
4152
+ const byPath = new Map;
4153
+ for (const entry of report) {
4154
+ byPath.set(normalizeMapKey(entry.filePath), entry);
4155
+ }
4156
+ const results = groupFiles.map((filePath) => {
4157
+ const entry = byPath.get(normalizeMapKey(filePath));
4158
+ const diagnostics = entry?.diagnostics ?? [];
4159
+ return {
4160
+ filePath,
4161
+ diagnostics,
4162
+ count: diagnostics.length,
4163
+ timedOut: false,
4164
+ contentHash: entry?.contentHash
4165
+ };
4166
+ });
4167
+ const extraClean = [];
4168
+ if (reanswerFor && reanswerFor.size > 0) {
4169
+ const asked = new Set(groupFiles.map((f) => normalizeMapKey(f)));
4170
+ const withFindings = new Set;
4171
+ for (const entry of report) {
4172
+ if (entry.diagnostics.length > 0) {
4173
+ withFindings.add(normalizeMapKey(entry.filePath));
4174
+ }
4175
+ }
4176
+ for (const [key, entry] of byPath) {
4177
+ if (asked.has(key) || !reanswerFor.has(key))
4178
+ continue;
4179
+ if (entry.diagnostics.length > 0 || withFindings.has(key))
4180
+ continue;
4181
+ extraClean.push({
4182
+ filePath: entry.filePath,
4183
+ diagnostics: [],
4184
+ count: 0,
4185
+ timedOut: false,
4186
+ contentHash: entry.contentHash
4187
+ });
4188
+ }
4189
+ }
4190
+ return { results, extraClean };
4191
+ } catch {
4192
+ return;
4193
+ }
4194
+ }
4195
+ async getAllDiagnostics() {
4196
+ const all = new Map;
4197
+ const bindingsByPath = new Map;
4198
+ const now = Date.now();
4199
+ for (const [_key, client] of this.state.clients) {
4200
+ const trackedPaths = client.getTrackedDiagnosticPaths();
4201
+ const existingPaths = new Set;
4202
+ await Promise.all(trackedPaths.map(async (filePath) => {
4203
+ try {
4204
+ await nodeFs.promises.access(filePath);
4205
+ existingPaths.add(filePath);
4206
+ } catch {}
4207
+ return true;
4208
+ }));
4209
+ client.pruneDiagnostics((filePath, ts) => !existingPaths.has(filePath) || now - ts > CASCADE_DIAGNOSTICS_TTL_MS);
4210
+ const clientDiags = client.getAllDiagnostics();
4211
+ for (const [filePath, entry] of clientDiags) {
4212
+ const attributed = attributeDiagnostics(client.serverId, entry.diags);
4213
+ const existing = all.get(filePath);
4214
+ if (existing) {
4215
+ existing.diags = mergeLspDiagnostics([
4216
+ ...existing.diags,
4217
+ ...attributed
4218
+ ]);
4219
+ existing.ts = Math.max(existing.ts, entry.ts);
4220
+ } else {
4221
+ all.set(filePath, { diags: attributed, ts: entry.ts });
4222
+ }
4223
+ const list = bindingsByPath.get(filePath) ?? [];
4224
+ list.push(entry.binding);
4225
+ bindingsByPath.set(filePath, list);
4226
+ }
4227
+ }
4228
+ for (const [filePath, entry] of all) {
4229
+ const stored = bindingsByPath.get(filePath) ?? [];
4230
+ let memo;
4231
+ Object.defineProperty(entry, "binding", {
4232
+ enumerable: false,
4233
+ configurable: true,
4234
+ get: () => memo ??= this.mergeBinding(filePath, stored)
4235
+ });
4236
+ }
4237
+ return all;
4238
+ }
4239
+ supportsLSP(filePath) {
4240
+ return getServersForFileWithConfig(filePath).length > 0;
4241
+ }
4242
+ isSpawnInFlight(_filePath, resolvedRoots = new Map) {
4243
+ for (const [serverId, root] of resolvedRoots) {
4244
+ if (this.state.inFlight.has(`${serverId}:${root}`))
4245
+ return true;
4246
+ }
4247
+ return false;
4248
+ }
4249
+ async hasWarmLSP(filePath) {
4250
+ const spawned = await this.getWarmClientForFile(filePath);
4251
+ return Boolean(spawned);
4252
+ }
4253
+ async hasLSP(filePath) {
4254
+ const spawned = await this.getClientForFile(filePath);
4255
+ return Boolean(spawned);
4256
+ }
4257
+ async shutdown(options = {}) {
4258
+ const resetStartedAt = Date.now();
4259
+ if (this.checkDestroyed())
4260
+ return;
4261
+ this.isDestroyed = true;
4262
+ this.shutdownController.abort();
4263
+ for (const [key, token] of this.outstandingAuxNotifyWrites) {
4264
+ this.releaseOutstandingAuxNotifyWrite(key, token);
4265
+ }
4266
+ for (const key of this.typeScriptIdleTimers.keys()) {
4267
+ this.clearTypeScriptIdleTimer(key);
4268
+ }
4269
+ if (!options.processExiting && this.state.inFlight.size > 0) {
4270
+ const pending = Array.from(this.state.inFlight.values());
4271
+ this.state.inFlight.clear();
4272
+ const settled = await Promise.allSettled(pending);
4273
+ for (const result of settled) {
4274
+ if (result.status === "fulfilled" && result.value?.client) {
4275
+ result.value.client.shutdown({ fast: true }).catch(() => {});
4276
+ }
4277
+ }
4278
+ } else {
4279
+ this.state.inFlight.clear();
4280
+ }
4281
+ const aliveClients = this.getAliveClientCount();
4282
+ await Promise.allSettled(Array.from(this.state.clients.values(), (client) => Promise.resolve().then(() => client.shutdown(options))));
4283
+ logLatency({
4284
+ type: "phase",
4285
+ phase: "lsp_service_reset",
4286
+ filePath: "",
4287
+ startedAt: new Date(resetStartedAt).toISOString(),
4288
+ durationMs: Date.now() - resetStartedAt,
4289
+ metadata: {
4290
+ reason: options.reason ?? null,
4291
+ aliveClients,
4292
+ fast: !!options.fast,
4293
+ processExiting: !!options.processExiting
4294
+ }
4295
+ });
4296
+ this.state.clients.clear();
4297
+ this.state.broken.clear();
4298
+ this.firstContactWaits.clear();
4299
+ this.lastSpawnVerdict.clear();
4300
+ for (const [key, token] of this.outstandingAuxNotifyWrites) {
4301
+ this.releaseOutstandingAuxNotifyWrite(key, token);
4302
+ }
4303
+ this.notifyStallDemotions.clear();
4304
+ this.auxNotifyDrainLatencyEwma.clear();
4305
+ this.auxNotifyInflight.clear();
4306
+ this.documentDrift.clear();
4307
+ this.workspaceProbeLogged.clear();
4308
+ this.warmStartLogged.clear();
4309
+ }
4310
+ getStatus() {
4311
+ return Array.from(this.state.clients.entries()).map(([key, client]) => {
4312
+ const [serverId, root] = key.split(":");
4313
+ return {
4314
+ serverId,
4315
+ root,
4316
+ connected: client.isAlive(),
4317
+ pullFailureHistory: (client.getPullFailureHistory?.() ?? []).map((entry) => ({
4318
+ ...entry,
4319
+ message: entry.message.slice(0, 200)
4320
+ }))
4321
+ };
4322
+ });
4323
+ }
4324
+ recordRuntimeExitWindow(key, exitedAt, uptimeMs) {
4325
+ if (exitedAt == null || uptimeMs == null || uptimeMs > RUNTIME_EXIT_WINDOW_UPTIME_CEILING_MS) {
4326
+ return false;
4327
+ }
4328
+ const deaths = this.runtimeExitWindow.get(key) ?? [];
4329
+ deaths.push(exitedAt);
4330
+ const windowRef = Math.max(...deaths);
4331
+ const windowStart = windowRef - RUNTIME_EXIT_WINDOW_MS;
4332
+ let pruned = deaths.filter((t) => t >= windowStart);
4333
+ if (pruned.length > RUNTIME_EXIT_WINDOW_TRIP_COUNT) {
4334
+ pruned = pruned.slice(pruned.length - RUNTIME_EXIT_WINDOW_TRIP_COUNT);
4335
+ }
4336
+ this.runtimeExitWindow.set(key, pruned);
4337
+ return pruned.length >= RUNTIME_EXIT_WINDOW_TRIP_COUNT;
4338
+ }
4339
+ getBrokenStatus() {
4340
+ const keys = new Set([
4341
+ ...this.state.broken.keys(),
4342
+ ...this.permanentlyBroken
4343
+ ]);
4344
+ return [...keys].map((key) => {
4345
+ const separator = key.indexOf(":");
4346
+ return {
4347
+ serverId: separator >= 0 ? key.slice(0, separator) : key,
4348
+ root: separator >= 0 ? key.slice(separator + 1) : "",
4349
+ failures: (this.failureCounts.get(key) ?? 0) + Math.max(this.runtimeExitCounts.get(key) ?? 0, this.runtimeExitWindow.get(key)?.length ?? 0),
4350
+ permanentlyBroken: this.permanentlyBroken.has(key),
4351
+ cooldownUntil: this.state.broken.get(key) ?? 0
4352
+ };
4353
+ });
4354
+ }
4355
+ getAliveClientCount() {
4356
+ let count = 0;
4357
+ for (const client of this.state.clients.values()) {
4358
+ if (client.isAlive())
4359
+ count++;
4360
+ }
4361
+ return count;
4362
+ }
4363
+ getAliveServerIds() {
4364
+ const primary = [];
4365
+ const aux = [];
4366
+ const seen = new Set;
4367
+ for (const client of this.state.clients.values()) {
4368
+ if (!client.isAlive())
4369
+ continue;
4370
+ const id = client.serverId;
4371
+ if (seen.has(id))
4372
+ continue;
4373
+ seen.add(id);
4374
+ const role = LSP_SERVERS.find((s) => s.id === id)?.role;
4375
+ (role === "auxiliary" ? aux : primary).push(id);
4376
+ }
4377
+ return [...primary, ...aux];
4378
+ }
4379
+ }
4380
+ const LSP_PROCESS_FAMILY = "lsp.service";
4381
+ const LSP_PROCESS_VERSION = 1;
4382
+ function lspProcessState() {
4383
+ let incompatibleHandoff;
4384
+ return getProcessSingleton(LSP_PROCESS_FAMILY, LSP_PROCESS_VERSION, () => ({ service: null, generationHandoff: incompatibleHandoff }), (value) => {
4385
+ if (!value || typeof value !== "object")
4386
+ return;
4387
+ const candidate = value;
4388
+ const previousHandoff = candidate.generationHandoff;
4389
+ const teardown = candidate.service?.shutdown ? candidate.service.shutdown({ fast: true, reason: "process_singleton_reset" }).catch(() => {
4390
+ return;
4391
+ }) : undefined;
4392
+ if (previousHandoff && teardown) {
4393
+ incompatibleHandoff = Promise.allSettled([
4394
+ previousHandoff,
4395
+ teardown
4396
+ ]).then(() => {
4397
+ return;
4398
+ });
4399
+ } else {
4400
+ incompatibleHandoff = previousHandoff ?? teardown;
4401
+ }
4402
+ });
4403
+ }
4404
+ function processService() {
4405
+ const state = lspProcessState();
4406
+ if (!state.service)
4407
+ state.service = new LSPService(state.generationHandoff, process.cwd());
4408
+ return state.service;
4409
+ }
4410
+ export function getLSPService() {
4411
+ return processService();
4412
+ }
4413
+ export async function hasAuxiliaryLspPublishedForRoot(serverId, filePath) {
4414
+ return getLSPService().hasServerPublishedForFileRoot(serverId, filePath);
4415
+ }
4416
+ export async function notifyExternalFileChange(filePath, type) {
4417
+ return getLSPService().notifyExternalFileChange(filePath, type);
4418
+ }
4419
+ export async function resyncGitChangedFiles(changedPaths) {
4420
+ return getLSPService().resyncGitChangedFiles(changedPaths);
4421
+ }
4422
+ export function resetLSPService(options = {}) {
4423
+ abortDeferredLspWork(`lsp service reset: ${options.reason ?? "unspecified"}`);
4424
+ resetLspLaunchAvailabilityGeneration();
4425
+ if (options.reason === "session_start") {
4426
+ resetClassicTsRepairGuard();
4427
+ clearWorkspaceSweepHoldForSessionStart();
4428
+ }
4429
+ const state = lspProcessState();
4430
+ const retiringService = state.service;
4431
+ state.service = null;
4432
+ if (!retiringService)
4433
+ return;
4434
+ const teardown = retiringService.shutdown(options);
4435
+ const pending = state.generationHandoff ? [state.generationHandoff, teardown] : [teardown];
4436
+ const handoff = Promise.allSettled(pending).then(() => {
4437
+ return;
4438
+ });
4439
+ state.generationHandoff = handoff;
4440
+ handoff.then(() => {
4441
+ if (state.generationHandoff === handoff) {
4442
+ state.generationHandoff = undefined;
4443
+ }
4444
+ });
4445
+ }
4446
+ export function __collectWorkspaceDiagnosticFilesForTest(root, maxFiles, signal, homeDir) {
4447
+ return collectWorkspaceDiagnosticFiles(path.resolve(root), maxFiles ?? getMaxWorkspaceDiagnosticFiles(), signal, homeDir);
4448
+ }