pi-lens 4.4.1 → 4.4.3

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 (140) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/config/dependency-cruiser-eager-allowlist.json +3 -1
  3. package/dist/{chunk-BJF25SUG.js → chunk-23WOO7TX.js} +11 -10
  4. package/dist/{chunk-WATC3IQR.js → chunk-2B73ZPOK.js} +3 -3
  5. package/dist/{chunk-N2IRDBZI.js → chunk-2BBLFX75.js} +101 -11
  6. package/dist/{chunk-TA5L5KWQ.js → chunk-2CD5RS6M.js} +2 -2
  7. package/dist/{chunk-3TTPKMEF.js → chunk-2VUQMUMT.js} +38 -37
  8. package/dist/{chunk-5JHXUETW.js → chunk-3EFKIGZN.js} +3 -3
  9. package/dist/chunk-3JNKMP3B.js +17 -0
  10. package/dist/{chunk-6P54TMWL.js → chunk-3VPVG5SU.js} +6 -6
  11. package/dist/{chunk-ZETYB3XG.js → chunk-3W7IEVVL.js} +44 -28
  12. package/dist/chunk-3YN6V5TV.js +85 -0
  13. package/dist/chunk-466Z26MG.js +30 -0
  14. package/dist/{chunk-PCHDGUKT.js → chunk-4UNXBNE2.js} +12 -11
  15. package/dist/{chunk-J7WJZ6W7.js → chunk-4W4VLAPJ.js} +12 -11
  16. package/dist/{chunk-F5VLZHUW.js → chunk-52FUZI6K.js} +4 -2
  17. package/dist/{chunk-H5EV3VMF.js → chunk-5NGR67U5.js} +2 -2
  18. package/dist/{chunk-CBD2B5CP.js → chunk-5UCAH366.js} +21 -20
  19. package/dist/{chunk-BHWHXMQ2.js → chunk-67WMNWEE.js} +9 -8
  20. package/dist/{chunk-USHG4DLH.js → chunk-6C7JWPCP.js} +7 -7
  21. package/dist/{chunk-VPWKF3GN.js → chunk-6GFRNVYR.js} +11 -10
  22. package/dist/{chunk-3JL6IFD7.js → chunk-6SDEP3AJ.js} +9 -8
  23. package/dist/{chunk-NJF2X5XF.js → chunk-6STA6Z7I.js} +19 -17
  24. package/dist/{chunk-KJCWXUUS.js → chunk-724UL2TJ.js} +6 -4
  25. package/dist/{chunk-MQA2YBAH.js → chunk-76MWC2K6.js} +3 -3
  26. package/dist/{chunk-YLN4K6WD.js → chunk-77M37OKR.js} +10 -8
  27. package/dist/{chunk-XHSCVSOL.js → chunk-7DYBLXYA.js} +3 -3
  28. package/dist/{chunk-OC7LYAU5.js → chunk-AB3YYG6N.js} +24 -10
  29. package/dist/{chunk-LD7WFTRX.js → chunk-ACXEQZOU.js} +16 -16
  30. package/dist/{chunk-VSMJK3ZJ.js → chunk-AOHZU2BM.js} +1 -1
  31. package/dist/{chunk-T4QZ6FEH.js → chunk-AW7R2GWG.js} +65 -8
  32. package/dist/{chunk-GAYGGICG.js → chunk-BKE7E7TC.js} +10 -9
  33. package/dist/{chunk-EUT3B22Y.js → chunk-BRGDSBJE.js} +13 -12
  34. package/dist/{chunk-T54VJRAD.js → chunk-C34ZXZ7O.js} +55 -11
  35. package/dist/{chunk-UGSMCQ4D.js → chunk-CHOW74WE.js} +1 -1
  36. package/dist/{chunk-H2W2AXD3.js → chunk-CMMHNUZ2.js} +66 -58
  37. package/dist/chunk-CSHFH5IN.js +17 -0
  38. package/dist/chunk-EC47DWFA.js +56 -0
  39. package/dist/{chunk-KJN4IOEJ.js → chunk-EJIUXPBX.js} +1 -1
  40. package/dist/{chunk-IQZXOZ7S.js → chunk-EU63SDCG.js} +720 -334
  41. package/dist/{chunk-LNOMA3MP.js → chunk-FCSOVF55.js} +107 -3
  42. package/dist/{chunk-CFYISHUW.js → chunk-FLKWCIZH.js} +8 -5
  43. package/dist/{chunk-OYTMZXRF.js → chunk-FMSO6RDZ.js} +16 -14
  44. package/dist/{chunk-IUKPRZER.js → chunk-GAEGEPOC.js} +2 -2
  45. package/dist/{chunk-6XX5HCLK.js → chunk-GBPP5P7X.js} +4 -4
  46. package/dist/{chunk-TQFBZVCG.js → chunk-GXW6K4YJ.js} +1 -1
  47. package/dist/{chunk-ZZUFZUQZ.js → chunk-H6MF6EVN.js} +331 -107
  48. package/dist/{chunk-3JSEWUWI.js → chunk-IBGIOBEE.js} +3 -3
  49. package/dist/chunk-IZSHAIJ7.js +26 -0
  50. package/dist/{chunk-7ELY2CSM.js → chunk-JAU72XNY.js} +16 -14
  51. package/dist/chunk-JMYYOTQG.js +77 -0
  52. package/dist/{chunk-ELVWJYRV.js → chunk-JSQFTSC6.js} +12 -11
  53. package/dist/{chunk-J6DFZDFA.js → chunk-KBG47GHP.js} +4 -4
  54. package/dist/chunk-KDERNTCQ.js +28 -0
  55. package/dist/chunk-KSX3TXSZ.js +69 -0
  56. package/dist/{chunk-XGUL4SLQ.js → chunk-L3BLXZG7.js} +2 -2
  57. package/dist/chunk-LOP6GRPH.js +31 -0
  58. package/dist/{chunk-MTDRLSI7.js → chunk-M6HSLBWY.js} +1064 -986
  59. package/dist/chunk-MQLUAEOC.js +162 -0
  60. package/dist/{chunk-3MRXIJ6I.js → chunk-MRXKYPCS.js} +2 -2
  61. package/dist/{chunk-D65GOCJB.js → chunk-MXGY6KPT.js} +19 -16
  62. package/dist/{chunk-G3RLJYA2.js → chunk-N2H3ZPZY.js} +47 -1
  63. package/dist/{chunk-HREZQCT3.js → chunk-N3X6Q4YT.js} +3 -3
  64. package/dist/{chunk-HSIRMK2D.js → chunk-OCAGUNBC.js} +15 -15
  65. package/dist/{chunk-ZOEYVXSV.js → chunk-OXXKQCSV.js} +6 -6
  66. package/dist/{chunk-S6AMWKPC.js → chunk-P7266HLM.js} +2 -2
  67. package/dist/{chunk-64LU2OVH.js → chunk-PHDMFAKM.js} +12 -2
  68. package/dist/{chunk-IMSQR4WO.js → chunk-PNQDPDRC.js} +1 -1
  69. package/dist/{chunk-IUR76K6A.js → chunk-Q7DUK4LX.js} +5 -5
  70. package/dist/{chunk-CULQYFLU.js → chunk-QGQ5CCCG.js} +2 -2
  71. package/dist/{chunk-UEAHAMEI.js → chunk-QHJ6DQ2R.js} +11 -11
  72. package/dist/{chunk-BNKLYOOO.js → chunk-QILIYSJF.js} +1 -1
  73. package/dist/{chunk-VYKKMYVT.js → chunk-QOBUHWHX.js} +11 -6
  74. package/dist/{chunk-3PNFWVHT.js → chunk-QQNQ7SJW.js} +2 -75
  75. package/dist/{chunk-7M4T5N3V.js → chunk-R3PCY3YS.js} +14 -13
  76. package/dist/{chunk-MGIWSROG.js → chunk-RPLINGVB.js} +7 -7
  77. package/dist/{chunk-CMU4RMJB.js → chunk-RWWYJ623.js} +10 -1
  78. package/dist/{chunk-3FERWMCH.js → chunk-RXCOMYPY.js} +27 -26
  79. package/dist/{chunk-7BX5DWSA.js → chunk-SAEFPSB6.js} +11 -11
  80. package/dist/{chunk-N7YNJE5R.js → chunk-SQ4TVJBR.js} +10 -10
  81. package/dist/{chunk-DMS52BEP.js → chunk-SS36ZX4J.js} +9 -7
  82. package/dist/{chunk-JONGANYH.js → chunk-T6CPIZNT.js} +32 -30
  83. package/dist/{chunk-6UYA642J.js → chunk-TYM6OUIT.js} +13 -12
  84. package/dist/{chunk-ALQRYH6O.js → chunk-UXGETRP4.js} +13 -12
  85. package/dist/{chunk-FGALODG3.js → chunk-UZ3TUTRV.js} +1 -1
  86. package/dist/{chunk-J3FZR3YY.js → chunk-VCHXCQFG.js} +3 -3
  87. package/dist/{chunk-3P4PF2BX.js → chunk-VCO24T4L.js} +34 -22
  88. package/dist/{chunk-O7HZX7CA.js → chunk-VRZP76YM.js} +1 -1
  89. package/dist/{chunk-VZHJS6DR.js → chunk-VSSBEUFT.js} +16 -15
  90. package/dist/{chunk-U52SAGRM.js → chunk-VWCXW2E7.js} +1 -1
  91. package/dist/{chunk-WW5BLB74.js → chunk-WOA4LN2R.js} +2 -2
  92. package/dist/{chunk-ZMOMPX2Z.js → chunk-WU567RSC.js} +11 -10
  93. package/dist/{chunk-N77WFUL7.js → chunk-XAGLWMJ4.js} +23 -22
  94. package/dist/{chunk-WSEWKCXU.js → chunk-XCJN5IJU.js} +4 -4
  95. package/dist/{chunk-IR6XFA5P.js → chunk-XIJZVPD5.js} +16 -14
  96. package/dist/{chunk-OYOB3ZFG.js → chunk-XMSXXAHW.js} +2 -2
  97. package/dist/{chunk-34OCDWOS.js → chunk-XTMJ7XDT.js} +1 -1
  98. package/dist/{chunk-Z6BB4JLQ.js → chunk-XVF2VVXP.js} +5 -3
  99. package/dist/{chunk-AFZYMAIV.js → chunk-YIUAVF7E.js} +37 -35
  100. package/dist/{chunk-NFXP47RV.js → chunk-YSZNHOBY.js} +85 -31
  101. package/dist/{chunk-V5MEBST3.js → chunk-YYOIQFRS.js} +21 -21
  102. package/dist/chunk-Z527N6YQ.js +28 -0
  103. package/dist/{chunk-D6YF5HKX.js → chunk-ZANLILYD.js} +22 -2
  104. package/dist/{chunk-2UMYMRI3.js → chunk-ZGDGW2V3.js} +2 -2
  105. package/dist/{chunk-6B5I6ICN.js → chunk-ZHFZWTZV.js} +12 -11
  106. package/dist/{chunk-OEX24ECI.js → chunk-ZWFS3OEF.js} +51 -41
  107. package/dist/{chunk-AXKPQ6ZI.js → chunk-ZXYDQ3TZ.js} +5 -5
  108. package/dist/index.js +73783 -71033
  109. package/dist/mcp/analyze-cli.js +18 -8
  110. package/dist/mcp/cli.js +48 -37
  111. package/dist/mcp/server.js +568 -397
  112. package/dist/mcp/worker.js +67 -55
  113. package/dist/probes/installer.js +20 -19
  114. package/docs/astplayground.md +1 -1
  115. package/docs/configuration.md +29 -0
  116. package/docs/dependencies.md +3 -0
  117. package/docs/features.md +2 -2
  118. package/docs/globalconfig.md +2 -0
  119. package/docs/language-coverage.md +2 -1
  120. package/docs/lsp-capability-matrix.md +29 -7
  121. package/docs/lsp-idle-eviction.md +38 -47
  122. package/docs/pi-lens-fixer.md +4 -0
  123. package/docs/pi-lens-merge-policy.md +17 -4
  124. package/docs/pi-lens-retro.md +2 -1
  125. package/docs/pi-lens-reviewer.md +18 -0
  126. package/docs/pi-lens-subagent.md +7 -0
  127. package/docs/real-harness.md +17 -0
  128. package/docs/schema/pi-lens-config-v1.json +37 -2
  129. package/docs/servercapabilities.md +15 -4
  130. package/docs/settings.md +2 -1
  131. package/package.json +1 -1
  132. package/dist/chunk-2FQ62SPM.js +0 -83
  133. package/dist/chunk-7AXO3ZDF.js +0 -30
  134. package/dist/chunk-KMGPUKFD.js +0 -25
  135. package/dist/chunk-OZJINN4O.js +0 -84
  136. package/dist/chunk-RJFED73B.js +0 -79
  137. package/dist/chunk-RTQNRM5X.js +0 -27
  138. package/dist/chunk-VGEVL5EQ.js +0 -29
  139. package/dist/chunk-WPE5FPGR.js +0 -54
  140. package/dist/chunk-Z7QQA2KT.js +0 -27
@@ -1,18 +1,22 @@
1
1
  import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
2
+ import {
3
+ resolveOpengrepConfig
4
+ } from "./chunk-VWCXW2E7.js";
2
5
  import {
3
6
  augmentPythonEnvironment,
4
7
  detectPythonEnvironment,
5
8
  detectPythonVenv,
6
9
  pythonEnvironmentToolCandidates
7
- } from "./chunk-LNOMA3MP.js";
10
+ } from "./chunk-FCSOVF55.js";
8
11
  import {
9
- resolveOpengrepConfig
10
- } from "./chunk-U52SAGRM.js";
12
+ resolveGitCheckout,
13
+ resolveLinkedWorktreeOwner
14
+ } from "./chunk-PNQDPDRC.js";
11
15
  import {
12
16
  extensionsForLanguage,
13
17
  getUserRuleRoot,
14
18
  ruleCorpusFingerprintForCycle
15
- } from "./chunk-KJCWXUUS.js";
19
+ } from "./chunk-724UL2TJ.js";
16
20
  import {
17
21
  load,
18
22
  resolvePackagePath
@@ -22,11 +26,11 @@ import {
22
26
  readCargoWorkspaceExclude,
23
27
  readCargoWorkspaceMembers,
24
28
  readTextFileOrUndefined
25
- } from "./chunk-S6AMWKPC.js";
29
+ } from "./chunk-P7266HLM.js";
26
30
  import {
27
31
  HOOK_WALL_BUDGET_MS,
28
32
  bounded
29
- } from "./chunk-F5VLZHUW.js";
33
+ } from "./chunk-52FUZI6K.js";
30
34
  import {
31
35
  ensureTool,
32
36
  findManagedToolBinary,
@@ -36,20 +40,21 @@ import {
36
40
  isZizmorAuditTarget,
37
41
  logSessionStart,
38
42
  resolveZizmorGitHubToken
39
- } from "./chunk-LD7WFTRX.js";
43
+ } from "./chunk-ACXEQZOU.js";
40
44
  import {
41
45
  resolveToolCwd
42
- } from "./chunk-IUR76K6A.js";
46
+ } from "./chunk-Q7DUK4LX.js";
43
47
  import {
44
- findGlobalBinary
45
- } from "./chunk-T4QZ6FEH.js";
48
+ findGlobalBinary,
49
+ isProjectLocalBinPath
50
+ } from "./chunk-AW7R2GWG.js";
46
51
  import {
47
52
  classifyProbeFailure,
48
53
  createAvailabilityLatch,
49
54
  describeInstallAttempt,
50
55
  logAvailabilityDecision,
51
56
  startHostStallSampler
52
- } from "./chunk-O7HZX7CA.js";
57
+ } from "./chunk-VRZP76YM.js";
53
58
  import {
54
59
  OWNER_TAG_ENV,
55
60
  SpawnFailureError,
@@ -62,22 +67,27 @@ import {
62
67
  holdOwnChildPid,
63
68
  isCommandAvailableAsync,
64
69
  isPathIgnoredByProject,
70
+ isPiLensInternalPath,
65
71
  ownerTagForChildren,
72
+ resolveWindowsCommandForEnvironment,
66
73
  safeSpawnAsync
67
- } from "./chunk-T54VJRAD.js";
74
+ } from "./chunk-C34ZXZ7O.js";
68
75
  import {
69
76
  DOTNET_CSHARP_ROOT_MARKERS,
70
77
  DOTNET_FSHARP_ROOT_MARKERS,
71
- KIND_EXTENSIONS
72
- } from "./chunk-64LU2OVH.js";
78
+ KIND_EXTENSIONS,
79
+ LEAN_ROOT_MARKERS
80
+ } from "./chunk-PHDMFAKM.js";
73
81
  import {
82
+ isRepoTier,
74
83
  lspSectionOf,
75
84
  migrationSubject,
85
+ provenanceFor,
76
86
  reportConfigReadFailure,
77
87
  reportPiLensConfigRecords,
78
88
  resolvePiLensConfig,
79
89
  summarizeConfigResolution
80
- } from "./chunk-N2IRDBZI.js";
90
+ } from "./chunk-2BBLFX75.js";
81
91
  import {
82
92
  FRESHNESS_CADENCE_MS,
83
93
  LSP_NAMESPACE_KEY,
@@ -85,23 +95,30 @@ import {
85
95
  dirMtimesStillFreshAsync,
86
96
  getPiLensGlobalConfigPath,
87
97
  unknownDirMtimeRecords
88
- } from "./chunk-Z6BB4JLQ.js";
98
+ } from "./chunk-XVF2VVXP.js";
89
99
  import {
100
+ getProjectTrustState
101
+ } from "./chunk-ZANLILYD.js";
102
+ import {
103
+ getDegradationLedgerGeneration,
90
104
  incrementDegradationCount,
91
105
  recordDegradationOnce
92
- } from "./chunk-7ELY2CSM.js";
106
+ } from "./chunk-JAU72XNY.js";
107
+ import {
108
+ logExtension
109
+ } from "./chunk-XTMJ7XDT.js";
93
110
  import {
94
111
  claimPhaseOncePerSession,
95
112
  currentSessionRecordId,
96
113
  logLatency,
97
114
  releasePhaseClaim
98
- } from "./chunk-3PNFWVHT.js";
115
+ } from "./chunk-QQNQ7SJW.js";
99
116
  import {
100
117
  getGlobalPiLensLogDir,
101
118
  getProcessSingleton,
102
119
  isTestMode,
103
120
  redactSecrets
104
- } from "./chunk-VSMJK3ZJ.js";
121
+ } from "./chunk-AOHZU2BM.js";
105
122
  import {
106
123
  STAGE_TMP_PATTERN
107
124
  } from "./chunk-LJDODBBZ.js";
