pi-lens 4.4.0 → 4.4.2

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 (150) hide show
  1. package/CHANGELOG.md +158 -0
  2. package/README.md +1 -0
  3. package/config/dependency-cruiser-eager-allowlist.json +3 -1
  4. package/dist/{chunk-IXEBJSUE.js → chunk-23WOO7TX.js} +11 -10
  5. package/dist/{chunk-E2WFBRHM.js → chunk-2B73ZPOK.js} +3 -3
  6. package/dist/{chunk-DQ2NZ7C4.js → chunk-2BBLFX75.js} +139 -11
  7. package/dist/{chunk-ZMOL6RQC.js → chunk-2CD5RS6M.js} +2 -2
  8. package/dist/{chunk-VD4JQCQC.js → chunk-2ZGIZPDG.js} +38 -37
  9. package/dist/{chunk-DIULIWKU.js → chunk-34WSVGEM.js} +29 -3
  10. package/dist/chunk-3JNKMP3B.js +17 -0
  11. package/dist/{chunk-HVUZXSWK.js → chunk-3UUPUE3O.js} +45 -41
  12. package/dist/{chunk-5RXBNSW2.js → chunk-3VPVG5SU.js} +16 -8
  13. package/dist/chunk-3WA4HP4I.js +55 -0
  14. package/dist/chunk-466Z26MG.js +30 -0
  15. package/dist/{chunk-34U7PLGE.js → chunk-4UNXBNE2.js} +12 -11
  16. package/dist/{chunk-E4MAEDPM.js → chunk-4W4VLAPJ.js} +12 -11
  17. package/dist/{chunk-GUOGVTCQ.js → chunk-52FUZI6K.js} +4 -2
  18. package/dist/chunk-5JM2524W.js +84 -0
  19. package/dist/{chunk-VHQLPIRJ.js → chunk-5NGR67U5.js} +2 -2
  20. package/dist/{chunk-DFVMIKMQ.js → chunk-66SXUJCA.js} +45 -9
  21. package/dist/{chunk-OQGFCZJ4.js → chunk-66VMUL7Q.js} +16 -16
  22. package/dist/{chunk-HWBZIGMJ.js → chunk-67WMNWEE.js} +9 -8
  23. package/dist/{chunk-6QIUX4EP.js → chunk-6C7JWPCP.js} +7 -7
  24. package/dist/{chunk-47D2BO4S.js → chunk-6GFRNVYR.js} +11 -10
  25. package/dist/{chunk-ZAWW3AF6.js → chunk-6SDEP3AJ.js} +9 -8
  26. package/dist/{chunk-TGPXLY2J.js → chunk-724UL2TJ.js} +38 -2
  27. package/dist/{chunk-YLF3PPT5.js → chunk-76MWC2K6.js} +3 -3
  28. package/dist/{chunk-F3P7VQ7H.js → chunk-7DYBLXYA.js} +3 -3
  29. package/dist/{chunk-AJ3QXTRQ.js → chunk-7ULNCDCO.js} +37 -35
  30. package/dist/{chunk-UK3CMEAL.js → chunk-AOHZU2BM.js} +1 -1
  31. package/dist/{chunk-F3AU2TMM.js → chunk-ARJ6PLFH.js} +44 -28
  32. package/dist/{chunk-U3VGR4VU.js → chunk-AW7R2GWG.js} +65 -8
  33. package/dist/{chunk-FJNKANOS.js → chunk-BKE7E7TC.js} +10 -9
  34. package/dist/{chunk-4W2PLC4J.js → chunk-BRGDSBJE.js} +13 -12
  35. package/dist/{chunk-VN2AXLEX.js → chunk-C34ZXZ7O.js} +102 -16
  36. package/dist/{chunk-T4ERT73Y.js → chunk-CF24OIYI.js} +21 -20
  37. package/dist/{chunk-NMABWBZN.js → chunk-CHOW74WE.js} +1 -1
  38. package/dist/{chunk-ARFCXG7S.js → chunk-CO2G5PHH.js} +2 -2
  39. package/dist/{chunk-AS44CFQ4.js → chunk-EETY4LF7.js} +24 -10
  40. package/dist/{chunk-I3R7GKDR.js → chunk-EJIUXPBX.js} +1 -1
  41. package/dist/{chunk-B3I2UUT4.js → chunk-ESBQHFMJ.js} +653 -251
  42. package/dist/{chunk-O5WILGMI.js → chunk-FLKWCIZH.js} +8 -5
  43. package/dist/{chunk-LNAKEO63.js → chunk-GAEGEPOC.js} +4 -4
  44. package/dist/{chunk-V7R425XG.js → chunk-GBPP5P7X.js} +4 -4
  45. package/dist/{chunk-XK4CIILI.js → chunk-GHB6AYCP.js} +27 -26
  46. package/dist/{chunk-7FBF7NPR.js → chunk-GOYRYZ2R.js} +29 -26
  47. package/dist/{chunk-JOLITGCC.js → chunk-GXW6K4YJ.js} +1 -1
  48. package/dist/{chunk-NXPS2L6U.js → chunk-GZYBXISR.js} +9 -9
  49. package/dist/{chunk-OAXNDZJ7.js → chunk-IBGIOBEE.js} +8 -4
  50. package/dist/{chunk-IIXOAGHR.js → chunk-IBRALRBR.js} +19 -15
  51. package/dist/{chunk-7F72PXY2.js → chunk-IXXDFY73.js} +3 -3
  52. package/dist/chunk-IZSHAIJ7.js +26 -0
  53. package/dist/{chunk-N3YQJI6O.js → chunk-JAU72XNY.js} +20 -15
  54. package/dist/chunk-JMYYOTQG.js +77 -0
  55. package/dist/{chunk-NL5GLMFI.js → chunk-JSQFTSC6.js} +12 -11
  56. package/dist/{chunk-IVP5KAB7.js → chunk-JWA5I3SR.js} +35 -21
  57. package/dist/{chunk-Y4T27COV.js → chunk-KBG47GHP.js} +4 -4
  58. package/dist/{chunk-PWFHBTSF.js → chunk-L3BLXZG7.js} +2 -2
  59. package/dist/chunk-LOP6GRPH.js +31 -0
  60. package/dist/chunk-MQLUAEOC.js +162 -0
  61. package/dist/{chunk-RPD5K5NC.js → chunk-MRXKYPCS.js} +2 -2
  62. package/dist/{chunk-Q5U6FMDI.js → chunk-N2H3ZPZY.js} +59 -7
  63. package/dist/{chunk-27BBHSZC.js → chunk-N3X6Q4YT.js} +3 -3
  64. package/dist/{chunk-UY752QSN.js → chunk-NW7BOILN.js} +19 -17
  65. package/dist/{chunk-TLGFBM3F.js → chunk-ODBNXWT4.js} +302 -113
  66. package/dist/{chunk-TT22N74J.js → chunk-OG5UUPN7.js} +13 -13
  67. package/dist/{chunk-C2AUYXQH.js → chunk-OXXKQCSV.js} +6 -6
  68. package/dist/{chunk-PAK6IDME.js → chunk-P7266HLM.js} +2 -2
  69. package/dist/{chunk-52H5B5TC.js → chunk-PFNSLWU5.js} +31 -30
  70. package/dist/{chunk-5ZEJHV35.js → chunk-PHDMFAKM.js} +12 -2
  71. package/dist/{chunk-3I5QFT3N.js → chunk-PNQDPDRC.js} +6 -3
  72. package/dist/{chunk-TVN5N6GY.js → chunk-PV6LR5P5.js} +6057 -1311
  73. package/dist/{chunk-AHDW3GPE.js → chunk-Q6ZWTCLY.js} +24 -22
  74. package/dist/{chunk-RUOG5D2C.js → chunk-Q7DUK4LX.js} +5 -5
  75. package/dist/{chunk-YSNFMJKN.js → chunk-QGQ5CCCG.js} +2 -2
  76. package/dist/{chunk-LW6XKDZQ.js → chunk-QHJ6DQ2R.js} +287 -38
  77. package/dist/{chunk-GVXA6CCE.js → chunk-QILIYSJF.js} +1 -1
  78. package/dist/{chunk-22AVIGXS.js → chunk-QOBUHWHX.js} +12 -7
  79. package/dist/{chunk-5THXD6X5.js → chunk-QQNQ7SJW.js} +2 -75
  80. package/dist/{chunk-4RERDEXR.js → chunk-QZVW3AAK.js} +50 -41
  81. package/dist/{chunk-AWE5TMPO.js → chunk-R3PCY3YS.js} +14 -13
  82. package/dist/{chunk-HHYAELLJ.js → chunk-RPLINGVB.js} +7 -7
  83. package/dist/{chunk-EOKRQ3SA.js → chunk-RWWYJ623.js} +10 -1
  84. package/dist/{chunk-YGVYKNSN.js → chunk-SAEFPSB6.js} +59 -12
  85. package/dist/{chunk-K6VYLB2U.js → chunk-TYM6OUIT.js} +13 -12
  86. package/dist/{chunk-AJXODZY6.js → chunk-UXGETRP4.js} +13 -12
  87. package/dist/{chunk-I7SPGOWI.js → chunk-UZ3TUTRV.js} +1 -1
  88. package/dist/{chunk-TO6WL3GM.js → chunk-VCHXCQFG.js} +12 -2
  89. package/dist/{chunk-BRJKOBZP.js → chunk-VITUCP5A.js} +17 -6
  90. package/dist/{chunk-WONERJTP.js → chunk-VRZP76YM.js} +1 -1
  91. package/dist/{chunk-OS32RNR2.js → chunk-VSSBEUFT.js} +16 -15
  92. package/dist/{chunk-VDBPVIKD.js → chunk-VWCXW2E7.js} +1 -1
  93. package/dist/{chunk-NKOUFPQF.js → chunk-WOA4LN2R.js} +2 -2
  94. package/dist/{chunk-RIISMHKN.js → chunk-WOKKRCJ5.js} +15 -13
  95. package/dist/{chunk-XECLWQZN.js → chunk-WU567RSC.js} +13 -10
  96. package/dist/{chunk-V5PXFSZK.js → chunk-XCJN5IJU.js} +4 -4
  97. package/dist/{chunk-ORTMY4Q4.js → chunk-XKIZ2HNN.js} +22 -14
  98. package/dist/{chunk-6VCD4VKZ.js → chunk-XMSXXAHW.js} +2 -2
  99. package/dist/chunk-XPZPXCBV.js +28 -0
  100. package/dist/{chunk-O6TQT6RI.js → chunk-XTMJ7XDT.js} +1 -1
  101. package/dist/{chunk-VNMT7C64.js → chunk-XVF2VVXP.js} +5 -3
  102. package/dist/chunk-Z527N6YQ.js +28 -0
  103. package/dist/{chunk-NTUJEGDZ.js → chunk-ZANLILYD.js} +22 -2
  104. package/dist/{chunk-6P62QVEA.js → chunk-ZGDGW2V3.js} +2 -2
  105. package/dist/{chunk-ODEEOXPH.js → chunk-ZHFZWTZV.js} +12 -11
  106. package/dist/{chunk-AJVLWNFN.js → chunk-ZXYDQ3TZ.js} +5 -5
  107. package/dist/index.js +102097 -97778
  108. package/dist/mcp/analyze-cli.js +18 -8
  109. package/dist/mcp/cli.js +48 -37
  110. package/dist/mcp/server.js +695 -426
  111. package/dist/mcp/worker.js +66 -55
  112. package/dist/probes/installer.js +20 -19
  113. package/docs/ast-grep_rules_catalog.md +2 -2
  114. package/docs/astplayground.md +1 -1
  115. package/docs/configuration.md +38 -2
  116. package/docs/custom-rules.md +25 -2
  117. package/docs/dependencies.md +3 -0
  118. package/docs/environment-variables.md +34 -6
  119. package/docs/features.md +39 -3
  120. package/docs/globalconfig.md +3 -1
  121. package/docs/language-coverage.md +2 -1
  122. package/docs/lsp-capability-matrix.md +29 -6
  123. package/docs/lsp-idle-eviction.md +38 -47
  124. package/docs/pi-lens-fixer.md +13 -5
  125. package/docs/pi-lens-merge-policy.md +48 -1
  126. package/docs/pi-lens-monitor.md +3 -1
  127. package/docs/pi-lens-retro.md +2 -1
  128. package/docs/pi-lens-reviewer.md +19 -3
  129. package/docs/pi-lens-subagent.md +2 -0
  130. package/docs/real-harness.md +17 -0
  131. package/docs/schema/pi-lens-config-v1.json +565 -0
  132. package/docs/servercapabilities.md +18 -4
  133. package/docs/settings.md +2 -1
  134. package/docs/word-index.md +6 -0
  135. package/package.json +1 -1
  136. package/rules/ast-grep-rules/rule-tests/no-identity-operator-on-literals-test.yml +12 -2
  137. package/rules/ast-grep-rules/rule-tests/throw-new-error-js-test.yml +2 -0
  138. package/rules/ast-grep-rules/rule-tests/throw-new-error-test.yml +2 -0
  139. package/rules/ast-grep-rules/rules/no-identity-operator-on-literals.yml +20 -12
  140. package/rules/ast-grep-rules/rules/throw-new-error-js.yml +2 -2
  141. package/rules/ast-grep-rules/rules/throw-new-error.yml +2 -2
  142. package/dist/chunk-54B42YRX.js +0 -27
  143. package/dist/chunk-CGGAIP7J.js +0 -29
  144. package/dist/chunk-D6QS3U6M.js +0 -30
  145. package/dist/chunk-HMFXILPV.js +0 -83
  146. package/dist/chunk-LSMJ6KBE.js +0 -4139
  147. package/dist/chunk-NTHH2J74.js +0 -27
  148. package/dist/chunk-RPD4ZSB7.js +0 -25
  149. package/dist/chunk-T6D4Q4AQ.js +0 -54
  150. package/dist/chunk-VCSRSYLF.js +0 -81
@@ -4,13 +4,15 @@ import {
4
4
  detectPythonEnvironment,
5
5
  detectPythonVenv,
6
6
  pythonEnvironmentToolCandidates
7
- } from "./chunk-DIULIWKU.js";
7
+ } from "./chunk-34WSVGEM.js";
8
8
  import {
9
9
  resolveOpengrepConfig
10
- } from "./chunk-VDBPVIKD.js";
10
+ } from "./chunk-VWCXW2E7.js";
11
11
  import {
12
- extensionsForLanguage
13
- } from "./chunk-TGPXLY2J.js";
12
+ extensionsForLanguage,
13
+ getUserRuleRoot,
14
+ ruleCorpusFingerprintForCycle
15
+ } from "./chunk-724UL2TJ.js";
14
16
  import {
15
17
  load,
16
18
  resolvePackagePath
@@ -20,11 +22,11 @@ import {
20
22
  readCargoWorkspaceExclude,
21
23
  readCargoWorkspaceMembers,
22
24
  readTextFileOrUndefined
23
- } from "./chunk-PAK6IDME.js";
25
+ } from "./chunk-P7266HLM.js";
24
26
  import {
25
27
  HOOK_WALL_BUDGET_MS,
26
28
  bounded
27
- } from "./chunk-GUOGVTCQ.js";
29
+ } from "./chunk-52FUZI6K.js";
28
30
  import {
29
31
  ensureTool,
30
32
  findManagedToolBinary,
@@ -34,20 +36,21 @@ import {
34
36
  isZizmorAuditTarget,
35
37
  logSessionStart,
36
38
  resolveZizmorGitHubToken
37
- } from "./chunk-OQGFCZJ4.js";
39
+ } from "./chunk-66VMUL7Q.js";
38
40
  import {
39
41
  resolveToolCwd
40
- } from "./chunk-RUOG5D2C.js";
42
+ } from "./chunk-Q7DUK4LX.js";
41
43
  import {
42
- findGlobalBinary
43
- } from "./chunk-U3VGR4VU.js";
44
+ findGlobalBinary,
45
+ isProjectLocalBinPath
46
+ } from "./chunk-AW7R2GWG.js";
44
47
  import {
45
48
  classifyProbeFailure,
46
49
  createAvailabilityLatch,
47
50
  describeInstallAttempt,
48
51
  logAvailabilityDecision,
49
52
  startHostStallSampler
50
- } from "./chunk-WONERJTP.js";
53
+ } from "./chunk-VRZP76YM.js";
51
54
  import {
52
55
  OWNER_TAG_ENV,
53
56
  SpawnFailureError,
@@ -61,21 +64,25 @@ import {
61
64
  isCommandAvailableAsync,
62
65
  isPathIgnoredByProject,
63
66
  ownerTagForChildren,
67
+ resolveWindowsCommandForEnvironment,
64
68
  safeSpawnAsync
65
- } from "./chunk-VN2AXLEX.js";
69
+ } from "./chunk-C34ZXZ7O.js";
66
70
  import {
67
71
  DOTNET_CSHARP_ROOT_MARKERS,
68
72
  DOTNET_FSHARP_ROOT_MARKERS,
69
- KIND_EXTENSIONS
70
- } from "./chunk-5ZEJHV35.js";
73
+ KIND_EXTENSIONS,
74
+ LEAN_ROOT_MARKERS
75
+ } from "./chunk-PHDMFAKM.js";
71
76
  import {
77
+ isRepoTier,
72
78
  lspSectionOf,
73
79
  migrationSubject,
80
+ provenanceFor,
74
81
  reportConfigReadFailure,
75
82
  reportPiLensConfigRecords,
76
83
  resolvePiLensConfig,
77
84
  summarizeConfigResolution
78
- } from "./chunk-DQ2NZ7C4.js";
85
+ } from "./chunk-2BBLFX75.js";
79
86
  import {
80
87
  FRESHNESS_CADENCE_MS,
81
88
  LSP_NAMESPACE_KEY,
@@ -83,23 +90,30 @@ import {
83
90
  dirMtimesStillFreshAsync,
84
91
  getPiLensGlobalConfigPath,
85
92
  unknownDirMtimeRecords
86
- } from "./chunk-VNMT7C64.js";
93
+ } from "./chunk-XVF2VVXP.js";
87
94
  import {
95
+ getProjectTrustState
96
+ } from "./chunk-ZANLILYD.js";
97
+ import {
98
+ getDegradationLedgerGeneration,
88
99
  incrementDegradationCount,
89
100
  recordDegradationOnce
90
- } from "./chunk-N3YQJI6O.js";
101
+ } from "./chunk-JAU72XNY.js";
102
+ import {
103
+ logExtension
104
+ } from "./chunk-XTMJ7XDT.js";
91
105
  import {
92
106
  claimPhaseOncePerSession,
93
107
  currentSessionRecordId,
94
108
  logLatency,
95
109
  releasePhaseClaim
96
- } from "./chunk-5THXD6X5.js";
110
+ } from "./chunk-QQNQ7SJW.js";
97
111
  import {
98
112
  getGlobalPiLensLogDir,
99
113
  getProcessSingleton,
100
114
  isTestMode,
101
115
  redactSecrets
102
- } from "./chunk-UK3CMEAL.js";
116
+ } from "./chunk-AOHZU2BM.js";
103
117
  import {
104
118
  STAGE_TMP_PATTERN
105
119
  } from "./chunk-LJDODBBZ.js";
@@ -113,6 +127,7 @@ import {
113
127
  findLocalToolConfig,
114
128
  isAtOrAboveHomeDir,
115
129
  isFullyQualified,
130
+ isUnderDir,
116
131
  isWindowsPath,
117
132
  matchesWorkspaceMemberPattern,
118
133
  normalizeEphemeralMapKey,
@@ -120,7 +135,7 @@ import {
120
135
  normalizeMapKey,
121
136
  pathsEqual,
122
137
  toPosix
123
- } from "./chunk-Q5U6FMDI.js";
138
+ } from "./chunk-N2H3ZPZY.js";
124
139
  import {
125
140
  __require
126
141
  } from "./chunk-NXL4FFQQ.js";
@@ -129,7 +144,7 @@ import {
129
144
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, statSync as statSync3 } from "node:fs";
130
145
  import { access, readFile, readdir, stat } from "node:fs/promises";
131
146
  import os6 from "node:os";
132
- import path7 from "node:path";
147
+ import path6 from "node:path";
133
148
 
134
149
  // dist/clients/sgconfig.js
135
150
  import { createHash } from "node:crypto";
@@ -162,6 +177,18 @@ function getAstGrepRuleSources(projectRoot = process.cwd()) {
162
177
  tier: "secondary"
163
178
  });
164
179
  }
180
+ const userRoot = getUserRuleRoot();
181
+ if (canonicalDir(userRoot) !== canonicalDir(root)) {
182
+ candidates.push({
183
+ dir: path.join(userRoot, "ast-grep-rules", "rules"),
184
+ origin: "user",
185
+ tier: "primary"
186
+ }, {
187
+ dir: path.join(userRoot, "ast-grep-rules", "coderabbit", "rules"),
188
+ origin: "user",
189
+ tier: "secondary"
190
+ });
191
+ }
165
192
  candidates.push({
166
193
  dir: path.join(packageRoot, "rules", "ast-grep-rules", "rules"),
167
194
  origin: "bundled",
@@ -246,7 +273,15 @@ function snapshotRuleSource(source) {
246
273
  }
247
274
  hash.update(content);
248
275
  hash.update("\0");
249
- files.push({ file, relativePath, documents: parseRuleDocuments(content) });
276
+ const documents = parseRuleDocuments(content);
277
+ if (source.origin === "user" && documents.some((document) => !document.id)) {
278
+ recordDegradationOnce({
279
+ kind: "ast-grep-rule-invalid",
280
+ subject: file,
281
+ reason: "user ast-grep rule has no valid rule id"
282
+ });
283
+ }
284
+ files.push({ file, relativePath, documents });
250
285
  }
251
286
  const snapshot = { ...source, files, digest: hash.digest("hex") };
252
287
  if (source.origin === "bundled")
@@ -267,6 +302,10 @@ function sourceFingerprint(sources) {
267
302
  }
268
303
  return hash.digest("hex");
269
304
  }