@@ -115,6 +132,8 @@ import {
115
132
  findLocalToolConfig,
116
133
  isAtOrAboveHomeDir,
117
134
  isFullyQualified,
135
+ isUnderDir,
136
+ isVendorPath,
118
137
  isWindowsPath,
119
138
  matchesWorkspaceMemberPattern,
120
139
  normalizeEphemeralMapKey,
@@ -122,56 +141,120 @@ import {
122
141
  normalizeMapKey,
123
142
  pathsEqual,
124
143
  toPosix
125
- } from "./chunk-G3RLJYA2.js";
144
+ } from "./chunk-N2H3ZPZY.js";
126
145
  import {
127
146
  __require
128
147
  } from "./chunk-NXL4FFQQ.js";
129
148
 
130
- // dist/clients/lsp/server-traits.js
131
- var ROLE_MEMBERS = {
132
- language: "language",
133
- auxiliary: "auxiliary"
134
- };
135
- var DEFAULT_LSP_SERVER_ROLE = "language";
136
- function isAuxiliary(bearer) {
137
- const declared = bearer?.role;
138
- const role = typeof declared === "string" && Object.hasOwn(ROLE_MEMBERS, declared) ? ROLE_MEMBERS[declared] : DEFAULT_LSP_SERVER_ROLE;
139
- switch (role) {
140
- case "auxiliary":
141
- return true;
142
- case "language":
149
+ // dist/clients/analysis-root.js
150
+ import * as fs from "node:fs";
151
+ import * as os from "node:os";
152
+ import * as nodePath from "node:path";
153
+ var PROJECT_MARKERS = [
154
+ ".git",
155
+ "package.json",
156
+ "pyproject.toml",
157
+ "Cargo.toml",
158
+ "go.mod",
159
+ "pom.xml",
160
+ "build.gradle",
161
+ "Gemfile",
162
+ "mix.exs",
163
+ "composer.json"
164
+ ];
165
+ function hasProjectMarker(dir) {
166
+ return PROJECT_MARKERS.some((marker) => {
167
+ try {
168
+ const stat2 = fs.statSync(nodePath.join(dir, marker));
169
+ return stat2.isFile() || marker === ".git";
170
+ } catch {
143
171
  return false;
172
+ }
173
+ });
174
+ }
175
+ function nearestProjectRoot(filePath) {
176
+ let dir = nodePath.dirname(nodePath.resolve(filePath));
177
+ const filesystemRoot = nodePath.parse(dir).root;
178
+ while (dir !== filesystemRoot) {
179
+ if (hasProjectMarker(dir))
180
+ return dir;
181
+ dir = nodePath.dirname(dir);
144
182
  }
183
+ return hasProjectMarker(filesystemRoot) ? filesystemRoot : void 0;
145
184
  }
146
- var DEFAULT_NOTIFY_INFLIGHT_LIMIT = 8;
147
- var DEFAULT_REPLY_ORDERING = "unmeasured";
148
- function serverTraits(declaration) {
149
- const declaredLimit = declaration.notifyInflightLimit;
150
- if (typeof declaredLimit === "number" && Number.isFinite(declaredLimit) && declaredLimit > 0) {
151
- return {
152
- role: declaration.role,
153
- notifyInflightLimit: Math.floor(declaredLimit),
154
- replyOrdering: declaration.replyOrdering ?? DEFAULT_REPLY_ORDERING
155
- };
185
+ function realPathOrResolved(filePath) {
186
+ const resolved = nodePath.resolve(filePath);
187
+ let current = resolved;
188
+ const missing = [];
189
+ while (true) {
190
+ try {
191
+ const real = fs.realpathSync.native(current);
192
+ return nodePath.join(real, ...missing.reverse());
193
+ } catch {
194
+ const parent = nodePath.dirname(current);
195
+ if (parent === current)
196
+ return resolved;
197
+ missing.push(nodePath.basename(current));
198
+ current = parent;
199
+ }
156
200
  }
157
- const envLimit = Number(process.env.PI_LENS_LSP_AUX_NOTIFY_INFLIGHT);
158
- return {
159
- role: declaration.role,
160
- notifyInflightLimit: Number.isFinite(envLimit) && envLimit > 0 ? Math.floor(envLimit) : DEFAULT_NOTIFY_INFLIGHT_LIMIT,
161
- replyOrdering: declaration.replyOrdering ?? DEFAULT_REPLY_ORDERING
162
- };
201
+ }
202
+ function resolveAnalysisRootPath(filePath, sessionRoot, homeDirOverride) {
203
+ const resolved = realPathOrResolved(filePath);
204
+ const session = realPathOrResolved(sessionRoot);
205
+ const tmp = realPathOrResolved(os.tmpdir());
206
+ const data = process.env.PI_LENS_HOME ? realPathOrResolved(process.env.PI_LENS_HOME) : void 0;
207
+ if (isVendorPath(resolved))
208
+ return void 0;
209
+ if (resolved === tmp || resolved === session || isUnderDir(session, resolved) || !isUnderDir(resolved, session) && (isPiLensInternalPath(resolved, session) || data !== void 0 && (resolved === data || isUnderDir(resolved, data))))
210
+ return void 0;
211
+ if (isUnderDir(resolved, session))
212
+ return session;
213
+ const checkout = resolveGitCheckout(session);
214
+ const linked = checkout && resolveLinkedWorktreeOwner(checkout, resolved);
215
+ if (linked && !isVendorPath(resolved))
216
+ return linked.root;
217
+ const candidate = nearestProjectRoot(resolved);
218
+ if (!candidate || candidate === nodePath.parse(candidate).root)
219
+ return void 0;
220
+ const home = realPathOrResolved(homeDirOverride ?? os.homedir());
221
+ if (isAtOrAboveHomeDir(candidate, home))
222
+ return void 0;
223
+ if (candidate === tmp || isUnderDir(session, candidate))
224
+ return void 0;
225
+ if (isVendorPath(candidate))
226
+ return void 0;
227
+ return candidate;
228
+ }
229
+ function resolveAnalysisRoot(filePath, sessionRoot, homeDirOverride) {
230
+ const resolved = realPathOrResolved(filePath);
231
+ if (isVendorPath(resolved))
232
+ return "none";
233
+ const checkout = resolveGitCheckout(sessionRoot);
234
+ if (checkout && resolveLinkedWorktreeOwner(checkout, resolved)) {
235
+ return "linked-worktree";
236
+ }
237
+ const root = resolveAnalysisRootPath(filePath, sessionRoot, homeDirOverride);
238
+ if (!root)
239
+ return "none";
240
+ if (root === nodePath.resolve(sessionRoot))
241
+ return "session";
242
+ return "adopted";
243
+ }
244
+ function canWriteAnalysisRoot(mode) {
245
+ return mode === "session" || mode === "linked-worktree";
163
246
  }
164
247
 
165
248
  // dist/clients/lsp/server.js
166
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, statSync as statSync3 } from "node:fs";
249
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, statSync as statSync4 } from "node:fs";
167
250
  import { access, readFile, readdir, stat } from "node:fs/promises";
168
- import os6 from "node:os";
169
- import path7 from "node:path";
251
+ import os7 from "node:os";
252
+ import path6 from "node:path";
170
253
 
171
254
  // dist/clients/sgconfig.js
172
255
  import { createHash } from "node:crypto";
173
- import * as fs from "node:fs";
174
- import * as os from "node:os";
256
+ import * as fs2 from "node:fs";
257
+ import * as os2 from "node:os";
175
258
  import * as path from "node:path";
176
259
  var SGCONFIG_NAMES = ["sgconfig.yml", "sgconfig.yaml"];
177
260
  function findLocalSgconfig(startDir) {
@@ -179,7 +262,7 @@ function findLocalSgconfig(startDir) {
179
262
  }
180
263
  function canonicalDir(dir) {
181
264
  try {
182
- return fs.realpathSync.native(dir);
265
+ return fs2.realpathSync.native(dir);
183
266
  } catch {
184
267
  return path.resolve(dir);
185
268
  }
@@ -222,7 +305,7 @@ function getAstGrepRuleSources(projectRoot = process.cwd()) {
222
305
  });
223
306
  const seen = /* @__PURE__ */ new Set();
224
307
  return candidates.filter((source) => {
225
- if (!fs.existsSync(source.dir))
308
+ if (!fs2.existsSync(source.dir))
226
309
  return false;
227
310
  const key = canonicalDir(source.dir);
228
311
  if (seen.has(key))
@@ -235,7 +318,7 @@ function findYamlFiles(dir) {
235
318
  const files = [];
236
319
  let entries;
237
320
  try {
238
- entries = fs.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
321
+ entries = fs2.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
239
322
  } catch {
240
323
  return files;
241
324
  }
@@ -287,7 +370,7 @@ function snapshotRuleSource(source) {
287
370
  hash.update("\0");
288
371
  let content;
289
372
  try {
290
- content = fs.readFileSync(file, "utf8");
373
+ content = fs2.readFileSync(file, "utf8");
291
374
  } catch {
292
375
  hash.update("missing");
293
376
  hash.update("\0");
@@ -329,8 +412,8 @@ function getAstGrepRuleFingerprint(projectRoot = process.cwd(), options = {}) {
329
412
  return ruleCorpusFingerprintForCycle("ast-grep", root, (fingerprintRoot) => sourceFingerprint(getAstGrepRuleSources(fingerprintRoot).map(snapshotRuleSource)), options.force);
330
413
  }
331
414
  function materializeMergedRuleDir(sources, mergedDir) {
332
- fs.rmSync(mergedDir, { recursive: true, force: true });
333
- fs.mkdirSync(mergedDir, { recursive: true });
415
+ fs2.rmSync(mergedDir, { recursive: true, force: true });
416
+ fs2.mkdirSync(mergedDir, { recursive: true });
334
417
  const claimedByEarlierSource = /* @__PURE__ */ new Set();
335
418
  for (const [sourceIndex, source] of sources.entries()) {
336
419
  const claimedThisSource = /* @__PURE__ */ new Set();
@@ -347,9 +430,9 @@ function materializeMergedRuleDir(sources, mergedDir) {
347
430
  continue;
348
431
  const destination = path.join(mergedDir, `source${sourceIndex}-${String(fileIndex).padStart(5, "0")}.yml`);
349
432
  if (survivingDocuments.length === file.documents.length) {
350
- fs.copyFileSync(file.file, destination);
433
+ fs2.copyFileSync(file.file, destination);
351
434
  } else {
352
- fs.writeFileSync(destination, `${survivingDocuments.map((document) => document.raw.trim()).join("\n---\n")}
435
+ fs2.writeFileSync(destination, `${survivingDocuments.map((document) => document.raw.trim()).join("\n---\n")}
353
436
  `);
354
437
  }
355
438
  }
@@ -365,14 +448,14 @@ function resolveBaselineSgconfig(projectRoot = process.cwd()) {
365
448
  const root = canonicalDir(path.resolve(projectRoot || process.cwd()));
366
449
  const fingerprint = getAstGrepRuleFingerprint(root, { force: true });
367
450
  const cached = cachedBaselines.get(root);
368
- if (cached?.fingerprint === fingerprint && fs.existsSync(cached.path) && fs.existsSync(cached.mergedDir)) {
451
+ if (cached?.fingerprint === fingerprint && fs2.existsSync(cached.path) && fs2.existsSync(cached.mergedDir)) {
369
452
  return cached.path;
370
453
  }
371
454
  const sources = getAstGrepRuleSources(root).map(snapshotRuleSource);
372
455
  if (sources.length === 0)
373
456
  return void 0;
374
- const dir = path.join(os.tmpdir(), "pi-lens-ast-grep");
375
- fs.mkdirSync(dir, { recursive: true });
457
+ const dir = path.join(os2.tmpdir(), "pi-lens-ast-grep");
458
+ fs2.mkdirSync(dir, { recursive: true });
376
459
  const artifactKey = rootArtifactKey(root);
377
460
  const file = path.join(dir, `baseline-${process.pid}-${artifactKey}.sgconfig.yml`);
378
461
  const mergedDir = path.join(dir, `baseline-${process.pid}-${artifactKey}.rules`);
@@ -384,7 +467,7 @@ function resolveBaselineSgconfig(projectRoot = process.cwd()) {
384
467
  cleanupStaleBaselines(dir, protectedArtifacts);
385
468
  materializeMergedRuleDir(sources, mergedDir);
386
469
  const ruleDirForYaml = mergedDir.split(path.sep).join("/");
387
- fs.writeFileSync(file, `ruleDirs:
470
+ fs2.writeFileSync(file, `ruleDirs:
388
471
  - ${JSON.stringify(ruleDirForYaml)}
389
472
  `);
390
473
  cachedBaselines.set(root, { fingerprint, path: file, mergedDir });
@@ -395,7 +478,7 @@ var SG_BASELINE_RETAINED_ENTRIES = 24;
395
478
  function enforceBaselineEntryCap(dir, keep) {
396
479
  let names;
397
480
  try {
398
- names = fs.readdirSync(dir);
481
+ names = fs2.readdirSync(dir);
399
482
  } catch {
400
483
  return;
401
484
  }
@@ -411,7 +494,7 @@ function enforceBaselineEntryCap(dir, keep) {
411
494
  try {
412
495
  candidates.push({
413
496
  full,
414
- mtimeMs: fs.statSync(full).mtimeMs,
497
+ mtimeMs: fs2.statSync(full).mtimeMs,
415
498
  recursive: isMergedDir
416
499
  });
417
500
  } catch {
@@ -424,7 +507,7 @@ function enforceBaselineEntryCap(dir, keep) {
424
507
  let evicted = 0;
425
508
  for (const candidate of candidates.slice(0, over)) {
426
509
  try {
427
- fs.rmSync(candidate.full, {
510
+ fs2.rmSync(candidate.full, {
428
511
  recursive: candidate.recursive,
429
512
  force: true
430
513
  });
@@ -448,7 +531,7 @@ function enforceBaselineEntryCap(dir, keep) {
448
531
  function cleanupStaleBaselines(dir, keep) {
449
532
  try {
450
533
  const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
451
- for (const name of fs.readdirSync(dir)) {
534
+ for (const name of fs2.readdirSync(dir)) {
452
535
  const isConfig = /^baseline(?:-\d+(?:-[a-f0-9]+)?)?\.sgconfig\.yml$/.test(name);
453
536
  const isMergedDir = /^baseline-\d+(?:-[a-f0-9]+)?\.rules$/.test(name);
454
537
  if (!isConfig && !isMergedDir)
@@ -457,9 +540,9 @@ function cleanupStaleBaselines(dir, keep) {
457
540
  if (keep.has(full))
458
541
  continue;
459
542
  try {
460
- const stat2 = fs.statSync(full);
543
+ const stat2 = fs2.statSync(full);
461
544
  if (stat2.mtimeMs < cutoff) {
462
- fs.rmSync(full, { recursive: stat2.isDirectory(), force: true });
545
+ fs2.rmSync(full, { recursive: stat2.isDirectory(), force: true });
463
546
  }
464
547
  } catch {
465
548
  }
@@ -478,9 +561,44 @@ function findLocalTyposConfig(startDir) {
478
561
  return findLocalToolConfig(startDir, LOCAL_TYPOS_CONFIG_NAMES);
479
562
  }
480
563
 
564
+ // dist/clients/lsp/server-traits.js
565
+ var ROLE_MEMBERS = {
566
+ language: "language",
567
+ auxiliary: "auxiliary"
568
+ };
569
+ var DEFAULT_LSP_SERVER_ROLE = "language";
570
+ function isAuxiliary(bearer) {
571
+ const declared = bearer?.role;
572
+ const role = typeof declared === "string" && Object.hasOwn(ROLE_MEMBERS, declared) ? ROLE_MEMBERS[declared] : DEFAULT_LSP_SERVER_ROLE;
573
+ switch (role) {
574
+ case "auxiliary":
575
+ return true;
576
+ case "language":
577
+ return false;
578
+ }
579
+ }
580
+ var DEFAULT_NOTIFY_INFLIGHT_LIMIT = 8;
581
+ var DEFAULT_REPLY_ORDERING = "unmeasured";
582
+ function serverTraits(declaration) {
583
+ const declaredLimit = declaration.notifyInflightLimit;
584
+ if (typeof declaredLimit === "number" && Number.isFinite(declaredLimit) && declaredLimit > 0) {
585
+ return {
586
+ role: declaration.role,
587
+ notifyInflightLimit: Math.floor(declaredLimit),
588
+ replyOrdering: declaration.replyOrdering ?? DEFAULT_REPLY_ORDERING
589
+ };
590
+ }
591
+ const envLimit = Number(process.env.PI_LENS_LSP_AUX_NOTIFY_INFLIGHT);
592
+ return {
593
+ role: declaration.role,
594
+ notifyInflightLimit: Number.isFinite(envLimit) && envLimit > 0 ? Math.floor(envLimit) : DEFAULT_NOTIFY_INFLIGHT_LIMIT,
595
+ replyOrdering: declaration.replyOrdering ?? DEFAULT_REPLY_ORDERING
596
+ };
597
+ }
598
+
481
599
  // dist/clients/lsp/config.js
482
- import * as os3 from "node:os";
483
- import path4 from "node:path";
600
+ import * as os4 from "node:os";
601
+ import path3 from "node:path";
484
602
 
485
603
  // dist/clients/dispatch/known-runner-ids.js
486
604
  var knownRunnerIds = /* @__PURE__ */ new Set();
@@ -495,13 +613,14 @@ function isKnownRunnerId(id) {
495
613
  }
496
614
 
497
615
  // dist/clients/lsp/launch.js
616
+ import { AsyncLocalStorage } from "node:async_hooks";
498
617
  import { execFileSync, spawn as nodeSpawn } from "node:child_process";
499
- import fs3 from "node:fs";
500
- import os2 from "node:os";
618
+ import fs4 from "node:fs";
619
+ import os3 from "node:os";
501
620
  import path2 from "node:path";
502
621
 
503
622
  // dist/clients/lsp/ruby-drive-dirs.js
504
- import * as fs2 from "node:fs";
623
+ import * as fs3 from "node:fs";
505
624
  var RUBY_VERSION_DIR = /^ruby\d/i;
506
625
  var rubyDirNamesCache = new BoundedLruCache(16);
507
626
  function rubyDriveKey(driveRoot) {
@@ -517,7 +636,7 @@ function getRubyVersionDirNamesSync(driveRoot) {
517
636
  return cached;
518
637
  let names;
519
638
  try {
520
- names = filterRubyDirNames(fs2.readdirSync(driveRoot));
639
+ names = filterRubyDirNames(fs3.readdirSync(driveRoot));
521
640
  } catch {
522
641
  names = [];
523
642
  }
@@ -531,7 +650,7 @@ async function getRubyVersionDirNamesAsync(driveRoot) {
531
650
  return cached;
532
651
  let names;
533
652
  try {
534
- names = filterRubyDirNames(await fs2.promises.readdir(driveRoot));
653
+ names = filterRubyDirNames(await fs3.promises.readdir(driveRoot));
535
654
  } catch {
536
655
  names = [];
537
656
  }
@@ -558,8 +677,8 @@ function logSessionStart2(msg) {
558
677
  const line = redactSecrets(`[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
559
678
  `);
560
679
  try {
561
- fs3.mkdirSync(SESSIONSTART_LOG_DIR, { recursive: true });
562
- fs3.appendFileSync(SESSIONSTART_LOG, line);
680
+ fs4.mkdirSync(SESSIONSTART_LOG_DIR, { recursive: true });
681
+ fs4.appendFileSync(SESSIONSTART_LOG, line);
563
682
  } catch {
564
683
  }
565
684
  }
@@ -640,7 +759,7 @@ async function buildAugmentedPath(basePath) {
640
759
  candidates.push(nodeDir);
641
760
  }
642
761
  if (isWindows) {
643
- const home = os2.homedir();
762
+ const home = os3.homedir();
644
763
  const driveRoot = path2.parse(home).root;
645
764
  candidates.push(path2.join(home, ".cargo", "bin"));
646
765
  candidates.push(path2.join(home, "go", "bin"));
@@ -662,7 +781,7 @@ async function buildAugmentedPath(basePath) {
662
781
  }
663
782
  const toAppend = [];
664
783
  for (const candidate of candidates) {
665
- if (!candidate || !fs3.existsSync(candidate))
784
+ if (!candidate || !fs4.existsSync(candidate))
666
785
  continue;
667
786
  const normalized = normalizePathEntry(candidate, process.platform);
668
787
  if (existing.has(normalized))
@@ -678,29 +797,39 @@ async function buildAugmentedPath(basePath) {
678
797
  }
679
798
  function isCmdShimValid(cmdPath) {
680
799
  try {
681
- const content = fs3.readFileSync(cmdPath, "utf-8");
800
+ const content = fs4.readFileSync(cmdPath, "utf-8");
682
801
  const match = content.match(/"%~dp0[/\\]\.\.[/\\]((?:[\w./@-]|\\)+\.(?:mjs|cjs|js))"/i);
683
802
  if (!match)
684
803
  return true;
685
804
  const relPath = match[1].replace(/[/\\]/g, path2.sep);
686
805
  const target = path2.resolve(path2.dirname(cmdPath), "..", relPath);
687
- return fs3.existsSync(target);
806
+ return fs4.existsSync(target);
688
807
  } catch {
689
808
  return true;
690
809
  }
691
810
  }
811
+ function isMissingWindowsStartupCommand(command, spawnCommand, cwd, env) {
812
+ if (!isWindows)
813
+ return false;
814
+ if (/\.(cmd|bat)$/i.test(spawnCommand) && !isCmdShimValid(spawnCommand)) {
815
+ return true;
816
+ }
817
+ if (spawnCommand !== command)
818
+ return false;
819
+ return resolveWindowsCommandForEnvironment(command, cwd, env) === null;
820
+ }
692
821
  function bypassPs1OnWindows(ps1Path, args) {
693
822
  const cmdPath = `${ps1Path.slice(0, -4)}.cmd`;
694
- if (fs3.existsSync(cmdPath)) {
823
+ if (fs4.existsSync(cmdPath)) {
695
824
  return { command: cmdPath, args, needsShell: true };
696
825
  }
697
826
  try {
698
- const content = fs3.readFileSync(ps1Path, "utf-8");
827
+ const content = fs4.readFileSync(ps1Path, "utf-8");
699
828
  const match = content.match(/"\$basedir[/\\]\.\.[/\\]((?:[\w./@-]|\\)+\.(?:mjs|cjs|js))"/i);
700
829
  if (match) {
701
830
  const relPath = match[1].replace(/[/\\]/g, path2.sep);
702
831
  const jsPath = path2.resolve(path2.dirname(ps1Path), "..", relPath);
703
- if (fs3.existsSync(jsPath)) {
832
+ if (fs4.existsSync(jsPath)) {
704
833
  return {
705
834
  command: process.execPath,
706
835
  args: [jsPath, ...args],
@@ -728,7 +857,7 @@ function findBinaryOnPath(command, env) {
728
857
  timeout: 5e3
729
858
  }).split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
730
859
  for (const candidate of result) {
731
- if (fs3.existsSync(candidate)) {
860
+ if (fs4.existsSync(candidate)) {
732
861
  return candidate;
733
862
  }
734
863
  }
@@ -736,6 +865,65 @@ function findBinaryOnPath(command, env) {
736
865
  }
737
866
  return void 0;
738
867
  }
868
+ var lspProjectCodePermission = new AsyncLocalStorage();
869
+ function withLspProjectCodePermission(allowed, spawn) {
870
+ return lspProjectCodePermission.run(allowed, spawn);
871
+ }
872
+ var unknownProjectLocalNoticeGeneration = -1;
873
+ function recordUntrustedLspExecution(request, trust) {
874
+ const generation = getDegradationLedgerGeneration();
875
+ 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)}`;
876
+ const metadata = request.kind === "project-code-server" ? {
877
+ field: request.kind,
878
+ serverId: request.serverId,
879
+ root: request.root,
880
+ trust
881
+ } : {
882
+ field: request.kind,
883
+ trust
884
+ };
885
+ recordDegradationOnce({
886
+ kind: "lsp-registry-decision",
887
+ subject,
888
+ reason: (request.kind === "project-local-binary" ? request.reason : void 0) ?? `LSP execution refused: ${request.kind} and project trust is ${trust}`,
889
+ metadata
890
+ });
891
+ if (unknownProjectLocalNoticeGeneration !== generation) {
892
+ unknownProjectLocalNoticeGeneration = generation;
893
+ const label = request.kind === "project-code-server" ? "project-code LSP server" : "project-local LSP binary";
894
+ logExtension({
895
+ subsystem: "lsp-registry",
896
+ level: "warn",
897
+ message: `${label} refused: mark the project trusted in pi or upgrade pi`,
898
+ metadata: { field: request.kind }
899
+ });
900
+ }
901
+ }
902
+ function refuseUntrustedLspExecution(request) {
903
+ const trust = getProjectTrustState();
904
+ const needsTrust = request.kind === "project-code-server" ? trust !== "trusted" : !admitProjectSuppliedTsserver(request.resolvedCommand, request.cwd, true);
905
+ if (!needsTrust)
906
+ return;
907
+ if (request.kind === "project-code-server")
908
+ recordUntrustedLspExecution(request, trust);
909
+ 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}`));
910
+ }
911
+ function admitProjectSuppliedTsserver(tsserverPath, cwd, projectSupplied, projectCodeAllowed = lspProjectCodePermission.getStore() ?? false) {
912
+ const trust = getProjectTrustState();
913
+ if (tsserverPath && (!projectSupplied || projectCodeAllowed && trust === "trusted"))
914
+ return tsserverPath;
915
+ recordUntrustedLspExecution({
916
+ kind: "project-local-binary",
917
+ command: tsserverPath ?? "tsserver.js",
918
+ resolvedCommand: tsserverPath ?? "tsserver.js",
919
+ cwd,
920
+ reason: tsserverPath ? "LSP execution refused: project compiler requires trusted session ownership" : "LSP execution refused: no admitted absolute TypeScript compiler"
921
+ }, trust);
922
+ return void 0;
923
+ }
924
+ function isProjectLocalLspBinary(resolvedCommand, cwd) {
925
+ return isProjectLocalBinPath(resolvedCommand, cwd);
926
+ }
739
927
  function trySpawn(command, args, cwd, env, needsShell) {
740
928
  let proc;
741
929
  if (needsShell) {
@@ -764,9 +952,6 @@ function trySpawn(command, args, cwd, env, needsShell) {
764
952
  if (!proc.stdin || !proc.stdout || !proc.stderr) {
765
953
  throw new Error(`Failed to spawn LSP server: ${command}`);
766
954
  }
767
- if (proc.exitCode !== null || proc.killed) {
768
- throw new Error(`LSP server ${command} exited immediately (code: ${proc.exitCode}). The binary may be missing or corrupted.`);
769
- }
770
955
  holdOwnChildPid(proc.pid, "lsp-spawn");
771
956
  return proc;
772
957
  }
@@ -842,6 +1027,14 @@ async function launchLSP(command, args = [], options = {}) {
842
1027
  needsShell = computeNeedsShell(spawnCommand);
843
1028
  }
844
1029
  }
1030
+ if (isProjectLocalLspBinary(spawnCommand, cwd)) {
1031
+ refuseUntrustedLspExecution({
1032
+ kind: "project-local-binary",
1033
+ command,
1034
+ resolvedCommand: spawnCommand,
1035
+ cwd
1036
+ });
1037
+ }
845
1038
  if (isWindows && /\.(cmd|bat)$/i.test(spawnCommand) && !isCmdShimValid(spawnCommand)) {
846
1039
  logSessionStart2(`lsp cmd-shim-invalid: ${spawnCommand} target missing \u2014 skipping candidate`);
847
1040
  const cause = Object.assign(new Error(`LSP .cmd shim target not found: ${spawnCommand}`), { code: "ENOENT" });
@@ -878,7 +1071,12 @@ async function launchLSP(command, args = [], options = {}) {
878
1071
  throw new Error(`Failed to spawn LSP server: ${command}`);
879
1072
  }
880
1073
  if (proc.exitCode !== null || proc.killed) {
881
- throw new Error(`LSP server ${command} exited immediately (code: ${proc.exitCode}). The binary may be missing or corrupted.`);
1074
+ const message = `LSP server ${command} exited immediately (code: ${proc.exitCode}). The binary may be missing or corrupted.`;
1075
+ const failure = new Error(message);
1076
+ if (proc.exitCode === 1 && isMissingWindowsStartupCommand(command, spawnCommand, cwd, env)) {
1077
+ throw new SpawnFailureError("tool-not-found", message, failure);
1078
+ }
1079
+ throw failure;
882
1080
  }
883
1081
  logSessionStart2(`lsp launch: command=${command} resolved=${spawnCommand} args=${JSON.stringify(args)} cwd=${cwd} shell=${needsShell ? "true" : "false"} pid=${proc.pid ?? 0}`);
884
1082
  const formatStartupStderr = (stderr) => {
@@ -895,7 +1093,7 @@ async function launchLSP(command, args = [], options = {}) {
895
1093
  };
896
1094
  proc.stderr?.on("data", onStartupStderr);
897
1095
  try {
898
- await new Promise((resolve3, reject) => {
1096
+ await new Promise((resolve4, reject) => {
899
1097
  let settled = false;
900
1098
  proc.on("error", (err) => {
901
1099
  if (!settled) {
@@ -911,7 +1109,13 @@ async function launchLSP(command, args = [], options = {}) {
911
1109
  settled = true;
912
1110
  const isWindowsCmd = isWindows && command.endsWith(".cmd");
913
1111
  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.`;
914
- reject(new Error(`LSP server ${command} exited immediately with code ${code}. ${errorMsg}${formatStartupStderr(startupStderr)}`));
1112
+ const message = `LSP server ${command} exited immediately with code ${code}. ${errorMsg}${formatStartupStderr(startupStderr)}`;
1113
+ const failure = new Error(message);
1114
+ if (code === 1 && isMissingWindowsStartupCommand(command, spawnCommand, cwd, env)) {
1115
+ reject(new SpawnFailureError("tool-not-found", message, failure));
1116
+ return;
1117
+ }
1118
+ reject(failure);
915
1119
  }
916
1120
  });
917
1121
  const startupFailureWindowMs = (() => {
@@ -926,7 +1130,7 @@ async function launchLSP(command, args = [], options = {}) {
926
1130
  setTimeout(() => {
927
1131
  if (!settled) {
928
1132
  settled = true;
929
- resolve3();
1133
+ resolve4();
930
1134
  }
931
1135
  }, startupFailureWindowMs);
932
1136
  });
@@ -951,61 +1155,13 @@ async function launchLSP(command, args = [], options = {}) {
951
1155
  };
952
1156
  }
953
1157
 
954
- // dist/clients/lsp/session-roots.js
955
- import path3 from "node:path";
956
- var SESSION_ROOT_CAP = 128;
957
- var sessionRoots = new BoundedFifoMap(SESSION_ROOT_CAP);
958
- function noteEvictedRoots(evicted) {
959
- const roots = evicted.map(([root]) => root);
960
- if (roots.length > 0) {
961
- incrementDegradationCount({
962
- kind: "lsp-session-root-evicted",
963
- subject: `cap=${SESSION_ROOT_CAP}`,
964
- 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]})`
965
- });
966
- }
967
- return roots;
968
- }
969
- function registerSessionRoot(cwd) {
970
- const root = path3.resolve(cwd);
971
- if (sessionRoots.has(root))
972
- return [];
973
- return noteEvictedRoots(sessionRoots.set(root, void 0));
974
- }
975
- function setSessionRootConfig(cwd, config) {
976
- return noteEvictedRoots(sessionRoots.set(path3.resolve(cwd), config));
977
- }
978
- function sessionRootConfigEntries() {
979
- return sessionRoots;
980
- }
981
- function isSessionRootRegistered(cwd) {
982
- return sessionRoots.has(path3.resolve(cwd));
983
- }
984
- function shouldInitializeSessionRoot(cwd, readyRoots) {
985
- const normalized = path3.resolve(cwd);
986
- return !readyRoots.has(normalized) || !isSessionRootRegistered(normalized);
987
- }
988
- function isOutsideAllSessionRoots(filePath) {
989
- if (sessionRoots.size === 0)
990
- return false;
991
- const resolved = path3.resolve(filePath);
992
- for (const root of sessionRoots.keys()) {
993
- if (isSameOrWithin(root, resolved))
994
- return false;
995
- }
996
- return true;
997
- }
998
- function getSessionRootsForTelemetry(limit = 4) {
999
- return [...sessionRoots.keys()].sort((a, b) => Number(a > b) - Number(a < b)).slice(0, limit);
1000
- }
1001
-
1002
1158
  // dist/clients/lsp/config.js
1003
1159
  function asRecord(value) {
1004
1160
  return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
1005
1161
  }
1006
1162
  var CONFIG_RESOLVED_PHASE = "config_resolved";
1007
1163
  function configResolutionKey(cwd) {
1008
- return normalizeFilePath(path4.resolve(normalizeFilePath(cwd)));
1164
+ return normalizeFilePath(path3.resolve(normalizeFilePath(cwd)));
1009
1165
  }
1010
1166
  function publishConfigResolutionPending(cwd) {
1011
1167
  logSessionStart(`session_start config_resolution_pending session=${currentSessionRecordId()} root=${configResolutionKey(cwd)}`);
@@ -1035,7 +1191,7 @@ function recordConfigResolved(cwd, resolution, lspConfig, homeDir, resolveMs) {
1035
1191
  });
1036
1192
  logSessionStart(`config resolved documents=${summary.documents.length} legacy=${legacyDocuments} records=${summary.recordCount} deniedServers=${deniedServers} resolveMs=${resolveMs} session=${sessionId} root=${root}`);
1037
1193
  }
1038
- async function loadLSPConfig(cwd, homeDir = os3.homedir(), options = {}) {
1194
+ async function loadLSPConfig(cwd, homeDir = os4.homedir(), options = {}) {
1039
1195
  const reporting = options.report !== false;
1040
1196
  publishConfigResolutionPending(cwd);
1041
1197
  const resolveStartedAt = Date.now();
@@ -1061,6 +1217,10 @@ async function loadLSPConfig(cwd, homeDir = os3.homedir(), options = {}) {
1061
1217
  });
1062
1218
  const coversProblems = newCoversClaimProblems();
1063
1219
  const config = lspConfigOf(resolution.value, coversProblems);
1220
+ Object.defineProperty(config, "__provenance", {
1221
+ value: resolution.provenance,
1222
+ enumerable: false
1223
+ });
1064
1224
  if (reporting) {
1065
1225
  reportPiLensConfigRecords([
1066
1226
  ...resolution.records,
@@ -1158,7 +1318,7 @@ function customServerSpecsOf(value) {
1158
1318
  const servers = asRecord(lspSectionOf(value).servers);
1159
1319
  return servers ? coversValidatedServers(servers) : {};
1160
1320
  }
1161
- function lspConfigOf(value, problems) {
1321
+ function lspConfigOf(value, problems, provenance) {
1162
1322
  const section = lspSectionOf(value);
1163
1323
  const config = {};
1164
1324
  const servers = asRecord(section.servers);
@@ -1174,6 +1334,12 @@ function lspConfigOf(value, problems) {
1174
1334
  if (Array.isArray(section.warmFiles)) {
1175
1335
  config.warmFiles = section.warmFiles;
1176
1336
  }
1337
+ if (provenance) {
1338
+ Object.defineProperty(config, "__provenance", {
1339
+ value: provenance,
1340
+ enumerable: false
1341
+ });
1342
+ }
1177
1343
  return config;
1178
1344
  }
1179
1345
  function createCustomServer(config, id) {
@@ -1209,26 +1375,74 @@ function createCustomServer(config, id) {
1209
1375
  cwd: root,
1210
1376
  env: config.env ? { ...process.env, ...config.env } : process.env
1211
1377
  });
1212
- return { process: proc };
1378
+ return {
1379
+ process: proc,
1380
+ ...config.initializationOptions ? { initialization: config.initializationOptions } : {}
1381
+ };
1213
1382
  }
1214
1383
  };
1215
1384
  }
1216
1385
  var EMPTY_CONFIG = {
1217
1386
  customServers: [],
1387
+ // Avoid reading LSP_SERVERS during the config/server module cycle. The
1388
+ // selection helper falls back to the live builtin table after evaluation.
1389
+ registeredServers: [],
1218
1390
  disabledServerIds: /* @__PURE__ */ new Set(),
1219
1391
  serverOverrides: /* @__PURE__ */ new Map()
1220
1392
  };
1221
1393
  var configInFlight = /* @__PURE__ */ new Map();
1222
1394
  function normalizeWorkspacePath(cwd) {
1223
- return path4.resolve(cwd);
1395
+ return path3.resolve(cwd);
1396
+ }
1397
+ var unknownTrustNoticeGeneration = -1;
1398
+ function sourceFor(provenance, key) {
1399
+ const direct = provenanceFor({ value: void 0, provenance }, key);
1400
+ if (direct)
1401
+ return direct;
1402
+ let fallback;
1403
+ for (const [candidate, entry] of provenance) {
1404
+ if (!candidate.startsWith(`${key}/`))
1405
+ continue;
1406
+ if (isRepoTier(entry.tier))
1407
+ return entry;
1408
+ fallback ??= entry;
1409
+ }
1410
+ return fallback;
1411
+ }
1412
+ function executableAllowed(provenance, trust) {
1413
+ return !provenance || !isRepoTier(provenance.tier) || trust === "trusted";
1414
+ }
1415
+ function registryDecision(id, field, provenance, trust, allowed) {
1416
+ if (allowed || !provenance || !isRepoTier(provenance.tier))
1417
+ return;
1418
+ const subject = `${id}:${field}:${provenance.tier}`;
1419
+ const reason = `project LSP ${field} refused for ${id}: pi project trust is ${trust}`;
1420
+ recordDegradationOnce({
1421
+ kind: "lsp-registry-decision",
1422
+ subject,
1423
+ reason,
1424
+ metadata: { serverId: id, field, tier: provenance.tier, trust }
1425
+ });
1426
+ if (trust === "unknown") {
1427
+ const generation = getDegradationLedgerGeneration();
1428
+ if (unknownTrustNoticeGeneration !== generation) {
1429
+ unknownTrustNoticeGeneration = generation;
1430
+ logExtension({
1431
+ subsystem: "lsp-registry",
1432
+ level: "warn",
1433
+ message: "project LSP executables refused: mark the project trusted in pi or upgrade pi",
1434
+ metadata: { serverId: id, field }
1435
+ });
1436
+ }
1437
+ }
1224
1438
  }
1225
1439
  function isSameOrChildPath(filePath, candidateRoot) {
1226
1440
  if (filePath === candidateRoot)
1227
1441
  return true;
1228
- return filePath.startsWith(`${candidateRoot}${path4.sep}`);
1442
+ return filePath.startsWith(`${candidateRoot}${path3.sep}`);
1229
1443
  }
1230
1444
  function getConfigForFile(filePath) {
1231
- const resolvedFilePath = path4.resolve(filePath);
1445
+ const resolvedFilePath = path3.resolve(filePath);
1232
1446
  let bestMatch;
1233
1447
  for (const [root, config] of sessionRootConfigEntries()) {
1234
1448
  if (config === void 0)
@@ -1242,13 +1456,33 @@ function getConfigForFile(filePath) {
1242
1456
  return bestMatch?.config ?? EMPTY_CONFIG;
1243
1457
  }
1244
1458
  function registerLSPConfig(config) {
1459
+ return compileLspRegistry(config, config.__provenance);
1460
+ }
1461
+ function compileLspRegistry(config, provenance = /* @__PURE__ */ new Map()) {
1462
+ const trust = getProjectTrustState();
1245
1463
  const customServers = [];
1246
1464
  const disabledServerIds = new Set(config.disabledServers ?? []);
1247
1465
  if (config.servers) {
1248
1466
  for (const [id, serverConfig] of Object.entries(config.servers)) {
1467
+ const commandSource = sourceFor(provenance, `/lsp/servers/${id}/command`);
1468
+ const commandAllowed = executableAllowed(commandSource, trust);
1469
+ registryDecision(id, "command", commandSource, trust, commandAllowed);
1470
+ if (!commandAllowed)
1471
+ continue;
1472
+ const envSource = sourceFor(provenance, `/lsp/servers/${id}/env`);
1473
+ const initSource = sourceFor(provenance, `/lsp/servers/${id}/initializationOptions`);
1474
+ const envAllowed = executableAllowed(envSource, trust);
1475
+ const initAllowed = executableAllowed(initSource, trust);
1476
+ registryDecision(id, "env", envSource, trust, envAllowed);
1477
+ registryDecision(id, "initializationOptions", initSource, trust, initAllowed);
1478
+ const admitted = { ...serverConfig };
1479
+ if (!envAllowed)
1480
+ delete admitted.env;
1481
+ if (!initAllowed)
1482
+ delete admitted.initializationOptions;
1249
1483
  try {
1250
- const server = createCustomServer(serverConfig, id);
1251
- customServers.push(server);
1484
+ const server = createCustomServer(admitted, id);
1485
+ customServers.push({ ...server, trustAllowed: true });
1252
1486
  } catch {
1253
1487
  }
1254
1488
  }
@@ -1258,15 +1492,53 @@ function registerLSPConfig(config) {
1258
1492
  for (const [id, entry] of Object.entries(config.serverOverrides)) {
1259
1493
  if (entry && typeof entry === "object" && !Array.isArray(entry)) {
1260
1494
  const initOpts = entry.initializationOptions;
1261
- if (initOpts !== void 0 && typeof initOpts === "object" && initOpts !== null && !Array.isArray(initOpts)) {
1495
+ const command = entry.command;
1496
+ const commandSource = sourceFor(provenance, `/lsp/serverOverrides/${id}/command`);
1497
+ const envSource = sourceFor(provenance, `/lsp/serverOverrides/${id}/env`);
1498
+ const initSource = sourceFor(provenance, `/lsp/serverOverrides/${id}/initializationOptions`);
1499
+ const commandAllowed = executableAllowed(commandSource, trust);
1500
+ const envAllowed = executableAllowed(envSource, trust);
1501
+ const overrideInitAllowed = executableAllowed(initSource, trust);
1502
+ registryDecision(id, "command override", commandSource, trust, commandAllowed);
1503
+ registryDecision(id, "env override", envSource, trust, envAllowed);
1504
+ registryDecision(id, "initializationOptions", initSource, trust, overrideInitAllowed);
1505
+ if (command !== void 0 || entry.env !== void 0 || initOpts !== void 0 && typeof initOpts === "object" && initOpts !== null && !Array.isArray(initOpts)) {
1262
1506
  serverOverrides.set(id, {
1263
- initializationOptions: initOpts
1507
+ ...overrideInitAllowed && initOpts !== void 0 ? { initializationOptions: initOpts } : {},
1508
+ ...commandAllowed && command !== void 0 ? { command } : {},
1509
+ ...envAllowed && entry.env ? { env: entry.env } : {}
1264
1510
  });
1265
1511
  }
1266
1512
  }
1267
1513
  }
1268
1514
  }
1269
- return { customServers, disabledServerIds, serverOverrides };
1515
+ const overriddenBuiltins = LSP_SERVERS.map((server) => {
1516
+ const override = serverOverrides.get(server.id);
1517
+ if (!override?.command)
1518
+ return { ...server };
1519
+ const argv = Array.isArray(override.command) ? override.command : [override.command, "--stdio"];
1520
+ return {
1521
+ ...server,
1522
+ trustAllowed: true,
1523
+ async spawn(root, options) {
1524
+ void options;
1525
+ const program = argv[0];
1526
+ if (!program)
1527
+ throw new Error(`empty command override for ${server.id}`);
1528
+ const proc = await launchLSP(program, argv.slice(1), {
1529
+ cwd: root,
1530
+ env: override.env ? { ...process.env, ...override.env } : process.env
1531
+ });
1532
+ return { process: proc };
1533
+ }
1534
+ };
1535
+ });
1536
+ return {
1537
+ customServers,
1538
+ registeredServers: overriddenBuiltins,
1539
+ disabledServerIds,
1540
+ serverOverrides
1541
+ };
1270
1542
  }
1271
1543
  function forgetConfigResolvedClaims(evictedRoots) {
1272
1544
  for (const root of evictedRoots) {
@@ -1280,7 +1552,7 @@ async function initLSPConfig(cwd) {
1280
1552
  if (existing)
1281
1553
  return existing;
1282
1554
  const promise = (async () => {
1283
- const config = registerLSPConfig(await loadLSPConfig(cwd, os3.homedir()));
1555
+ const config = registerLSPConfig(await loadLSPConfig(cwd, os4.homedir()));
1284
1556
  forgetConfigResolvedClaims(setSessionRootConfig(normalizedCwd, config));
1285
1557
  })();
1286
1558
  configInFlight.set(normalizedCwd, promise);
@@ -1293,7 +1565,8 @@ async function initLSPConfig(cwd) {
1293
1565
  }
1294
1566
  }
1295
1567
  function registeredServers(config) {
1296
- return [...LSP_SERVERS, ...config.customServers];
1568
+ const builtins = config.registeredServers.length ? config.registeredServers : LSP_SERVERS;
1569
+ return [...builtins, ...config.customServers];
1297
1570
  }
1298
1571
  function selectionReason(server, config, filePath, ext, base) {
1299
1572
  if (config.disabledServerIds.has(server.id))
@@ -1313,8 +1586,8 @@ function selectionReason(server, config, filePath, ext, base) {
1313
1586
  return "selected";
1314
1587
  }
1315
1588
  function explainServersForFile(filePath, config = getConfigForFile(filePath)) {
1316
- const ext = path4.extname(filePath).toLowerCase();
1317
- const base = path4.basename(filePath).toLowerCase();
1589
+ const ext = path3.extname(filePath).toLowerCase();
1590
+ const base = path3.basename(filePath).toLowerCase();
1318
1591
  return registeredServers(config).map((server) => {
1319
1592
  const reason = selectionReason(server, config, filePath, ext, base);
1320
1593
  return { server, selected: reason === "selected", reason };
@@ -1322,8 +1595,8 @@ function explainServersForFile(filePath, config = getConfigForFile(filePath)) {
1322
1595
  }
1323
1596
  function getServersForFileWithConfig(filePath) {
1324
1597
  const config = getConfigForFile(filePath);
1325
- const ext = path4.extname(filePath).toLowerCase();
1326
- const base = path4.basename(filePath).toLowerCase();
1598
+ const ext = path3.extname(filePath).toLowerCase();
1599
+ const base = path3.basename(filePath).toLowerCase();
1327
1600
  return registeredServers(config).filter((server) => selectionReason(server, config, filePath, ext, base) === "selected");
1328
1601
  }
1329
1602
  function primaryServerEntry(filePath) {
@@ -1384,11 +1657,15 @@ function resolveAstGrepNativeExe(platform = process.platform, arch = process.arc
1384
1657
  }
1385
1658
  var SERVER_DIAGNOSTIC_STRATEGIES = {
1386
1659
  typescript: {
1387
- seedFirstPush: true,
1660
+ seedFirstPush: false,
1388
1661
  pullRetryBudgetMs: 0,
1389
1662
  debounceMs: 50,
1390
1663
  aggregateWaitMs: 1e3,
1391
1664
  expectSemanticSecondPush: false,
1665
+ // #3310 / nightly run 37930006227: the clean TypeScript fixture
1666
+ // published an empty pre-index result before its settled result.
1667
+ // Hold that first publish so it cannot resolve a push wait as clean.
1668
+ emptyFirstPublish: "indexing",
1392
1669
  // Tier 3 (#458): typescript-language-server publishes nothing on a
1393
1670
  // clean→clean edit (docs/lsp-capability-matrix.md, re-confirmed
1394
1671
  // 2026-07-12). It's the lone core-set tier-3 server, which is exactly
@@ -1788,32 +2065,32 @@ function classifyCascadeWaitTier(lspService, filePath, snapshots) {
1788
2065
  }
1789
2066
 
1790
2067
  // dist/clients/lsp/lombok.js
1791
- import * as fs4 from "node:fs";
1792
- import * as os4 from "node:os";
1793
- import * as path5 from "node:path";
2068
+ import * as fs5 from "node:fs";
2069
+ import * as os5 from "node:os";
2070
+ import * as path4 from "node:path";
1794
2071
  var LOMBOK_ENV_KEYS = ["PI_LENS_LOMBOK_JAR", "LOMBOK_JAR"];
1795
2072
  var LOMBOK_PROJECT_FILES = [
1796
2073
  "lombok.config",
1797
2074
  "pom.xml",
1798
2075
  "build.gradle",
1799
2076
  "build.gradle.kts",
1800
- path5.join("gradle", "libs.versions.toml")
2077
+ path4.join("gradle", "libs.versions.toml")
1801
2078
  ];
1802
2079
  function fileExists(filePath) {
1803
2080
  if (!filePath)
1804
2081
  return false;
1805
2082
  try {
1806
- return fs4.statSync(filePath).isFile();
2083
+ return fs5.statSync(filePath).isFile();
1807
2084
  } catch {
1808
2085
  return false;
1809
2086
  }
1810
2087
  }
1811
2088
  function readTextIfSmall(filePath, maxBytes = 512 * 1024) {
1812
2089
  try {
1813
- const stat2 = fs4.statSync(filePath);
2090
+ const stat2 = fs5.statSync(filePath);
1814
2091
  if (!stat2.isFile() || stat2.size > maxBytes)
1815
2092
  return "";
1816
- return fs4.readFileSync(filePath, "utf-8");
2093
+ return fs5.readFileSync(filePath, "utf-8");
1817
2094
  } catch {
1818
2095
  return "";
1819
2096
  }
@@ -1822,13 +2099,13 @@ function containsLombokDependency(text) {
1822
2099
  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);
1823
2100
  }
1824
2101
  function hasLombokProject(root) {
1825
- const lombokConfig = path5.join(root, "lombok.config");
2102
+ const lombokConfig = path4.join(root, "lombok.config");
1826
2103
  if (fileExists(lombokConfig))
1827
2104
  return true;
1828
2105
  for (const name of LOMBOK_PROJECT_FILES) {
1829
2106
  if (name === "lombok.config")
1830
2107
  continue;
1831
- const text = readTextIfSmall(path5.join(root, name));
2108
+ const text = readTextIfSmall(path4.join(root, name));
1832
2109
  if (text && containsLombokDependency(text))
1833
2110
  return true;
1834
2111
  }
@@ -1838,23 +2115,23 @@ function envJarCandidate(env) {
1838
2115
  for (const key of LOMBOK_ENV_KEYS) {
1839
2116
  const value = env[key]?.trim();
1840
2117
  if (fileExists(value))
1841
- return path5.resolve(value);
2118
+ return path4.resolve(value);
1842
2119
  }
1843
2120
  return void 0;
1844
2121
  }
1845
2122
  function localJarCandidates(root) {
1846
2123
  return [
1847
- path5.join(root, "lombok.jar"),
1848
- path5.join(root, ".lombok", "lombok.jar"),
1849
- path5.join(root, "lib", "lombok.jar"),
1850
- path5.join(root, "libs", "lombok.jar")
2124
+ path4.join(root, "lombok.jar"),
2125
+ path4.join(root, ".lombok", "lombok.jar"),
2126
+ path4.join(root, "lib", "lombok.jar"),
2127
+ path4.join(root, "libs", "lombok.jar")
1851
2128
  ];
1852
2129
  }
1853
2130
  function newestExistingJar(candidates) {
1854
2131
  let best;
1855
2132
  for (const candidate of candidates) {
1856
2133
  try {
1857
- const stat2 = fs4.statSync(candidate);
2134
+ const stat2 = fs5.statSync(candidate);
1858
2135
  if (!stat2.isFile())
1859
2136
  continue;
1860
2137
  if (!best || stat2.mtimeMs > best.mtimeMs) {
@@ -1865,35 +2142,35 @@ function newestExistingJar(candidates) {
1865
2142
  }
1866
2143
  return best?.filePath;
1867
2144
  }
1868
- function mavenLocalLombokJar(home = os4.homedir()) {
1869
- const base = path5.join(home, ".m2", "repository", "org", "projectlombok", "lombok");
2145
+ function mavenLocalLombokJar(home = os5.homedir()) {
2146
+ const base = path4.join(home, ".m2", "repository", "org", "projectlombok", "lombok");
1870
2147
  let versions = [];
1871
2148
  try {
1872
- versions = fs4.readdirSync(base);
2149
+ versions = fs5.readdirSync(base);
1873
2150
  } catch {
1874
2151
  return void 0;
1875
2152
  }
1876
- return newestExistingJar(versions.map((version) => path5.join(base, version, `lombok-${version}.jar`)));
2153
+ return newestExistingJar(versions.map((version) => path4.join(base, version, `lombok-${version}.jar`)));
1877
2154
  }
1878
- function gradleCacheLombokJar(home = os4.homedir()) {
1879
- const base = path5.join(home, ".gradle", "caches", "modules-2", "files-2.1", "org.projectlombok", "lombok");
2155
+ function gradleCacheLombokJar(home = os5.homedir()) {
2156
+ const base = path4.join(home, ".gradle", "caches", "modules-2", "files-2.1", "org.projectlombok", "lombok");
1880
2157
  const candidates = [];
1881
2158
  let versions = [];
1882
2159
  try {
1883
- versions = fs4.readdirSync(base);
2160
+ versions = fs5.readdirSync(base);
1884
2161
  } catch {
1885
2162
  return void 0;
1886
2163
  }
1887
2164
  for (const version of versions) {
1888
- const versionDir = path5.join(base, version);
2165
+ const versionDir = path4.join(base, version);
1889
2166
  let hashes = [];
1890
2167
  try {
1891
- hashes = fs4.readdirSync(versionDir);
2168
+ hashes = fs5.readdirSync(versionDir);
1892
2169
  } catch {
1893
2170
  continue;
1894
2171
  }
1895
2172
  for (const hash of hashes) {
1896
- candidates.push(path5.join(versionDir, hash, `lombok-${version}.jar`));
2173
+ candidates.push(path4.join(versionDir, hash, `lombok-${version}.jar`));
1897
2174
  }
1898
2175
  }
1899
2176
  return newestExistingJar(candidates);
@@ -1925,16 +2202,16 @@ function createLombokJdtlsArgs(root, baseEnv = process.env) {
1925
2202
 
1926
2203
  // dist/clients/lsp/jvm-runtime.js
1927
2204
  import { existsSync as existsSync2, readdirSync as readdirSync4 } from "node:fs";
1928
- import os5 from "node:os";
1929
- import path6 from "node:path";
2205
+ import os6 from "node:os";
2206
+ import path5 from "node:path";
1930
2207
  var JAVA_EXE = process.platform === "win32" ? "java.exe" : "java";
1931
2208
  var JAVA_PROBE_TIMEOUT_MS = 5e3;
1932
2209
  var MIN_JAVA_MAJOR = 17;
1933
2210
  function jdkHomeFrom(dir) {
1934
- if (existsSync2(path6.join(dir, "bin", JAVA_EXE)))
2211
+ if (existsSync2(path5.join(dir, "bin", JAVA_EXE)))
1935
2212
  return dir;
1936
- const macHome = path6.join(dir, "Contents", "Home");
1937
- if (existsSync2(path6.join(macHome, "bin", JAVA_EXE)))
2213
+ const macHome = path5.join(dir, "Contents", "Home");
2214
+ if (existsSync2(path5.join(macHome, "bin", JAVA_EXE)))
1938
2215
  return macHome;
1939
2216
  return void 0;
1940
2217
  }
@@ -1947,34 +2224,34 @@ function parseMajorVersion(name) {
1947
2224
  }
1948
2225
  function childDirs(base) {
1949
2226
  try {
1950
- return readdirSync4(base, { withFileTypes: true }).filter((e) => e.isDirectory() || e.isSymbolicLink()).map((e) => path6.join(base, e.name));
2227
+ return readdirSync4(base, { withFileTypes: true }).filter((e) => e.isDirectory() || e.isSymbolicLink()).map((e) => path5.join(base, e.name));
1951
2228
  } catch {
1952
2229
  return [];
1953
2230
  }
1954
2231
  }
1955
2232
  function candidateRoots() {
1956
- const home = os5.homedir();
2233
+ const home = os6.homedir();
1957
2234
  const roots = [];
1958
- roots.push(path6.join(home, ".jdks"));
1959
- roots.push(path6.join(getGlobalPiLensDir(), "tools"));
2235
+ roots.push(path5.join(home, ".jdks"));
2236
+ roots.push(path5.join(getGlobalPiLensDir(), "tools"));
1960
2237
  if (process.platform === "win32") {
1961
2238
  const progFiles = [
1962
2239
  process.env.ProgramFiles,
1963
2240
  process.env["ProgramFiles(x86)"],
1964
2241
  process.env.ProgramW6432,
1965
- path6.join(process.env.LOCALAPPDATA ?? path6.join(home, "AppData", "Local"), "Programs")
2242
+ path5.join(process.env.LOCALAPPDATA ?? path5.join(home, "AppData", "Local"), "Programs")
1966
2243
  ].filter((p) => Boolean(p));
1967
2244
  for (const base of progFiles) {
1968
- roots.push(path6.join(base, "Eclipse Adoptium"));
1969
- roots.push(path6.join(base, "Java"));
1970
- roots.push(path6.join(base, "Microsoft"));
1971
- roots.push(path6.join(base, "Zulu"));
1972
- roots.push(path6.join(base, "Amazon Corretto"));
1973
- roots.push(path6.join(base, "BellSoft"));
2245
+ roots.push(path5.join(base, "Eclipse Adoptium"));
2246
+ roots.push(path5.join(base, "Java"));
2247
+ roots.push(path5.join(base, "Microsoft"));
2248
+ roots.push(path5.join(base, "Zulu"));
2249
+ roots.push(path5.join(base, "Amazon Corretto"));
2250
+ roots.push(path5.join(base, "BellSoft"));
1974
2251
  }
1975
2252
  } else if (process.platform === "darwin") {
1976
2253
  roots.push("/Library/Java/JavaVirtualMachines");
1977
- roots.push(path6.join(home, "Library", "Java", "JavaVirtualMachines"));
2254
+ roots.push(path5.join(home, "Library", "Java", "JavaVirtualMachines"));
1978
2255
  roots.push("/opt/homebrew/opt");
1979
2256
  roots.push("/usr/local/opt");
1980
2257
  } else {
@@ -1998,7 +2275,7 @@ function discoverJdkHome(roots = candidateRoots(), javaHome = process.env.JAVA_H
1998
2275
  continue;
1999
2276
  found.push({
2000
2277
  home: resolved,
2001
- major: parseMajorVersion(path6.basename(dir))
2278
+ major: parseMajorVersion(path5.basename(dir))
2002
2279
  });
2003
2280
  }
2004
2281
  }
@@ -2097,12 +2374,12 @@ async function resolveJavaRuntimeEnv() {
2097
2374
  }
2098
2375
  const home = discoverJdkHome();
2099
2376
  if (home) {
2100
- const binDir = path6.join(home, "bin");
2377
+ const binDir = path5.join(home, "bin");
2101
2378
  const currentPath = process.env.PATH ?? process.env.Path ?? "";
2102
2379
  _cachedEnv = {
2103
2380
  value: {
2104
2381
  JAVA_HOME: home,
2105
- PATH: binDir + path6.delimiter + currentPath
2382
+ PATH: binDir + path5.delimiter + currentPath
2106
2383
  }
2107
2384
  };
2108
2385
  return _cachedEnv.value;
@@ -2158,7 +2435,7 @@ async function resolveLspServerCwd(server, filePath, sessionCwd, onRootFailure,
2158
2435
  rootFailed = true;
2159
2436
  onRootFailure?.(reason);
2160
2437
  }
2161
- const isFileDirFallback = serverRoot !== void 0 && rootMarkers?.length && path7.resolve(serverRoot) === path7.resolve(path7.dirname(filePath));
2438
+ const isFileDirFallback = serverRoot !== void 0 && rootMarkers?.length && path6.resolve(serverRoot) === path6.resolve(path6.dirname(filePath));
2162
2439
  if (!serverRoot || isFileDirFallback) {
2163
2440
  const probedMarker = onRootFallback && !rootFailed && rootMarkers?.length ? resolveToolCwd("lsp", server.id, filePath, {
2164
2441
  cwd: sessionCwd,
@@ -2167,7 +2444,7 @@ async function resolveLspServerCwd(server, filePath, sessionCwd, onRootFailure,
2167
2444
  }).marker : void 0;
2168
2445
  const refusedMarker = probedMarker !== void 0 && rootMarkers?.includes(probedMarker) ? probedMarker : void 0;
2169
2446
  const fallbackCwd = rootMarkers?.length ? resolveToolCwd("lsp", server.id, filePath, {
2170
- cwd: path7.dirname(path7.resolve(filePath)),
2447
+ cwd: path6.dirname(path6.resolve(filePath)),
2171
2448
  rootMarkers
2172
2449
  }) : void 0;
2173
2450
  if (!serverRoot) {
@@ -2237,21 +2514,21 @@ var PROJECT_BOUNDARY_MARKERS = [
2237
2514
  var loggedRootCeilingClamps = /* @__PURE__ */ new Set();
2238
2515
  var posixCaseInsensitiveByPath = /* @__PURE__ */ new Map();
2239
2516
  function posixFilesystemIsCaseInsensitive(root) {
2240
- const resolved = path7.resolve(root);
2517
+ const resolved = path6.resolve(root);
2241
2518
  const cached = posixCaseInsensitiveByPath.get(resolved);
2242
2519
  if (cached !== void 0)
2243
2520
  return cached;
2244
2521
  if (!existsSync3(resolved)) {
2245
2522
  return false;
2246
2523
  }
2247
- const name = path7.basename(resolved);
2524
+ const name = path6.basename(resolved);
2248
2525
  const alternate = name.toLowerCase() === name ? name.toUpperCase() : name.toLowerCase();
2249
2526
  let insensitive = false;
2250
- const alternatePath = path7.join(path7.dirname(resolved), alternate);
2527
+ const alternatePath = path6.join(path6.dirname(resolved), alternate);
2251
2528
  if (alternate !== name && existsSync3(alternatePath)) {
2252
2529
  try {
2253
- const actual = statSync3(resolved);
2254
- const alternateStat = statSync3(alternatePath);
2530
+ const actual = statSync4(resolved);
2531
+ const alternateStat = statSync4(alternatePath);
2255
2532
  insensitive = actual.dev === alternateStat.dev && actual.ino === alternateStat.ino;
2256
2533
  } catch {
2257
2534
  insensitive = false;
@@ -2265,7 +2542,7 @@ function resetLSPCaseSensitivityState() {
2265
2542
  }
2266
2543
  function isSameOrWithin(ancestor, candidate, deps = {}) {
2267
2544
  const windowsShaped = isWindowsPath(ancestor) || isWindowsPath(candidate);
2268
- const pathApi = windowsShaped ? path7.win32 : path7;
2545
+ const pathApi = windowsShaped ? path6.win32 : path6;
2269
2546
  const resolvedAncestor = pathApi.resolve(ancestor);
2270
2547
  const resolvedCandidate = pathApi.resolve(candidate);
2271
2548
  let caseInsensitive = false;
@@ -2285,7 +2562,7 @@ function isSameOrWithin(ancestor, candidate, deps = {}) {
2285
2562
  }
2286
2563
  function enforceLspRootCeiling(root, sessionCwd, filePath) {
2287
2564
  const windowsShaped = isWindowsPath(root) || isWindowsPath(sessionCwd);
2288
- const pathApi = windowsShaped ? path7.win32 : path7;
2565
+ const pathApi = windowsShaped ? path6.win32 : path6;
2289
2566
  const resolvedRoot = pathApi.resolve(root);
2290
2567
  const resolvedCwd = pathApi.resolve(sessionCwd);
2291
2568
  if (filePath && !isSameOrWithin(resolvedCwd, pathApi.resolve(filePath))) {
@@ -2310,8 +2587,8 @@ async function hasProjectBoundaryMarker(dir) {
2310
2587
  return false;
2311
2588
  }
2312
2589
  function pathSegments(dir) {
2313
- const parsed = path7.parse(path7.resolve(dir));
2314
- return path7.relative(parsed.root, path7.resolve(dir)).split(path7.sep).filter(Boolean).map((segment) => segment.toLowerCase());
2590
+ const parsed = path6.parse(path6.resolve(dir));
2591
+ return path6.relative(parsed.root, path6.resolve(dir)).split(path6.sep).filter(Boolean).map((segment) => segment.toLowerCase());
2315
2592
  }
2316
2593
  function hasFixtureConvention(dir) {
2317
2594
  const segments = pathSegments(dir);
@@ -2323,18 +2600,18 @@ function hasAtomicStageSegment(dir) {
2323
2600
  return pathSegments(dir).some((segment) => STAGE_TMP_PATTERN.test(segment));
2324
2601
  }
2325
2602
  async function findGitBoundary(dir) {
2326
- let current = path7.resolve(dir);
2327
- const fsRoot = path7.parse(current).root;
2603
+ let current = path6.resolve(dir);
2604
+ const fsRoot = path6.parse(current).root;
2328
2605
  while (true) {
2329
2606
  if (await markerExists(current, ".git"))
2330
2607
  return current;
2331
2608
  if (current === fsRoot)
2332
2609
  return void 0;
2333
- current = path7.dirname(current);
2610
+ current = path6.dirname(current);
2334
2611
  }
2335
2612
  }
2336
2613
  async function isExcludedLspRoot(dir) {
2337
- const candidate = path7.resolve(dir);
2614
+ const candidate = path6.resolve(dir);
2338
2615
  if (hasFixtureConvention(candidate) || hasAtomicStageSegment(candidate))
2339
2616
  return true;
2340
2617
  const gitRoot = await findGitBoundary(candidate);
@@ -2346,9 +2623,9 @@ async function isExcludedLspRoot(dir) {
2346
2623
  }
2347
2624
  async function nearestNonExcludedFallbackRoot(candidate) {
2348
2625
  if (!await isExcludedLspRoot(candidate))
2349
- return path7.resolve(candidate);
2350
- let current = path7.dirname(path7.resolve(candidate));
2351
- const fsRoot = path7.parse(current).root;
2626
+ return path6.resolve(candidate);
2627
+ let current = path6.dirname(path6.resolve(candidate));
2628
+ const fsRoot = path6.parse(current).root;
2352
2629
  let nearestAllowed;
2353
2630
  while (true) {
2354
2631
  if (!await isExcludedLspRoot(current)) {
@@ -2360,7 +2637,7 @@ async function nearestNonExcludedFallbackRoot(candidate) {
2360
2637
  }
2361
2638
  if (current === fsRoot)
2362
2639
  break;
2363
- current = path7.dirname(current);
2640
+ current = path6.dirname(current);
2364
2641
  }
2365
2642
  return nearestAllowed ?? fsRoot;
2366
2643
  }
@@ -2401,7 +2678,7 @@ async function installEvidenceForLaunch(toolId, installed, attempt) {
2401
2678
  const confirmedManagedPath = await findManagedToolBinary(toolId);
2402
2679
  return {
2403
2680
  ...evidence,
2404
- binary: path7.basename(installed),
2681
+ binary: path6.basename(installed),
2405
2682
  ...confirmedManagedPath !== void 0 && pathsEqual(confirmedManagedPath, installed) && {
2406
2683
  source: "managed-dir"
2407
2684
  }
@@ -2454,7 +2731,7 @@ function markDirectLspCommandUnavailable(command) {
2454
2731
  directLspCommandUnavailableUntil.set(command, Date.now() + DIRECT_LSP_NEGATIVE_TTL_MS);
2455
2732
  directLspCommandSkipLoggedUntil.delete(command);
2456
2733
  }
2457
- var PI_LENS_BIN_DIR2 = path7.join(getGlobalPiLensDir(), "bin");
2734
+ var PI_LENS_BIN_DIR2 = path6.join(getGlobalPiLensDir(), "bin");
2458
2735
  async function resolveAndLaunch(spec, allowInstall) {
2459
2736
  const generation = lspLaunchAvailabilityGeneration.capture();
2460
2737
  const toolLabel = spec.managedToolId ?? spec.candidates[spec.candidates.length - 1] ?? "unknown";
@@ -2519,7 +2796,7 @@ async function resolveAndLaunch(spec, allowInstall) {
2519
2796
  producer: "lsp-launch",
2520
2797
  classifiedBy: "probe",
2521
2798
  evidence: {
2522
- binary: path7.basename(managedCandidate),
2799
+ binary: path6.basename(managedCandidate),
2523
2800
  source: "managed-dir",
2524
2801
  correctsLatchedRow: false
2525
2802
  }
@@ -2812,7 +3089,7 @@ async function resolveAndLaunchTreeBinary(spec, allowInstall) {
2812
3089
  return void 0;
2813
3090
  }
2814
3091
  const suffix = process.platform === "win32" ? ".exe" : "";
2815
- const binPath = path7.join(bundleDir, ...spec.binRelPath.split("/")) + suffix;
3092
+ const binPath = path6.join(bundleDir, ...spec.binRelPath.split("/")) + suffix;
2816
3093
  try {
2817
3094
  const proc = await launchLSP(binPath, spec.args, {
2818
3095
  cwd: spec.cwd,
@@ -2827,7 +3104,7 @@ async function resolveAndLaunchTreeBinary(spec, allowInstall) {
2827
3104
  }
2828
3105
  }
2829
3106
  function nodeBinLocalCandidates(root, baseName) {
2830
- const localBase = path7.join(root, "node_modules", ".bin", baseName);
3107
+ const localBase = path6.join(root, "node_modules", ".bin", baseName);
2831
3108
  if (process.platform === "win32") {
2832
3109
  return [`${localBase}.cmd`, `${localBase}.exe`];
2833
3110
  }
@@ -2837,7 +3114,7 @@ function nodeBinCandidates(root, baseName) {
2837
3114
  return [...nodeBinLocalCandidates(root, baseName), baseName];
2838
3115
  }
2839
3116
  function piAgentExtensionsRootKey(file) {
2840
- const dirKey = normalizeEphemeralMapKey(path7.dirname(path7.resolve(file)));
3117
+ const dirKey = normalizeEphemeralMapKey(path6.dirname(path6.resolve(file)));
2841
3118
  const marker = "/.pi/agent/extensions";
2842
3119
  const index = dirKey.indexOf(marker);
2843
3120
  if (index === -1)
@@ -2845,26 +3122,26 @@ function piAgentExtensionsRootKey(file) {
2845
3122
  return dirKey.slice(0, index + marker.length);
2846
3123
  }
2847
3124
  function IgnoreHomeRoot(primary) {
2848
- const homeKey = normalizeEphemeralMapKey(path7.resolve(os6.homedir()));
3125
+ const homeKey = normalizeEphemeralMapKey(path6.resolve(os7.homedir()));
2849
3126
  return withRootMarkers(async (file) => {
2850
3127
  const root = await primary(file);
2851
3128
  if (!root)
2852
3129
  return void 0;
2853
- return normalizeEphemeralMapKey(path7.resolve(root)) === homeKey ? void 0 : root;
3130
+ return normalizeEphemeralMapKey(path6.resolve(root)) === homeKey ? void 0 : root;
2854
3131
  }, primary.rootMarkers ?? []);
2855
3132
  }
2856
3133
  function rubyBinCandidates(baseName) {
2857
3134
  const candidates = [];
2858
- const home = os6.homedir();
3135
+ const home = os7.homedir();
2859
3136
  const isWin = process.platform === "win32";
2860
3137
  const ext = isWin ? ".bat" : "";
2861
- candidates.push(path7.join(home, ".local", "share", "mise", "installs", "ruby", "bin", `${baseName}${ext}`));
2862
- candidates.push(path7.join(home, ".asdf", "installs", "ruby", "bin", `${baseName}${ext}`));
3138
+ candidates.push(path6.join(home, ".local", "share", "mise", "installs", "ruby", "bin", `${baseName}${ext}`));
3139
+ candidates.push(path6.join(home, ".asdf", "installs", "ruby", "bin", `${baseName}${ext}`));
2863
3140
  if (isWin) {
2864
- const driveRoot = path7.parse(home).root;
3141
+ const driveRoot = path6.parse(home).root;
2865
3142
  for (const entry of getRubyVersionDirNamesSync(driveRoot)) {
2866
- candidates.push(path7.join(driveRoot, entry, "bin", `${baseName}.bat`));
2867
- candidates.push(path7.join(driveRoot, entry, "bin", baseName));
3143
+ candidates.push(path6.join(driveRoot, entry, "bin", `${baseName}.bat`));
3144
+ candidates.push(path6.join(driveRoot, entry, "bin", baseName));
2868
3145
  }
2869
3146
  }
2870
3147
  return candidates;
@@ -2884,6 +3161,7 @@ function createInteractiveServer(spec) {
2884
3161
  rootMarkers: spec.root.rootMarkers,
2885
3162
  fallbackFor: spec.fallbackFor,
2886
3163
  requiresProjectRoot: spec.requiresProjectRoot,
3164
+ executesProjectCode: spec.executesProjectCode,
2887
3165
  availabilityKey: typeof spec.command === "string" && isSimpleCommand(spec.command) ? spec.command : void 0,
2888
3166
  async spawn(root) {
2889
3167
  const command = typeof spec.command === "function" ? spec.command(root) : spec.command;
@@ -2911,8 +3189,8 @@ function createInteractiveServer(spec) {
2911
3189
  function PriorityRoot(markerGroups, excludePatterns, stopDir) {
2912
3190
  const resolvers = markerGroups.map((markers) => NearestRoot(markers, excludePatterns, stopDir));
2913
3191
  return withRootMarkers(async (file) => {
2914
- for (const resolve3 of resolvers) {
2915
- const root = await resolve3(file);
3192
+ for (const resolve4 of resolvers) {
3193
+ const root = await resolve4(file);
2916
3194
  if (root)
2917
3195
  return root;
2918
3196
  }
@@ -2920,7 +3198,7 @@ function PriorityRoot(markerGroups, excludePatterns, stopDir) {
2920
3198
  }, markerGroups.flat());
2921
3199
  }
2922
3200
  var FileDirRoot = async (file) => {
2923
- const candidate = path7.resolve(path7.dirname(file));
3201
+ const candidate = path6.resolve(path6.dirname(file));
2924
3202
  return nearestNonExcludedFallbackRoot(candidate);
2925
3203
  };
2926
3204
  function RootWithFallback(primary, fallback = FileDirRoot) {
@@ -2942,16 +3220,16 @@ function markerProbeDir(dir, pattern) {
2942
3220
  const normalized = toPosix(pattern);
2943
3221
  const slash = normalized.lastIndexOf("/");
2944
3222
  const parentPattern = slash >= 0 ? normalized.slice(0, slash) : "";
2945
- return parentPattern ? path7.join(dir, ...parentPattern.split("/").filter(Boolean)) : dir;
3223
+ return parentPattern ? path6.join(dir, ...parentPattern.split("/").filter(Boolean)) : dir;
2946
3224
  }
2947
3225
  async function markerExists(dir, pattern) {
2948
3226
  if (!pattern.includes("*")) {
2949
3227
  try {
2950
- await stat(path7.join(dir, pattern));
3228
+ await stat(path6.join(dir, pattern));
2951
3229
  return true;
2952
3230
  } catch (err) {
2953
3231
  if (isPermissionFsError(err)) {
2954
- logSessionStart(`lsp root marker skipped: permission error stat ${path7.join(dir, pattern)}`);
3232
+ logSessionStart(`lsp root marker skipped: permission error stat ${path6.join(dir, pattern)}`);
2955
3233
  }
2956
3234
  return false;
2957
3235
  }
@@ -2987,7 +3265,7 @@ function NearestRoot(includePatterns, excludePatterns, stopDir) {
2987
3265
  subdirPatterns.push(pattern);
2988
3266
  }
2989
3267
  return withRootMarkers(async (file) => {
2990
- const startDir = path7.resolve(path7.dirname(file));
3268
+ const startDir = path6.resolve(path6.dirname(file));
2991
3269
  const dirKey = normalizeMapKey(startDir);
2992
3270
  const cached = cache.get(dirKey);
2993
3271
  if (cached !== void 0 && Date.now() - cached.walkedAt < FRESHNESS_CADENCE_MS && await bounded(dirMtimesStillFreshAsync(cached.probedDirs), rootMtimeBound("lsp-root-memo-freshness")) === true)
@@ -2999,10 +3277,10 @@ function NearestRoot(includePatterns, excludePatterns, stopDir) {
2999
3277
  const walkedAt = Date.now();
3000
3278
  const promise = (async () => {
3001
3279
  let currentDir = startDir;
3002
- const fsRoot = path7.parse(currentDir).root;
3003
- const stop = stopDir ? path7.resolve(stopDir) : fsRoot;
3280
+ const fsRoot = path6.parse(currentDir).root;
3281
+ const stop = stopDir ? path6.resolve(stopDir) : fsRoot;
3004
3282
  while (true) {
3005
- if (stop !== fsRoot && currentDir.startsWith(stop + path7.sep) === false && currentDir !== stop) {
3283
+ if (stop !== fsRoot && currentDir.startsWith(stop + path6.sep) === false && currentDir !== stop) {
3006
3284
  break;
3007
3285
  }
3008
3286
  const stepDirs = [
@@ -3020,19 +3298,19 @@ function NearestRoot(includePatterns, excludePatterns, stopDir) {
3020
3298
  }
3021
3299
  }
3022
3300
  if (excluded) {
3023
- currentDir = path7.dirname(currentDir);
3301
+ currentDir = path6.dirname(currentDir);
3024
3302
  continue;
3025
3303
  }
3026
3304
  }
3027
3305
  for (const pattern of includePatterns) {
3028
- if (await markerExists(currentDir, pattern) && (pattern !== ".git" || currentDir !== os6.tmpdir()) && !await isExcludedLspRoot(currentDir)) {
3306
+ if (await markerExists(currentDir, pattern) && (pattern !== ".git" || currentDir !== os7.tmpdir()) && !await isExcludedLspRoot(currentDir)) {
3029
3307
  return enforceLspRootCeiling(currentDir, process.cwd(), file);
3030
3308
  }
3031
3309
  }
3032
3310
  if (currentDir === stop || currentDir === fsRoot) {
3033
3311
  break;
3034
3312
  }
3035
- currentDir = path7.dirname(currentDir);
3313
+ currentDir = path6.dirname(currentDir);
3036
3314
  }
3037
3315
  return void 0;
3038
3316
  })();
@@ -3073,18 +3351,18 @@ async function findAncestorFile(startDir, ...relativeSegments) {
3073
3351
  return findAncestorFileAmong(startDir, [relativeSegments]);
3074
3352
  }
3075
3353
  async function findAncestorFileAmong(startDir, candidateSegmentLists) {
3076
- const fs5 = await import("node:fs/promises");
3077
- let currentDir = path7.resolve(startDir);
3354
+ const fs6 = await import("node:fs/promises");
3355
+ let currentDir = path6.resolve(startDir);
3078
3356
  while (!isAtOrAboveHomeDir(currentDir)) {
3079
3357
  for (const segments of candidateSegmentLists) {
3080
- const candidate = path7.join(currentDir, ...segments);
3358
+ const candidate = path6.join(currentDir, ...segments);
3081
3359
  try {
3082
- await fs5.access(candidate);
3360
+ await fs6.access(candidate);
3083
3361
  return candidate;
3084
3362
  } catch {
3085
3363
  }
3086
3364
  }
3087
- const parent = path7.dirname(currentDir);
3365
+ const parent = path6.dirname(currentDir);
3088
3366
  if (parent === currentDir)
3089
3367
  break;
3090
3368
  currentDir = parent;
@@ -3100,19 +3378,19 @@ function resetClassicTsRepairGuard() {
3100
3378
  classicTsRepairState().attempted = false;
3101
3379
  }
3102
3380
  function typescriptDirsForTsc(tscPath) {
3103
- const binDir = path7.dirname(tscPath);
3381
+ const binDir = path6.dirname(tscPath);
3104
3382
  return [
3105
- path7.join(binDir, "..", "typescript"),
3106
- path7.join(binDir, "..", "..", "typescript")
3383
+ path6.join(binDir, "..", "typescript"),
3384
+ path6.join(binDir, "..", "..", "typescript")
3107
3385
  ];
3108
3386
  }
3109
3387
  async function typescriptVersionForTsc(tscPath) {
3110
- if (!path7.isAbsolute(tscPath))
3388
+ if (!path6.isAbsolute(tscPath))
3111
3389
  return void 0;
3112
3390
  for (const dir of typescriptDirsForTsc(tscPath)) {
3113
3391
  let manifest;
3114
3392
  try {
3115
- manifest = await readFile(path7.join(dir, "package.json"), "utf8");
3393
+ manifest = await readFile(path6.join(dir, "package.json"), "utf8");
3116
3394
  } catch {
3117
3395
  continue;
3118
3396
  }
@@ -3134,25 +3412,45 @@ async function typescriptVersionForTsc(tscPath) {
3134
3412
  }
3135
3413
  return void 0;
3136
3414
  }
3137
- async function findTsserverPath(root, allowInstall) {
3138
- const fs5 = await import("node:fs/promises");
3415
+ async function findTsserverPath(root, allowInstall, options) {
3416
+ const fs6 = await import("node:fs/promises");
3417
+ const projectRoots = await Promise.all([root, options?.sessionRoot ?? process.cwd(), process.cwd()].map((dir) => fs6.realpath(dir).catch(() => path6.resolve(dir))));
3418
+ const admitCandidate = async (candidate, projectSupplied) => {
3419
+ try {
3420
+ const real = await fs6.realpath(candidate);
3421
+ if (path6.basename(real) !== "tsserver.js" || !(await fs6.stat(real)).isFile())
3422
+ return void 0;
3423
+ const manifest = JSON.parse(await fs6.readFile(path6.join(path6.dirname(real), "..", "package.json"), "utf8"));
3424
+ if (typeof manifest?.version !== "string" || !manifest.version)
3425
+ return void 0;
3426
+ return admitProjectSuppliedTsserver(real, options?.sessionRoot ?? process.cwd(), projectSupplied || projectRoots.some((dir) => isUnderDir(real, dir)), options?.projectCodeAllowed);
3427
+ } catch {
3428
+ return void 0;
3429
+ }
3430
+ };
3139
3431
  const ancestorHit = await findAncestorFile(root, "node_modules", "typescript", "lib", "tsserver.js");
3140
- if (ancestorHit)
3141
- return ancestorHit;
3142
- const cwdCandidate = path7.join(process.cwd(), "node_modules", "typescript", "lib", "tsserver.js");
3432
+ if (ancestorHit) {
3433
+ const admittedAncestor = await admitCandidate(ancestorHit, true);
3434
+ if (admittedAncestor)
3435
+ return admittedAncestor;
3436
+ }
3437
+ const cwdCandidate = path6.join(process.cwd(), "node_modules", "typescript", "lib", "tsserver.js");
3143
3438
  try {
3144
- await fs5.access(cwdCandidate);
3145
- return cwdCandidate;
3439
+ await fs6.access(cwdCandidate);
3440
+ const admittedCwd = await admitCandidate(cwdCandidate, false);
3441
+ if (admittedCwd)
3442
+ return admittedCwd;
3146
3443
  } catch {
3147
3444
  }
3148
3445
  const tsserverForTsc = async (tscPath) => {
3149
- if (!tscPath)
3446
+ if (!tscPath || !path6.isAbsolute(tscPath))
3150
3447
  return void 0;
3151
3448
  for (const dir of typescriptDirsForTsc(tscPath)) {
3152
- const candidate = path7.join(dir, "lib", "tsserver.js");
3449
+ const candidate = path6.join(dir, "lib", "tsserver.js");
3153
3450
  try {
3154
- await fs5.access(candidate);
3155
- return candidate;
3451
+ const admitted = await admitCandidate(candidate, false);
3452
+ if (admitted)
3453
+ return admitted;
3156
3454
  } catch {
3157
3455
  }
3158
3456
  }
@@ -3178,10 +3476,10 @@ async function findTsserverPath(root, allowInstall) {
3178
3476
  return tsserverForTsc(repairedTsc);
3179
3477
  }
3180
3478
  async function findNativeTypeScriptLsp(root) {
3181
- let currentDir = path7.resolve(root);
3479
+ let currentDir = path6.resolve(root);
3182
3480
  while (!isAtOrAboveHomeDir(currentDir)) {
3183
- const typescriptDir = path7.join(currentDir, "node_modules", "typescript");
3184
- const packageJsonPath = path7.join(typescriptDir, "package.json");
3481
+ const typescriptDir = path6.join(currentDir, "node_modules", "typescript");
3482
+ const packageJsonPath = path6.join(typescriptDir, "package.json");
3185
3483
  let packageJsonText;
3186
3484
  try {
3187
3485
  packageJsonText = await readFile(packageJsonPath, "utf8");
@@ -3195,7 +3493,7 @@ async function findNativeTypeScriptLsp(root) {
3195
3493
  return void 0;
3196
3494
  } catch {
3197
3495
  }
3198
- const parent = path7.dirname(currentDir);
3496
+ const parent = path6.dirname(currentDir);
3199
3497
  if (parent === currentDir)
3200
3498
  return void 0;
3201
3499
  currentDir = parent;
@@ -3215,7 +3513,7 @@ async function findNativeTypeScriptLsp(root) {
3215
3513
  const major = /^\d+$/.test(majorText) ? Number(majorText) : Number.NaN;
3216
3514
  if (!Number.isFinite(major) || major < 7)
3217
3515
  return void 0;
3218
- const localTsc = path7.join(currentDir, "node_modules", ".bin", "tsc");
3516
+ const localTsc = path6.join(currentDir, "node_modules", ".bin", "tsc");
3219
3517
  const candidates = process.platform === "win32" ? [`${localTsc}.cmd`, `${localTsc}.exe`, localTsc] : [localTsc];
3220
3518
  for (const command of candidates) {
3221
3519
  try {
@@ -3229,29 +3527,29 @@ async function findNativeTypeScriptLsp(root) {
3229
3527
  return void 0;
3230
3528
  }
3231
3529
  function dotnetToolCandidates(tool) {
3232
- const home = os6.homedir();
3530
+ const home = os7.homedir();
3233
3531
  return [
3234
- path7.join(PI_LENS_BIN_DIR2, `${tool}.exe`),
3235
- path7.join(PI_LENS_BIN_DIR2, tool),
3236
- path7.join(home, ".dotnet", "tools", `${tool}.exe`),
3237
- path7.join(home, ".dotnet", "tools", tool),
3532
+ path6.join(PI_LENS_BIN_DIR2, `${tool}.exe`),
3533
+ path6.join(PI_LENS_BIN_DIR2, tool),
3534
+ path6.join(home, ".dotnet", "tools", `${tool}.exe`),
3535
+ path6.join(home, ".dotnet", "tools", tool),
3238
3536
  tool
3239
3537
  ].filter(Boolean);
3240
3538
  }
3241
3539
  function binExeVariants(dir, tool) {
3242
- return process.platform === "win32" ? [path7.join(dir, `${tool}.exe`), path7.join(dir, tool)] : [path7.join(dir, tool)];
3540
+ return process.platform === "win32" ? [path6.join(dir, `${tool}.exe`), path6.join(dir, tool)] : [path6.join(dir, tool)];
3243
3541
  }
3244
3542
  function goBinCandidates(tool) {
3245
- const gopath = process.env.GOPATH?.split(path7.delimiter)[0] || path7.join(os6.homedir(), "go");
3246
- return [tool, ...binExeVariants(path7.join(gopath, "bin"), tool)];
3543
+ const gopath = process.env.GOPATH?.split(path6.delimiter)[0] || path6.join(os7.homedir(), "go");
3544
+ return [tool, ...binExeVariants(path6.join(gopath, "bin"), tool)];
3247
3545
  }
3248
3546
  function cargoBinCandidates(tool) {
3249
- const cargoHome = process.env.CARGO_HOME || path7.join(os6.homedir(), ".cargo");
3250
- return [tool, ...binExeVariants(path7.join(cargoHome, "bin"), tool)];
3547
+ const cargoHome = process.env.CARGO_HOME || path6.join(os7.homedir(), ".cargo");
3548
+ return [tool, ...binExeVariants(path6.join(cargoHome, "bin"), tool)];
3251
3549
  }
3252
3550
  async function tryGemInstall(gem) {
3253
- const { join: join3 } = await import("node:path");
3254
- const binDir = join3(getGlobalPiLensDir(), "bin");
3551
+ const { join: join4 } = await import("node:path");
3552
+ const binDir = join4(getGlobalPiLensDir(), "bin");
3255
3553
  const { mkdir } = await import("node:fs/promises");
3256
3554
  await mkdir(binDir, { recursive: true });
3257
3555
  const result = await safeSpawnAsync("gem", ["install", gem, "--bindir", binDir, "--no-document"], { timeout: 18e4, ignoreAmbientSignal: true });
@@ -3285,7 +3583,7 @@ var TS_TOOLING_MARKERS = [
3285
3583
  ];
3286
3584
  var TS_FAMILY_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".mts", ".cts"]);
3287
3585
  function isTsFamilyFile(file) {
3288
- return TS_FAMILY_EXTENSIONS.has(path7.extname(file).toLowerCase());
3586
+ return TS_FAMILY_EXTENSIONS.has(path6.extname(file).toLowerCase());
3289
3587
  }
3290
3588
  function tsConfigMarkersForFile(file) {
3291
3589
  return isTsFamilyFile(file) ? ["tsconfig.json"] : TS_CONFIG_MARKERS;
@@ -3306,40 +3604,40 @@ async function findTypeScriptProjectRoot(file) {
3306
3604
  return isSameOrWithin(toolingRoot, configRoot) ? configRoot : toolingRoot;
3307
3605
  }
3308
3606
  async function findExtensionBoundedRoot(file, extensionRootKey) {
3309
- const startDir = path7.resolve(path7.dirname(file));
3607
+ const startDir = path6.resolve(path6.dirname(file));
3310
3608
  let currentDir = startDir;
3311
3609
  const configMarkers = tsConfigMarkersForFile(file);
3312
3610
  while (true) {
3313
3611
  for (const pattern of configMarkers) {
3314
3612
  try {
3315
- await stat(path7.join(currentDir, pattern));
3613
+ await stat(path6.join(currentDir, pattern));
3316
3614
  return currentDir;
3317
3615
  } catch {
3318
3616
  }
3319
3617
  }
3320
3618
  for (const pattern of TS_TOOLING_MARKERS) {
3321
3619
  try {
3322
- await stat(path7.join(currentDir, pattern));
3620
+ await stat(path6.join(currentDir, pattern));
3323
3621
  return currentDir;
3324
3622
  } catch {
3325
3623
  }
3326
3624
  }
3327
- const currentKey = normalizeEphemeralMapKey(path7.resolve(currentDir));
3625
+ const currentKey = normalizeEphemeralMapKey(path6.resolve(currentDir));
3328
3626
  if (currentKey === extensionRootKey)
3329
3627
  return void 0;
3330
- const parent = path7.dirname(currentDir);
3628
+ const parent = path6.dirname(currentDir);
3331
3629
  if (parent === currentDir)
3332
3630
  return void 0;
3333
3631
  currentDir = parent;
3334
3632
  }
3335
3633
  }
3336
3634
  async function hasAgentLevelProjectMarker(extensionRootKey) {
3337
- const agentDir = path7.dirname(extensionRootKey);
3635
+ const agentDir = path6.dirname(extensionRootKey);
3338
3636
  if (!agentDir || agentDir === extensionRootKey)
3339
3637
  return false;
3340
3638
  for (const pattern of [...TS_CONFIG_MARKERS, ...TS_TOOLING_MARKERS]) {
3341
3639
  try {
3342
- await stat(path7.join(agentDir, pattern));
3640
+ await stat(path6.join(agentDir, pattern));
3343
3641
  return true;
3344
3642
  } catch {
3345
3643
  }
@@ -3367,14 +3665,14 @@ var TypeScriptServer = {
3367
3665
  idleEviction: "transparent",
3368
3666
  role: "language",
3369
3667
  name: "TypeScript Language Server",
3668
+ projectLocalCommand: "typescript-language-server",
3370
3669
  extensions: JS_TS_LSP_EXTENSIONS,
3371
3670
  autoPropagateDiagnostics: true,
3372
3671
  root: TypeScriptRoot,
3373
3672
  rootMarkers: TypeScriptRoot.rootMarkers,
3374
3673
  async spawn(root, options) {
3375
- const fs5 = await import("node:fs/promises");
3376
3674
  const nativeLsp = await findNativeTypeScriptLsp(root);
3377
- if (nativeLsp) {
3675
+ if (nativeLsp && admitProjectSuppliedTsserver(nativeLsp.command, root, true, options?.projectCodeAllowed)) {
3378
3676
  const env2 = await getToolEnvironment();
3379
3677
  logSessionStart(`lsp typescript-native: version=${nativeLsp.version} command=${nativeLsp.command}`);
3380
3678
  const proc2 = await launchLSP(nativeLsp.command, ["--lsp", "--stdio"], {
@@ -3398,14 +3696,10 @@ var TypeScriptServer = {
3398
3696
  return void 0;
3399
3697
  }
3400
3698
  }
3401
- let tsserverPath = await findTsserverPath(root, options?.allowInstall);
3699
+ const tsserverPath = await findTsserverPath(root, options?.allowInstall, options);
3402
3700
  if (!tsserverPath) {
3403
- const localCandidate = path7.join(path7.dirname(lspPath), "..", "typescript", "lib", "tsserver.js");
3404
- try {
3405
- await fs5.access(localCandidate);
3406
- tsserverPath = localCandidate;
3407
- } catch {
3408
- }
3701
+ admitProjectSuppliedTsserver(void 0, root, true, options?.projectCodeAllowed);
3702
+ return void 0;
3409
3703
  }
3410
3704
  if (tsserverPath)
3411
3705
  source = "managed";
@@ -3521,13 +3815,13 @@ var PythonServer = {
3521
3815
  if (!pyrightPath)
3522
3816
  return void 0;
3523
3817
  source = "managed";
3524
- const binDir = path7.dirname(pyrightPath);
3818
+ const binDir = path6.dirname(pyrightPath);
3525
3819
  const isWindows2 = process.platform === "win32";
3526
3820
  const managedCandidates = isWindows2 ? [
3527
- path7.join(binDir, "pyright-langserver.cmd"),
3528
- path7.join(binDir, "pyright-langserver"),
3821
+ path6.join(binDir, "pyright-langserver.cmd"),
3822
+ path6.join(binDir, "pyright-langserver"),
3529
3823
  "pyright-langserver"
3530
- ] : [path7.join(binDir, "pyright-langserver"), "pyright-langserver"];
3824
+ ] : [path6.join(binDir, "pyright-langserver"), "pyright-langserver"];
3531
3825
  const resolved = await resolveAndLaunch({ candidates: managedCandidates, args: ["--stdio"], cwd: root, env }, false);
3532
3826
  if (!resolved)
3533
3827
  return void 0;
@@ -3596,7 +3890,7 @@ var GoServer = {
3596
3890
  }
3597
3891
  };
3598
3892
  function cargoWorkspaceDeclaresMember(workspaceContent, parentDir, childDir) {
3599
- const relativePath = path7.relative(parentDir, childDir).split(path7.sep).join("/");
3893
+ const relativePath = path6.relative(parentDir, childDir).split(path6.sep).join("/");
3600
3894
  if (relativePath === "" || relativePath.startsWith(".."))
3601
3895
  return false;
3602
3896
  const matches = (pattern) => matchesWorkspaceMemberPattern(pattern, relativePath, CARGO_WORKSPACE_MEMBER_DIALECT, () => recordDegradationOnce({
@@ -3610,7 +3904,7 @@ function cargoWorkspaceDeclaresMember(workspaceContent, parentDir, childDir) {
3610
3904
  return readCargoWorkspaceMembers(workspaceContent).some(matches);
3611
3905
  }
3612
3906
  function sessionHoistCeiling(sessionCwd, file) {
3613
- return isSameOrWithin(path7.resolve(sessionCwd), path7.resolve(file)) ? path7.resolve(sessionCwd) : void 0;
3907
+ return isSameOrWithin(path6.resolve(sessionCwd), path6.resolve(file)) ? path6.resolve(sessionCwd) : void 0;
3614
3908
  }
3615
3909
  function RustWorkspaceRoot() {
3616
3910
  const crateRoot = createRootDetector(["Cargo.toml", "Cargo.lock"]);
@@ -3621,14 +3915,14 @@ function RustWorkspaceRoot() {
3621
3915
  const sessionCwd = process.cwd();
3622
3916
  const stop = sessionHoistCeiling(sessionCwd, file);
3623
3917
  let current = root;
3624
- const fsRoot = path7.parse(current).root;
3918
+ const fsRoot = path6.parse(current).root;
3625
3919
  while (true) {
3626
3920
  if (stop !== void 0 && current === stop)
3627
3921
  break;
3628
- const parent = path7.dirname(current);
3922
+ const parent = path6.dirname(current);
3629
3923
  if (parent === current || parent === fsRoot)
3630
3924
  break;
3631
- const parentCargoPath = path7.join(parent, "Cargo.toml");
3925
+ const parentCargoPath = path6.join(parent, "Cargo.toml");
3632
3926
  const parentCargoContent = await readTextFileOrUndefined(parentCargoPath);
3633
3927
  if (parentCargoContent !== void 0 && hasCargoWorkspaceTable(parentCargoContent)) {
3634
3928
  if (cargoWorkspaceDeclaresMember(parentCargoContent, parent, root)) {
@@ -3656,9 +3950,9 @@ async function declaresMavenModule(parentDir, parentPomPath, childDir) {
3656
3950
  const modulesBlock = content.match(/<modules>([\s\S]*?)<\/modules>/);
3657
3951
  if (!modulesBlock)
3658
3952
  return false;
3659
- const resolvedChild = path7.resolve(childDir);
3953
+ const resolvedChild = path6.resolve(childDir);
3660
3954
  for (const match of modulesBlock[1].matchAll(/<module>\s*([^<]+?)\s*<\/module>/g)) {
3661
- if (pathsEqual(path7.resolve(parentDir, match[1]), resolvedChild))
3955
+ if (pathsEqual(path6.resolve(parentDir, match[1]), resolvedChild))
3662
3956
  return true;
3663
3957
  }
3664
3958
  return false;
@@ -3682,15 +3976,15 @@ function JavaWorkspaceRoot() {
3682
3976
  const stop = sessionHoistCeiling(sessionCwd, file);
3683
3977
  let current = root;
3684
3978
  let lastHop = root;
3685
- const fsRoot = path7.parse(current).root;
3979
+ const fsRoot = path6.parse(current).root;
3686
3980
  while (true) {
3687
3981
  if (stop !== void 0 && current === stop)
3688
3982
  break;
3689
- const parent = path7.dirname(current);
3983
+ const parent = path6.dirname(current);
3690
3984
  if (parent === current || parent === fsRoot)
3691
3985
  break;
3692
3986
  if (await markerExists(parent, "pom.xml")) {
3693
- const parentPom = path7.join(parent, "pom.xml");
3987
+ const parentPom = path6.join(parent, "pom.xml");
3694
3988
  if (!await declaresMavenModule(parent, parentPom, lastHop))
3695
3989
  break;
3696
3990
  current = parent;
@@ -3709,6 +4003,7 @@ var RustServer = {
3709
4003
  // Measured (#3750): rust-analyzer answers an empty result for a detached file.
3710
4004
  requiresProjectRoot: true,
3711
4005
  name: "rust-analyzer",
4006
+ executesProjectCode: true,
3712
4007
  extensions: KIND_EXTENSIONS["rust"],
3713
4008
  // No FileDirRoot fallback (#201): rust-analyzer is a heavy workspace server
3714
4009
  // that is useless without a Cargo manifest. With the fallback, every .rs file
@@ -3742,6 +4037,7 @@ var RubyServer = {
3742
4037
  idleEviction: "unmeasured",
3743
4038
  role: "language",
3744
4039
  name: "Ruby LSP",
4040
+ executesProjectCode: true,
3745
4041
  extensions: KIND_EXTENSIONS["ruby"],
3746
4042
  root: RootWithFallback(PriorityRoot([["Gemfile", ".ruby-version"], [".git"]])),
3747
4043
  // Ruby LSP may need extra time to finish composed-bundle setup before it can
@@ -3794,17 +4090,17 @@ var PHPServer = {
3794
4090
  return {
3795
4091
  ...result,
3796
4092
  initialization: {
3797
- storagePath: path7.join(getGlobalPiLensDir(), "intelephense")
4093
+ storagePath: path6.join(getGlobalPiLensDir(), "intelephense")
3798
4094
  }
3799
4095
  };
3800
4096
  }
3801
4097
  };
3802
4098
  function buildPsesArgs(bundleDir) {
3803
- const script = path7.join(bundleDir, "PowerShellEditorServices", "Start-EditorServices.ps1");
3804
- const sessionDir = path7.join(getGlobalPiLensDir(), "pses", `${process.pid}-${Date.now()}`);
4099
+ const script = path6.join(bundleDir, "PowerShellEditorServices", "Start-EditorServices.ps1");
4100
+ const sessionDir = path6.join(getGlobalPiLensDir(), "pses", `${process.pid}-${Date.now()}`);
3805
4101
  mkdirSync2(sessionDir, { recursive: true });
3806
- const logPath = path7.join(sessionDir, "pses.log");
3807
- const sessionDetailsPath = path7.join(sessionDir, "session.json");
4102
+ const logPath = path6.join(sessionDir, "pses.log");
4103
+ const sessionDetailsPath = path6.join(sessionDir, "session.json");
3808
4104
  return [
3809
4105
  "-NoLogo",
3810
4106
  "-NoProfile",
@@ -3838,6 +4134,7 @@ var PowerShellServer = {
3838
4134
  idleEviction: "unmeasured",
3839
4135
  role: "language",
3840
4136
  name: "PowerShell Editor Services",
4137
+ executesProjectCode: true,
3841
4138
  extensions: KIND_EXTENSIONS["powershell"],
3842
4139
  // Index at the workspace (script modules reference siblings); fall back to the
3843
4140
  // file dir.
@@ -3858,6 +4155,7 @@ var CSharpServer = {
3858
4155
  // Documented (#3750): csharp-ls needs a solution or project.
3859
4156
  requiresProjectRoot: true,
3860
4157
  name: "csharp-ls",
4158
+ executesProjectCode: true,
3861
4159
  extensions: KIND_EXTENSIONS["csharp"],
3862
4160
  // No FileDirRoot fallback (#201): csharp-ls is a workspace server and should
3863
4161
  // not spawn once per source directory before a .sln/.csproj exists. Glob root
@@ -3881,6 +4179,7 @@ var CSharpServer = {
3881
4179
  var OmniSharpServer = createInteractiveServer({
3882
4180
  id: "omnisharp",
3883
4181
  name: "OmniSharp",
4182
+ executesProjectCode: true,
3884
4183
  // Documented (#3750): OmniSharp needs a solution or project.
3885
4184
  requiresProjectRoot: true,
3886
4185
  fallbackFor: "csharp",
@@ -3897,6 +4196,7 @@ var FSharpServer = {
3897
4196
  // Documented (#3750): FSAutocomplete needs a project.
3898
4197
  requiresProjectRoot: true,
3899
4198
  name: "FSAutocomplete",
4199
+ executesProjectCode: true,
3900
4200
  extensions: KIND_EXTENSIONS["fsharp"],
3901
4201
  root: createRootDetector([...DOTNET_FSHARP_ROOT_MARKERS]),
3902
4202
  async spawn(root, options) {
@@ -3916,6 +4216,7 @@ var FSharpServer = {
3916
4216
  var JavaServer = createInteractiveServer({
3917
4217
  id: "java",
3918
4218
  name: "JDT Language Server",
4219
+ executesProjectCode: true,
3919
4220
  extensions: KIND_EXTENSIONS["java"],
3920
4221
  root: RootWithFallback(JavaWorkspaceRoot()),
3921
4222
  language: "java",
@@ -3928,6 +4229,7 @@ var KotlinServer = {
3928
4229
  idleEviction: "unmeasured",
3929
4230
  role: "language",
3930
4231
  name: "Kotlin Language Server",
4232
+ executesProjectCode: true,
3931
4233
  extensions: KIND_EXTENSIONS["kotlin"],
3932
4234
  root: RootWithFallback(createRootDetector(["build.gradle.kts", "build.gradle", "pom.xml"])),
3933
4235
  async spawn(root, options) {
@@ -3943,9 +4245,26 @@ var KotlinServer = {
3943
4245
  }, options?.allowInstall);
3944
4246
  }
3945
4247
  };
4248
+ var LeanServer = createInteractiveServer({
4249
+ id: "lean",
4250
+ name: "Lean 4 Language Server",
4251
+ extensions: KIND_EXTENSIONS.lean,
4252
+ root: RootWithFallback(createRootDetector([...LEAN_ROOT_MARKERS])),
4253
+ language: "lean",
4254
+ // Documented (#3750): `lake serve` analyses a file only when a Lake project
4255
+ // roots it; a root-fallback empty answer is unconfirmed, not clean.
4256
+ requiresProjectRoot: true,
4257
+ // `lake serve` loads and interprets the project's `lakefile.lean` and its
4258
+ // imported build scripts, so it executes project-owned code: refuse it until
4259
+ // the host grants project trust (#4269).
4260
+ executesProjectCode: true,
4261
+ command: "lake",
4262
+ args: ["serve"]
4263
+ });
3946
4264
  var SwiftServer = createInteractiveServer({
3947
4265
  id: "swift",
3948
4266
  name: "SourceKit-LSP",
4267
+ executesProjectCode: true,
3949
4268
  extensions: KIND_EXTENSIONS["swift"],
3950
4269
  root: createRootDetector(["Package.swift"]),
3951
4270
  language: "swift",
@@ -3953,6 +4272,7 @@ var SwiftServer = createInteractiveServer({
3953
4272
  });
3954
4273
  var DartServer = createInteractiveServer({
3955
4274
  id: "dart",
4275
+ executesProjectCode: true,
3956
4276
  name: "Dart Analysis Server",
3957
4277
  extensions: KIND_EXTENSIONS["dart"],
3958
4278
  root: RootWithFallback(createRootDetector(["pubspec.yaml"])),
@@ -4006,6 +4326,7 @@ var CppServer = createTreeBinaryServer({
4006
4326
  });
4007
4327
  var ZigServer = {
4008
4328
  id: "zig",
4329
+ executesProjectCode: true,
4009
4330
  idleEviction: "unmeasured",
4010
4331
  role: "language",
4011
4332
  name: "ZLS",
@@ -4023,6 +4344,7 @@ var ZigServer = {
4023
4344
  var HaskellServer = createInteractiveServer({
4024
4345
  id: "haskell",
4025
4346
  name: "Haskell Language Server",
4347
+ executesProjectCode: true,
4026
4348
  extensions: KIND_EXTENSIONS["haskell"],
4027
4349
  root: createRootDetector(["stack.yaml", "cabal.project", "*.cabal"]),
4028
4350
  language: "haskell",
@@ -4032,6 +4354,7 @@ var HaskellServer = createInteractiveServer({
4032
4354
  var ElixirServer = createInteractiveServer({
4033
4355
  id: "elixir",
4034
4356
  name: "ElixirLS",
4357
+ executesProjectCode: true,
4035
4358
  extensions: KIND_EXTENSIONS["elixir"],
4036
4359
  root: RootWithFallback(createRootDetector(["mix.exs"])),
4037
4360
  language: "elixir",
@@ -4042,6 +4365,7 @@ var ElixirExpertServer = {
4042
4365
  idleEviction: "unmeasured",
4043
4366
  role: "language",
4044
4367
  name: "Expert",
4368
+ executesProjectCode: true,
4045
4369
  fallbackFor: "elixir",
4046
4370
  extensions: KIND_EXTENSIONS["elixir"],
4047
4371
  root: RootWithFallback(createRootDetector(["mix.exs"])),
@@ -4074,6 +4398,7 @@ var GleamServer = {
4074
4398
  };
4075
4399
  var TinymistServer = {
4076
4400
  id: "tinymist",
4401
+ executesProjectCode: true,
4077
4402
  idleEviction: "unmeasured",
4078
4403
  role: "language",
4079
4404
  name: "Tinymist",
@@ -4110,6 +4435,7 @@ var MarksmanServer = {
4110
4435
  };
4111
4436
  var OCamlServer = createInteractiveServer({
4112
4437
  id: "ocaml",
4438
+ executesProjectCode: true,
4113
4439
  name: "ocamllsp",
4114
4440
  extensions: KIND_EXTENSIONS["ocaml"],
4115
4441
  root: createRootDetector(["dune-project", "opam"]),
@@ -4118,6 +4444,7 @@ var OCamlServer = createInteractiveServer({
4118
4444
  });
4119
4445
  var ClojureServer = {
4120
4446
  id: "clojure",
4447
+ executesProjectCode: true,
4121
4448
  idleEviction: "transparent",
4122
4449
  role: "language",
4123
4450
  name: "Clojure LSP",
@@ -4150,6 +4477,7 @@ var CueServer = {
4150
4477
  };
4151
4478
  var TerraformServer = {
4152
4479
  id: "terraform",
4480
+ executesProjectCode: true,
4153
4481
  idleEviction: "unmeasured",
4154
4482
  role: "language",
4155
4483
  name: "Terraform LSP",
@@ -4166,6 +4494,7 @@ var TerraformServer = {
4166
4494
  };
4167
4495
  var NixServer = createInteractiveServer({
4168
4496
  id: "nix",
4497
+ executesProjectCode: true,
4169
4498
  name: "nixd",
4170
4499
  extensions: KIND_EXTENSIONS["nix"],
4171
4500
  root: createRootDetector(["flake.nix"]),
@@ -4234,6 +4563,7 @@ var FishServer = {
4234
4563
  };
4235
4564
  var CMakeServer = {
4236
4565
  id: "cmake",
4566
+ executesProjectCode: true,
4237
4567
  idleEviction: "unmeasured",
4238
4568
  role: "language",
4239
4569
  name: "CMake Language Server",
@@ -4404,6 +4734,7 @@ var PrismaServer = {
4404
4734
  };
4405
4735
  var VueServer = {
4406
4736
  id: "vue",
4737
+ executesProjectCode: true,
4407
4738
  idleEviction: "unmeasured",
4408
4739
  role: "language",
4409
4740
  name: "Vue Language Server",
@@ -4426,9 +4757,9 @@ var VueServer = {
4426
4757
  clientWaitTimeoutMs: 3e4,
4427
4758
  initializeTimeoutMs: 3e4,
4428
4759
  async spawn(root, options) {
4429
- const tsserverPath = await findTsserverPath(root, options?.allowInstall);
4430
- const hasPackageJson = existsSync3(path7.join(root, "package.json"));
4431
- const hasNodeModules = existsSync3(path7.join(root, "node_modules"));
4760
+ const tsserverPath = await findTsserverPath(root, options?.allowInstall, options);
4761
+ const hasPackageJson = existsSync3(path6.join(root, "package.json"));
4762
+ const hasNodeModules = existsSync3(path6.join(root, "node_modules"));
4432
4763
  if (hasPackageJson && !hasNodeModules) {
4433
4764
  logSessionStart(`lsp vue: node_modules missing in ${root} \u2014 Vue navigation may be limited. Run: npm install (or pnpm/yarn install) in this project.`);
4434
4765
  }
@@ -4443,12 +4774,13 @@ var VueServer = {
4443
4774
  return {
4444
4775
  process: proc.process,
4445
4776
  source: proc.source,
4446
- initialization: tsserverPath ? { typescript: { tsdk: path7.dirname(tsserverPath) } } : void 0
4777
+ initialization: tsserverPath ? { typescript: { tsdk: path6.dirname(tsserverPath) } } : void 0
4447
4778
  };
4448
4779
  }
4449
4780
  };
4450
4781
  var SvelteServer = {
4451
4782
  id: "svelte",
4783
+ executesProjectCode: true,
4452
4784
  idleEviction: "unmeasured",
4453
4785
  role: "language",
4454
4786
  name: "Svelte Language Server",
@@ -4469,7 +4801,7 @@ var SvelteServer = {
4469
4801
  clientWaitTimeoutMs: 2e4,
4470
4802
  initializeTimeoutMs: 2e4,
4471
4803
  async spawn(root, options) {
4472
- const tsserverPath = await findTsserverPath(root, options?.allowInstall);
4804
+ const tsserverPath = await findTsserverPath(root, options?.allowInstall, options);
4473
4805
  const proc = await resolveAndLaunch({
4474
4806
  candidates: [
4475
4807
  ...nodeBinCandidates(root, "svelteserver"),
@@ -4484,7 +4816,7 @@ var SvelteServer = {
4484
4816
  return {
4485
4817
  process: proc.process,
4486
4818
  source: proc.source,
4487
- initialization: tsserverPath ? { typescript: { tsdk: path7.dirname(tsserverPath) } } : void 0
4819
+ initialization: tsserverPath ? { typescript: { tsdk: path6.dirname(tsserverPath) } } : void 0
4488
4820
  };
4489
4821
  }
4490
4822
  };
@@ -4735,6 +5067,7 @@ var LSP_SERVERS = [
4735
5067
  FSharpServer,
4736
5068
  JavaServer,
4737
5069
  KotlinServer,
5070
+ LeanServer,
4738
5071
  SwiftServer,
4739
5072
  DartServer,
4740
5073
  LuaServer,
@@ -4777,6 +5110,54 @@ function getServerById(id) {
4777
5110
  return LSP_SERVERS.find((server) => server.id === id);
4778
5111
  }
4779
5112
 
5113
+ // dist/clients/lsp/session-roots.js
5114
+ import path7 from "node:path";
5115
+ var SESSION_ROOT_CAP = 128;
5116
+ var sessionRoots = new BoundedFifoMap(SESSION_ROOT_CAP);
5117
+ function noteEvictedRoots(evicted) {
5118
+ const roots = evicted.map(([root]) => root);
5119
+ if (roots.length > 0) {
5120
+ incrementDegradationCount({
5121
+ kind: "lsp-session-root-evicted",
5122
+ subject: `cap=${SESSION_ROOT_CAP}`,
5123
+ 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]})`
5124
+ });
5125
+ }
5126
+ return roots;
5127
+ }
5128
+ function registerSessionRoot(cwd) {
5129
+ const root = path7.resolve(cwd);
5130
+ if (sessionRoots.has(root))
5131
+ return [];
5132
+ return noteEvictedRoots(sessionRoots.set(root, void 0));
5133
+ }
5134
+ function setSessionRootConfig(cwd, config) {
5135
+ return noteEvictedRoots(sessionRoots.set(path7.resolve(cwd), config));
5136
+ }
5137
+ function sessionRootConfigEntries() {
5138
+ return sessionRoots;
5139
+ }
5140
+ function isSessionRootRegistered(cwd) {
5141
+ return sessionRoots.has(path7.resolve(cwd));
5142
+ }
5143
+ function shouldInitializeSessionRoot(cwd, readyRoots) {
5144
+ const normalized = path7.resolve(cwd);
5145
+ return !readyRoots.has(normalized) || !isSessionRootRegistered(normalized);
5146
+ }
5147
+ function isOutsideAllSessionRoots(filePath) {
5148
+ if (sessionRoots.size === 0)
5149
+ return false;
5150
+ const resolved = path7.resolve(filePath);
5151
+ for (const root of sessionRoots.keys()) {
5152
+ if (isSameOrWithin(root, resolved))
5153
+ return false;
5154
+ }
5155
+ return true;
5156
+ }
5157
+ function getSessionRootsForTelemetry(limit = 4) {
5158
+ return [...sessionRoots.keys()].sort((a, b) => Number(a > b) - Number(a < b)).slice(0, limit);
5159
+ }
5160
+
4780
5161
  export {
4781
5162
  findLocalTyposConfig,
4782
5163
  getAstGrepRuleSources,
@@ -4784,6 +5165,11 @@ export {
4784
5165
  isAuxiliary,
4785
5166
  serverTraits,
4786
5167
  registerRunnerId,
5168
+ withLspProjectCodePermission,
5169
+ refuseUntrustedLspExecution,
5170
+ resolveAnalysisRootPath,
5171
+ resolveAnalysisRoot,
5172
+ canWriteAnalysisRoot,
4787
5173
  BUILTIN_SERVER_RUNNER_COVERS,
4788
5174
  describeRootFallback,
4789
5175
  resolveLspServerCwd,