305
+ function getAstGrepRuleFingerprint(projectRoot = process.cwd(), options = {}) {
306
+ const root = path.resolve(projectRoot || process.cwd());
307
+ return ruleCorpusFingerprintForCycle("ast-grep", root, (fingerprintRoot) => sourceFingerprint(getAstGrepRuleSources(fingerprintRoot).map(snapshotRuleSource)), options.force);
308
+ }
270
309
  function materializeMergedRuleDir(sources, mergedDir) {
271
310
  fs.rmSync(mergedDir, { recursive: true, force: true });
272
311
  fs.mkdirSync(mergedDir, { recursive: true });
@@ -302,14 +341,14 @@ function rootArtifactKey(root) {
302
341
  }
303
342
  function resolveBaselineSgconfig(projectRoot = process.cwd()) {
304
343
  const root = canonicalDir(path.resolve(projectRoot || process.cwd()));
305
- const sources = getAstGrepRuleSources(root).map(snapshotRuleSource);
306
- if (sources.length === 0)
307
- return void 0;
308
- const fingerprint = sourceFingerprint(sources);
344
+ const fingerprint = getAstGrepRuleFingerprint(root, { force: true });
309
345
  const cached = cachedBaselines.get(root);
310
346
  if (cached?.fingerprint === fingerprint && fs.existsSync(cached.path) && fs.existsSync(cached.mergedDir)) {
311
347
  return cached.path;
312
348
  }
349
+ const sources = getAstGrepRuleSources(root).map(snapshotRuleSource);
350
+ if (sources.length === 0)
351
+ return void 0;
313
352
  const dir = path.join(os.tmpdir(), "pi-lens-ast-grep");
314
353
  fs.mkdirSync(dir, { recursive: true });
315
354
  const artifactKey = rootArtifactKey(root);
@@ -417,9 +456,44 @@ function findLocalTyposConfig(startDir) {
417
456
  return findLocalToolConfig(startDir, LOCAL_TYPOS_CONFIG_NAMES);
418
457
  }
419
458
 
459
+ // dist/clients/lsp/server-traits.js
460
+ var ROLE_MEMBERS = {
461
+ language: "language",
462
+ auxiliary: "auxiliary"
463
+ };
464
+ var DEFAULT_LSP_SERVER_ROLE = "language";
465
+ function isAuxiliary(bearer) {
466
+ const declared = bearer?.role;
467
+ const role = typeof declared === "string" && Object.hasOwn(ROLE_MEMBERS, declared) ? ROLE_MEMBERS[declared] : DEFAULT_LSP_SERVER_ROLE;
468
+ switch (role) {
469
+ case "auxiliary":
470
+ return true;
471
+ case "language":
472
+ return false;
473
+ }
474
+ }
475
+ var DEFAULT_NOTIFY_INFLIGHT_LIMIT = 8;
476
+ var DEFAULT_REPLY_ORDERING = "unmeasured";
477
+ function serverTraits(declaration) {
478
+ const declaredLimit = declaration.notifyInflightLimit;
479
+ if (typeof declaredLimit === "number" && Number.isFinite(declaredLimit) && declaredLimit > 0) {
480
+ return {
481
+ role: declaration.role,
482
+ notifyInflightLimit: Math.floor(declaredLimit),
483
+ replyOrdering: declaration.replyOrdering ?? DEFAULT_REPLY_ORDERING
484
+ };
485
+ }
486
+ const envLimit = Number(process.env.PI_LENS_LSP_AUX_NOTIFY_INFLIGHT);
487
+ return {
488
+ role: declaration.role,
489
+ notifyInflightLimit: Number.isFinite(envLimit) && envLimit > 0 ? Math.floor(envLimit) : DEFAULT_NOTIFY_INFLIGHT_LIMIT,
490
+ replyOrdering: declaration.replyOrdering ?? DEFAULT_REPLY_ORDERING
491
+ };
492
+ }
493
+
420
494
  // dist/clients/lsp/config.js
421
495
  import * as os3 from "node:os";
422
- import path4 from "node:path";
496
+ import path3 from "node:path";
423
497
 
424
498
  // dist/clients/dispatch/known-runner-ids.js
425
499
  var knownRunnerIds = /* @__PURE__ */ new Set();
@@ -434,6 +508,7 @@ function isKnownRunnerId(id) {
434
508
  }
435
509
 
436
510
  // dist/clients/lsp/launch.js
511
+ import { AsyncLocalStorage } from "node:async_hooks";
437
512
  import { execFileSync, spawn as nodeSpawn } from "node:child_process";
438
513
  import fs3 from "node:fs";
439
514
  import os2 from "node:os";
@@ -628,6 +703,16 @@ function isCmdShimValid(cmdPath) {
628
703
  return true;
629
704
  }
630
705
  }
706
+ function isMissingWindowsStartupCommand(command, spawnCommand, cwd, env) {
707
+ if (!isWindows)
708
+ return false;
709
+ if (/\.(cmd|bat)$/i.test(spawnCommand) && !isCmdShimValid(spawnCommand)) {
710
+ return true;
711
+ }
712
+ if (spawnCommand !== command)
713
+ return false;
714
+ return resolveWindowsCommandForEnvironment(command, cwd, env) === null;
715
+ }
631
716
  function bypassPs1OnWindows(ps1Path, args) {
632
717
  const cmdPath = `${ps1Path.slice(0, -4)}.cmd`;
633
718
  if (fs3.existsSync(cmdPath)) {
@@ -675,6 +760,65 @@ function findBinaryOnPath(command, env) {
675
760
  }
676
761
  return void 0;
677
762
  }
763
+ var lspProjectCodePermission = new AsyncLocalStorage();
764
+ function withLspProjectCodePermission(allowed, spawn) {
765
+ return lspProjectCodePermission.run(allowed, spawn);
766
+ }
767
+ var unknownProjectLocalNoticeGeneration = -1;
768
+ function recordUntrustedLspExecution(request, trust) {
769
+ const generation = getDegradationLedgerGeneration();
770
+ const subject = request.kind === "project-code-server" ? `project-code-server:g${generation}:${request.serverId}:${request.root}` : `project-local-binary:g${generation}:${path2.basename(request.command)}`;
771
+ const metadata = request.kind === "project-code-server" ? {
772
+ field: request.kind,
773
+ serverId: request.serverId,
774
+ root: request.root,
775
+ trust
776
+ } : {
777
+ field: request.kind,
778
+ trust
779
+ };
780
+ recordDegradationOnce({
781
+ kind: "lsp-registry-decision",
782
+ subject,
783
+ reason: (request.kind === "project-local-binary" ? request.reason : void 0) ?? `LSP execution refused: ${request.kind} and project trust is ${trust}`,
784
+ metadata
785
+ });
786
+ if (unknownProjectLocalNoticeGeneration !== generation) {
787
+ unknownProjectLocalNoticeGeneration = generation;
788
+ const label = request.kind === "project-code-server" ? "project-code LSP server" : "project-local LSP binary";
789
+ logExtension({
790
+ subsystem: "lsp-registry",
791
+ level: "warn",
792
+ message: `${label} refused: mark the project trusted in pi or upgrade pi`,
793
+ metadata: { field: request.kind }
794
+ });
795
+ }
796
+ }
797
+ function refuseUntrustedLspExecution(request) {
798
+ const trust = getProjectTrustState();
799
+ const needsTrust = request.kind === "project-code-server" ? trust !== "trusted" : !admitProjectSuppliedTsserver(request.resolvedCommand, request.cwd, true);
800
+ if (!needsTrust)
801
+ return;
802
+ if (request.kind === "project-code-server")
803
+ recordUntrustedLspExecution(request, trust);
804
+ throw new SpawnFailureError("spawn-failed", `LSP ${request.kind === "project-code-server" ? "project-code server" : "project-local binary"} refused: project trust is ${trust}`, new Error(`project trust is ${trust}`));
805
+ }
806
+ function admitProjectSuppliedTsserver(tsserverPath, cwd, projectSupplied, projectCodeAllowed = lspProjectCodePermission.getStore() ?? false) {
807
+ const trust = getProjectTrustState();
808
+ if (tsserverPath && (!projectSupplied || projectCodeAllowed && trust === "trusted"))
809
+ return tsserverPath;
810
+ recordUntrustedLspExecution({
811
+ kind: "project-local-binary",
812
+ command: tsserverPath ?? "tsserver.js",
813
+ resolvedCommand: tsserverPath ?? "tsserver.js",
814
+ cwd,
815
+ reason: tsserverPath ? "LSP execution refused: project compiler requires trusted session ownership" : "LSP execution refused: no admitted absolute TypeScript compiler"
816
+ }, trust);
817
+ return void 0;
818
+ }
819
+ function isProjectLocalLspBinary(resolvedCommand, cwd) {
820
+ return isProjectLocalBinPath(resolvedCommand, cwd);
821
+ }
678
822
  function trySpawn(command, args, cwd, env, needsShell) {
679
823
  let proc;
680
824
  if (needsShell) {
@@ -703,9 +847,6 @@ function trySpawn(command, args, cwd, env, needsShell) {
703
847
  if (!proc.stdin || !proc.stdout || !proc.stderr) {
704
848
  throw new Error(`Failed to spawn LSP server: ${command}`);
705
849
  }
706
- if (proc.exitCode !== null || proc.killed) {
707
- throw new Error(`LSP server ${command} exited immediately (code: ${proc.exitCode}). The binary may be missing or corrupted.`);
708
- }
709
850
  holdOwnChildPid(proc.pid, "lsp-spawn");
710
851
  return proc;
711
852
  }
@@ -781,6 +922,14 @@ async function launchLSP(command, args = [], options = {}) {
781
922
  needsShell = computeNeedsShell(spawnCommand);
782
923
  }
783
924
  }
925
+ if (isProjectLocalLspBinary(spawnCommand, cwd)) {
926
+ refuseUntrustedLspExecution({
927
+ kind: "project-local-binary",
928
+ command,
929
+ resolvedCommand: spawnCommand,
930
+ cwd
931
+ });
932
+ }
784
933
  if (isWindows && /\.(cmd|bat)$/i.test(spawnCommand) && !isCmdShimValid(spawnCommand)) {
785
934
  logSessionStart2(`lsp cmd-shim-invalid: ${spawnCommand} target missing \u2014 skipping candidate`);
786
935
  const cause = Object.assign(new Error(`LSP .cmd shim target not found: ${spawnCommand}`), { code: "ENOENT" });
@@ -817,7 +966,12 @@ async function launchLSP(command, args = [], options = {}) {
817
966
  throw new Error(`Failed to spawn LSP server: ${command}`);
818
967
  }
819
968
  if (proc.exitCode !== null || proc.killed) {
820
- throw new Error(`LSP server ${command} exited immediately (code: ${proc.exitCode}). The binary may be missing or corrupted.`);
969
+ const message = `LSP server ${command} exited immediately (code: ${proc.exitCode}). The binary may be missing or corrupted.`;
970
+ const failure = new Error(message);
971
+ if (proc.exitCode === 1 && isMissingWindowsStartupCommand(command, spawnCommand, cwd, env)) {
972
+ throw new SpawnFailureError("tool-not-found", message, failure);
973
+ }
974
+ throw failure;
821
975
  }
822
976
  logSessionStart2(`lsp launch: command=${command} resolved=${spawnCommand} args=${JSON.stringify(args)} cwd=${cwd} shell=${needsShell ? "true" : "false"} pid=${proc.pid ?? 0}`);
823
977
  const formatStartupStderr = (stderr) => {
@@ -850,7 +1004,13 @@ async function launchLSP(command, args = [], options = {}) {
850
1004
  settled = true;
851
1005
  const isWindowsCmd = isWindows && command.endsWith(".cmd");
852
1006
  const errorMsg = isWindowsCmd && code === 1 ? `npm .cmd shim failed (underlying binary not installed). Run 'npm install' in this project or use a global installation.` : `The binary may be missing or corrupted.`;
853
- reject(new Error(`LSP server ${command} exited immediately with code ${code}. ${errorMsg}${formatStartupStderr(startupStderr)}`));
1007
+ const message = `LSP server ${command} exited immediately with code ${code}. ${errorMsg}${formatStartupStderr(startupStderr)}`;
1008
+ const failure = new Error(message);
1009
+ if (code === 1 && isMissingWindowsStartupCommand(command, spawnCommand, cwd, env)) {
1010
+ reject(new SpawnFailureError("tool-not-found", message, failure));
1011
+ return;
1012
+ }
1013
+ reject(failure);
854
1014
  }
855
1015
  });
856
1016
  const startupFailureWindowMs = (() => {
@@ -890,61 +1050,13 @@ async function launchLSP(command, args = [], options = {}) {
890
1050
  };
891
1051
  }
892
1052
 
893
- // dist/clients/lsp/session-roots.js
894
- import path3 from "node:path";
895
- var SESSION_ROOT_CAP = 128;
896
- var sessionRoots = new BoundedFifoMap(SESSION_ROOT_CAP);
897
- function noteEvictedRoots(evicted) {
898
- const roots = evicted.map(([root]) => root);
899
- if (roots.length > 0) {
900
- incrementDegradationCount({
901
- kind: "lsp-session-root-evicted",
902
- subject: `cap=${SESSION_ROOT_CAP}`,
903
- reason: `session root registry at capacity; will load the dropped root again on the next session start or tool call naming it (dropped: ${roots[0]})`
904
- });
905
- }
906
- return roots;
907
- }
908
- function registerSessionRoot(cwd) {
909
- const root = path3.resolve(cwd);
910
- if (sessionRoots.has(root))
911
- return [];
912
- return noteEvictedRoots(sessionRoots.set(root, void 0));
913
- }
914
- function setSessionRootConfig(cwd, config) {
915
- return noteEvictedRoots(sessionRoots.set(path3.resolve(cwd), config));
916
- }
917
- function sessionRootConfigEntries() {
918
- return sessionRoots;
919
- }
920
- function isSessionRootRegistered(cwd) {
921
- return sessionRoots.has(path3.resolve(cwd));
922
- }
923
- function shouldInitializeSessionRoot(cwd, readyRoots) {
924
- const normalized = path3.resolve(cwd);
925
- return !readyRoots.has(normalized) || !isSessionRootRegistered(normalized);
926
- }
927
- function isOutsideAllSessionRoots(filePath) {
928
- if (sessionRoots.size === 0)
929
- return false;
930
- const resolved = path3.resolve(filePath);
931
- for (const root of sessionRoots.keys()) {
932
- if (isSameOrWithin(root, resolved))
933
- return false;
934
- }
935
- return true;
936
- }
937
- function getSessionRootsForTelemetry(limit = 4) {
938
- return [...sessionRoots.keys()].sort((a, b) => Number(a > b) - Number(a < b)).slice(0, limit);
939
- }
940
-
941
1053
  // dist/clients/lsp/config.js
942
1054
  function asRecord(value) {
943
1055
  return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
944
1056
  }
945
1057
  var CONFIG_RESOLVED_PHASE = "config_resolved";
946
1058
  function configResolutionKey(cwd) {
947
- return normalizeFilePath(path4.resolve(normalizeFilePath(cwd)));
1059
+ return normalizeFilePath(path3.resolve(normalizeFilePath(cwd)));
948
1060
  }
949
1061
  function publishConfigResolutionPending(cwd) {
950
1062
  logSessionStart(`session_start config_resolution_pending session=${currentSessionRecordId()} root=${configResolutionKey(cwd)}`);
@@ -1000,6 +1112,10 @@ async function loadLSPConfig(cwd, homeDir = os3.homedir(), options = {}) {
1000
1112
  });
1001
1113
  const coversProblems = newCoversClaimProblems();
1002
1114
  const config = lspConfigOf(resolution.value, coversProblems);
1115
+ Object.defineProperty(config, "__provenance", {
1116
+ value: resolution.provenance,
1117
+ enumerable: false
1118
+ });
1003
1119
  if (reporting) {
1004
1120
  reportPiLensConfigRecords([
1005
1121
  ...resolution.records,
@@ -1097,7 +1213,7 @@ function customServerSpecsOf(value) {
1097
1213
  const servers = asRecord(lspSectionOf(value).servers);
1098
1214
  return servers ? coversValidatedServers(servers) : {};
1099
1215
  }
1100
- function lspConfigOf(value, problems) {
1216
+ function lspConfigOf(value, problems, provenance) {
1101
1217
  const section = lspSectionOf(value);
1102
1218
  const config = {};
1103
1219
  const servers = asRecord(section.servers);
@@ -1113,6 +1229,12 @@ function lspConfigOf(value, problems) {
1113
1229
  if (Array.isArray(section.warmFiles)) {
1114
1230
  config.warmFiles = section.warmFiles;
1115
1231
  }
1232
+ if (provenance) {
1233
+ Object.defineProperty(config, "__provenance", {
1234
+ value: provenance,
1235
+ enumerable: false
1236
+ });
1237
+ }
1116
1238
  return config;
1117
1239
  }
1118
1240
  function createCustomServer(config, id) {
@@ -1125,6 +1247,14 @@ function createCustomServer(config, id) {
1125
1247
  // be probeable before the claim can defer a CLI runner.
1126
1248
  command: config.command,
1127
1249
  extensions: config.extensions,
1250
+ // A config-declared server is a language server. The public
1251
+ // `lsp.servers.<id>.role` field is validated and projected by
1252
+ // `ResolvedLspConfig` (clients/lsp/resolved-config.ts) but stays
1253
+ // RESERVED and inert here: honouring it would change which servers a
1254
+ // file selects as primary, and that is the catalog slice's call, not
1255
+ // this loader's (#2416 slice 1). The stated default is applied rather
1256
+ // than left absent (#1488).
1257
+ role: DEFAULT_LSP_SERVER_ROLE,
1128
1258
  idleEviction: "unmeasured",
1129
1259
  // The config-declared covers channel (#3968): the claim the loader
1130
1260
  // validated (`lspConfigOf`'s projection drops unknown runner ids) rides
@@ -1140,26 +1270,74 @@ function createCustomServer(config, id) {
1140
1270
  cwd: root,
1141
1271
  env: config.env ? { ...process.env, ...config.env } : process.env
1142
1272
  });
1143
- return { process: proc };
1273
+ return {
1274
+ process: proc,
1275
+ ...config.initializationOptions ? { initialization: config.initializationOptions } : {}
1276
+ };
1144
1277
  }
1145
1278
  };
1146
1279
  }
1147
1280
  var EMPTY_CONFIG = {
1148
1281
  customServers: [],
1282
+ // Avoid reading LSP_SERVERS during the config/server module cycle. The
1283
+ // selection helper falls back to the live builtin table after evaluation.
1284
+ registeredServers: [],
1149
1285
  disabledServerIds: /* @__PURE__ */ new Set(),
1150
1286
  serverOverrides: /* @__PURE__ */ new Map()
1151
1287
  };
1152
1288
  var configInFlight = /* @__PURE__ */ new Map();
1153
1289
  function normalizeWorkspacePath(cwd) {
1154
- return path4.resolve(cwd);
1290
+ return path3.resolve(cwd);
1291
+ }
1292
+ var unknownTrustNoticeGeneration = -1;
1293
+ function sourceFor(provenance, key) {
1294
+ const direct = provenanceFor({ value: void 0, provenance }, key);
1295
+ if (direct)
1296
+ return direct;
1297
+ let fallback;
1298
+ for (const [candidate, entry] of provenance) {
1299
+ if (!candidate.startsWith(`${key}/`))
1300
+ continue;
1301
+ if (isRepoTier(entry.tier))
1302
+ return entry;
1303
+ fallback ??= entry;
1304
+ }
1305
+ return fallback;
1306
+ }
1307
+ function executableAllowed(provenance, trust) {
1308
+ return !provenance || !isRepoTier(provenance.tier) || trust === "trusted";
1309
+ }
1310
+ function registryDecision(id, field, provenance, trust, allowed) {
1311
+ if (allowed || !provenance || !isRepoTier(provenance.tier))
1312
+ return;
1313
+ const subject = `${id}:${field}:${provenance.tier}`;
1314
+ const reason = `project LSP ${field} refused for ${id}: pi project trust is ${trust}`;
1315
+ recordDegradationOnce({
1316
+ kind: "lsp-registry-decision",
1317
+ subject,
1318
+ reason,
1319
+ metadata: { serverId: id, field, tier: provenance.tier, trust }
1320
+ });
1321
+ if (trust === "unknown") {
1322
+ const generation = getDegradationLedgerGeneration();
1323
+ if (unknownTrustNoticeGeneration !== generation) {
1324
+ unknownTrustNoticeGeneration = generation;
1325
+ logExtension({
1326
+ subsystem: "lsp-registry",
1327
+ level: "warn",
1328
+ message: "project LSP executables refused: mark the project trusted in pi or upgrade pi",
1329
+ metadata: { serverId: id, field }
1330
+ });
1331
+ }
1332
+ }
1155
1333
  }
1156
1334
  function isSameOrChildPath(filePath, candidateRoot) {
1157
1335
  if (filePath === candidateRoot)
1158
1336
  return true;
1159
- return filePath.startsWith(`${candidateRoot}${path4.sep}`);
1337
+ return filePath.startsWith(`${candidateRoot}${path3.sep}`);
1160
1338
  }
1161
1339
  function getConfigForFile(filePath) {
1162
- const resolvedFilePath = path4.resolve(filePath);
1340
+ const resolvedFilePath = path3.resolve(filePath);
1163
1341
  let bestMatch;
1164
1342
  for (const [root, config] of sessionRootConfigEntries()) {
1165
1343
  if (config === void 0)
@@ -1173,13 +1351,33 @@ function getConfigForFile(filePath) {
1173
1351
  return bestMatch?.config ?? EMPTY_CONFIG;
1174
1352
  }
1175
1353
  function registerLSPConfig(config) {
1354
+ return compileLspRegistry(config, config.__provenance);
1355
+ }
1356
+ function compileLspRegistry(config, provenance = /* @__PURE__ */ new Map()) {
1357
+ const trust = getProjectTrustState();
1176
1358
  const customServers = [];
1177
1359
  const disabledServerIds = new Set(config.disabledServers ?? []);
1178
1360
  if (config.servers) {
1179
1361
  for (const [id, serverConfig] of Object.entries(config.servers)) {
1362
+ const commandSource = sourceFor(provenance, `/lsp/servers/${id}/command`);
1363
+ const commandAllowed = executableAllowed(commandSource, trust);
1364
+ registryDecision(id, "command", commandSource, trust, commandAllowed);
1365
+ if (!commandAllowed)
1366
+ continue;
1367
+ const envSource = sourceFor(provenance, `/lsp/servers/${id}/env`);
1368
+ const initSource = sourceFor(provenance, `/lsp/servers/${id}/initializationOptions`);
1369
+ const envAllowed = executableAllowed(envSource, trust);
1370
+ const initAllowed = executableAllowed(initSource, trust);
1371
+ registryDecision(id, "env", envSource, trust, envAllowed);
1372
+ registryDecision(id, "initializationOptions", initSource, trust, initAllowed);
1373
+ const admitted = { ...serverConfig };
1374
+ if (!envAllowed)
1375
+ delete admitted.env;
1376
+ if (!initAllowed)
1377
+ delete admitted.initializationOptions;
1180
1378
  try {
1181
- const server = createCustomServer(serverConfig, id);
1182
- customServers.push(server);
1379
+ const server = createCustomServer(admitted, id);
1380
+ customServers.push({ ...server, trustAllowed: true });
1183
1381
  } catch {
1184
1382
  }
1185
1383
  }
@@ -1189,15 +1387,53 @@ function registerLSPConfig(config) {
1189
1387
  for (const [id, entry] of Object.entries(config.serverOverrides)) {
1190
1388
  if (entry && typeof entry === "object" && !Array.isArray(entry)) {
1191
1389
  const initOpts = entry.initializationOptions;
1192
- if (initOpts !== void 0 && typeof initOpts === "object" && initOpts !== null && !Array.isArray(initOpts)) {
1390
+ const command = entry.command;
1391
+ const commandSource = sourceFor(provenance, `/lsp/serverOverrides/${id}/command`);
1392
+ const envSource = sourceFor(provenance, `/lsp/serverOverrides/${id}/env`);
1393
+ const initSource = sourceFor(provenance, `/lsp/serverOverrides/${id}/initializationOptions`);
1394
+ const commandAllowed = executableAllowed(commandSource, trust);
1395
+ const envAllowed = executableAllowed(envSource, trust);
1396
+ const overrideInitAllowed = executableAllowed(initSource, trust);
1397
+ registryDecision(id, "command override", commandSource, trust, commandAllowed);
1398
+ registryDecision(id, "env override", envSource, trust, envAllowed);
1399
+ registryDecision(id, "initializationOptions", initSource, trust, overrideInitAllowed);
1400
+ if (command !== void 0 || entry.env !== void 0 || initOpts !== void 0 && typeof initOpts === "object" && initOpts !== null && !Array.isArray(initOpts)) {
1193
1401
  serverOverrides.set(id, {
1194
- initializationOptions: initOpts
1402
+ ...overrideInitAllowed && initOpts !== void 0 ? { initializationOptions: initOpts } : {},
1403
+ ...commandAllowed && command !== void 0 ? { command } : {},
1404
+ ...envAllowed && entry.env ? { env: entry.env } : {}
1195
1405
  });
1196
1406
  }
1197
1407
  }
1198
1408
  }
1199
1409
  }
1200
- return { customServers, disabledServerIds, serverOverrides };
1410
+ const overriddenBuiltins = LSP_SERVERS.map((server) => {
1411
+ const override = serverOverrides.get(server.id);
1412
+ if (!override?.command)
1413
+ return { ...server };
1414
+ const argv = Array.isArray(override.command) ? override.command : [override.command, "--stdio"];
1415
+ return {
1416
+ ...server,
1417
+ trustAllowed: true,
1418
+ async spawn(root, options) {
1419
+ void options;
1420
+ const program = argv[0];
1421
+ if (!program)
1422
+ throw new Error(`empty command override for ${server.id}`);
1423
+ const proc = await launchLSP(program, argv.slice(1), {
1424
+ cwd: root,
1425
+ env: override.env ? { ...process.env, ...override.env } : process.env
1426
+ });
1427
+ return { process: proc };
1428
+ }
1429
+ };
1430
+ });
1431
+ return {
1432
+ customServers,
1433
+ registeredServers: overriddenBuiltins,
1434
+ disabledServerIds,
1435
+ serverOverrides
1436
+ };
1201
1437
  }
1202
1438
  function forgetConfigResolvedClaims(evictedRoots) {
1203
1439
  for (const root of evictedRoots) {
@@ -1224,7 +1460,8 @@ async function initLSPConfig(cwd) {
1224
1460
  }
1225
1461
  }
1226
1462
  function registeredServers(config) {
1227
- return [...LSP_SERVERS, ...config.customServers];
1463
+ const builtins = config.registeredServers.length ? config.registeredServers : LSP_SERVERS;
1464
+ return [...builtins, ...config.customServers];
1228
1465
  }
1229
1466
  function selectionReason(server, config, filePath, ext, base) {
1230
1467
  if (config.disabledServerIds.has(server.id))
@@ -1244,8 +1481,8 @@ function selectionReason(server, config, filePath, ext, base) {
1244
1481
  return "selected";
1245
1482
  }
1246
1483
  function explainServersForFile(filePath, config = getConfigForFile(filePath)) {
1247
- const ext = path4.extname(filePath).toLowerCase();
1248
- const base = path4.basename(filePath).toLowerCase();
1484
+ const ext = path3.extname(filePath).toLowerCase();
1485
+ const base = path3.basename(filePath).toLowerCase();
1249
1486
  return registeredServers(config).map((server) => {
1250
1487
  const reason = selectionReason(server, config, filePath, ext, base);
1251
1488
  return { server, selected: reason === "selected", reason };
@@ -1253,12 +1490,12 @@ function explainServersForFile(filePath, config = getConfigForFile(filePath)) {
1253
1490
  }
1254
1491
  function getServersForFileWithConfig(filePath) {
1255
1492
  const config = getConfigForFile(filePath);
1256
- const ext = path4.extname(filePath).toLowerCase();
1257
- const base = path4.basename(filePath).toLowerCase();
1493
+ const ext = path3.extname(filePath).toLowerCase();
1494
+ const base = path3.basename(filePath).toLowerCase();
1258
1495
  return registeredServers(config).filter((server) => selectionReason(server, config, filePath, ext, base) === "selected");
1259
1496
  }
1260
1497
  function primaryServerEntry(filePath) {
1261
- return getServersForFileWithConfig(filePath).find((s) => s.role !== "auxiliary");
1498
+ return getServersForFileWithConfig(filePath).find((s) => !isAuxiliary(s));
1262
1499
  }
1263
1500
  function primaryServerId(filePath) {
1264
1501
  return primaryServerEntry(filePath)?.id;
@@ -1315,11 +1552,15 @@ function resolveAstGrepNativeExe(platform = process.platform, arch = process.arc
1315
1552
  }
1316
1553
  var SERVER_DIAGNOSTIC_STRATEGIES = {
1317
1554
  typescript: {
1318
- seedFirstPush: true,
1555
+ seedFirstPush: false,
1319
1556
  pullRetryBudgetMs: 0,
1320
1557
  debounceMs: 50,
1321
1558
  aggregateWaitMs: 1e3,
1322
1559
  expectSemanticSecondPush: false,
1560
+ // #3310 / nightly run 37930006227: the clean TypeScript fixture
1561
+ // published an empty pre-index result before its settled result.
1562
+ // Hold that first publish so it cannot resolve a push wait as clean.
1563
+ emptyFirstPublish: "indexing",
1323
1564
  // Tier 3 (#458): typescript-language-server publishes nothing on a
1324
1565
  // clean→clean edit (docs/lsp-capability-matrix.md, re-confirmed
1325
1566
  // 2026-07-12). It's the lone core-set tier-3 server, which is exactly
@@ -1701,7 +1942,7 @@ function classifyServerWaitTier(serverId, snapshot) {
1701
1942
  return "tier3-silent";
1702
1943
  }
1703
1944
  function resolvePrimaryServerForWaitPolicy(filePath, snapshots) {
1704
- const servers = getServersForFileWithConfig(filePath).filter((s) => s.role !== "auxiliary");
1945
+ const servers = getServersForFileWithConfig(filePath).filter((s) => !isAuxiliary(s));
1705
1946
  const primary = servers[0];
1706
1947
  if (!primary)
1707
1948
  return void 0;
@@ -1721,14 +1962,14 @@ function classifyCascadeWaitTier(lspService, filePath, snapshots) {
1721
1962
  // dist/clients/lsp/lombok.js
1722
1963
  import * as fs4 from "node:fs";
1723
1964
  import * as os4 from "node:os";
1724
- import * as path5 from "node:path";
1965
+ import * as path4 from "node:path";
1725
1966
  var LOMBOK_ENV_KEYS = ["PI_LENS_LOMBOK_JAR", "LOMBOK_JAR"];
1726
1967
  var LOMBOK_PROJECT_FILES = [
1727
1968
  "lombok.config",
1728
1969
  "pom.xml",
1729
1970
  "build.gradle",
1730
1971
  "build.gradle.kts",
1731
- path5.join("gradle", "libs.versions.toml")
1972
+ path4.join("gradle", "libs.versions.toml")
1732
1973
  ];
1733
1974
  function fileExists(filePath) {
1734
1975
  if (!filePath)
@@ -1753,13 +1994,13 @@ function containsLombokDependency(text) {
1753
1994
  return /\borg\.projectlombok\b/i.test(text) || /<artifactId>\s*lombok\s*<\/artifactId>/i.test(text) || /\b(annotationProcessor|compileOnly|testCompileOnly|testAnnotationProcessor)\b[^\n]*\blombok\b/i.test(text);
1754
1995
  }
1755
1996
  function hasLombokProject(root) {
1756
- const lombokConfig = path5.join(root, "lombok.config");
1997
+ const lombokConfig = path4.join(root, "lombok.config");
1757
1998
  if (fileExists(lombokConfig))
1758
1999
  return true;
1759
2000
  for (const name of LOMBOK_PROJECT_FILES) {
1760
2001
  if (name === "lombok.config")
1761
2002
  continue;
1762
- const text = readTextIfSmall(path5.join(root, name));
2003
+ const text = readTextIfSmall(path4.join(root, name));
1763
2004
  if (text && containsLombokDependency(text))
1764
2005
  return true;
1765
2006
  }
@@ -1769,16 +2010,16 @@ function envJarCandidate(env) {
1769
2010
  for (const key of LOMBOK_ENV_KEYS) {
1770
2011
  const value = env[key]?.trim();
1771
2012
  if (fileExists(value))
1772
- return path5.resolve(value);
2013
+ return path4.resolve(value);
1773
2014
  }
1774
2015
  return void 0;
1775
2016
  }
1776
2017
  function localJarCandidates(root) {
1777
2018
  return [
1778
- path5.join(root, "lombok.jar"),
1779
- path5.join(root, ".lombok", "lombok.jar"),
1780
- path5.join(root, "lib", "lombok.jar"),
1781
- path5.join(root, "libs", "lombok.jar")
2019
+ path4.join(root, "lombok.jar"),
2020
+ path4.join(root, ".lombok", "lombok.jar"),
2021
+ path4.join(root, "lib", "lombok.jar"),
2022
+ path4.join(root, "libs", "lombok.jar")
1782
2023
  ];
1783
2024
  }
1784
2025
  function newestExistingJar(candidates) {
@@ -1797,17 +2038,17 @@ function newestExistingJar(candidates) {
1797
2038
  return best?.filePath;
1798
2039
  }
1799
2040
  function mavenLocalLombokJar(home = os4.homedir()) {
1800
- const base = path5.join(home, ".m2", "repository", "org", "projectlombok", "lombok");
2041
+ const base = path4.join(home, ".m2", "repository", "org", "projectlombok", "lombok");
1801
2042
  let versions = [];
1802
2043
  try {
1803
2044
  versions = fs4.readdirSync(base);
1804
2045
  } catch {
1805
2046
  return void 0;
1806
2047
  }
1807
- return newestExistingJar(versions.map((version) => path5.join(base, version, `lombok-${version}.jar`)));
2048
+ return newestExistingJar(versions.map((version) => path4.join(base, version, `lombok-${version}.jar`)));
1808
2049
  }
1809
2050
  function gradleCacheLombokJar(home = os4.homedir()) {
1810
- const base = path5.join(home, ".gradle", "caches", "modules-2", "files-2.1", "org.projectlombok", "lombok");
2051
+ const base = path4.join(home, ".gradle", "caches", "modules-2", "files-2.1", "org.projectlombok", "lombok");
1811
2052
  const candidates = [];
1812
2053
  let versions = [];
1813
2054
  try {
@@ -1816,7 +2057,7 @@ function gradleCacheLombokJar(home = os4.homedir()) {
1816
2057
  return void 0;
1817
2058
  }
1818
2059
  for (const version of versions) {
1819
- const versionDir = path5.join(base, version);
2060
+ const versionDir = path4.join(base, version);
1820
2061
  let hashes = [];
1821
2062
  try {
1822
2063
  hashes = fs4.readdirSync(versionDir);
@@ -1824,7 +2065,7 @@ function gradleCacheLombokJar(home = os4.homedir()) {
1824
2065
  continue;
1825
2066
  }
1826
2067
  for (const hash of hashes) {
1827
- candidates.push(path5.join(versionDir, hash, `lombok-${version}.jar`));
2068
+ candidates.push(path4.join(versionDir, hash, `lombok-${version}.jar`));
1828
2069
  }
1829
2070
  }
1830
2071
  return newestExistingJar(candidates);
@@ -1857,15 +2098,15 @@ function createLombokJdtlsArgs(root, baseEnv = process.env) {
1857
2098
  // dist/clients/lsp/jvm-runtime.js
1858
2099
  import { existsSync as existsSync2, readdirSync as readdirSync4 } from "node:fs";
1859
2100
  import os5 from "node:os";
1860
- import path6 from "node:path";
2101
+ import path5 from "node:path";
1861
2102
  var JAVA_EXE = process.platform === "win32" ? "java.exe" : "java";
1862
2103
  var JAVA_PROBE_TIMEOUT_MS = 5e3;
1863
2104
  var MIN_JAVA_MAJOR = 17;
1864
2105
  function jdkHomeFrom(dir) {
1865
- if (existsSync2(path6.join(dir, "bin", JAVA_EXE)))
2106
+ if (existsSync2(path5.join(dir, "bin", JAVA_EXE)))
1866
2107
  return dir;
1867
- const macHome = path6.join(dir, "Contents", "Home");
1868
- if (existsSync2(path6.join(macHome, "bin", JAVA_EXE)))
2108
+ const macHome = path5.join(dir, "Contents", "Home");
2109
+ if (existsSync2(path5.join(macHome, "bin", JAVA_EXE)))
1869
2110
  return macHome;
1870
2111
  return void 0;
1871
2112
  }
@@ -1878,7 +2119,7 @@ function parseMajorVersion(name) {
1878
2119
  }
1879
2120
  function childDirs(base) {
1880
2121
  try {
1881
- return readdirSync4(base, { withFileTypes: true }).filter((e) => e.isDirectory() || e.isSymbolicLink()).map((e) => path6.join(base, e.name));
2122
+ return readdirSync4(base, { withFileTypes: true }).filter((e) => e.isDirectory() || e.isSymbolicLink()).map((e) => path5.join(base, e.name));
1882
2123
  } catch {
1883
2124
  return [];
1884
2125
  }
@@ -1886,26 +2127,26 @@ function childDirs(base) {
1886
2127
  function candidateRoots() {
1887
2128
  const home = os5.homedir();
1888
2129
  const roots = [];
1889
- roots.push(path6.join(home, ".jdks"));
1890
- roots.push(path6.join(getGlobalPiLensDir(), "tools"));
2130
+ roots.push(path5.join(home, ".jdks"));
2131
+ roots.push(path5.join(getGlobalPiLensDir(), "tools"));
1891
2132
  if (process.platform === "win32") {
1892
2133
  const progFiles = [
1893
2134
  process.env.ProgramFiles,
1894
2135
  process.env["ProgramFiles(x86)"],
1895
2136
  process.env.ProgramW6432,
1896
- path6.join(process.env.LOCALAPPDATA ?? path6.join(home, "AppData", "Local"), "Programs")
2137
+ path5.join(process.env.LOCALAPPDATA ?? path5.join(home, "AppData", "Local"), "Programs")
1897
2138
  ].filter((p) => Boolean(p));
1898
2139
  for (const base of progFiles) {
1899
- roots.push(path6.join(base, "Eclipse Adoptium"));
1900
- roots.push(path6.join(base, "Java"));
1901
- roots.push(path6.join(base, "Microsoft"));
1902
- roots.push(path6.join(base, "Zulu"));
1903
- roots.push(path6.join(base, "Amazon Corretto"));
1904
- roots.push(path6.join(base, "BellSoft"));
2140
+ roots.push(path5.join(base, "Eclipse Adoptium"));
2141
+ roots.push(path5.join(base, "Java"));
2142
+ roots.push(path5.join(base, "Microsoft"));
2143
+ roots.push(path5.join(base, "Zulu"));
2144
+ roots.push(path5.join(base, "Amazon Corretto"));
2145
+ roots.push(path5.join(base, "BellSoft"));
1905
2146
  }
1906
2147
  } else if (process.platform === "darwin") {
1907
2148
  roots.push("/Library/Java/JavaVirtualMachines");
1908
- roots.push(path6.join(home, "Library", "Java", "JavaVirtualMachines"));
2149
+ roots.push(path5.join(home, "Library", "Java", "JavaVirtualMachines"));
1909
2150
  roots.push("/opt/homebrew/opt");
1910
2151
  roots.push("/usr/local/opt");
1911
2152
  } else {
@@ -1929,7 +2170,7 @@ function discoverJdkHome(roots = candidateRoots(), javaHome = process.env.JAVA_H
1929
2170
  continue;
1930
2171
  found.push({
1931
2172
  home: resolved,
1932
- major: parseMajorVersion(path6.basename(dir))
2173
+ major: parseMajorVersion(path5.basename(dir))
1933
2174
  });
1934
2175
  }
1935
2176
  }
@@ -2028,12 +2269,12 @@ async function resolveJavaRuntimeEnv() {
2028
2269
  }
2029
2270
  const home = discoverJdkHome();
2030
2271
  if (home) {
2031
- const binDir = path6.join(home, "bin");
2272
+ const binDir = path5.join(home, "bin");
2032
2273
  const currentPath = process.env.PATH ?? process.env.Path ?? "";
2033
2274
  _cachedEnv = {
2034
2275
  value: {
2035
2276
  JAVA_HOME: home,
2036
- PATH: binDir + path6.delimiter + currentPath
2277
+ PATH: binDir + path5.delimiter + currentPath
2037
2278
  }
2038
2279
  };
2039
2280
  return _cachedEnv.value;
@@ -2089,7 +2330,7 @@ async function resolveLspServerCwd(server, filePath, sessionCwd, onRootFailure,
2089
2330
  rootFailed = true;
2090
2331
  onRootFailure?.(reason);
2091
2332
  }
2092
- const isFileDirFallback = serverRoot !== void 0 && rootMarkers?.length && path7.resolve(serverRoot) === path7.resolve(path7.dirname(filePath));
2333
+ const isFileDirFallback = serverRoot !== void 0 && rootMarkers?.length && path6.resolve(serverRoot) === path6.resolve(path6.dirname(filePath));
2093
2334
  if (!serverRoot || isFileDirFallback) {
2094
2335
  const probedMarker = onRootFallback && !rootFailed && rootMarkers?.length ? resolveToolCwd("lsp", server.id, filePath, {
2095
2336
  cwd: sessionCwd,
@@ -2098,7 +2339,7 @@ async function resolveLspServerCwd(server, filePath, sessionCwd, onRootFailure,
2098
2339
  }).marker : void 0;
2099
2340
  const refusedMarker = probedMarker !== void 0 && rootMarkers?.includes(probedMarker) ? probedMarker : void 0;
2100
2341
  const fallbackCwd = rootMarkers?.length ? resolveToolCwd("lsp", server.id, filePath, {
2101
- cwd: path7.dirname(path7.resolve(filePath)),
2342
+ cwd: path6.dirname(path6.resolve(filePath)),
2102
2343
  rootMarkers
2103
2344
  }) : void 0;
2104
2345
  if (!serverRoot) {
@@ -2168,17 +2409,17 @@ var PROJECT_BOUNDARY_MARKERS = [
2168
2409
  var loggedRootCeilingClamps = /* @__PURE__ */ new Set();
2169
2410
  var posixCaseInsensitiveByPath = /* @__PURE__ */ new Map();
2170
2411
  function posixFilesystemIsCaseInsensitive(root) {
2171
- const resolved = path7.resolve(root);
2412
+ const resolved = path6.resolve(root);
2172
2413
  const cached = posixCaseInsensitiveByPath.get(resolved);
2173
2414
  if (cached !== void 0)
2174
2415
  return cached;
2175
2416
  if (!existsSync3(resolved)) {
2176
2417
  return false;
2177
2418
  }
2178
- const name = path7.basename(resolved);
2419
+ const name = path6.basename(resolved);
2179
2420
  const alternate = name.toLowerCase() === name ? name.toUpperCase() : name.toLowerCase();
2180
2421
  let insensitive = false;
2181
- const alternatePath = path7.join(path7.dirname(resolved), alternate);
2422
+ const alternatePath = path6.join(path6.dirname(resolved), alternate);
2182
2423
  if (alternate !== name && existsSync3(alternatePath)) {
2183
2424
  try {
2184
2425
  const actual = statSync3(resolved);
@@ -2196,7 +2437,7 @@ function resetLSPCaseSensitivityState() {
2196
2437
  }
2197
2438
  function isSameOrWithin(ancestor, candidate, deps = {}) {
2198
2439
  const windowsShaped = isWindowsPath(ancestor) || isWindowsPath(candidate);
2199
- const pathApi = windowsShaped ? path7.win32 : path7;
2440
+ const pathApi = windowsShaped ? path6.win32 : path6;
2200
2441
  const resolvedAncestor = pathApi.resolve(ancestor);
2201
2442
  const resolvedCandidate = pathApi.resolve(candidate);
2202
2443
  let caseInsensitive = false;
@@ -2216,7 +2457,7 @@ function isSameOrWithin(ancestor, candidate, deps = {}) {
2216
2457
  }
2217
2458
  function enforceLspRootCeiling(root, sessionCwd, filePath) {
2218
2459
  const windowsShaped = isWindowsPath(root) || isWindowsPath(sessionCwd);
2219
- const pathApi = windowsShaped ? path7.win32 : path7;
2460
+ const pathApi = windowsShaped ? path6.win32 : path6;
2220
2461
  const resolvedRoot = pathApi.resolve(root);
2221
2462
  const resolvedCwd = pathApi.resolve(sessionCwd);
2222
2463
  if (filePath && !isSameOrWithin(resolvedCwd, pathApi.resolve(filePath))) {
@@ -2241,8 +2482,8 @@ async function hasProjectBoundaryMarker(dir) {
2241
2482
  return false;
2242
2483
  }
2243
2484
  function pathSegments(dir) {
2244
- const parsed = path7.parse(path7.resolve(dir));
2245
- return path7.relative(parsed.root, path7.resolve(dir)).split(path7.sep).filter(Boolean).map((segment) => segment.toLowerCase());
2485
+ const parsed = path6.parse(path6.resolve(dir));
2486
+ return path6.relative(parsed.root, path6.resolve(dir)).split(path6.sep).filter(Boolean).map((segment) => segment.toLowerCase());
2246
2487
  }
2247
2488
  function hasFixtureConvention(dir) {
2248
2489
  const segments = pathSegments(dir);
@@ -2254,18 +2495,18 @@ function hasAtomicStageSegment(dir) {
2254
2495
  return pathSegments(dir).some((segment) => STAGE_TMP_PATTERN.test(segment));
2255
2496
  }
2256
2497
  async function findGitBoundary(dir) {
2257
- let current = path7.resolve(dir);
2258
- const fsRoot = path7.parse(current).root;
2498
+ let current = path6.resolve(dir);
2499
+ const fsRoot = path6.parse(current).root;
2259
2500
  while (true) {
2260
2501
  if (await markerExists(current, ".git"))
2261
2502
  return current;
2262
2503
  if (current === fsRoot)
2263
2504
  return void 0;
2264
- current = path7.dirname(current);
2505
+ current = path6.dirname(current);
2265
2506
  }
2266
2507
  }
2267
2508
  async function isExcludedLspRoot(dir) {
2268
- const candidate = path7.resolve(dir);
2509
+ const candidate = path6.resolve(dir);
2269
2510
  if (hasFixtureConvention(candidate) || hasAtomicStageSegment(candidate))
2270
2511
  return true;
2271
2512
  const gitRoot = await findGitBoundary(candidate);
@@ -2277,9 +2518,9 @@ async function isExcludedLspRoot(dir) {
2277
2518
  }
2278
2519
  async function nearestNonExcludedFallbackRoot(candidate) {
2279
2520
  if (!await isExcludedLspRoot(candidate))
2280
- return path7.resolve(candidate);
2281
- let current = path7.dirname(path7.resolve(candidate));
2282
- const fsRoot = path7.parse(current).root;
2521
+ return path6.resolve(candidate);
2522
+ let current = path6.dirname(path6.resolve(candidate));
2523
+ const fsRoot = path6.parse(current).root;
2283
2524
  let nearestAllowed;
2284
2525
  while (true) {
2285
2526
  if (!await isExcludedLspRoot(current)) {
@@ -2291,7 +2532,7 @@ async function nearestNonExcludedFallbackRoot(candidate) {
2291
2532
  }
2292
2533
  if (current === fsRoot)
2293
2534
  break;
2294
- current = path7.dirname(current);
2535
+ current = path6.dirname(current);
2295
2536
  }
2296
2537
  return nearestAllowed ?? fsRoot;
2297
2538
  }
@@ -2332,7 +2573,7 @@ async function installEvidenceForLaunch(toolId, installed, attempt) {
2332
2573
  const confirmedManagedPath = await findManagedToolBinary(toolId);
2333
2574
  return {
2334
2575
  ...evidence,
2335
- binary: path7.basename(installed),
2576
+ binary: path6.basename(installed),
2336
2577
  ...confirmedManagedPath !== void 0 && pathsEqual(confirmedManagedPath, installed) && {
2337
2578
  source: "managed-dir"
2338
2579
  }
@@ -2385,7 +2626,7 @@ function markDirectLspCommandUnavailable(command) {
2385
2626
  directLspCommandUnavailableUntil.set(command, Date.now() + DIRECT_LSP_NEGATIVE_TTL_MS);
2386
2627
  directLspCommandSkipLoggedUntil.delete(command);
2387
2628
  }
2388
- var PI_LENS_BIN_DIR2 = path7.join(getGlobalPiLensDir(), "bin");
2629
+ var PI_LENS_BIN_DIR2 = path6.join(getGlobalPiLensDir(), "bin");
2389
2630
  async function resolveAndLaunch(spec, allowInstall) {
2390
2631
  const generation = lspLaunchAvailabilityGeneration.capture();
2391
2632
  const toolLabel = spec.managedToolId ?? spec.candidates[spec.candidates.length - 1] ?? "unknown";
@@ -2450,7 +2691,7 @@ async function resolveAndLaunch(spec, allowInstall) {
2450
2691
  producer: "lsp-launch",
2451
2692
  classifiedBy: "probe",
2452
2693
  evidence: {
2453
- binary: path7.basename(managedCandidate),
2694
+ binary: path6.basename(managedCandidate),
2454
2695
  source: "managed-dir",
2455
2696
  correctsLatchedRow: false
2456
2697
  }
@@ -2743,7 +2984,7 @@ async function resolveAndLaunchTreeBinary(spec, allowInstall) {
2743
2984
  return void 0;
2744
2985
  }
2745
2986
  const suffix = process.platform === "win32" ? ".exe" : "";
2746
- const binPath = path7.join(bundleDir, ...spec.binRelPath.split("/")) + suffix;
2987
+ const binPath = path6.join(bundleDir, ...spec.binRelPath.split("/")) + suffix;
2747
2988
  try {
2748
2989
  const proc = await launchLSP(binPath, spec.args, {
2749
2990
  cwd: spec.cwd,
@@ -2758,7 +2999,7 @@ async function resolveAndLaunchTreeBinary(spec, allowInstall) {
2758
2999
  }
2759
3000
  }
2760
3001
  function nodeBinLocalCandidates(root, baseName) {
2761
- const localBase = path7.join(root, "node_modules", ".bin", baseName);
3002
+ const localBase = path6.join(root, "node_modules", ".bin", baseName);
2762
3003
  if (process.platform === "win32") {
2763
3004
  return [`${localBase}.cmd`, `${localBase}.exe`];
2764
3005
  }
@@ -2768,7 +3009,7 @@ function nodeBinCandidates(root, baseName) {
2768
3009
  return [...nodeBinLocalCandidates(root, baseName), baseName];
2769
3010
  }
2770
3011
  function piAgentExtensionsRootKey(file) {
2771
- const dirKey = normalizeEphemeralMapKey(path7.dirname(path7.resolve(file)));
3012
+ const dirKey = normalizeEphemeralMapKey(path6.dirname(path6.resolve(file)));
2772
3013
  const marker = "/.pi/agent/extensions";
2773
3014
  const index = dirKey.indexOf(marker);
2774
3015
  if (index === -1)
@@ -2776,12 +3017,12 @@ function piAgentExtensionsRootKey(file) {
2776
3017
  return dirKey.slice(0, index + marker.length);
2777
3018
  }
2778
3019
  function IgnoreHomeRoot(primary) {
2779
- const homeKey = normalizeEphemeralMapKey(path7.resolve(os6.homedir()));
3020
+ const homeKey = normalizeEphemeralMapKey(path6.resolve(os6.homedir()));
2780
3021
  return withRootMarkers(async (file) => {
2781
3022
  const root = await primary(file);
2782
3023
  if (!root)
2783
3024
  return void 0;
2784
- return normalizeEphemeralMapKey(path7.resolve(root)) === homeKey ? void 0 : root;
3025
+ return normalizeEphemeralMapKey(path6.resolve(root)) === homeKey ? void 0 : root;
2785
3026
  }, primary.rootMarkers ?? []);
2786
3027
  }
2787
3028
  function rubyBinCandidates(baseName) {
@@ -2789,13 +3030,13 @@ function rubyBinCandidates(baseName) {
2789
3030
  const home = os6.homedir();
2790
3031
  const isWin = process.platform === "win32";
2791
3032
  const ext = isWin ? ".bat" : "";
2792
- candidates.push(path7.join(home, ".local", "share", "mise", "installs", "ruby", "bin", `${baseName}${ext}`));
2793
- candidates.push(path7.join(home, ".asdf", "installs", "ruby", "bin", `${baseName}${ext}`));
3033
+ candidates.push(path6.join(home, ".local", "share", "mise", "installs", "ruby", "bin", `${baseName}${ext}`));
3034
+ candidates.push(path6.join(home, ".asdf", "installs", "ruby", "bin", `${baseName}${ext}`));
2794
3035
  if (isWin) {
2795
- const driveRoot = path7.parse(home).root;
3036
+ const driveRoot = path6.parse(home).root;
2796
3037
  for (const entry of getRubyVersionDirNamesSync(driveRoot)) {
2797
- candidates.push(path7.join(driveRoot, entry, "bin", `${baseName}.bat`));
2798
- candidates.push(path7.join(driveRoot, entry, "bin", baseName));
3038
+ candidates.push(path6.join(driveRoot, entry, "bin", `${baseName}.bat`));
3039
+ candidates.push(path6.join(driveRoot, entry, "bin", baseName));
2799
3040
  }
2800
3041
  }
2801
3042
  return candidates;
@@ -2806,10 +3047,16 @@ function createInteractiveServer(spec) {
2806
3047
  name: spec.name,
2807
3048
  extensions: spec.extensions,
2808
3049
  idleEviction: "unmeasured",
3050
+ // `InteractiveServerSpec` declares no role, so the factory applies the
3051
+ // stated default rather than leaving `role` absent for each consumer to
3052
+ // read by negation (#1488). An auxiliary would declare its role on a
3053
+ // row of its own, the way the four scanner rows do.
3054
+ role: DEFAULT_LSP_SERVER_ROLE,
2809
3055
  root: spec.root,
2810
3056
  rootMarkers: spec.root.rootMarkers,
2811
3057
  fallbackFor: spec.fallbackFor,
2812
3058
  requiresProjectRoot: spec.requiresProjectRoot,
3059
+ executesProjectCode: spec.executesProjectCode,
2813
3060
  availabilityKey: typeof spec.command === "string" && isSimpleCommand(spec.command) ? spec.command : void 0,
2814
3061
  async spawn(root) {
2815
3062
  const command = typeof spec.command === "function" ? spec.command(root) : spec.command;
@@ -2846,7 +3093,7 @@ function PriorityRoot(markerGroups, excludePatterns, stopDir) {
2846
3093
  }, markerGroups.flat());
2847
3094
  }
2848
3095
  var FileDirRoot = async (file) => {
2849
- const candidate = path7.resolve(path7.dirname(file));
3096
+ const candidate = path6.resolve(path6.dirname(file));
2850
3097
  return nearestNonExcludedFallbackRoot(candidate);
2851
3098
  };
2852
3099
  function RootWithFallback(primary, fallback = FileDirRoot) {
@@ -2868,16 +3115,16 @@ function markerProbeDir(dir, pattern) {
2868
3115
  const normalized = toPosix(pattern);
2869
3116
  const slash = normalized.lastIndexOf("/");
2870
3117
  const parentPattern = slash >= 0 ? normalized.slice(0, slash) : "";
2871
- return parentPattern ? path7.join(dir, ...parentPattern.split("/").filter(Boolean)) : dir;
3118
+ return parentPattern ? path6.join(dir, ...parentPattern.split("/").filter(Boolean)) : dir;
2872
3119
  }
2873
3120
  async function markerExists(dir, pattern) {
2874
3121
  if (!pattern.includes("*")) {
2875
3122
  try {
2876
- await stat(path7.join(dir, pattern));
3123
+ await stat(path6.join(dir, pattern));
2877
3124
  return true;
2878
3125
  } catch (err) {
2879
3126
  if (isPermissionFsError(err)) {
2880
- logSessionStart(`lsp root marker skipped: permission error stat ${path7.join(dir, pattern)}`);
3127
+ logSessionStart(`lsp root marker skipped: permission error stat ${path6.join(dir, pattern)}`);
2881
3128
  }
2882
3129
  return false;
2883
3130
  }
@@ -2913,7 +3160,7 @@ function NearestRoot(includePatterns, excludePatterns, stopDir) {
2913
3160
  subdirPatterns.push(pattern);
2914
3161
  }
2915
3162
  return withRootMarkers(async (file) => {
2916
- const startDir = path7.resolve(path7.dirname(file));
3163
+ const startDir = path6.resolve(path6.dirname(file));
2917
3164
  const dirKey = normalizeMapKey(startDir);
2918
3165
  const cached = cache.get(dirKey);
2919
3166
  if (cached !== void 0 && Date.now() - cached.walkedAt < FRESHNESS_CADENCE_MS && await bounded(dirMtimesStillFreshAsync(cached.probedDirs), rootMtimeBound("lsp-root-memo-freshness")) === true)
@@ -2925,10 +3172,10 @@ function NearestRoot(includePatterns, excludePatterns, stopDir) {
2925
3172
  const walkedAt = Date.now();
2926
3173
  const promise = (async () => {
2927
3174
  let currentDir = startDir;
2928
- const fsRoot = path7.parse(currentDir).root;
2929
- const stop = stopDir ? path7.resolve(stopDir) : fsRoot;
3175
+ const fsRoot = path6.parse(currentDir).root;
3176
+ const stop = stopDir ? path6.resolve(stopDir) : fsRoot;
2930
3177
  while (true) {
2931
- if (stop !== fsRoot && currentDir.startsWith(stop + path7.sep) === false && currentDir !== stop) {
3178
+ if (stop !== fsRoot && currentDir.startsWith(stop + path6.sep) === false && currentDir !== stop) {
2932
3179
  break;
2933
3180
  }
2934
3181
  const stepDirs = [
@@ -2946,7 +3193,7 @@ function NearestRoot(includePatterns, excludePatterns, stopDir) {
2946
3193
  }
2947
3194
  }
2948
3195
  if (excluded) {
2949
- currentDir = path7.dirname(currentDir);
3196
+ currentDir = path6.dirname(currentDir);
2950
3197
  continue;
2951
3198
  }
2952
3199
  }
@@ -2958,7 +3205,7 @@ function NearestRoot(includePatterns, excludePatterns, stopDir) {
2958
3205
  if (currentDir === stop || currentDir === fsRoot) {
2959
3206
  break;
2960
3207
  }
2961
- currentDir = path7.dirname(currentDir);
3208
+ currentDir = path6.dirname(currentDir);
2962
3209
  }
2963
3210
  return void 0;
2964
3211
  })();
@@ -3000,17 +3247,17 @@ async function findAncestorFile(startDir, ...relativeSegments) {
3000
3247
  }
3001
3248
  async function findAncestorFileAmong(startDir, candidateSegmentLists) {
3002
3249
  const fs5 = await import("node:fs/promises");
3003
- let currentDir = path7.resolve(startDir);
3250
+ let currentDir = path6.resolve(startDir);
3004
3251
  while (!isAtOrAboveHomeDir(currentDir)) {
3005
3252
  for (const segments of candidateSegmentLists) {
3006
- const candidate = path7.join(currentDir, ...segments);
3253
+ const candidate = path6.join(currentDir, ...segments);
3007
3254
  try {
3008
3255
  await fs5.access(candidate);
3009
3256
  return candidate;
3010
3257
  } catch {
3011
3258
  }
3012
3259
  }
3013
- const parent = path7.dirname(currentDir);
3260
+ const parent = path6.dirname(currentDir);
3014
3261
  if (parent === currentDir)
3015
3262
  break;
3016
3263
  currentDir = parent;
@@ -3026,19 +3273,19 @@ function resetClassicTsRepairGuard() {
3026
3273
  classicTsRepairState().attempted = false;
3027
3274
  }
3028
3275
  function typescriptDirsForTsc(tscPath) {
3029
- const binDir = path7.dirname(tscPath);
3276
+ const binDir = path6.dirname(tscPath);
3030
3277
  return [
3031
- path7.join(binDir, "..", "typescript"),
3032
- path7.join(binDir, "..", "..", "typescript")
3278
+ path6.join(binDir, "..", "typescript"),
3279
+ path6.join(binDir, "..", "..", "typescript")
3033
3280
  ];
3034
3281
  }
3035
3282
  async function typescriptVersionForTsc(tscPath) {
3036
- if (!path7.isAbsolute(tscPath))
3283
+ if (!path6.isAbsolute(tscPath))
3037
3284
  return void 0;
3038
3285
  for (const dir of typescriptDirsForTsc(tscPath)) {
3039
3286
  let manifest;
3040
3287
  try {
3041
- manifest = await readFile(path7.join(dir, "package.json"), "utf8");
3288
+ manifest = await readFile(path6.join(dir, "package.json"), "utf8");
3042
3289
  } catch {
3043
3290
  continue;
3044
3291
  }
@@ -3060,25 +3307,45 @@ async function typescriptVersionForTsc(tscPath) {
3060
3307
  }
3061
3308
  return void 0;
3062
3309
  }
3063
- async function findTsserverPath(root, allowInstall) {
3310
+ async function findTsserverPath(root, allowInstall, options) {
3064
3311
  const fs5 = await import("node:fs/promises");
3312
+ const projectRoots = await Promise.all([root, options?.sessionRoot ?? process.cwd(), process.cwd()].map((dir) => fs5.realpath(dir).catch(() => path6.resolve(dir))));
3313
+ const admitCandidate = async (candidate, projectSupplied) => {
3314
+ try {
3315
+ const real = await fs5.realpath(candidate);
3316
+ if (path6.basename(real) !== "tsserver.js" || !(await fs5.stat(real)).isFile())
3317
+ return void 0;
3318
+ const manifest = JSON.parse(await fs5.readFile(path6.join(path6.dirname(real), "..", "package.json"), "utf8"));
3319
+ if (typeof manifest?.version !== "string" || !manifest.version)
3320
+ return void 0;
3321
+ return admitProjectSuppliedTsserver(real, options?.sessionRoot ?? process.cwd(), projectSupplied || projectRoots.some((dir) => isUnderDir(real, dir)), options?.projectCodeAllowed);
3322
+ } catch {
3323
+ return void 0;
3324
+ }
3325
+ };
3065
3326
  const ancestorHit = await findAncestorFile(root, "node_modules", "typescript", "lib", "tsserver.js");
3066
- if (ancestorHit)
3067
- return ancestorHit;
3068
- const cwdCandidate = path7.join(process.cwd(), "node_modules", "typescript", "lib", "tsserver.js");
3327
+ if (ancestorHit) {
3328
+ const admittedAncestor = await admitCandidate(ancestorHit, true);
3329
+ if (admittedAncestor)
3330
+ return admittedAncestor;
3331
+ }
3332
+ const cwdCandidate = path6.join(process.cwd(), "node_modules", "typescript", "lib", "tsserver.js");
3069
3333
  try {
3070
3334
  await fs5.access(cwdCandidate);
3071
- return cwdCandidate;
3335
+ const admittedCwd = await admitCandidate(cwdCandidate, false);
3336
+ if (admittedCwd)
3337
+ return admittedCwd;
3072
3338
  } catch {
3073
3339
  }
3074
3340
  const tsserverForTsc = async (tscPath) => {
3075
- if (!tscPath)
3341
+ if (!tscPath || !path6.isAbsolute(tscPath))
3076
3342
  return void 0;
3077
3343
  for (const dir of typescriptDirsForTsc(tscPath)) {
3078
- const candidate = path7.join(dir, "lib", "tsserver.js");
3344
+ const candidate = path6.join(dir, "lib", "tsserver.js");
3079
3345
  try {
3080
- await fs5.access(candidate);
3081
- return candidate;
3346
+ const admitted = await admitCandidate(candidate, false);
3347
+ if (admitted)
3348
+ return admitted;
3082
3349
  } catch {
3083
3350
  }
3084
3351
  }
@@ -3104,10 +3371,10 @@ async function findTsserverPath(root, allowInstall) {
3104
3371
  return tsserverForTsc(repairedTsc);
3105
3372
  }
3106
3373
  async function findNativeTypeScriptLsp(root) {
3107
- let currentDir = path7.resolve(root);
3374
+ let currentDir = path6.resolve(root);
3108
3375
  while (!isAtOrAboveHomeDir(currentDir)) {
3109
- const typescriptDir = path7.join(currentDir, "node_modules", "typescript");
3110
- const packageJsonPath = path7.join(typescriptDir, "package.json");
3376
+ const typescriptDir = path6.join(currentDir, "node_modules", "typescript");
3377
+ const packageJsonPath = path6.join(typescriptDir, "package.json");
3111
3378
  let packageJsonText;
3112
3379
  try {
3113
3380
  packageJsonText = await readFile(packageJsonPath, "utf8");
@@ -3121,7 +3388,7 @@ async function findNativeTypeScriptLsp(root) {
3121
3388
  return void 0;
3122
3389
  } catch {
3123
3390
  }
3124
- const parent = path7.dirname(currentDir);
3391
+ const parent = path6.dirname(currentDir);
3125
3392
  if (parent === currentDir)
3126
3393
  return void 0;
3127
3394
  currentDir = parent;
@@ -3141,7 +3408,7 @@ async function findNativeTypeScriptLsp(root) {
3141
3408
  const major = /^\d+$/.test(majorText) ? Number(majorText) : Number.NaN;
3142
3409
  if (!Number.isFinite(major) || major < 7)
3143
3410
  return void 0;
3144
- const localTsc = path7.join(currentDir, "node_modules", ".bin", "tsc");
3411
+ const localTsc = path6.join(currentDir, "node_modules", ".bin", "tsc");
3145
3412
  const candidates = process.platform === "win32" ? [`${localTsc}.cmd`, `${localTsc}.exe`, localTsc] : [localTsc];
3146
3413
  for (const command of candidates) {
3147
3414
  try {
@@ -3157,23 +3424,23 @@ async function findNativeTypeScriptLsp(root) {
3157
3424
  function dotnetToolCandidates(tool) {
3158
3425
  const home = os6.homedir();
3159
3426
  return [
3160
- path7.join(PI_LENS_BIN_DIR2, `${tool}.exe`),
3161
- path7.join(PI_LENS_BIN_DIR2, tool),
3162
- path7.join(home, ".dotnet", "tools", `${tool}.exe`),
3163
- path7.join(home, ".dotnet", "tools", tool),
3427
+ path6.join(PI_LENS_BIN_DIR2, `${tool}.exe`),
3428
+ path6.join(PI_LENS_BIN_DIR2, tool),
3429
+ path6.join(home, ".dotnet", "tools", `${tool}.exe`),
3430
+ path6.join(home, ".dotnet", "tools", tool),
3164
3431
  tool
3165
3432
  ].filter(Boolean);
3166
3433
  }
3167
3434
  function binExeVariants(dir, tool) {
3168
- return process.platform === "win32" ? [path7.join(dir, `${tool}.exe`), path7.join(dir, tool)] : [path7.join(dir, tool)];
3435
+ return process.platform === "win32" ? [path6.join(dir, `${tool}.exe`), path6.join(dir, tool)] : [path6.join(dir, tool)];
3169
3436
  }
3170
3437
  function goBinCandidates(tool) {
3171
- const gopath = process.env.GOPATH?.split(path7.delimiter)[0] || path7.join(os6.homedir(), "go");
3172
- return [tool, ...binExeVariants(path7.join(gopath, "bin"), tool)];
3438
+ const gopath = process.env.GOPATH?.split(path6.delimiter)[0] || path6.join(os6.homedir(), "go");
3439
+ return [tool, ...binExeVariants(path6.join(gopath, "bin"), tool)];
3173
3440
  }
3174
3441
  function cargoBinCandidates(tool) {
3175
- const cargoHome = process.env.CARGO_HOME || path7.join(os6.homedir(), ".cargo");
3176
- return [tool, ...binExeVariants(path7.join(cargoHome, "bin"), tool)];
3442
+ const cargoHome = process.env.CARGO_HOME || path6.join(os6.homedir(), ".cargo");
3443
+ return [tool, ...binExeVariants(path6.join(cargoHome, "bin"), tool)];
3177
3444
  }
3178
3445
  async function tryGemInstall(gem) {
3179
3446
  const { join: join3 } = await import("node:path");
@@ -3211,7 +3478,7 @@ var TS_TOOLING_MARKERS = [
3211
3478
  ];
3212
3479
  var TS_FAMILY_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".mts", ".cts"]);
3213
3480
  function isTsFamilyFile(file) {
3214
- return TS_FAMILY_EXTENSIONS.has(path7.extname(file).toLowerCase());
3481
+ return TS_FAMILY_EXTENSIONS.has(path6.extname(file).toLowerCase());
3215
3482
  }
3216
3483
  function tsConfigMarkersForFile(file) {
3217
3484
  return isTsFamilyFile(file) ? ["tsconfig.json"] : TS_CONFIG_MARKERS;
@@ -3232,40 +3499,40 @@ async function findTypeScriptProjectRoot(file) {
3232
3499
  return isSameOrWithin(toolingRoot, configRoot) ? configRoot : toolingRoot;
3233
3500
  }
3234
3501
  async function findExtensionBoundedRoot(file, extensionRootKey) {
3235
- const startDir = path7.resolve(path7.dirname(file));
3502
+ const startDir = path6.resolve(path6.dirname(file));
3236
3503
  let currentDir = startDir;
3237
3504
  const configMarkers = tsConfigMarkersForFile(file);
3238
3505
  while (true) {
3239
3506
  for (const pattern of configMarkers) {
3240
3507
  try {
3241
- await stat(path7.join(currentDir, pattern));
3508
+ await stat(path6.join(currentDir, pattern));
3242
3509
  return currentDir;
3243
3510
  } catch {
3244
3511
  }
3245
3512
  }
3246
3513
  for (const pattern of TS_TOOLING_MARKERS) {
3247
3514
  try {
3248
- await stat(path7.join(currentDir, pattern));
3515
+ await stat(path6.join(currentDir, pattern));
3249
3516
  return currentDir;
3250
3517
  } catch {
3251
3518
  }
3252
3519
  }
3253
- const currentKey = normalizeEphemeralMapKey(path7.resolve(currentDir));
3520
+ const currentKey = normalizeEphemeralMapKey(path6.resolve(currentDir));
3254
3521
  if (currentKey === extensionRootKey)
3255
3522
  return void 0;
3256
- const parent = path7.dirname(currentDir);
3523
+ const parent = path6.dirname(currentDir);
3257
3524
  if (parent === currentDir)
3258
3525
  return void 0;
3259
3526
  currentDir = parent;
3260
3527
  }
3261
3528
  }
3262
3529
  async function hasAgentLevelProjectMarker(extensionRootKey) {
3263
- const agentDir = path7.dirname(extensionRootKey);
3530
+ const agentDir = path6.dirname(extensionRootKey);
3264
3531
  if (!agentDir || agentDir === extensionRootKey)
3265
3532
  return false;
3266
3533
  for (const pattern of [...TS_CONFIG_MARKERS, ...TS_TOOLING_MARKERS]) {
3267
3534
  try {
3268
- await stat(path7.join(agentDir, pattern));
3535
+ await stat(path6.join(agentDir, pattern));
3269
3536
  return true;
3270
3537
  } catch {
3271
3538
  }
@@ -3291,15 +3558,16 @@ var TypeScriptRoot = withRootMarkers(DenoExcludeRoot(async (file) => {
3291
3558
  var TypeScriptServer = {
3292
3559
  id: "typescript",
3293
3560
  idleEviction: "transparent",
3561
+ role: "language",
3294
3562
  name: "TypeScript Language Server",
3563
+ projectLocalCommand: "typescript-language-server",
3295
3564
  extensions: JS_TS_LSP_EXTENSIONS,
3296
3565
  autoPropagateDiagnostics: true,
3297
3566
  root: TypeScriptRoot,
3298
3567
  rootMarkers: TypeScriptRoot.rootMarkers,
3299
3568
  async spawn(root, options) {
3300
- const fs5 = await import("node:fs/promises");
3301
3569
  const nativeLsp = await findNativeTypeScriptLsp(root);
3302
- if (nativeLsp) {
3570
+ if (nativeLsp && admitProjectSuppliedTsserver(nativeLsp.command, root, true, options?.projectCodeAllowed)) {
3303
3571
  const env2 = await getToolEnvironment();
3304
3572
  logSessionStart(`lsp typescript-native: version=${nativeLsp.version} command=${nativeLsp.command}`);
3305
3573
  const proc2 = await launchLSP(nativeLsp.command, ["--lsp", "--stdio"], {
@@ -3323,14 +3591,10 @@ var TypeScriptServer = {
3323
3591
  return void 0;
3324
3592
  }
3325
3593
  }
3326
- let tsserverPath = await findTsserverPath(root, options?.allowInstall);
3594
+ const tsserverPath = await findTsserverPath(root, options?.allowInstall, options);
3327
3595
  if (!tsserverPath) {
3328
- const localCandidate = path7.join(path7.dirname(lspPath), "..", "typescript", "lib", "tsserver.js");
3329
- try {
3330
- await fs5.access(localCandidate);
3331
- tsserverPath = localCandidate;
3332
- } catch {
3333
- }
3596
+ admitProjectSuppliedTsserver(void 0, root, true, options?.projectCodeAllowed);
3597
+ return void 0;
3334
3598
  }
3335
3599
  if (tsserverPath)
3336
3600
  source = "managed";
@@ -3353,6 +3617,7 @@ var TypeScriptServer = {
3353
3617
  var DenoServer = {
3354
3618
  id: "deno",
3355
3619
  idleEviction: "transparent",
3620
+ role: "language",
3356
3621
  name: "Deno Language Server",
3357
3622
  fallbackFor: "typescript",
3358
3623
  extensions: JS_TS_LSP_EXTENSIONS,
@@ -3370,6 +3635,7 @@ var DenoServer = {
3370
3635
  var PythonServer = {
3371
3636
  id: "python",
3372
3637
  idleEviction: "transparent",
3638
+ role: "language",
3373
3639
  name: "Pyright Language Server",
3374
3640
  extensions: KIND_EXTENSIONS["python"],
3375
3641
  root: RootWithFallback(createRootDetector([
@@ -3444,13 +3710,13 @@ var PythonServer = {
3444
3710
  if (!pyrightPath)
3445
3711
  return void 0;
3446
3712
  source = "managed";
3447
- const binDir = path7.dirname(pyrightPath);
3713
+ const binDir = path6.dirname(pyrightPath);
3448
3714
  const isWindows2 = process.platform === "win32";
3449
3715
  const managedCandidates = isWindows2 ? [
3450
- path7.join(binDir, "pyright-langserver.cmd"),
3451
- path7.join(binDir, "pyright-langserver"),
3716
+ path6.join(binDir, "pyright-langserver.cmd"),
3717
+ path6.join(binDir, "pyright-langserver"),
3452
3718
  "pyright-langserver"
3453
- ] : [path7.join(binDir, "pyright-langserver"), "pyright-langserver"];
3719
+ ] : [path6.join(binDir, "pyright-langserver"), "pyright-langserver"];
3454
3720
  const resolved = await resolveAndLaunch({ candidates: managedCandidates, args: ["--stdio"], cwd: root, env }, false);
3455
3721
  if (!resolved)
3456
3722
  return void 0;
@@ -3463,7 +3729,8 @@ var PythonServer = {
3463
3729
  };
3464
3730
  var PythonJediServer = {
3465
3731
  id: "python-jedi",
3466
- idleEviction: "unmeasured",
3732
+ idleEviction: "transparent",
3733
+ role: "language",
3467
3734
  name: "Jedi Language Server",
3468
3735
  fallbackFor: "python",
3469
3736
  extensions: KIND_EXTENSIONS["python"],
@@ -3495,6 +3762,7 @@ var PythonJediServer = {
3495
3762
  var GoServer = {
3496
3763
  id: "go",
3497
3764
  idleEviction: "unmeasured",
3765
+ role: "language",
3498
3766
  name: "gopls",
3499
3767
  extensions: KIND_EXTENSIONS["go"],
3500
3768
  root: RootWithFallback(WorkspacePriorityRoot([["go.work"], ["go.mod", "go.sum"], [".git"]])),
@@ -3517,7 +3785,7 @@ var GoServer = {
3517
3785
  }
3518
3786
  };
3519
3787
  function cargoWorkspaceDeclaresMember(workspaceContent, parentDir, childDir) {
3520
- const relativePath = path7.relative(parentDir, childDir).split(path7.sep).join("/");
3788
+ const relativePath = path6.relative(parentDir, childDir).split(path6.sep).join("/");
3521
3789
  if (relativePath === "" || relativePath.startsWith(".."))
3522
3790
  return false;
3523
3791
  const matches = (pattern) => matchesWorkspaceMemberPattern(pattern, relativePath, CARGO_WORKSPACE_MEMBER_DIALECT, () => recordDegradationOnce({
@@ -3531,7 +3799,7 @@ function cargoWorkspaceDeclaresMember(workspaceContent, parentDir, childDir) {
3531
3799
  return readCargoWorkspaceMembers(workspaceContent).some(matches);
3532
3800
  }
3533
3801
  function sessionHoistCeiling(sessionCwd, file) {
3534
- return isSameOrWithin(path7.resolve(sessionCwd), path7.resolve(file)) ? path7.resolve(sessionCwd) : void 0;
3802
+ return isSameOrWithin(path6.resolve(sessionCwd), path6.resolve(file)) ? path6.resolve(sessionCwd) : void 0;
3535
3803
  }
3536
3804
  function RustWorkspaceRoot() {
3537
3805
  const crateRoot = createRootDetector(["Cargo.toml", "Cargo.lock"]);
@@ -3542,14 +3810,14 @@ function RustWorkspaceRoot() {
3542
3810
  const sessionCwd = process.cwd();
3543
3811
  const stop = sessionHoistCeiling(sessionCwd, file);
3544
3812
  let current = root;
3545
- const fsRoot = path7.parse(current).root;
3813
+ const fsRoot = path6.parse(current).root;
3546
3814
  while (true) {
3547
3815
  if (stop !== void 0 && current === stop)
3548
3816
  break;
3549
- const parent = path7.dirname(current);
3817
+ const parent = path6.dirname(current);
3550
3818
  if (parent === current || parent === fsRoot)
3551
3819
  break;
3552
- const parentCargoPath = path7.join(parent, "Cargo.toml");
3820
+ const parentCargoPath = path6.join(parent, "Cargo.toml");
3553
3821
  const parentCargoContent = await readTextFileOrUndefined(parentCargoPath);
3554
3822
  if (parentCargoContent !== void 0 && hasCargoWorkspaceTable(parentCargoContent)) {
3555
3823
  if (cargoWorkspaceDeclaresMember(parentCargoContent, parent, root)) {
@@ -3577,9 +3845,9 @@ async function declaresMavenModule(parentDir, parentPomPath, childDir) {
3577
3845
  const modulesBlock = content.match(/<modules>([\s\S]*?)<\/modules>/);
3578
3846
  if (!modulesBlock)
3579
3847
  return false;
3580
- const resolvedChild = path7.resolve(childDir);
3848
+ const resolvedChild = path6.resolve(childDir);
3581
3849
  for (const match of modulesBlock[1].matchAll(/<module>\s*([^<]+?)\s*<\/module>/g)) {
3582
- if (pathsEqual(path7.resolve(parentDir, match[1]), resolvedChild))
3850
+ if (pathsEqual(path6.resolve(parentDir, match[1]), resolvedChild))
3583
3851
  return true;
3584
3852
  }
3585
3853
  return false;
@@ -3603,15 +3871,15 @@ function JavaWorkspaceRoot() {
3603
3871
  const stop = sessionHoistCeiling(sessionCwd, file);
3604
3872
  let current = root;
3605
3873
  let lastHop = root;
3606
- const fsRoot = path7.parse(current).root;
3874
+ const fsRoot = path6.parse(current).root;
3607
3875
  while (true) {
3608
3876
  if (stop !== void 0 && current === stop)
3609
3877
  break;
3610
- const parent = path7.dirname(current);
3878
+ const parent = path6.dirname(current);
3611
3879
  if (parent === current || parent === fsRoot)
3612
3880
  break;
3613
3881
  if (await markerExists(parent, "pom.xml")) {
3614
- const parentPom = path7.join(parent, "pom.xml");
3882
+ const parentPom = path6.join(parent, "pom.xml");
3615
3883
  if (!await declaresMavenModule(parent, parentPom, lastHop))
3616
3884
  break;
3617
3885
  current = parent;
@@ -3626,9 +3894,11 @@ function JavaWorkspaceRoot() {
3626
3894
  var RustServer = {
3627
3895
  id: "rust",
3628
3896
  idleEviction: "unmeasured",
3897
+ role: "language",
3629
3898
  // Measured (#3750): rust-analyzer answers an empty result for a detached file.
3630
3899
  requiresProjectRoot: true,
3631
3900
  name: "rust-analyzer",
3901
+ executesProjectCode: true,
3632
3902
  extensions: KIND_EXTENSIONS["rust"],
3633
3903
  // No FileDirRoot fallback (#201): rust-analyzer is a heavy workspace server
3634
3904
  // that is useless without a Cargo manifest. With the fallback, every .rs file
@@ -3660,7 +3930,9 @@ var RustServer = {
3660
3930
  var RubyServer = {
3661
3931
  id: "ruby",
3662
3932
  idleEviction: "unmeasured",
3933
+ role: "language",
3663
3934
  name: "Ruby LSP",
3935
+ executesProjectCode: true,
3664
3936
  extensions: KIND_EXTENSIONS["ruby"],
3665
3937
  root: RootWithFallback(PriorityRoot([["Gemfile", ".ruby-version"], [".git"]])),
3666
3938
  // Ruby LSP may need extra time to finish composed-bundle setup before it can
@@ -3697,6 +3969,7 @@ var RubyServer = {
3697
3969
  var PHPServer = {
3698
3970
  id: "php",
3699
3971
  idleEviction: "transparent",
3972
+ role: "language",
3700
3973
  name: "Intelephense",
3701
3974
  extensions: KIND_EXTENSIONS["php"],
3702
3975
  root: RootWithFallback(createRootDetector(["composer.json", "composer.lock"])),
@@ -3712,17 +3985,17 @@ var PHPServer = {
3712
3985
  return {
3713
3986
  ...result,
3714
3987
  initialization: {
3715
- storagePath: path7.join(getGlobalPiLensDir(), "intelephense")
3988
+ storagePath: path6.join(getGlobalPiLensDir(), "intelephense")
3716
3989
  }
3717
3990
  };
3718
3991
  }
3719
3992
  };
3720
3993
  function buildPsesArgs(bundleDir) {
3721
- const script = path7.join(bundleDir, "PowerShellEditorServices", "Start-EditorServices.ps1");
3722
- const sessionDir = path7.join(getGlobalPiLensDir(), "pses", `${process.pid}-${Date.now()}`);
3994
+ const script = path6.join(bundleDir, "PowerShellEditorServices", "Start-EditorServices.ps1");
3995
+ const sessionDir = path6.join(getGlobalPiLensDir(), "pses", `${process.pid}-${Date.now()}`);
3723
3996
  mkdirSync2(sessionDir, { recursive: true });
3724
- const logPath = path7.join(sessionDir, "pses.log");
3725
- const sessionDetailsPath = path7.join(sessionDir, "session.json");
3997
+ const logPath = path6.join(sessionDir, "pses.log");
3998
+ const sessionDetailsPath = path6.join(sessionDir, "session.json");
3726
3999
  return [
3727
4000
  "-NoLogo",
3728
4001
  "-NoProfile",
@@ -3754,7 +4027,9 @@ function buildPsesArgs(bundleDir) {
3754
4027
  var PowerShellServer = {
3755
4028
  id: "powershell",
3756
4029
  idleEviction: "unmeasured",
4030
+ role: "language",
3757
4031
  name: "PowerShell Editor Services",
4032
+ executesProjectCode: true,
3758
4033
  extensions: KIND_EXTENSIONS["powershell"],
3759
4034
  // Index at the workspace (script modules reference siblings); fall back to the
3760
4035
  // file dir.
@@ -3771,9 +4046,11 @@ var PowerShellServer = {
3771
4046
  var CSharpServer = {
3772
4047
  id: "csharp",
3773
4048
  idleEviction: "unmeasured",
4049
+ role: "language",
3774
4050
  // Documented (#3750): csharp-ls needs a solution or project.
3775
4051
  requiresProjectRoot: true,
3776
4052
  name: "csharp-ls",
4053
+ executesProjectCode: true,
3777
4054
  extensions: KIND_EXTENSIONS["csharp"],
3778
4055
  // No FileDirRoot fallback (#201): csharp-ls is a workspace server and should
3779
4056
  // not spawn once per source directory before a .sln/.csproj exists. Glob root
@@ -3797,6 +4074,7 @@ var CSharpServer = {
3797
4074
  var OmniSharpServer = createInteractiveServer({
3798
4075
  id: "omnisharp",
3799
4076
  name: "OmniSharp",
4077
+ executesProjectCode: true,
3800
4078
  // Documented (#3750): OmniSharp needs a solution or project.
3801
4079
  requiresProjectRoot: true,
3802
4080
  fallbackFor: "csharp",
@@ -3809,9 +4087,11 @@ var OmniSharpServer = createInteractiveServer({
3809
4087
  var FSharpServer = {
3810
4088
  id: "fsharp",
3811
4089
  idleEviction: "unmeasured",
4090
+ role: "language",
3812
4091
  // Documented (#3750): FSAutocomplete needs a project.
3813
4092
  requiresProjectRoot: true,
3814
4093
  name: "FSAutocomplete",
4094
+ executesProjectCode: true,
3815
4095
  extensions: KIND_EXTENSIONS["fsharp"],
3816
4096
  root: createRootDetector([...DOTNET_FSHARP_ROOT_MARKERS]),
3817
4097
  async spawn(root, options) {
@@ -3831,6 +4111,7 @@ var FSharpServer = {
3831
4111
  var JavaServer = createInteractiveServer({
3832
4112
  id: "java",
3833
4113
  name: "JDT Language Server",
4114
+ executesProjectCode: true,
3834
4115
  extensions: KIND_EXTENSIONS["java"],
3835
4116
  root: RootWithFallback(JavaWorkspaceRoot()),
3836
4117
  language: "java",
@@ -3841,7 +4122,9 @@ var JavaServer = createInteractiveServer({
3841
4122
  var KotlinServer = {
3842
4123
  id: "kotlin",
3843
4124
  idleEviction: "unmeasured",
4125
+ role: "language",
3844
4126
  name: "Kotlin Language Server",
4127
+ executesProjectCode: true,
3845
4128
  extensions: KIND_EXTENSIONS["kotlin"],
3846
4129
  root: RootWithFallback(createRootDetector(["build.gradle.kts", "build.gradle", "pom.xml"])),
3847
4130
  async spawn(root, options) {
@@ -3857,9 +4140,26 @@ var KotlinServer = {
3857
4140
  }, options?.allowInstall);
3858
4141
  }
3859
4142
  };
4143
+ var LeanServer = createInteractiveServer({
4144
+ id: "lean",
4145
+ name: "Lean 4 Language Server",
4146
+ extensions: KIND_EXTENSIONS.lean,
4147
+ root: RootWithFallback(createRootDetector([...LEAN_ROOT_MARKERS])),
4148
+ language: "lean",
4149
+ // Documented (#3750): `lake serve` analyses a file only when a Lake project
4150
+ // roots it; a root-fallback empty answer is unconfirmed, not clean.
4151
+ requiresProjectRoot: true,
4152
+ // `lake serve` loads and interprets the project's `lakefile.lean` and its
4153
+ // imported build scripts, so it executes project-owned code: refuse it until
4154
+ // the host grants project trust (#4269).
4155
+ executesProjectCode: true,
4156
+ command: "lake",
4157
+ args: ["serve"]
4158
+ });
3860
4159
  var SwiftServer = createInteractiveServer({
3861
4160
  id: "swift",
3862
4161
  name: "SourceKit-LSP",
4162
+ executesProjectCode: true,
3863
4163
  extensions: KIND_EXTENSIONS["swift"],
3864
4164
  root: createRootDetector(["Package.swift"]),
3865
4165
  language: "swift",
@@ -3867,6 +4167,7 @@ var SwiftServer = createInteractiveServer({
3867
4167
  });
3868
4168
  var DartServer = createInteractiveServer({
3869
4169
  id: "dart",
4170
+ executesProjectCode: true,
3870
4171
  name: "Dart Analysis Server",
3871
4172
  extensions: KIND_EXTENSIONS["dart"],
3872
4173
  root: RootWithFallback(createRootDetector(["pubspec.yaml"])),
@@ -3880,6 +4181,9 @@ function createTreeBinaryServer(spec) {
3880
4181
  name: spec.name,
3881
4182
  extensions: spec.extensions,
3882
4183
  idleEviction: spec.idleEviction ?? "unmeasured",
4184
+ // The spec declares no role, so the factory applies the stated default
4185
+ // (#1488) — see `createInteractiveServer`.
4186
+ role: DEFAULT_LSP_SERVER_ROLE,
3883
4187
  root: spec.root,
3884
4188
  spawn(root, options) {
3885
4189
  return resolveAndLaunchTreeBinary({
@@ -3917,7 +4221,9 @@ var CppServer = createTreeBinaryServer({
3917
4221
  });
3918
4222
  var ZigServer = {
3919
4223
  id: "zig",
4224
+ executesProjectCode: true,
3920
4225
  idleEviction: "unmeasured",
4226
+ role: "language",
3921
4227
  name: "ZLS",
3922
4228
  extensions: KIND_EXTENSIONS["zig"],
3923
4229
  root: RootWithFallback(createRootDetector(["build.zig"])),
@@ -3933,6 +4239,7 @@ var ZigServer = {
3933
4239
  var HaskellServer = createInteractiveServer({
3934
4240
  id: "haskell",
3935
4241
  name: "Haskell Language Server",
4242
+ executesProjectCode: true,
3936
4243
  extensions: KIND_EXTENSIONS["haskell"],
3937
4244
  root: createRootDetector(["stack.yaml", "cabal.project", "*.cabal"]),
3938
4245
  language: "haskell",
@@ -3942,6 +4249,7 @@ var HaskellServer = createInteractiveServer({
3942
4249
  var ElixirServer = createInteractiveServer({
3943
4250
  id: "elixir",
3944
4251
  name: "ElixirLS",
4252
+ executesProjectCode: true,
3945
4253
  extensions: KIND_EXTENSIONS["elixir"],
3946
4254
  root: RootWithFallback(createRootDetector(["mix.exs"])),
3947
4255
  language: "elixir",
@@ -3950,7 +4258,9 @@ var ElixirServer = createInteractiveServer({
3950
4258
  var ElixirExpertServer = {
3951
4259
  id: "expert",
3952
4260
  idleEviction: "unmeasured",
4261
+ role: "language",
3953
4262
  name: "Expert",
4263
+ executesProjectCode: true,
3954
4264
  fallbackFor: "elixir",
3955
4265
  extensions: KIND_EXTENSIONS["elixir"],
3956
4266
  root: RootWithFallback(createRootDetector(["mix.exs"])),
@@ -3968,6 +4278,7 @@ var ElixirExpertServer = {
3968
4278
  var GleamServer = {
3969
4279
  id: "gleam",
3970
4280
  idleEviction: "unmeasured",
4281
+ role: "language",
3971
4282
  name: "Gleam LSP",
3972
4283
  extensions: KIND_EXTENSIONS["gleam"],
3973
4284
  root: RootWithFallback(createRootDetector(["gleam.toml"])),
@@ -3982,7 +4293,9 @@ var GleamServer = {
3982
4293
  };
3983
4294
  var TinymistServer = {
3984
4295
  id: "tinymist",
4296
+ executesProjectCode: true,
3985
4297
  idleEviction: "unmeasured",
4298
+ role: "language",
3986
4299
  name: "Tinymist",
3987
4300
  extensions: extensionsForLanguage("typst"),
3988
4301
  root: RootWithFallback(createRootDetector(["typst.toml", ".git"])),
@@ -3999,6 +4312,7 @@ var TinymistServer = {
3999
4312
  var MarksmanServer = {
4000
4313
  id: "marksman",
4001
4314
  idleEviction: "transparent",
4315
+ role: "language",
4002
4316
  name: "Marksman",
4003
4317
  extensions: KIND_EXTENSIONS["markdown"],
4004
4318
  // Index at the workspace root so cross-file checks (broken intra-repo links,
@@ -4016,6 +4330,7 @@ var MarksmanServer = {
4016
4330
  };
4017
4331
  var OCamlServer = createInteractiveServer({
4018
4332
  id: "ocaml",
4333
+ executesProjectCode: true,
4019
4334
  name: "ocamllsp",
4020
4335
  extensions: KIND_EXTENSIONS["ocaml"],
4021
4336
  root: createRootDetector(["dune-project", "opam"]),
@@ -4024,7 +4339,9 @@ var OCamlServer = createInteractiveServer({
4024
4339
  });
4025
4340
  var ClojureServer = {
4026
4341
  id: "clojure",
4342
+ executesProjectCode: true,
4027
4343
  idleEviction: "transparent",
4344
+ role: "language",
4028
4345
  name: "Clojure LSP",
4029
4346
  extensions: KIND_EXTENSIONS["clojure"],
4030
4347
  root: createRootDetector(["deps.edn", "project.clj"]),
@@ -4040,6 +4357,7 @@ var ClojureServer = {
4040
4357
  var CueServer = {
4041
4358
  id: "cue",
4042
4359
  idleEviction: "unmeasured",
4360
+ role: "language",
4043
4361
  name: "CUE Language Server",
4044
4362
  extensions: KIND_EXTENSIONS["cue"],
4045
4363
  root: RootWithFallback(createRootDetector(["cue.mod", ".git"])),
@@ -4054,7 +4372,9 @@ var CueServer = {
4054
4372
  };
4055
4373
  var TerraformServer = {
4056
4374
  id: "terraform",
4375
+ executesProjectCode: true,
4057
4376
  idleEviction: "unmeasured",
4377
+ role: "language",
4058
4378
  name: "Terraform LSP",
4059
4379
  extensions: KIND_EXTENSIONS["terraform"],
4060
4380
  root: RootWithFallback(createRootDetector([".terraform.lock.hcl", ".terraform"])),
@@ -4069,6 +4389,7 @@ var TerraformServer = {
4069
4389
  };
4070
4390
  var NixServer = createInteractiveServer({
4071
4391
  id: "nix",
4392
+ executesProjectCode: true,
4072
4393
  name: "nixd",
4073
4394
  extensions: KIND_EXTENSIONS["nix"],
4074
4395
  root: createRootDetector(["flake.nix"]),
@@ -4078,6 +4399,7 @@ var NixServer = createInteractiveServer({
4078
4399
  var BashServer = {
4079
4400
  id: "bash",
4080
4401
  idleEviction: "transparent",
4402
+ role: "language",
4081
4403
  name: "Bash Language Server",
4082
4404
  // #3968: narrowed from [".bash", ".sh", ".zsh"] — bash-language-server does
4083
4405
  // not support zsh (its own analyzer refuses the dialect), and its ≥5.7.0
@@ -4121,6 +4443,7 @@ var ShuckServer = createInteractiveServer({
4121
4443
  var FishServer = {
4122
4444
  id: "fish",
4123
4445
  idleEviction: "transparent",
4446
+ role: "language",
4124
4447
  name: "Fish Language Server",
4125
4448
  extensions: KIND_EXTENSIONS["fish"],
4126
4449
  root: RootWithFallback(createRootDetector([".git"])),
@@ -4135,7 +4458,9 @@ var FishServer = {
4135
4458
  };
4136
4459
  var CMakeServer = {
4137
4460
  id: "cmake",
4461
+ executesProjectCode: true,
4138
4462
  idleEviction: "unmeasured",
4463
+ role: "language",
4139
4464
  name: "CMake Language Server",
4140
4465
  // CMake's canonical project file has no .cmake suffix. The configured-server
4141
4466
  // matcher supports exact basenames as well as extensions.
@@ -4152,7 +4477,8 @@ var CMakeServer = {
4152
4477
  };
4153
4478
  var DockerServer = {
4154
4479
  id: "docker",
4155
- idleEviction: "unmeasured",
4480
+ idleEviction: "transparent",
4481
+ role: "language",
4156
4482
  name: "Dockerfile Language Server",
4157
4483
  extensions: [".dockerfile", "Dockerfile"],
4158
4484
  root: RootWithFallback(PriorityRoot([
@@ -4176,6 +4502,7 @@ var DockerServer = {
4176
4502
  var DockerOfficialServer = {
4177
4503
  id: "docker-official",
4178
4504
  idleEviction: "unmeasured",
4505
+ role: "language",
4179
4506
  name: "Docker Language Server (official)",
4180
4507
  fallbackFor: "docker",
4181
4508
  extensions: [".dockerfile", "Dockerfile"],
@@ -4199,6 +4526,7 @@ var DockerOfficialServer = {
4199
4526
  var YamlServer = {
4200
4527
  id: "yaml",
4201
4528
  idleEviction: "transparent",
4529
+ role: "language",
4202
4530
  name: "YAML Language Server",
4203
4531
  extensions: KIND_EXTENSIONS["yaml"],
4204
4532
  root: RootWithFallback(PriorityRoot([
@@ -4216,7 +4544,8 @@ var YamlServer = {
4216
4544
  };
4217
4545
  var JsonServer = {
4218
4546
  id: "json",
4219
- idleEviction: "unmeasured",
4547
+ idleEviction: "transparent",
4548
+ role: "language",
4220
4549
  name: "VSCode JSON Language Server",
4221
4550
  extensions: KIND_EXTENSIONS["json"],
4222
4551
  root: RootWithFallback(WorkspacePriorityRoot([
@@ -4242,6 +4571,7 @@ var JsonServer = {
4242
4571
  var HtmlServer = {
4243
4572
  id: "html",
4244
4573
  idleEviction: "transparent",
4574
+ role: "language",
4245
4575
  name: "VSCode HTML Language Server",
4246
4576
  extensions: KIND_EXTENSIONS["html"],
4247
4577
  root: RootWithFallback(IgnoreHomeRoot(PriorityRoot([["package.json", "index.html", "vite.config.ts"]]))),
@@ -4257,6 +4587,7 @@ var HtmlServer = {
4257
4587
  var TomlServer = {
4258
4588
  id: "toml",
4259
4589
  idleEviction: "unmeasured",
4590
+ role: "language",
4260
4591
  name: "Taplo",
4261
4592
  extensions: KIND_EXTENSIONS["toml"],
4262
4593
  root: RootWithFallback(PriorityRoot([["pyproject.toml", "Cargo.toml", "taplo.toml"], [".git"]])),
@@ -4272,6 +4603,7 @@ var TomlServer = {
4272
4603
  var PrismaServer = {
4273
4604
  id: "prisma",
4274
4605
  idleEviction: "transparent",
4606
+ role: "language",
4275
4607
  name: "Prisma Language Server",
4276
4608
  extensions: KIND_EXTENSIONS["prisma"],
4277
4609
  root: RootWithFallback(createRootDetector(["prisma/schema.prisma", "schema.prisma"])),
@@ -4297,7 +4629,9 @@ var PrismaServer = {
4297
4629
  };
4298
4630
  var VueServer = {
4299
4631
  id: "vue",
4632
+ executesProjectCode: true,
4300
4633
  idleEviction: "unmeasured",
4634
+ role: "language",
4301
4635
  name: "Vue Language Server",
4302
4636
  extensions: [".vue"],
4303
4637
  root: RootWithFallback(IgnoreHomeRoot(createRootDetector([
@@ -4318,9 +4652,9 @@ var VueServer = {
4318
4652
  clientWaitTimeoutMs: 3e4,
4319
4653
  initializeTimeoutMs: 3e4,
4320
4654
  async spawn(root, options) {
4321
- const tsserverPath = await findTsserverPath(root, options?.allowInstall);
4322
- const hasPackageJson = existsSync3(path7.join(root, "package.json"));
4323
- const hasNodeModules = existsSync3(path7.join(root, "node_modules"));
4655
+ const tsserverPath = await findTsserverPath(root, options?.allowInstall, options);
4656
+ const hasPackageJson = existsSync3(path6.join(root, "package.json"));
4657
+ const hasNodeModules = existsSync3(path6.join(root, "node_modules"));
4324
4658
  if (hasPackageJson && !hasNodeModules) {
4325
4659
  logSessionStart(`lsp vue: node_modules missing in ${root} \u2014 Vue navigation may be limited. Run: npm install (or pnpm/yarn install) in this project.`);
4326
4660
  }
@@ -4335,13 +4669,15 @@ var VueServer = {
4335
4669
  return {
4336
4670
  process: proc.process,
4337
4671
  source: proc.source,
4338
- initialization: tsserverPath ? { typescript: { tsdk: path7.dirname(tsserverPath) } } : void 0
4672
+ initialization: tsserverPath ? { typescript: { tsdk: path6.dirname(tsserverPath) } } : void 0
4339
4673
  };
4340
4674
  }
4341
4675
  };
4342
4676
  var SvelteServer = {
4343
4677
  id: "svelte",
4678
+ executesProjectCode: true,
4344
4679
  idleEviction: "unmeasured",
4680
+ role: "language",
4345
4681
  name: "Svelte Language Server",
4346
4682
  extensions: [".svelte"],
4347
4683
  root: RootWithFallback(IgnoreHomeRoot(createRootDetector([
@@ -4360,7 +4696,7 @@ var SvelteServer = {
4360
4696
  clientWaitTimeoutMs: 2e4,
4361
4697
  initializeTimeoutMs: 2e4,
4362
4698
  async spawn(root, options) {
4363
- const tsserverPath = await findTsserverPath(root, options?.allowInstall);
4699
+ const tsserverPath = await findTsserverPath(root, options?.allowInstall, options);
4364
4700
  const proc = await resolveAndLaunch({
4365
4701
  candidates: [
4366
4702
  ...nodeBinCandidates(root, "svelteserver"),
@@ -4375,13 +4711,14 @@ var SvelteServer = {
4375
4711
  return {
4376
4712
  process: proc.process,
4377
4713
  source: proc.source,
4378
- initialization: tsserverPath ? { typescript: { tsdk: path7.dirname(tsserverPath) } } : void 0
4714
+ initialization: tsserverPath ? { typescript: { tsdk: path6.dirname(tsserverPath) } } : void 0
4379
4715
  };
4380
4716
  }
4381
4717
  };
4382
4718
  var CssServer = {
4383
4719
  id: "css",
4384
4720
  idleEviction: "transparent",
4721
+ role: "language",
4385
4722
  name: "CSS Language Server",
4386
4723
  extensions: KIND_EXTENSIONS["css"],
4387
4724
  root: RootWithFallback(IgnoreHomeRoot(PriorityRoot([
@@ -4439,8 +4776,8 @@ function opengrepInitialization(root) {
4439
4776
  var OpengrepServer = {
4440
4777
  id: "opengrep",
4441
4778
  idleEviction: "transparent",
4442
- name: "Opengrep Security Scanner",
4443
4779
  role: "auxiliary",
4780
+ name: "Opengrep Security Scanner",
4444
4781
  extensions: OPENGREP_EXTENSIONS,
4445
4782
  // Stable per-repo root so ONE warm server serves the whole project (a
4446
4783
  // per-directory root would spawn a fresh server — and re-pay rule load —
@@ -4489,8 +4826,8 @@ var AST_GREP_EXTENSIONS = Array.from(new Set(AST_GREP_KINDS.flatMap((k) => KIND_
4489
4826
  var AstGrepServer = {
4490
4827
  id: "ast-grep",
4491
4828
  idleEviction: "unmeasured",
4492
- name: "ast-grep structural linter",
4493
4829
  role: "auxiliary",
4830
+ name: "ast-grep structural linter",
4494
4831
  extensions: AST_GREP_EXTENSIONS,
4495
4832
  // Attaches everywhere (#239 Phase 2): prefer a project `sgconfig.y[a]ml` root,
4496
4833
  // else the repo root (.git) or cwd — like Opengrep. When there's no project
@@ -4506,6 +4843,13 @@ var AstGrepServer = {
4506
4843
  // the whole file on every didOpen, so it absorbs a sweep more slowly than the
4507
4844
  // other scanners — hold it to half the shared default.
4508
4845
  notifyInflightLimit: 4,
4846
+ // #1722, measured against the real binary over 30 repository files rather
4847
+ // than assumed: ast-grep-lsp is tower-lsp-server and drains its message
4848
+ // stream in order on one task, so the notify barrier's round-trip proves
4849
+ // the backlog was scanned. Declared here because the barrier's soundness
4850
+ // rests on it (#1756 stage 1); the evidence table is on
4851
+ // `LspReplyOrdering`.
4852
+ replyOrdering: "single-task-ordered",
4509
4853
  // First scan of a session compiles the rules.
4510
4854
  initializeTimeoutMs: 15e3,
4511
4855
  async spawn(root, options) {
@@ -4531,8 +4875,8 @@ var ZIZMOR_EXTENSIONS = KIND_EXTENSIONS["yaml"];
4531
4875
  var ZizmorServer = {
4532
4876
  id: "zizmor",
4533
4877
  idleEviction: "unmeasured",
4534
- name: "zizmor Actions Security Scanner",
4535
4878
  role: "auxiliary",
4879
+ name: "zizmor Actions Security Scanner",
4536
4880
  extensions: ZIZMOR_EXTENSIONS,
4537
4881
  pathFilter: isZizmorAuditTarget,
4538
4882
  // Stable per-repo root so ONE warm server serves the whole project (like
@@ -4579,8 +4923,8 @@ function typosInitialization(root) {
4579
4923
  var TyposServer = {
4580
4924
  id: "typos",
4581
4925
  idleEviction: "unmeasured",
4582
- name: "typos Spell Checker",
4583
4926
  role: "auxiliary",
4927
+ name: "typos Spell Checker",
4584
4928
  extensions: TYPOS_EXTENSIONS,
4585
4929
  // Stable per-repo root so ONE warm server serves the whole project (like the
4586
4930
  // other auxiliaries) — typos.toml discovery is repo-relative.
@@ -4618,6 +4962,7 @@ var LSP_SERVERS = [
4618
4962
  FSharpServer,
4619
4963
  JavaServer,
4620
4964
  KotlinServer,
4965
+ LeanServer,
4621
4966
  SwiftServer,
4622
4967
  DartServer,
4623
4968
  LuaServer,
@@ -4656,11 +5001,67 @@ var LSP_SERVERS = [
4656
5001
  ZizmorServer,
4657
5002
  TyposServer
4658
5003
  ];
5004
+ function getServerById(id) {
5005
+ return LSP_SERVERS.find((server) => server.id === id);
5006
+ }
5007
+
5008
+ // dist/clients/lsp/session-roots.js
5009
+ import path7 from "node:path";
5010
+ var SESSION_ROOT_CAP = 128;
5011
+ var sessionRoots = new BoundedFifoMap(SESSION_ROOT_CAP);
5012
+ function noteEvictedRoots(evicted) {
5013
+ const roots = evicted.map(([root]) => root);
5014
+ if (roots.length > 0) {
5015
+ incrementDegradationCount({
5016
+ kind: "lsp-session-root-evicted",
5017
+ subject: `cap=${SESSION_ROOT_CAP}`,
5018
+ reason: `session root registry at capacity; will load the dropped root again on the next session start or tool call naming it (dropped: ${roots[0]})`
5019
+ });
5020
+ }
5021
+ return roots;
5022
+ }
5023
+ function registerSessionRoot(cwd) {
5024
+ const root = path7.resolve(cwd);
5025
+ if (sessionRoots.has(root))
5026
+ return [];
5027
+ return noteEvictedRoots(sessionRoots.set(root, void 0));
5028
+ }
5029
+ function setSessionRootConfig(cwd, config) {
5030
+ return noteEvictedRoots(sessionRoots.set(path7.resolve(cwd), config));
5031
+ }
5032
+ function sessionRootConfigEntries() {
5033
+ return sessionRoots;
5034
+ }
5035
+ function isSessionRootRegistered(cwd) {
5036
+ return sessionRoots.has(path7.resolve(cwd));
5037
+ }
5038
+ function shouldInitializeSessionRoot(cwd, readyRoots) {
5039
+ const normalized = path7.resolve(cwd);
5040
+ return !readyRoots.has(normalized) || !isSessionRootRegistered(normalized);
5041
+ }
5042
+ function isOutsideAllSessionRoots(filePath) {
5043
+ if (sessionRoots.size === 0)
5044
+ return false;
5045
+ const resolved = path7.resolve(filePath);
5046
+ for (const root of sessionRoots.keys()) {
5047
+ if (isSameOrWithin(root, resolved))
5048
+ return false;
5049
+ }
5050
+ return true;
5051
+ }
5052
+ function getSessionRootsForTelemetry(limit = 4) {
5053
+ return [...sessionRoots.keys()].sort((a, b) => Number(a > b) - Number(a < b)).slice(0, limit);
5054
+ }
4659
5055
 
4660
5056
  export {
4661
5057
  findLocalTyposConfig,
4662
5058
  getAstGrepRuleSources,
5059
+ getAstGrepRuleFingerprint,
5060
+ isAuxiliary,
5061
+ serverTraits,
4663
5062
  registerRunnerId,
5063
+ withLspProjectCodePermission,
5064
+ refuseUntrustedLspExecution,
4664
5065
  BUILTIN_SERVER_RUNNER_COVERS,
4665
5066
  describeRootFallback,
4666
5067
  resolveLspServerCwd,
@@ -4673,6 +5074,7 @@ export {
4673
5074
  isDirectLspCommandTemporarilyUnavailable,
4674
5075
  resetClassicTsRepairGuard,
4675
5076
  LSP_SERVERS,
5077
+ getServerById,
4676
5078
  shouldInitializeSessionRoot,
4677
5079
  isOutsideAllSessionRoots,
4678
5080
  getSessionRootsForTelemetry,