pi-lens 4.4.0 → 4.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/CHANGELOG.md +158 -0
  2. package/README.md +1 -0
  3. package/config/dependency-cruiser-eager-allowlist.json +3 -1
  4. package/dist/{chunk-IXEBJSUE.js → chunk-23WOO7TX.js} +11 -10
  5. package/dist/{chunk-E2WFBRHM.js → chunk-2B73ZPOK.js} +3 -3
  6. package/dist/{chunk-DQ2NZ7C4.js → chunk-2BBLFX75.js} +139 -11
  7. package/dist/{chunk-ZMOL6RQC.js → chunk-2CD5RS6M.js} +2 -2
  8. package/dist/{chunk-VD4JQCQC.js → chunk-2ZGIZPDG.js} +38 -37
  9. package/dist/{chunk-DIULIWKU.js → chunk-34WSVGEM.js} +29 -3
  10. package/dist/chunk-3JNKMP3B.js +17 -0
  11. package/dist/{chunk-HVUZXSWK.js → chunk-3UUPUE3O.js} +45 -41
  12. package/dist/{chunk-5RXBNSW2.js → chunk-3VPVG5SU.js} +16 -8
  13. package/dist/chunk-3WA4HP4I.js +55 -0
  14. package/dist/chunk-466Z26MG.js +30 -0
  15. package/dist/{chunk-34U7PLGE.js → chunk-4UNXBNE2.js} +12 -11
  16. package/dist/{chunk-E4MAEDPM.js → chunk-4W4VLAPJ.js} +12 -11
  17. package/dist/{chunk-GUOGVTCQ.js → chunk-52FUZI6K.js} +4 -2
  18. package/dist/chunk-5JM2524W.js +84 -0
  19. package/dist/{chunk-VHQLPIRJ.js → chunk-5NGR67U5.js} +2 -2
  20. package/dist/{chunk-DFVMIKMQ.js → chunk-66SXUJCA.js} +45 -9
  21. package/dist/{chunk-OQGFCZJ4.js → chunk-66VMUL7Q.js} +16 -16
  22. package/dist/{chunk-HWBZIGMJ.js → chunk-67WMNWEE.js} +9 -8
  23. package/dist/{chunk-6QIUX4EP.js → chunk-6C7JWPCP.js} +7 -7
  24. package/dist/{chunk-47D2BO4S.js → chunk-6GFRNVYR.js} +11 -10
  25. package/dist/{chunk-ZAWW3AF6.js → chunk-6SDEP3AJ.js} +9 -8
  26. package/dist/{chunk-TGPXLY2J.js → chunk-724UL2TJ.js} +38 -2
  27. package/dist/{chunk-YLF3PPT5.js → chunk-76MWC2K6.js} +3 -3
  28. package/dist/{chunk-F3P7VQ7H.js → chunk-7DYBLXYA.js} +3 -3
  29. package/dist/{chunk-AJ3QXTRQ.js → chunk-7ULNCDCO.js} +37 -35
  30. package/dist/{chunk-UK3CMEAL.js → chunk-AOHZU2BM.js} +1 -1
  31. package/dist/{chunk-F3AU2TMM.js → chunk-ARJ6PLFH.js} +44 -28
  32. package/dist/{chunk-U3VGR4VU.js → chunk-AW7R2GWG.js} +65 -8
  33. package/dist/{chunk-FJNKANOS.js → chunk-BKE7E7TC.js} +10 -9
  34. package/dist/{chunk-4W2PLC4J.js → chunk-BRGDSBJE.js} +13 -12
  35. package/dist/{chunk-VN2AXLEX.js → chunk-C34ZXZ7O.js} +102 -16
  36. package/dist/{chunk-T4ERT73Y.js → chunk-CF24OIYI.js} +21 -20
  37. package/dist/{chunk-NMABWBZN.js → chunk-CHOW74WE.js} +1 -1
  38. package/dist/{chunk-ARFCXG7S.js → chunk-CO2G5PHH.js} +2 -2
  39. package/dist/{chunk-AS44CFQ4.js → chunk-EETY4LF7.js} +24 -10
  40. package/dist/{chunk-I3R7GKDR.js → chunk-EJIUXPBX.js} +1 -1
  41. package/dist/{chunk-B3I2UUT4.js → chunk-ESBQHFMJ.js} +653 -251
  42. package/dist/{chunk-O5WILGMI.js → chunk-FLKWCIZH.js} +8 -5
  43. package/dist/{chunk-LNAKEO63.js → chunk-GAEGEPOC.js} +4 -4
  44. package/dist/{chunk-V7R425XG.js → chunk-GBPP5P7X.js} +4 -4
  45. package/dist/{chunk-XK4CIILI.js → chunk-GHB6AYCP.js} +27 -26
  46. package/dist/{chunk-7FBF7NPR.js → chunk-GOYRYZ2R.js} +29 -26
  47. package/dist/{chunk-JOLITGCC.js → chunk-GXW6K4YJ.js} +1 -1
  48. package/dist/{chunk-NXPS2L6U.js → chunk-GZYBXISR.js} +9 -9
  49. package/dist/{chunk-OAXNDZJ7.js → chunk-IBGIOBEE.js} +8 -4
  50. package/dist/{chunk-IIXOAGHR.js → chunk-IBRALRBR.js} +19 -15
  51. package/dist/{chunk-7F72PXY2.js → chunk-IXXDFY73.js} +3 -3
  52. package/dist/chunk-IZSHAIJ7.js +26 -0
  53. package/dist/{chunk-N3YQJI6O.js → chunk-JAU72XNY.js} +20 -15
  54. package/dist/chunk-JMYYOTQG.js +77 -0
  55. package/dist/{chunk-NL5GLMFI.js → chunk-JSQFTSC6.js} +12 -11
  56. package/dist/{chunk-IVP5KAB7.js → chunk-JWA5I3SR.js} +35 -21
  57. package/dist/{chunk-Y4T27COV.js → chunk-KBG47GHP.js} +4 -4
  58. package/dist/{chunk-PWFHBTSF.js → chunk-L3BLXZG7.js} +2 -2
  59. package/dist/chunk-LOP6GRPH.js +31 -0
  60. package/dist/chunk-MQLUAEOC.js +162 -0
  61. package/dist/{chunk-RPD5K5NC.js → chunk-MRXKYPCS.js} +2 -2
  62. package/dist/{chunk-Q5U6FMDI.js → chunk-N2H3ZPZY.js} +59 -7
  63. package/dist/{chunk-27BBHSZC.js → chunk-N3X6Q4YT.js} +3 -3
  64. package/dist/{chunk-UY752QSN.js → chunk-NW7BOILN.js} +19 -17
  65. package/dist/{chunk-TLGFBM3F.js → chunk-ODBNXWT4.js} +302 -113
  66. package/dist/{chunk-TT22N74J.js → chunk-OG5UUPN7.js} +13 -13
  67. package/dist/{chunk-C2AUYXQH.js → chunk-OXXKQCSV.js} +6 -6
  68. package/dist/{chunk-PAK6IDME.js → chunk-P7266HLM.js} +2 -2
  69. package/dist/{chunk-52H5B5TC.js → chunk-PFNSLWU5.js} +31 -30
  70. package/dist/{chunk-5ZEJHV35.js → chunk-PHDMFAKM.js} +12 -2
  71. package/dist/{chunk-3I5QFT3N.js → chunk-PNQDPDRC.js} +6 -3
  72. package/dist/{chunk-TVN5N6GY.js → chunk-PV6LR5P5.js} +6057 -1311
  73. package/dist/{chunk-AHDW3GPE.js → chunk-Q6ZWTCLY.js} +24 -22
  74. package/dist/{chunk-RUOG5D2C.js → chunk-Q7DUK4LX.js} +5 -5
  75. package/dist/{chunk-YSNFMJKN.js → chunk-QGQ5CCCG.js} +2 -2
  76. package/dist/{chunk-LW6XKDZQ.js → chunk-QHJ6DQ2R.js} +287 -38
  77. package/dist/{chunk-GVXA6CCE.js → chunk-QILIYSJF.js} +1 -1
  78. package/dist/{chunk-22AVIGXS.js → chunk-QOBUHWHX.js} +12 -7
  79. package/dist/{chunk-5THXD6X5.js → chunk-QQNQ7SJW.js} +2 -75
  80. package/dist/{chunk-4RERDEXR.js → chunk-QZVW3AAK.js} +50 -41
  81. package/dist/{chunk-AWE5TMPO.js → chunk-R3PCY3YS.js} +14 -13
  82. package/dist/{chunk-HHYAELLJ.js → chunk-RPLINGVB.js} +7 -7
  83. package/dist/{chunk-EOKRQ3SA.js → chunk-RWWYJ623.js} +10 -1
  84. package/dist/{chunk-YGVYKNSN.js → chunk-SAEFPSB6.js} +59 -12
  85. package/dist/{chunk-K6VYLB2U.js → chunk-TYM6OUIT.js} +13 -12
  86. package/dist/{chunk-AJXODZY6.js → chunk-UXGETRP4.js} +13 -12
  87. package/dist/{chunk-I7SPGOWI.js → chunk-UZ3TUTRV.js} +1 -1
  88. package/dist/{chunk-TO6WL3GM.js → chunk-VCHXCQFG.js} +12 -2
  89. package/dist/{chunk-BRJKOBZP.js → chunk-VITUCP5A.js} +17 -6
  90. package/dist/{chunk-WONERJTP.js → chunk-VRZP76YM.js} +1 -1
  91. package/dist/{chunk-OS32RNR2.js → chunk-VSSBEUFT.js} +16 -15
  92. package/dist/{chunk-VDBPVIKD.js → chunk-VWCXW2E7.js} +1 -1
  93. package/dist/{chunk-NKOUFPQF.js → chunk-WOA4LN2R.js} +2 -2
  94. package/dist/{chunk-RIISMHKN.js → chunk-WOKKRCJ5.js} +15 -13
  95. package/dist/{chunk-XECLWQZN.js → chunk-WU567RSC.js} +13 -10
  96. package/dist/{chunk-V5PXFSZK.js → chunk-XCJN5IJU.js} +4 -4
  97. package/dist/{chunk-ORTMY4Q4.js → chunk-XKIZ2HNN.js} +22 -14
  98. package/dist/{chunk-6VCD4VKZ.js → chunk-XMSXXAHW.js} +2 -2
  99. package/dist/chunk-XPZPXCBV.js +28 -0
  100. package/dist/{chunk-O6TQT6RI.js → chunk-XTMJ7XDT.js} +1 -1
  101. package/dist/{chunk-VNMT7C64.js → chunk-XVF2VVXP.js} +5 -3
  102. package/dist/chunk-Z527N6YQ.js +28 -0
  103. package/dist/{chunk-NTUJEGDZ.js → chunk-ZANLILYD.js} +22 -2
  104. package/dist/{chunk-6P62QVEA.js → chunk-ZGDGW2V3.js} +2 -2
  105. package/dist/{chunk-ODEEOXPH.js → chunk-ZHFZWTZV.js} +12 -11
  106. package/dist/{chunk-AJVLWNFN.js → chunk-ZXYDQ3TZ.js} +5 -5
  107. package/dist/index.js +102097 -97778
  108. package/dist/mcp/analyze-cli.js +18 -8
  109. package/dist/mcp/cli.js +48 -37
  110. package/dist/mcp/server.js +695 -426
  111. package/dist/mcp/worker.js +66 -55
  112. package/dist/probes/installer.js +20 -19
  113. package/docs/ast-grep_rules_catalog.md +2 -2
  114. package/docs/astplayground.md +1 -1
  115. package/docs/configuration.md +38 -2
  116. package/docs/custom-rules.md +25 -2
  117. package/docs/dependencies.md +3 -0
  118. package/docs/environment-variables.md +34 -6
  119. package/docs/features.md +39 -3
  120. package/docs/globalconfig.md +3 -1
  121. package/docs/language-coverage.md +2 -1
  122. package/docs/lsp-capability-matrix.md +29 -6
  123. package/docs/lsp-idle-eviction.md +38 -47
  124. package/docs/pi-lens-fixer.md +13 -5
  125. package/docs/pi-lens-merge-policy.md +48 -1
  126. package/docs/pi-lens-monitor.md +3 -1
  127. package/docs/pi-lens-retro.md +2 -1
  128. package/docs/pi-lens-reviewer.md +19 -3
  129. package/docs/pi-lens-subagent.md +2 -0
  130. package/docs/real-harness.md +17 -0
  131. package/docs/schema/pi-lens-config-v1.json +565 -0
  132. package/docs/servercapabilities.md +18 -4
  133. package/docs/settings.md +2 -1
  134. package/docs/word-index.md +6 -0
  135. package/package.json +1 -1
  136. package/rules/ast-grep-rules/rule-tests/no-identity-operator-on-literals-test.yml +12 -2
  137. package/rules/ast-grep-rules/rule-tests/throw-new-error-js-test.yml +2 -0
  138. package/rules/ast-grep-rules/rule-tests/throw-new-error-test.yml +2 -0
  139. package/rules/ast-grep-rules/rules/no-identity-operator-on-literals.yml +20 -12
  140. package/rules/ast-grep-rules/rules/throw-new-error-js.yml +2 -2
  141. package/rules/ast-grep-rules/rules/throw-new-error.yml +2 -2
  142. package/dist/chunk-54B42YRX.js +0 -27
  143. package/dist/chunk-CGGAIP7J.js +0 -29
  144. package/dist/chunk-D6QS3U6M.js +0 -30
  145. package/dist/chunk-HMFXILPV.js +0 -83
  146. package/dist/chunk-LSMJ6KBE.js +0 -4139
  147. package/dist/chunk-NTHH2J74.js +0 -27
  148. package/dist/chunk-RPD4ZSB7.js +0 -25
  149. package/dist/chunk-T6D4Q4AQ.js +0 -54
  150. package/dist/chunk-VCSRSYLF.js +0 -81
@@ -7,33 +7,32 @@ import {
7
7
  deadCodeIssues,
8
8
  formatDeadCodeDelta,
9
9
  stableFindingKey
10
- } from "../chunk-TT22N74J.js";
10
+ } from "../chunk-OG5UUPN7.js";
11
11
  import {
12
12
  clearFormatterCache
13
- } from "../chunk-F3AU2TMM.js";
13
+ } from "../chunk-ARJ6PLFH.js";
14
14
  import {
15
15
  findNearestProjectRoot,
16
- getStartupScanMaxEntries,
17
16
  isStartupScanVerdictFresh,
18
17
  resolveStartupScanContext
19
- } from "../chunk-5RXBNSW2.js";
18
+ } from "../chunk-3VPVG5SU.js";
20
19
  import {
21
20
  loadCallGraph
22
- } from "../chunk-NKOUFPQF.js";
21
+ } from "../chunk-WOA4LN2R.js";
23
22
  import {
24
23
  RUNNERS,
25
24
  formatRunDurationMs,
26
25
  isExcludedTestTarget,
27
26
  isRunnerErrorResult
28
- } from "../chunk-IVP5KAB7.js";
27
+ } from "../chunk-JWA5I3SR.js";
29
28
  import {
30
29
  GovulncheckClient
31
- } from "../chunk-NXPS2L6U.js";
30
+ } from "../chunk-GZYBXISR.js";
32
31
  import {
33
32
  GitleaksClient,
34
33
  classifyAndFilterFindings
35
- } from "../chunk-BRJKOBZP.js";
36
- import "../chunk-6P62QVEA.js";
34
+ } from "../chunk-VITUCP5A.js";
35
+ import "../chunk-ZGDGW2V3.js";
37
36
  import {
38
37
  isHardFailureSummary
39
38
  } from "../chunk-7U2IDNXN.js";
@@ -54,12 +53,13 @@ import {
54
53
  peekTurnEndFindings,
55
54
  snapshotAdvisoryProvenance,
56
55
  validateAdvisoryProvenance
57
- } from "../chunk-Y4T27COV.js";
56
+ } from "../chunk-KBG47GHP.js";
58
57
  import {
59
58
  CacheManager,
60
59
  DELTA_UNUSED_PROMOTION_NOTE,
61
60
  MCP_TURN_STATE_OWNER_ID,
62
61
  acquireWarmWordIndex,
62
+ admitsProjectWideFindings,
63
63
  analyzeFile,
64
64
  applyFindingPolicy,
65
65
  applyLspFindingPolicy,
@@ -99,56 +99,42 @@ import {
99
99
  resetPendingRunnerFindings,
100
100
  resetPsScriptAnalyzerAvailability,
101
101
  runProviders
102
- } from "../chunk-TLGFBM3F.js";
102
+ } from "../chunk-ODBNXWT4.js";
103
103
  import {
104
104
  resetLazyInstallAttempts
105
- } from "../chunk-C2AUYXQH.js";
105
+ } from "../chunk-OXXKQCSV.js";
106
106
  import {
107
107
  isTestRoleCollateral
108
- } from "../chunk-I3R7GKDR.js";
108
+ } from "../chunk-EJIUXPBX.js";
109
109
  import "../chunk-FC4RP4I2.js";
110
110
  import {
111
111
  TrivyClient
112
- } from "../chunk-DFVMIKMQ.js";
113
- import "../chunk-TO6WL3GM.js";
114
- import "../chunk-7F72PXY2.js";
112
+ } from "../chunk-66SXUJCA.js";
113
+ import "../chunk-VCHXCQFG.js";
114
+ import "../chunk-IXXDFY73.js";
115
115
  import {
116
116
  resetRustAvailability
117
- } from "../chunk-RPD5K5NC.js";
117
+ } from "../chunk-MRXKYPCS.js";
118
118
  import {
119
119
  findingsResult,
120
120
  hasUsableResult
121
- } from "../chunk-ZMOL6RQC.js";
122
- import "../chunk-I7SPGOWI.js";
123
- import "../chunk-AS44CFQ4.js";
121
+ } from "../chunk-2CD5RS6M.js";
122
+ import "../chunk-UZ3TUTRV.js";
123
+ import "../chunk-EETY4LF7.js";
124
124
  import {
125
125
  getSgCommand,
126
126
  resetDispatchAvailabilityState,
127
127
  resolveManagedToolClient
128
- } from "../chunk-7FBF7NPR.js";
129
- import "../chunk-HHYAELLJ.js";
130
- import "../chunk-V5PXFSZK.js";
131
- import "../chunk-YLF3PPT5.js";
132
- import "../chunk-JOLITGCC.js";
133
- import "../chunk-E2WFBRHM.js";
128
+ } from "../chunk-GOYRYZ2R.js";
129
+ import "../chunk-RPLINGVB.js";
130
+ import "../chunk-XCJN5IJU.js";
131
+ import "../chunk-76MWC2K6.js";
132
+ import "../chunk-GXW6K4YJ.js";
133
+ import "../chunk-2B73ZPOK.js";
134
134
  import {
135
135
  resetGoAvailability
136
- } from "../chunk-6VCD4VKZ.js";
137
- import "../chunk-F3P7VQ7H.js";
138
- import {
139
- SYMBOL_KIND_NAMES,
140
- captureReviewGraphStructuralIr,
141
- formatReviewGraphRevisionDriftNote,
142
- publishReviewGraphFileIr,
143
- reviewGraphIrContentHash
144
- } from "../chunk-LSMJ6KBE.js";
145
- import {
146
- buildSymbolId
147
- } from "../chunk-3MED75HT.js";
148
- import {
149
- resolveGitCheckout,
150
- resolveLinkedWorktreeOwner
151
- } from "../chunk-3I5QFT3N.js";
136
+ } from "../chunk-XMSXXAHW.js";
137
+ import "../chunk-7DYBLXYA.js";
152
138
  import {
153
139
  DEPENDENCY_DRIFT_MAX_DELIVERIES,
154
140
  MAX_LATE_AUX_REARMS,
@@ -156,7 +142,7 @@ import {
156
142
  RUNTIME_CONFIG,
157
143
  ReadGuard,
158
144
  STALE_LINE_MARKER,
159
- SWEEP_IDLE_SAFETY_MARGIN_MS,
145
+ SYMBOL_KIND_NAMES,
160
146
  Text,
161
147
  TreeSitterSymbolExtractor,
162
148
  WriteOrderingGuard,
@@ -171,6 +157,8 @@ import {
171
157
  beginScope,
172
158
  buildScopeKey,
173
159
  canRearmPendingAuxiliary,
160
+ canWriteAnalysisRoot,
161
+ captureReviewGraphStructuralIr,
174
162
  classifyDiagnosticTier,
175
163
  clearAllSessions,
176
164
  countRetainedSuppressedRows,
@@ -184,10 +172,10 @@ import {
184
172
  drainRenderedDependencyDriftFilePaths,
185
173
  exceedsLspSyncLimits,
186
174
  findAuxiliaryProfileForSource,
175
+ formatReviewGraphRevisionDriftNote,
187
176
  getActiveSessionId,
188
177
  getDisposition,
189
178
  getFileDiagnosticSummaries,
190
- getFullScanWallClockMs,
191
179
  getLSPService,
192
180
  getLspBudgetIdleTimeoutMs,
193
181
  getMutationBridge,
@@ -201,13 +189,13 @@ import {
201
189
  isBlocking,
202
190
  isPendingAuxiliaryPastRearmTtl,
203
191
  isWarmAttached,
204
- isWorkspaceSweepActive,
205
192
  loadLspService,
206
193
  logBusEvent,
207
194
  markWidgetFileBlockersStale,
208
195
  newLspMutationCorrelationId,
209
196
  nextOrderTurn,
210
197
  pendingAuxiliaryCoverageSize,
198
+ publishReviewGraphFileIr,
211
199
  rearmPendingAuxiliaryCoverage,
212
200
  reconcileCorrelatedScanDiagnostics,
213
201
  reconcileScanDiagnostics,
@@ -230,8 +218,8 @@ import {
230
218
  retagAuxiliaryDiagnostics,
231
219
  retireScope,
232
220
  retireWidgetDependencyDriftBlockers,
221
+ reviewGraphIrContentHash,
233
222
  runPerServerGroups,
234
- runWhenWorkspaceSweepIdle,
235
223
  scopeCell,
236
224
  setSessionLanguages,
237
225
  shouldShortenLspIdleTimeout,
@@ -244,18 +232,25 @@ import {
244
232
  widgetDiagnosticUri,
245
233
  workspaceEditTextPaths,
246
234
  writeOrderToken
247
- } from "../chunk-TVN5N6GY.js";
235
+ } from "../chunk-PV6LR5P5.js";
236
+ import {
237
+ buildSymbolId
238
+ } from "../chunk-3MED75HT.js";
248
239
  import {
249
240
  emitBounded,
250
241
  resetBoundedTelemetry
251
- } from "../chunk-ARFCXG7S.js";
242
+ } from "../chunk-CO2G5PHH.js";
252
243
  import {
253
244
  hashText,
254
245
  normalizeMessage,
255
246
  primePersistedMutationAttribution,
256
247
  resetMutationAttribution,
257
248
  stableFindingId
258
- } from "../chunk-OAXNDZJ7.js";
249
+ } from "../chunk-IBGIOBEE.js";
250
+ import {
251
+ resolveGitCheckout,
252
+ resolveLinkedWorktreeOwner
253
+ } from "../chunk-PNQDPDRC.js";
259
254
  import {
260
255
  mapWithConcurrency
261
256
  } from "../chunk-XIMYZ3JA.js";
@@ -264,8 +259,10 @@ import {
264
259
  customServerSpecsOf,
265
260
  describeRootFallback,
266
261
  explainServersForFile,
262
+ getAstGrepRuleFingerprint,
267
263
  getServersForFileWithConfig,
268
264
  initLSPConfig,
265
+ isAuxiliary,
269
266
  isSameOrWithin,
270
267
  loadLSPConfig,
271
268
  lspConfigOf,
@@ -275,11 +272,26 @@ import {
275
272
  resetLSPCaseSensitivityState,
276
273
  resolveLspCwdForFile,
277
274
  shouldInitializeSessionRoot
278
- } from "../chunk-B3I2UUT4.js";
275
+ } from "../chunk-ESBQHFMJ.js";
279
276
  import {
280
277
  hasAmbientPythonEnvironment
281
- } from "../chunk-DIULIWKU.js";
282
- import "../chunk-VDBPVIKD.js";
278
+ } from "../chunk-34WSVGEM.js";
279
+ import "../chunk-VWCXW2E7.js";
280
+ import {
281
+ PROJECT_SNAPSHOT_VERSION,
282
+ getProjectSnapshotLegacyPath,
283
+ getProjectSnapshotPath,
284
+ hydrateRuntimeFromProjectSnapshot,
285
+ hydrateRuntimeFromProjectSnapshotIfIdle,
286
+ isProjectSnapshotFresh,
287
+ isProjectSnapshotMetaStale,
288
+ loadProjectSnapshot,
289
+ loadProjectSnapshotExportsAndRules,
290
+ loadProjectSnapshotWithoutWordIndex,
291
+ readJsonCache,
292
+ readProjectSnapshotMeta,
293
+ saveRuntimeProjectSnapshot
294
+ } from "../chunk-ZXYDQ3TZ.js";
283
295
  import "../chunk-5W3R6LFT.js";
284
296
  import {
285
297
  EXT_TO_LANG,
@@ -293,51 +305,43 @@ import {
293
305
  queryLoader,
294
306
  reportBundledResourceDirHealth,
295
307
  resolveTreeSitterLanguage
296
- } from "../chunk-LW6XKDZQ.js";
308
+ } from "../chunk-QHJ6DQ2R.js";
297
309
  import "../chunk-Z54ZSOBZ.js";
298
310
  import {
299
311
  SCAN_LANGUAGE_PRIORITY,
300
312
  extensionsForLanguage,
301
313
  extensionsForLanguageToken,
314
+ getUserRuleRoot,
302
315
  resolveLanguage
303
- } from "../chunk-TGPXLY2J.js";
316
+ } from "../chunk-724UL2TJ.js";
304
317
  import {
305
318
  dump,
306
319
  resolvePackagePath
307
320
  } from "../chunk-QOZXJ4NA.js";
308
- import "../chunk-V7R425XG.js";
309
- import "../chunk-PAK6IDME.js";
321
+ import "../chunk-GBPP5P7X.js";
322
+ import "../chunk-P7266HLM.js";
323
+ import {
324
+ SWEEP_IDLE_SAFETY_MARGIN_MS,
325
+ getFullScanWallClockMs,
326
+ isWorkspaceSweepActive,
327
+ runWhenWorkspaceSweepIdle
328
+ } from "../chunk-MQLUAEOC.js";
310
329
  import {
311
330
  HOOK_WALL_BUDGET_MS,
312
331
  bounded,
313
332
  combineAbortSignals,
314
333
  withDeadline
315
- } from "../chunk-GUOGVTCQ.js";
316
- import {
317
- PROJECT_SNAPSHOT_VERSION,
318
- getProjectSnapshotLegacyPath,
319
- getProjectSnapshotPath,
320
- hydrateRuntimeFromProjectSnapshot,
321
- hydrateRuntimeFromProjectSnapshotIfIdle,
322
- isProjectSnapshotFresh,
323
- isProjectSnapshotMetaStale,
324
- loadProjectSnapshot,
325
- loadProjectSnapshotExportsAndRules,
326
- loadProjectSnapshotWithoutWordIndex,
327
- readJsonCache,
328
- readProjectSnapshotMeta,
329
- saveRuntimeProjectSnapshot
330
- } from "../chunk-AJVLWNFN.js";
334
+ } from "../chunk-52FUZI6K.js";
331
335
  import {
332
336
  centralityFromReverseDeps,
333
337
  getWordIndexBuildStatus,
334
338
  logWordIndex,
335
339
  searchWordIndex,
336
340
  triggerBackgroundWordIndexBuild
337
- } from "../chunk-YGVYKNSN.js";
341
+ } from "../chunk-SAEFPSB6.js";
338
342
  import {
339
343
  getProjectDiagnosticsScannerMaxFiles
340
- } from "../chunk-LNAKEO63.js";
344
+ } from "../chunk-GAEGEPOC.js";
341
345
  import {
342
346
  PathKeyedMap
343
347
  } from "../chunk-Z3CCVDUB.js";
@@ -350,30 +354,27 @@ import {
350
354
  readTurnEndStatus,
351
355
  resolveRunnerPath,
352
356
  toRunnerDisplayPath
353
- } from "../chunk-PWFHBTSF.js";
357
+ } from "../chunk-L3BLXZG7.js";
354
358
  import "../chunk-2BEMIWEW.js";
355
359
  import {
356
360
  commitDurableStore,
357
361
  resetResolvedPathCache,
358
362
  resetZizmorTokenAvailability
359
- } from "../chunk-OQGFCZJ4.js";
363
+ } from "../chunk-66VMUL7Q.js";
360
364
  import {
361
365
  resolveToolCwd
362
- } from "../chunk-RUOG5D2C.js";
366
+ } from "../chunk-Q7DUK4LX.js";
363
367
  import {
364
368
  _resetPackageManagerCache,
365
369
  findGlobalBinary,
366
370
  pmBinary,
367
371
  resolveNodePackageManager,
368
372
  runScriptArgs
369
- } from "../chunk-U3VGR4VU.js";
370
- import {
371
- isToolInstallAllowedByTrust,
372
- projectTrustDenialReason
373
- } from "../chunk-NTUJEGDZ.js";
373
+ } from "../chunk-AW7R2GWG.js";
374
374
  import {
375
+ getIsolatedNpxSpawnOptions,
375
376
  probeToolAsync
376
- } from "../chunk-EOKRQ3SA.js";
377
+ } from "../chunk-RWWYJ623.js";
377
378
  import {
378
379
  classifyProbeFailure,
379
380
  createAvailabilityLatch,
@@ -381,20 +382,20 @@ import {
381
382
  logAvailabilityDecision,
382
383
  resetInstallRetryLatches,
383
384
  startHostStallSampler
384
- } from "../chunk-WONERJTP.js";
385
+ } from "../chunk-VRZP76YM.js";
385
386
  import {
386
387
  canRunStartupHeavyScans,
387
388
  detectProjectLanguageProfile,
388
389
  getDefaultStartupTools
389
- } from "../chunk-22AVIGXS.js";
390
- import "../chunk-GVXA6CCE.js";
390
+ } from "../chunk-QOBUHWHX.js";
391
+ import "../chunk-QILIYSJF.js";
391
392
  import {
392
393
  collectSourceFilesWithBudgetAsync,
393
394
  getSlowFsVerdict,
394
395
  isSlowFs,
395
396
  slowFsDegradationNotice
396
- } from "../chunk-6QIUX4EP.js";
397
- import "../chunk-VHQLPIRJ.js";
397
+ } from "../chunk-6C7JWPCP.js";
398
+ import "../chunk-5NGR67U5.js";
398
399
  import {
399
400
  displayProjectDataPath,
400
401
  drainProjectDataDirMigrations,
@@ -421,7 +422,7 @@ import {
421
422
  sweepDeadEphemeralDataDirs,
422
423
  updateHeartbeat,
423
424
  withGenerationLockSync
424
- } from "../chunk-VN2AXLEX.js";
425
+ } from "../chunk-C34ZXZ7O.js";
425
426
  import {
426
427
  createSingleFlight
427
428
  } from "../chunk-BW5KFIQC.js";
@@ -431,7 +432,7 @@ import {
431
432
  } from "../chunk-QYF5S6P6.js";
432
433
  import {
433
434
  detectFileKind
434
- } from "../chunk-5ZEJHV35.js";
435
+ } from "../chunk-PHDMFAKM.js";
435
436
  import {
436
437
  compareKeys,
437
438
  detectFileRole,
@@ -441,12 +442,18 @@ import {
441
442
  provenanceView,
442
443
  resolvePiLensConfig,
443
444
  summarizeConfigResolution
444
- } from "../chunk-DQ2NZ7C4.js";
445
+ } from "../chunk-2BBLFX75.js";
445
446
  import {
446
447
  LSP_NAMESPACE_KEY,
447
448
  getPiLensGlobalConfigPath,
448
449
  resetWorkspaceTopology
449
- } from "../chunk-VNMT7C64.js";
450
+ } from "../chunk-XVF2VVXP.js";
451
+ import {
452
+ getProjectTrustState,
453
+ isToolInstallAllowedByTrust,
454
+ projectTrustDenialReason,
455
+ setProjectTrustState
456
+ } from "../chunk-ZANLILYD.js";
450
457
  import {
451
458
  DEFAULT_MAX_OUTPUT_BYTES,
452
459
  createBoundedOutputSink,
@@ -467,28 +474,30 @@ import {
467
474
  resolveLensToolEnabled,
468
475
  resolveLensToolName,
469
476
  toolRegistryEntryForMcp
470
- } from "../chunk-N3YQJI6O.js";
477
+ } from "../chunk-JAU72XNY.js";
471
478
  import {
472
479
  createSubsystemLogger,
473
480
  flushExtensionLog,
474
481
  logExtension
475
- } from "../chunk-O6TQT6RI.js";
482
+ } from "../chunk-XTMJ7XDT.js";
483
+ import "../chunk-JMYYOTQG.js";
476
484
  import {
477
485
  claimPhaseOncePerSession,
478
486
  logLatency
479
- } from "../chunk-5THXD6X5.js";
487
+ } from "../chunk-QQNQ7SJW.js";
480
488
  import {
481
489
  beginTurnContext,
482
490
  createNdjsonLogger,
483
491
  getGlobalPiLensLogDir,
492
+ getProcessSingleton,
484
493
  isTestMode,
485
494
  runLogCleanup,
486
495
  setTurnContextSession
487
- } from "../chunk-UK3CMEAL.js";
496
+ } from "../chunk-AOHZU2BM.js";
488
497
  import "../chunk-LJDODBBZ.js";
489
498
  import {
490
499
  FactStore
491
- } from "../chunk-NMABWBZN.js";
500
+ } from "../chunk-CHOW74WE.js";
492
501
  import {
493
502
  BoundedFifoMap,
494
503
  BoundedLruCache
@@ -503,15 +512,16 @@ import {
503
512
  normalizeMapKey,
504
513
  pathsEqual,
505
514
  realpathOrResolve,
515
+ resolveHostToolPath,
506
516
  toProjectRelativePath,
507
517
  uriToPath
508
- } from "../chunk-Q5U6FMDI.js";
518
+ } from "../chunk-N2H3ZPZY.js";
509
519
  import "../chunk-NXL4FFQQ.js";
510
520
 
511
521
  // dist/mcp/server.js
512
522
  import * as fs25 from "node:fs";
513
523
  import * as net from "node:net";
514
- import * as path38 from "node:path";
524
+ import * as path39 from "node:path";
515
525
  import { fileURLToPath as fileURLToPath3 } from "node:url";
516
526
 
517
527
  // dist/tools/render-compact.js
@@ -821,37 +831,59 @@ function checkAstGrepRulesHealth(ruleDir) {
821
831
  var AstGrepRuleManager = class {
822
832
  ruleDir;
823
833
  log;
834
+ fingerprint;
824
835
  ruleDescriptions = null;
825
- constructor(ruleDir, log) {
836
+ ruleFingerprint = null;
837
+ constructor(ruleDir, log, fingerprint = () => getAstGrepRuleFingerprint()) {
826
838
  this.ruleDir = ruleDir;
827
839
  this.log = log;
840
+ this.fingerprint = fingerprint;
828
841
  }
829
842
  loadRuleDescriptions() {
830
- if (this.ruleDescriptions !== null)
843
+ const fingerprint = this.fingerprint();
844
+ if (this.ruleDescriptions !== null && this.ruleFingerprint === fingerprint)
831
845
  return this.ruleDescriptions;
832
846
  const descriptions = /* @__PURE__ */ new Map();
833
- const possiblePaths = candidateAstGrepRulesPaths(this.ruleDir);
834
- const rulesPath = possiblePaths.find((p) => fs2.existsSync(p));
847
+ const roots = Array.isArray(this.ruleDir) ? this.ruleDir : [this.ruleDir];
848
+ const possiblePaths = roots.flatMap(candidateAstGrepRulesPaths);
849
+ const rulesPaths = [];
850
+ if (Array.isArray(this.ruleDir)) {
851
+ for (const candidate of possiblePaths) {
852
+ if (fs2.existsSync(candidate))
853
+ rulesPaths.push(candidate);
854
+ }
855
+ } else {
856
+ for (const candidate of possiblePaths) {
857
+ if (!fs2.existsSync(candidate))
858
+ continue;
859
+ rulesPaths.push(candidate);
860
+ break;
861
+ }
862
+ }
863
+ const rulesPath = rulesPaths[0];
835
864
  if (!rulesPath) {
836
865
  this.log(`Rule descriptions: no rules directory found in ${possiblePaths.join(", ")}`);
837
866
  this.ruleDescriptions = descriptions;
867
+ this.ruleFingerprint = fingerprint;
838
868
  return descriptions;
839
869
  }
840
870
  try {
841
- const files = fs2.readdirSync(rulesPath).filter((f) => f.endsWith(".yml"));
842
- this.log(`Loaded ${files.length} rule descriptions from ${rulesPath}`);
843
- for (const file of files) {
844
- const filePath = path2.join(rulesPath, file);
845
- const content = fs2.readFileSync(filePath, "utf-8");
846
- const rule = this.parseRuleYaml(content);
847
- if (rule) {
848
- descriptions.set(rule.id, rule);
871
+ for (const resolvedPath of rulesPaths) {
872
+ const files = fs2.readdirSync(resolvedPath).filter((f) => f.endsWith(".yml"));
873
+ this.log(`Loaded ${files.length} rule descriptions from ${resolvedPath}`);
874
+ for (const file of files) {
875
+ const filePath = path2.join(resolvedPath, file);
876
+ const content = fs2.readFileSync(filePath, "utf-8");
877
+ const rule = this.parseRuleYaml(content);
878
+ if (rule && !descriptions.has(rule.id))
879
+ descriptions.set(rule.id, rule);
849
880
  }
850
881
  }
851
882
  } catch (err) {
852
883
  this.log(`Failed to load rule descriptions: ${err.message}`);
853
884
  }
854
885
  this.ruleDescriptions = descriptions;
886
+ this.ruleFingerprint = fingerprint;
855
887
  return descriptions;
856
888
  }
857
889
  parseRuleYaml(content) {
@@ -953,6 +985,7 @@ var SgRunner = class {
953
985
  log;
954
986
  sgPath = null;
955
987
  sgArgsPrefix = [];
988
+ sgSpawnOptions;
956
989
  /**
957
990
  * Availability memo, backed by the shared transient-aware latch (#1476).
958
991
  *
@@ -1034,17 +1067,25 @@ var SgRunner = class {
1034
1067
  this.sweepFallbackCause = "probe-timeout";
1035
1068
  this.sweepHostStallMs = 0;
1036
1069
  this.sweepDurablyMissing = [];
1070
+ const trustedProject = getProjectTrustState() === "trusted";
1037
1071
  const pathCommand = await this.probeCommandCandidates([
1038
- { cmd: "ast-grep", argsPrefix: [] },
1039
- { cmd: "sg", argsPrefix: [] },
1040
- // `npx --no -- ast-grep` starts a Node process before it can answer, so
1072
+ ...trustedProject ? [
1073
+ { cmd: "ast-grep", argsPrefix: [] },
1074
+ { cmd: "sg", argsPrefix: [] }
1075
+ ] : [],
1076
+ // The scoped npx fallback starts a Node process before it can answer, so
1041
1077
  // it is the candidate most likely to blow a 5 s budget on a cold or busy
1042
1078
  // box. Marked a fallback so its timeout cannot veto the install.
1043
- { cmd: "npx", argsPrefix: ["--no", "--", "ast-grep"], fallback: true }
1079
+ {
1080
+ cmd: "npx",
1081
+ argsPrefix: ["--no", "--package", "@ast-grep/cli", "--", "ast-grep"],
1082
+ fallback: true
1083
+ }
1044
1084
  ]);
1045
1085
  if (pathCommand) {
1046
1086
  this.sgPath = pathCommand.cmd;
1047
1087
  this.sgArgsPrefix = pathCommand.argsPrefix;
1088
+ this.sgSpawnOptions = pathCommand.cmd === "npx" ? getIsolatedNpxSpawnOptions() : void 0;
1048
1089
  this.noteAvailable(startedAt, `ast-grep found on PATH: ${pathCommand.cmd}`);
1049
1090
  return true;
1050
1091
  }
@@ -1057,7 +1098,7 @@ var SgRunner = class {
1057
1098
  return true;
1058
1099
  }
1059
1100
  }
1060
- const platformBinary = await this.probePlatformPackageBinary();
1101
+ const platformBinary = trustedProject ? await this.probePlatformPackageBinary() : void 0;
1061
1102
  if (platformBinary) {
1062
1103
  this.sgPath = platformBinary;
1063
1104
  this.sgArgsPrefix = [];
@@ -1090,6 +1131,7 @@ var SgRunner = class {
1090
1131
  if (installed.outcome === "success") {
1091
1132
  this.sgPath = installed.value;
1092
1133
  this.sgArgsPrefix = [];
1134
+ this.sgSpawnOptions = void 0;
1093
1135
  this.noteAvailable(startedAt, `ast-grep auto-installed: ${installed.value}`);
1094
1136
  return true;
1095
1137
  }
@@ -1109,7 +1151,7 @@ var SgRunner = class {
1109
1151
  * sweep already asked, and the sweep returns at the first one that answers,
1110
1152
  * so at this point the flag means "a tier the winner is supposed to lose to
1111
1153
  * never got a fair hearing". Latching that pinned the session to
1112
- * `npx --no -- ast-grep` — a Node start per invocation — over a healthy
1154
+ * `npx` — a Node start per invocation — over a healthy
1113
1155
  * ast-grep on PATH, until the next restart. Provisional instead: served now,
1114
1156
  * re-swept once the stalled tier's cooldown expires.
1115
1157
  *
@@ -1252,7 +1294,10 @@ var SgRunner = class {
1252
1294
  let result;
1253
1295
  let hostStallMs;
1254
1296
  try {
1255
- result = await probeToolAsync(cmd, [...argsPrefix, "--version"], {
1297
+ result = cmd === "npx" ? await safeSpawnAsync(cmd, [...argsPrefix, "--version"], {
1298
+ ...getIsolatedNpxSpawnOptions(),
1299
+ timeout: PROBE_TIMEOUT_MS
1300
+ }) : await probeToolAsync(cmd, [...argsPrefix, "--version"], {
1256
1301
  timeout: PROBE_TIMEOUT_MS
1257
1302
  });
1258
1303
  } finally {
@@ -1339,6 +1384,7 @@ Original error: ${stderr}`;
1339
1384
  async execRaw(args, timeout = DEFAULT_EXEC_TIMEOUT_MS, options = {}) {
1340
1385
  const command = this.getSgCommand();
1341
1386
  const result = await safeSpawnAsync(command.cmd, [...command.argsPrefix, ...args], {
1387
+ ...this.sgSpawnOptions,
1342
1388
  timeout,
1343
1389
  deadlineAt: options.deadlineAt,
1344
1390
  signal: options.signal,
@@ -1367,6 +1413,7 @@ Original error: ${stderr}`;
1367
1413
  const hasBash = Boolean(process.env.MSYSTEM || process.env.GIT_SHELL);
1368
1414
  const useBash = isWindows && hasBash;
1369
1415
  const result = await safeSpawnAsync(useBash ? "bash" : command.cmd, useBash ? buildBashRunArgs(command.cmd, allArgs) : allArgs, {
1416
+ ...this.sgSpawnOptions,
1370
1417
  timeout: DEFAULT_EXEC_TIMEOUT_MS,
1371
1418
  deadlineAt: options.deadlineAt,
1372
1419
  signal: options.signal,
@@ -1515,6 +1562,7 @@ Original error: ${stderr}`;
1515
1562
  ...sgExcludeArgsForProject(dir),
1516
1563
  dir
1517
1564
  ], {
1565
+ ...sgCmd === "npx" ? getIsolatedNpxSpawnOptions() : {},
1518
1566
  timeout,
1519
1567
  deadlineAt: options.deadlineAt,
1520
1568
  signal: options.signal,
@@ -1552,6 +1600,7 @@ Original error: ${stderr}`;
1552
1600
  dir
1553
1601
  ];
1554
1602
  const spawnOptions = {
1603
+ ...sgCmd === "npx" ? getIsolatedNpxSpawnOptions() : {},
1555
1604
  timeout,
1556
1605
  deadlineAt: options.deadlineAt,
1557
1606
  signal: options.signal,
@@ -1641,7 +1690,7 @@ function reportAstGrepRulesHealth(bundledRuleDir) {
1641
1690
  });
1642
1691
  reportBundledResourceDirHealth("ast-grep-rules-dir-missing", bundledRuleDir, health, "ast-grep rule descriptions");
1643
1692
  }
1644
- function recordAstGrepApply(matches, lineage) {
1693
+ function recordAstGrepApply(matches, lineage, toolCallId) {
1645
1694
  const bridge = getMutationBridge();
1646
1695
  if (!bridge || matches.length === 0)
1647
1696
  return;
@@ -1657,13 +1706,22 @@ function recordAstGrepApply(matches, lineage) {
1657
1706
  }
1658
1707
  for (const [filePath, editRanges] of rangesByFile) {
1659
1708
  bridge.recordMutation({
1660
- filePath,
1709
+ // ast-grep echoes the spelling passed to its CLI. Resolve that
1710
+ // tool-relative spelling at the producer boundary, then use the
1711
+ // existing canonical path seam before recordability is checked (#4140).
1712
+ filePath: normalizeFilePath(path4.resolve(process.cwd(), filePath)),
1661
1713
  kind: "edit",
1662
1714
  editRanges,
1663
1715
  consumer: "ast_grep_replace",
1716
+ provenance: "observed",
1664
1717
  // #3763: the call's session, so an apply that lands after `/new`
1665
1718
  // writes none of the next session's state.
1666
- ...lineage && { lineage }
1719
+ ...lineage && { lineage },
1720
+ // #4187 R4-1: the apply rewrites every matched file, which an
1721
+ // `ast_grep_replace` call names only when its `paths` are files. The
1722
+ // call's own id lets the guard advance exactly the paths that call
1723
+ // checked, and end the authorship of every other file it rewrote.
1724
+ ...toolCallId !== void 0 && { toolCallId }
1667
1725
  });
1668
1726
  }
1669
1727
  }
@@ -1732,6 +1790,9 @@ function validateInputShape(value, maxChars, label) {
1732
1790
  }
1733
1791
  return void 0;
1734
1792
  }
1793
+ function extractRuleLanguage(ruleYaml) {
1794
+ return /^[^\S\n\r\u2028\u2029]*language:\s*([^\s#]+)/im.exec(ruleYaml)?.[1];
1795
+ }
1735
1796
  function stderrHasError(stderr) {
1736
1797
  return stderr.split(/\r?\n/).some((line) => /^\s*(error|Error):/.test(line));
1737
1798
  }
@@ -1766,7 +1827,7 @@ var AstGrepClient = class {
1766
1827
  this.log = verbose ? createSubsystemLogger("ast-grep") : () => {
1767
1828
  };
1768
1829
  this.ensureRulesHealthReported();
1769
- this.ruleManager = new AstGrepRuleManager(this.ruleDir, this.log);
1830
+ this.ruleManager = new AstGrepRuleManager(ruleDir ? this.ruleDir : [projectRuleDir, getUserRuleRoot(), this.ruleDir], this.log, () => getAstGrepRuleFingerprint(process.cwd()));
1770
1831
  this.runner = new SgRunner(verbose);
1771
1832
  }
1772
1833
  /**
@@ -1810,7 +1871,7 @@ var AstGrepClient = class {
1810
1871
  * Replace using a raw YAML rule that includes a `fix:` field (Phase 3/4 of #125).
1811
1872
  * Dry-run returns matches for preview; apply writes fixes to disk.
1812
1873
  */
1813
- async replaceWithRule(ruleYaml, paths, apply) {
1874
+ async replaceWithRule(ruleYaml, paths, apply, options) {
1814
1875
  const allMatches = [];
1815
1876
  for (const scanPath of paths) {
1816
1877
  if (apply) {
@@ -1843,6 +1904,8 @@ var AstGrepClient = class {
1843
1904
  }
1844
1905
  allMatches.push(...result.matches);
1845
1906
  }
1907
+ if (apply)
1908
+ recordAstGrepApply(allMatches, options?.lineage, options?.toolCallId);
1846
1909
  return {
1847
1910
  matches: allMatches,
1848
1911
  totalMatches: allMatches.length,
@@ -1964,7 +2027,7 @@ var AstGrepClient = class {
1964
2027
  const shapeError = validateInputShape(ruleYaml, MAX_VALIDATE_RULE_CHARS, "rule");
1965
2028
  if (shapeError)
1966
2029
  return { valid: false, error: shapeError };
1967
- const language = /^\s*language:\s*([^\s#]+)/im.exec(ruleYaml)?.[1] ?? "typescript";
2030
+ const language = extractRuleLanguage(ruleYaml) ?? "typescript";
1968
2031
  const snippet = validationSnippetFor(language);
1969
2032
  const tmpDir = fs4.mkdtempSync(path4.join(os2.tmpdir(), "pi-lens-sg-rule-"));
1970
2033
  try {
@@ -2109,7 +2172,7 @@ var AstGrepClient = class {
2109
2172
  error: applyResult.error
2110
2173
  };
2111
2174
  }
2112
- recordAstGrepApply(preCheck.matches, options?.lineage);
2175
+ recordAstGrepApply(preCheck.matches, options?.lineage, options?.toolCallId);
2113
2176
  return {
2114
2177
  matches: preCheck.matches,
2115
2178
  totalMatches: preCheck.totalMatches,
@@ -2239,7 +2302,7 @@ message: found
2239
2302
  };
2240
2303
 
2241
2304
  // dist/clients/lens-engine.js
2242
- import * as path28 from "node:path";
2305
+ import * as path29 from "node:path";
2243
2306
 
2244
2307
  // dist/clients/project-diagnostics/scanner.js
2245
2308
  import * as fs6 from "node:fs";
@@ -2686,7 +2749,7 @@ function resolveRebuildScript(serverFilePath) {
2686
2749
  return serverFilePath.replace(/\\/g, "/").includes("/dist/") ? "build:dist" : "build";
2687
2750
  }
2688
2751
  function analyzeFileFresh(workerPath, file, cwd, options = {}, timeoutMs = 12e4) {
2689
- return new Promise((resolve30) => {
2752
+ return new Promise((resolve32) => {
2690
2753
  const args = [workerPath, `--file=${file}`, `--cwd=${cwd}`];
2691
2754
  if (options.flags)
2692
2755
  args.push(`--flags=${JSON.stringify(options.flags)}`);
@@ -2696,7 +2759,7 @@ function analyzeFileFresh(workerPath, file, cwd, options = {}, timeoutMs = 12e4)
2696
2759
  stdio: ["ignore", "pipe", "pipe"]
2697
2760
  });
2698
2761
  } catch (err) {
2699
- resolve30({
2762
+ resolve32({
2700
2763
  error: `failed to fork worker: ${err instanceof Error ? err.message : String(err)}`
2701
2764
  });
2702
2765
  return;
@@ -2709,7 +2772,7 @@ function analyzeFileFresh(workerPath, file, cwd, options = {}, timeoutMs = 12e4)
2709
2772
  return;
2710
2773
  settled = true;
2711
2774
  clearTimeout(timer);
2712
- resolve30(outcome);
2775
+ resolve32(outcome);
2713
2776
  };
2714
2777
  const timer = setTimeout(() => {
2715
2778
  child.kill();
@@ -2817,7 +2880,7 @@ function summarizeScan(diagnostics) {
2817
2880
  // dist/clients/mcp/session.js
2818
2881
  import * as crypto from "node:crypto";
2819
2882
  import * as fs18 from "node:fs";
2820
- import * as path24 from "node:path";
2883
+ import * as path25 from "node:path";
2821
2884
 
2822
2885
  // dist/clients/bootstrap.js
2823
2886
  var residentClients = null;
@@ -2874,29 +2937,29 @@ async function buildBootstrapClients() {
2874
2937
  }
2875
2938
  }
2876
2939
  const [ruffClient, biomeClient, knipClient, todoScanner, jscpdClient, depChecker, testRunnerClient, metricsClient, complexityClient, goClient, govulncheckClient, gitleaksClient, trivyClient, opengrepClient, rustClient, agentBehaviorClient2, deadCodeClients] = await Promise.all([
2877
- load("ruff", async () => new (await import("../chunk-52H5B5TC.js")).RuffClient()),
2878
- load("biome", async () => new (await import("../chunk-4RERDEXR.js")).BiomeClient()),
2879
- load("knip", async () => new (await import("../chunk-AJ3QXTRQ.js")).KnipClient()),
2880
- load("todo", async () => new (await import("../chunk-NL5GLMFI.js")).TodoScanner()),
2881
- load("jscpd", async () => new (await import("../chunk-HVUZXSWK.js")).JscpdClient()),
2882
- load("dependency-checker", async () => new (await import("../chunk-T6D4Q4AQ.js")).DependencyChecker()),
2883
- load("test-runner", async () => new (await import("../chunk-AHDW3GPE.js")).TestRunnerClient()),
2884
- load("metrics", async () => new (await import("../chunk-27BBHSZC.js")).MetricsClient()),
2885
- load("complexity", async () => new (await import("../chunk-OS32RNR2.js")).ComplexityClient()),
2940
+ load("ruff", async () => new (await import("../chunk-PFNSLWU5.js")).RuffClient()),
2941
+ load("biome", async () => new (await import("../chunk-QZVW3AAK.js")).BiomeClient()),
2942
+ load("knip", async () => new (await import("../chunk-7ULNCDCO.js")).KnipClient()),
2943
+ load("todo", async () => new (await import("../chunk-JSQFTSC6.js")).TodoScanner()),
2944
+ load("jscpd", async () => new (await import("../chunk-3UUPUE3O.js")).JscpdClient()),
2945
+ load("dependency-checker", async () => new (await import("../chunk-3WA4HP4I.js")).DependencyChecker()),
2946
+ load("test-runner", async () => new (await import("../chunk-Q6ZWTCLY.js")).TestRunnerClient()),
2947
+ load("metrics", async () => new (await import("../chunk-N3X6Q4YT.js")).MetricsClient()),
2948
+ load("complexity", async () => new (await import("../chunk-VSSBEUFT.js")).ComplexityClient()),
2886
2949
  // The process-wide singleton, NOT a fresh instance (#2455 fix round 4,
2887
2950
  // F2). `handleSessionStart` reads this object for its "Active tools"
2888
2951
  // line while `resetGoAvailability` re-arms the singleton's latch; a
2889
2952
  // second instance here meant the reset never reached the surface a
2890
2953
  // user actually sees.
2891
- load("go", async () => (await import("../chunk-NTHH2J74.js")).goClient),
2892
- load("govulncheck", async () => new (await import("../chunk-54B42YRX.js")).GovulncheckClient()),
2893
- load("gitleaks", async () => new (await import("../chunk-RIISMHKN.js")).GitleaksClient()),
2894
- load("trivy", async () => new (await import("../chunk-IIXOAGHR.js")).TrivyClient()),
2895
- load("opengrep", async () => new (await import("../chunk-ORTMY4Q4.js")).OpengrepClient()),
2954
+ load("go", async () => (await import("../chunk-Z527N6YQ.js")).goClient),
2955
+ load("govulncheck", async () => new (await import("../chunk-XPZPXCBV.js")).GovulncheckClient()),
2956
+ load("gitleaks", async () => new (await import("../chunk-WOKKRCJ5.js")).GitleaksClient()),
2957
+ load("trivy", async () => new (await import("../chunk-IBRALRBR.js")).TrivyClient()),
2958
+ load("opengrep", async () => new (await import("../chunk-XKIZ2HNN.js")).OpengrepClient()),
2896
2959
  // The process-wide singleton, for the same reason as "go" above.
2897
- load("rust", async () => (await import("../chunk-4W2PLC4J.js")).rustClient),
2898
- load("agent-behavior", async () => (await import("../chunk-IXEBJSUE.js")).agentBehaviorClient),
2899
- loadList("dead-code", async () => (await import("../chunk-UY752QSN.js")).getDeadCodeClients())
2960
+ load("rust", async () => (await import("../chunk-BRGDSBJE.js")).rustClient),
2961
+ load("agent-behavior", async () => (await import("../chunk-23WOO7TX.js")).agentBehaviorClient),
2962
+ loadList("dead-code", async () => (await import("../chunk-NW7BOILN.js")).getDeadCodeClients())
2900
2963
  ]);
2901
2964
  if (failures.length > 0)
2902
2965
  await logBootstrapFailures(failures);
@@ -2983,7 +3046,7 @@ function resetAnalyzerBootstrapSessionState() {
2983
3046
  }
2984
3047
 
2985
3048
  // dist/clients/formatters-lazy.js
2986
- var lazyFormatters = createLazyImport(() => import("../chunk-XK4CIILI.js"));
3049
+ var lazyFormatters = createLazyImport(() => import("../chunk-GHB6AYCP.js"));
2987
3050
  function warmFormatters() {
2988
3051
  return lazyFormatters.get();
2989
3052
  }
@@ -3332,6 +3395,13 @@ function deriveProviderFromModelId(modelId) {
3332
3395
 
3333
3396
  // dist/clients/runtime-coordinator.js
3334
3397
  var MAX_PENDING_CASCADE_RUNS = 32;
3398
+ function nextTelemetrySessionId() {
3399
+ const state = getProcessSingleton("runtime-coordinator-telemetry", 1, () => ({
3400
+ processId: randomBytes(6).toString("hex"),
3401
+ nextGeneration: 0
3402
+ }));
3403
+ return `lens-${state.processId}-${++state.nextGeneration}`;
3404
+ }
3335
3405
  var MAX_CUT_ADVISORY_LANES = 16;
3336
3406
  var MAX_MUTATION_RECEIPTS = 512;
3337
3407
  function recordViewIncomplete(cwd, cause) {
@@ -3403,7 +3473,8 @@ var RuntimeCoordinator = class {
3403
3473
  rules: [],
3404
3474
  hasCustomRules: false
3405
3475
  };
3406
- _telemetrySessionId = `lens-${Date.now().toString(36)}`;
3476
+ /** Process-unique and monotonic across coordinator reloads (#4213). */
3477
+ _telemetrySessionId = nextTelemetrySessionId();
3407
3478
  _lifecycleReason;
3408
3479
  _hasStableSessionId = false;
3409
3480
  _telemetryModel = "unknown";
@@ -3472,9 +3543,10 @@ var RuntimeCoordinator = class {
3472
3543
  /** tool_call → tool_result path-attribution correlation (#1642). */
3473
3544
  _toolCallAttributions = new BoundedLruCache(TOOL_CALL_ATTRIBUTION_CAPACITY);
3474
3545
  /**
3475
- * #3555: reads carry no {@link ToolCallAttribution} (it is recorded for
3476
- * mutations, and its origin cwd would change how a read's path resolves),
3477
- * so a widening rides its own correlation, with the same bound.
3546
+ * #3555: a widening rides its own correlation, with the same bound. Since
3547
+ * #4138 a read also carries a {@link ToolCallAttribution} (its origin cwd
3548
+ * is what a relative read path resolves against at tool_result); the
3549
+ * widening stays separate because it outlives a blocked call's attribution.
3478
3550
  */
3479
3551
  _readWidenings = new BoundedLruCache(TOOL_CALL_ATTRIBUTION_CAPACITY);
3480
3552
  _lspReadWarmState = /* @__PURE__ */ new Map();
@@ -3543,7 +3615,7 @@ var RuntimeCoordinator = class {
3543
3615
  this._droppedMutationReceipts = 0;
3544
3616
  this._actionableWarningsThisTurn.clear(this._telemetrySessionId);
3545
3617
  this._codeQualityWarningsThisTurn.clear(this._telemetrySessionId);
3546
- this._telemetrySessionId = `lens-${Date.now().toString(36)}-${randomBytes(4).toString("hex")}`;
3618
+ this._telemetrySessionId = nextTelemetrySessionId();
3547
3619
  this._hasStableSessionId = false;
3548
3620
  this._telemetryModel = "unknown";
3549
3621
  this._telemetryModelId = "";
@@ -4013,7 +4085,9 @@ var RuntimeCoordinator = class {
4013
4085
  * session; it is therefore required. `filePath` is the edited file the
4014
4086
  * compute belongs to, the subject of a dropped admission's ledger row.
4015
4087
  */
4016
- appendCascadePromise(p, generation, filePath) {
4088
+ appendCascadePromise(p, generation, filePath, analysisRootMode = "session") {
4089
+ if (!canWriteAnalysisRoot(analysisRootMode))
4090
+ return;
4017
4091
  if (generation.guardedWrite(filePath, () => true) === void 0)
4018
4092
  return;
4019
4093
  if (this._pendingCascadeRuns.length < MAX_PENDING_CASCADE_RUNS) {
@@ -4075,8 +4149,8 @@ var RuntimeCoordinator = class {
4075
4149
  });
4076
4150
  return entry;
4077
4151
  });
4078
- const timeout = new Promise((resolve30) => {
4079
- setTimeout(resolve30, maxWaitMs).unref?.();
4152
+ const timeout = new Promise((resolve32) => {
4153
+ setTimeout(resolve32, maxWaitMs).unref?.();
4080
4154
  });
4081
4155
  await Promise.race([
4082
4156
  Promise.allSettled(tracked.map((t) => t.promise)),
@@ -4782,7 +4856,9 @@ var RuntimeCoordinator = class {
4782
4856
  * for a same-kind re-touch. Callers publish each kind's first transition
4783
4857
  * without spamming repeated edits before `agent_end`.
4784
4858
  */
4785
- deferMutation(filePath, cwd, toolName, turnStateCwd, kind, ownerSessionId, originCwd, readGuardBranchEpoch = this.readGuard.currentBranchEpoch) {
4859
+ deferMutation(filePath, cwd, toolName, turnStateCwd, kind, ownerSessionId, originCwd, readGuardBranchEpoch = this.readGuard.currentBranchEpoch, analysisRootMode = "session") {
4860
+ if (!canWriteAnalysisRoot(analysisRootMode))
4861
+ return false;
4786
4862
  const key = path8.resolve(filePath);
4787
4863
  const now = Date.now();
4788
4864
  const resolvedOriginCwd = originCwd ?? turnStateCwd;
@@ -4817,8 +4893,19 @@ var RuntimeCoordinator = class {
4817
4893
  });
4818
4894
  return true;
4819
4895
  }
4820
- deferFormat(filePath, cwd, toolName, turnStateCwd, ownerSessionId, originCwd) {
4821
- return this.deferMutation(filePath, cwd, toolName, turnStateCwd, "format", ownerSessionId, originCwd);
4896
+ deferFormat(filePath, cwd, toolName, turnStateCwd, ownerSessionId, originCwd, analysisRootMode = "session") {
4897
+ return this.deferMutation(filePath, cwd, toolName, turnStateCwd, "format", ownerSessionId, originCwd, void 0, analysisRootMode);
4898
+ }
4899
+ /** Return the ownership identity of a queued format record for bus events. */
4900
+ deferredFormatIdentity(filePath) {
4901
+ const record = this._pendingDeferredMutations.get(path8.resolve(filePath));
4902
+ if (!record || !record.kinds.has("format"))
4903
+ return void 0;
4904
+ return {
4905
+ ownerSessionId: record.ownerSessionId,
4906
+ turnIndex: record.queuedTurnIndex,
4907
+ batchId: record.queuedTurnId
4908
+ };
4822
4909
  }
4823
4910
  get pendingDeferredFormatCount() {
4824
4911
  return this._pendingDeferredMutations.size;
@@ -4946,7 +5033,7 @@ var RuntimeCoordinator = class {
4946
5033
 
4947
5034
  // dist/clients/runtime-session.js
4948
5035
  import * as nodeFs from "node:fs";
4949
- import * as path12 from "node:path";
5036
+ import * as path13 from "node:path";
4950
5037
 
4951
5038
  // dist/clients/message-end-attribution.js
4952
5039
  var lastStableSessionId;
@@ -5224,6 +5311,38 @@ function emitSituationalDeadWeight() {
5224
5311
  });
5225
5312
  }
5226
5313
 
5314
+ // dist/clients/deferred-test-targets.js
5315
+ import * as path12 from "node:path";
5316
+ var TEST_RUNNER_MAX_PERSISTED_TARGETS = 64;
5317
+ function deferralEntryKey(entry) {
5318
+ return `${entry.sessionId ?? ""}\0${normalizeMapKey(path12.resolve(entry.testFile))}`;
5319
+ }
5320
+ function mergeDeferredTargets(existing, incoming) {
5321
+ const byKey = /* @__PURE__ */ new Map();
5322
+ for (const entry of [...existing, ...incoming]) {
5323
+ const key = deferralEntryKey(entry);
5324
+ const prior = byKey.get(key);
5325
+ if (prior && (prior.attempts ?? 0) >= (entry.attempts ?? 0))
5326
+ continue;
5327
+ byKey.set(key, entry);
5328
+ }
5329
+ return [...byKey.values()];
5330
+ }
5331
+ function boundDeferredTargets(entries, max, isOwn) {
5332
+ const foreign = [];
5333
+ const own = [];
5334
+ for (const entry of entries) {
5335
+ if (isOwn(entry))
5336
+ own.push(entry);
5337
+ else
5338
+ foreign.push(entry);
5339
+ }
5340
+ const ordered = [...foreign, ...own];
5341
+ if (ordered.length <= max)
5342
+ return ordered;
5343
+ return ordered.slice(-max);
5344
+ }
5345
+
5227
5346
  // dist/clients/test-runner-delivery.js
5228
5347
  var pending = /* @__PURE__ */ new Map();
5229
5348
  function resetTestRunnerDelivery() {
@@ -5231,6 +5350,30 @@ function resetTestRunnerDelivery() {
5231
5350
  }
5232
5351
 
5233
5352
  // dist/clients/runtime-session.js
5353
+ function notifyStartupWarmSkip(args) {
5354
+ if (!args.runtime.isCurrentSession(args.sessionGeneration))
5355
+ return;
5356
+ const reason = args.scan.reason;
5357
+ if (reason !== "too-many-source-files" && reason !== "too-many-entries") {
5358
+ return;
5359
+ }
5360
+ const bound = reason === "too-many-entries" ? {
5361
+ name: "maxScanEntries",
5362
+ value: args.scan.maxScanEntries,
5363
+ hint: `set PI_LENS_STARTUP_SCAN_MAX_ENTRIES=<n> to override the ${args.scan.maxScanEntries}-entry cap`
5364
+ } : {
5365
+ name: "maxProjectFiles",
5366
+ value: args.scan.maxProjectFiles,
5367
+ hint: `set maxProjectFiles in .pi-lens.json to override the ${args.scan.maxProjectFiles}-source-file cap`
5368
+ };
5369
+ recordDegradationOnce({
5370
+ kind: "startup-warm-skipped",
5371
+ subject: args.analysisRoot,
5372
+ reason: `${reason}; ${bound.name}=${bound.value}`,
5373
+ metadata: { reason, [bound.name]: bound.value }
5374
+ });
5375
+ args.notify(`\u{1F4E6} Project-size limits disabled background warm scans (heavy scans, TODO scan, LSP pre-warm) (${bound.hint}).`, "warning");
5376
+ }
5234
5377
  async function demandBootstrapDeps(deps, reason) {
5235
5378
  const clients = await deps.bootstrap.request(reason);
5236
5379
  if (!clients)
@@ -5256,7 +5399,7 @@ function logHostReadyDelay(deps, cwd) {
5256
5399
  });
5257
5400
  }
5258
5401
  function resolveSnapshotRoot(cwd) {
5259
- const resolvedCwd = path12.resolve(cwd);
5402
+ const resolvedCwd = path13.resolve(cwd);
5260
5403
  const nearest = findNearestProjectRoot(resolvedCwd);
5261
5404
  if (!nearest || isAtOrAboveHomeDir(nearest)) {
5262
5405
  return resolvedCwd;
@@ -5359,8 +5502,8 @@ async function readSequenceWithBudget(args) {
5359
5502
  const { snapshotRoot, base, cwd, runtime, sessionGeneration, dbg, onDeferredSequenceResolved } = args;
5360
5503
  const readPromise = readLatestProjectSequenceAsync(snapshotRoot, base);
5361
5504
  let timeoutHandle;
5362
- const timeoutPromise = new Promise((resolve30) => {
5363
- timeoutHandle = setTimeout(() => resolve30({ timedOut: true }), sequenceReadBudgetMs());
5505
+ const timeoutPromise = new Promise((resolve32) => {
5506
+ timeoutHandle = setTimeout(() => resolve32({ timedOut: true }), sequenceReadBudgetMs());
5364
5507
  timeoutHandle.unref();
5365
5508
  });
5366
5509
  const raced = await Promise.race([
@@ -5429,7 +5572,7 @@ async function igniteWarmFiles(cwd, warmFiles, runtime, sessionGeneration, dbg)
5429
5572
  for (const relPath of warmFiles) {
5430
5573
  if (!runtime.isCurrentSession(sessionGeneration))
5431
5574
  return;
5432
- const filePath = path12.isAbsolute(relPath) ? relPath : path12.resolve(cwd, relPath);
5575
+ const filePath = path13.isAbsolute(relPath) ? relPath : path13.resolve(cwd, relPath);
5433
5576
  if (!nodeFs.existsSync(filePath)) {
5434
5577
  dbg(`session_start lsp-warm: not found: ${relPath}`);
5435
5578
  errors++;
@@ -5460,8 +5603,8 @@ async function igniteDominantLanguageWarm(cwd, runtime, sessionGeneration, dbg)
5460
5603
  if (!runtime.isCurrentSession(sessionGeneration))
5461
5604
  return;
5462
5605
  const lspService = (await loadLspService()).getLSPService();
5463
- const { collectSourceFilesAsync } = await import("../chunk-E4MAEDPM.js");
5464
- const { CODE_KINDS, detectFileKind: detectFileKind2 } = await import("../chunk-O5WILGMI.js");
5606
+ const { collectSourceFilesAsync } = await import("../chunk-4W4VLAPJ.js");
5607
+ const { CODE_KINDS, detectFileKind: detectFileKind2 } = await import("../chunk-FLKWCIZH.js");
5465
5608
  const files = await collectSourceFilesAsync(cwd, {
5466
5609
  inspectGeneratedHeaders: false
5467
5610
  });
@@ -5495,7 +5638,7 @@ async function igniteDominantLanguageWarm(cwd, runtime, sessionGeneration, dbg)
5495
5638
  clientScope: "primary",
5496
5639
  maxClientWaitMs: 2e3
5497
5640
  });
5498
- dbg(`session_start lsp-warm: dominant=${kind} via ${path12.basename(sample)}`);
5641
+ dbg(`session_start lsp-warm: dominant=${kind} via ${path13.basename(sample)}`);
5499
5642
  return;
5500
5643
  }
5501
5644
  dbg("session_start lsp-warm: no dominant-language LSP file found to warm");
@@ -5527,7 +5670,7 @@ function scheduleManagedToolRefresh(dbg) {
5527
5670
  const timer = setTimeout(() => {
5528
5671
  void (async () => {
5529
5672
  try {
5530
- const refresh = await import("../chunk-T4ERT73Y.js");
5673
+ const refresh = await import("../chunk-CF24OIYI.js");
5531
5674
  const outcome = await refresh.runManagedToolRefresh();
5532
5675
  if (outcome.skipped) {
5533
5676
  dbg(`session_start tool-refresh: skipped (${outcome.skipped})`);
@@ -5544,7 +5687,7 @@ function scheduleManagedToolRefresh(dbg) {
5544
5687
  timer.unref?.();
5545
5688
  }
5546
5689
  async function probePrettierInstall(ensureTool, dbg, analysisRoot) {
5547
- const pkgPath = path12.join(analysisRoot, "package.json");
5690
+ const pkgPath = path13.join(analysisRoot, "package.json");
5548
5691
  try {
5549
5692
  const raw = await nodeFs.promises.readFile(pkgPath, "utf-8");
5550
5693
  const pkg = JSON.parse(raw);
@@ -5574,7 +5717,7 @@ function scanOneTodoFile(scanner, filePath, items) {
5574
5717
  async function collectTodoBaselineItems(scanner, analysisRoot, stillCurrent) {
5575
5718
  const items = [];
5576
5719
  try {
5577
- const { getSourceFilesAsync } = await import("../chunk-RPD4ZSB7.js");
5720
+ const { getSourceFilesAsync } = await import("../chunk-IZSHAIJ7.js");
5578
5721
  const files = await getSourceFilesAsync(analysisRoot, true);
5579
5722
  if (!stillCurrent())
5580
5723
  return items;
@@ -5603,7 +5746,7 @@ async function buildOrRefreshWordIndex(args) {
5603
5746
  const snapshot = loadProjectSnapshot(snapshotRoot);
5604
5747
  const snapshotLoadMs = Date.now() - snapshotLoadStartMs;
5605
5748
  if (snapshot?.wordIndex) {
5606
- const { deserializeWordIndex, refreshWordIndexIncrementally, countWordIndexPostingEntries: countWordIndexPostingEntries2, estimateWordIndexResidentBytes: estimateWordIndexResidentBytes2 } = await import("../chunk-XECLWQZN.js");
5749
+ const { deserializeWordIndex, refreshWordIndexIncrementally, countWordIndexPostingEntries: countWordIndexPostingEntries2, estimateWordIndexResidentBytes: estimateWordIndexResidentBytes2 } = await import("../chunk-WU567RSC.js");
5607
5750
  const deserializeStartMs = Date.now();
5608
5751
  const index = deserializeWordIndex(snapshot.wordIndex);
5609
5752
  const deserializeMs = Date.now() - deserializeStartMs;
@@ -5675,7 +5818,7 @@ async function buildOrRefreshWordIndex(args) {
5675
5818
  }
5676
5819
  }
5677
5820
  }
5678
- const { buildWordIndexAsync, collectWordIndexDocs, countWordIndexPostingEntries, estimateWordIndexResidentBytes } = await import("../chunk-XECLWQZN.js");
5821
+ const { buildWordIndexAsync, collectWordIndexDocs, countWordIndexPostingEntries, estimateWordIndexResidentBytes } = await import("../chunk-WU567RSC.js");
5679
5822
  const docs = await collectWordIndexDocs(analysisRoot, () => runtime.isCurrentSession(sessionGeneration), rebuildPreflightFiles);
5680
5823
  if (!runtime.isCurrentSession(sessionGeneration))
5681
5824
  return;
@@ -5745,7 +5888,7 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
5745
5888
  const queuedAt = Date.now();
5746
5889
  dbg(`session_start task ${name}: scheduled`);
5747
5890
  runtime.markStartupScanInFlight(name, sessionGeneration);
5748
- const completion = new Promise((resolve30) => {
5891
+ const completion = new Promise((resolve32) => {
5749
5892
  const fire = () => {
5750
5893
  const startedAt = Date.now();
5751
5894
  dbg(`session_start task ${name}: start queuedMs=${startedAt - queuedAt}`);
@@ -5757,7 +5900,7 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
5757
5900
  }).finally(() => {
5758
5901
  runtime.clearStartupScanInFlight(name, sessionGeneration);
5759
5902
  dbg(`session_start task ${name}: end`);
5760
- resolve30();
5903
+ resolve32();
5761
5904
  });
5762
5905
  };
5763
5906
  const delay = taskDeferMsByName[name] ?? 0;
@@ -5834,7 +5977,7 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
5834
5977
  if (await jscpdClient.ensureAvailable()) {
5835
5978
  if (!runtime.isCurrentSession(sessionGeneration))
5836
5979
  return;
5837
- const isTsProject = nodeFs.existsSync(path12.join(analysisRoot, "tsconfig.json"));
5980
+ const isTsProject = nodeFs.existsSync(path13.join(analysisRoot, "tsconfig.json"));
5838
5981
  const scannerKey = isTsProject ? "jscpd-ts" : "jscpd";
5839
5982
  const cached = cacheManager2.readCache(scannerKey, analysisRoot);
5840
5983
  if (cached) {
@@ -6040,9 +6183,9 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
6040
6183
  });
6041
6184
  let callGraphIdentity;
6042
6185
  const callGraphTask = runTask("call-graph", async () => {
6043
- const { FactStore: FactStore2 } = await import("../chunk-YSNFMJKN.js");
6044
- const { buildOrUpdateGraph, extractSymbolsAndRefsFromGraph, getReviewGraphCacheIdentity } = await import("../chunk-VD4JQCQC.js");
6045
- const { buildCallGraph, saveCallGraph, loadCallGraph: loadCallGraph2 } = await import("../chunk-FJNKANOS.js");
6186
+ const { FactStore: FactStore2 } = await import("../chunk-QGQ5CCCG.js");
6187
+ const { buildOrUpdateGraph, extractSymbolsAndRefsFromGraph, getReviewGraphCacheIdentity } = await import("../chunk-2ZGIZPDG.js");
6188
+ const { buildCallGraph, saveCallGraph, loadCallGraph: loadCallGraph2 } = await import("../chunk-BKE7E7TC.js");
6046
6189
  if (!runtime.isCurrentSession(sessionGeneration))
6047
6190
  return;
6048
6191
  const startMs = Date.now();
@@ -6083,7 +6226,7 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
6083
6226
  return;
6084
6227
  if (!runtime.callGraph)
6085
6228
  return;
6086
- const { buildCodebaseModel, saveCodebaseModel, DEFAULT_CODEBASE_MODEL_TOKEN_BUDGET } = await import("../chunk-K6VYLB2U.js");
6229
+ const { buildCodebaseModel, saveCodebaseModel, DEFAULT_CODEBASE_MODEL_TOKEN_BUDGET } = await import("../chunk-TYM6OUIT.js");
6087
6230
  if (!callGraphIdentity)
6088
6231
  return;
6089
6232
  const model = buildCodebaseModel(runtime.callGraph, analysisRoot, DEFAULT_CODEBASE_MODEL_TOKEN_BUDGET, callGraphIdentity);
@@ -6156,11 +6299,79 @@ function scheduleDeferredToolProbesWithClients(deps, languageProfile, startupDef
6156
6299
  dbg(`session_start tools (deferred probes complete, ${Date.now() - warmStart}ms): ${summary}`);
6157
6300
  })();
6158
6301
  }
6159
- var SESSION_START_GUIDANCE = [
6160
- "\u{1F4CC} pi-lens active \u2014 automated checks run on every edit/write; blocking errors (including pre-existing) show inline and must be fixed.\nKey tools (see each tool's own description for args):\n\u2022 lens_diagnostics \u2014 source=session reads cache; empty cache \u2260 clean; use source=lsp scope=paths for changed files with absent or stale findings (aggregate hosts: lens(action=diagnostics)).\n\u2022 symbol_search \u2192 module_report \u2192 read_symbol/read_enclosing \u2014 ranked identifier search, then navigable outline/callback handles + exact body reads; cheaper than reading a whole file before editing.\n\u2022 Situational (activate via pi_lens_activate_tools): lsp_navigation, ast_grep_search, ast_grep_replace. Use ast_grep_search with dump=true to inspect AST nodes."
6161
- ];
6302
+ function readSubstituteGuidance(isEnabled) {
6303
+ const units = [];
6304
+ if (isEnabled("symbol_search")) {
6305
+ units.push({ name: "symbol_search", role: "ranked identifier search" });
6306
+ }
6307
+ if (isEnabled("module_report")) {
6308
+ units.push({
6309
+ name: "module_report",
6310
+ role: "navigable outline/callback handles"
6311
+ });
6312
+ }
6313
+ const readSymbol2 = isEnabled("read_symbol");
6314
+ const readEnclosing2 = isEnabled("read_enclosing");
6315
+ if (readSymbol2 && readEnclosing2) {
6316
+ units.push({
6317
+ name: "read_symbol/read_enclosing",
6318
+ role: "exact body reads"
6319
+ });
6320
+ } else if (readSymbol2) {
6321
+ units.push({ name: "read_symbol", role: "exact body reads" });
6322
+ } else if (readEnclosing2) {
6323
+ units.push({ name: "read_enclosing", role: "exact body reads" });
6324
+ }
6325
+ if (units.length === 0)
6326
+ return void 0;
6327
+ const roles = units.map((unit) => unit.role);
6328
+ const roleTail = roles.length === 1 ? roles[0] : roles.length === 2 ? `${roles[0]}, then ${roles[1]}` : `${roles[0]}, then ${roles[1]} + ${roles[2]}`;
6329
+ return `\u2022 ${units.map((unit) => unit.name).join(" \u2192 ")} \u2014 ${roleTail}; cheaper than reading a whole file before editing.`;
6330
+ }
6331
+ function situationalGuidance(isEnabled) {
6332
+ const active = [];
6333
+ for (const name of [
6334
+ "lsp_navigation",
6335
+ "ast_grep_search",
6336
+ "ast_grep_replace"
6337
+ ]) {
6338
+ if (isEnabled(name))
6339
+ active.push(name);
6340
+ }
6341
+ if (active.length === 0)
6342
+ return void 0;
6343
+ let text = `\u2022 Situational (activate via pi_lens_activate_tools): ${active.join(", ")}.`;
6344
+ if (isEnabled("ast_grep_search")) {
6345
+ text += " Use ast_grep_search with dump=true to inspect AST nodes.";
6346
+ }
6347
+ return text;
6348
+ }
6349
+ function renderSessionStartGuidance(options = {}) {
6350
+ const disabled = new Set(options.disabledToolNames ?? []);
6351
+ const isEnabled = (name) => !disabled.has(name);
6352
+ const requirePreExistingFixes = options.requirePreExistingFixes !== false;
6353
+ const header = "\u{1F4CC} pi-lens active \u2014 automated checks run on every edit/write; " + (requirePreExistingFixes ? "blocking errors (including pre-existing) show inline and must be fixed." : "blocking errors you introduce must be fixed; pre-existing ones are only reported.");
6354
+ const bullets = [];
6355
+ if (isEnabled("lens_diagnostics")) {
6356
+ bullets.push("\u2022 lens_diagnostics \u2014 source=session reads cache; empty cache \u2260 clean; use source=lsp scope=paths for changed files with absent or stale findings (aggregate hosts: lens(action=diagnostics)).");
6357
+ }
6358
+ const readSubstitute = readSubstituteGuidance(isEnabled);
6359
+ if (readSubstitute)
6360
+ bullets.push(readSubstitute);
6361
+ const situational = situationalGuidance(isEnabled);
6362
+ if (situational)
6363
+ bullets.push(situational);
6364
+ const lines = [header];
6365
+ if (bullets.length > 0) {
6366
+ lines.push("Key tools (see each tool's own description for args):");
6367
+ lines.push(...bullets);
6368
+ }
6369
+ return [lines.join("\n")];
6370
+ }
6371
+ var SESSION_START_GUIDANCE = renderSessionStartGuidance();
6162
6372
  async function handleSessionStart(deps) {
6163
6373
  resetDegradationLedger();
6374
+ let sessionGenerationAtStart = deps.runtime.sessionGeneration;
6164
6375
  resetAnalyzerBootstrapSessionState();
6165
6376
  resetProjectDataDirSessionState();
6166
6377
  resetTestRunnerDelivery();
@@ -6199,8 +6410,8 @@ async function handleSessionStart(deps) {
6199
6410
  try {
6200
6411
  warmupDbg("warmup: starting background warmup");
6201
6412
  const scanContextStartedAt = Date.now();
6202
- const startupScanModule = await import("../chunk-ODEEOXPH.js");
6203
- const languageProfileModule = await import("../chunk-AWE5TMPO.js");
6413
+ const startupScanModule = await import("../chunk-ZHFZWTZV.js");
6414
+ const languageProfileModule = await import("../chunk-R3PCY3YS.js");
6204
6415
  const warmupSnapshotRoot = resolveSnapshotRoot(warmupCwd);
6205
6416
  const cachedSnapshot = loadProjectSnapshot(warmupSnapshotRoot);
6206
6417
  warmupOwnSnapshotRead = cachedSnapshot ? {
@@ -6212,7 +6423,7 @@ async function handleSessionStart(deps) {
6212
6423
  const cachedVerdict = cachedSnapshot?.startupScan;
6213
6424
  let scan;
6214
6425
  if (cachedVerdict && startupScanModule.isStartupScanVerdictFresh(cachedVerdict)) {
6215
- scan = { ...cachedVerdict, cwd: path12.resolve(warmupCwd) };
6426
+ scan = { ...cachedVerdict, cwd: path13.resolve(warmupCwd) };
6216
6427
  warmupDbg(`warmup: scan-context reused from cache (canWarm=${scan.canWarmCaches}${scan.reason ? `, reason=${scan.reason}` : ""})`);
6217
6428
  } else {
6218
6429
  scan = await startupScanModule.resolveStartupScanContextAsync(warmupCwd);
@@ -6241,6 +6452,13 @@ async function handleSessionStart(deps) {
6241
6452
  });
6242
6453
  if (!scan.canWarmCaches) {
6243
6454
  warmupDbg(`warmup: skipping language-profile (canWarm=false, reason=${scan.reason ?? "unknown"})`);
6455
+ notifyStartupWarmSkip({
6456
+ scan,
6457
+ analysisRoot: scan.projectRoot ?? warmupCwd,
6458
+ notify: deps.notify,
6459
+ runtime: deps.runtime,
6460
+ sessionGeneration: sessionGenerationAtStart
6461
+ });
6244
6462
  return;
6245
6463
  }
6246
6464
  const languageRoot = scan.projectRoot ?? warmupCwd;
@@ -6356,6 +6574,7 @@ async function handleSessionStart(deps) {
6356
6574
  deps.bootstrap.peek()?.knipClient.resetSessionState?.();
6357
6575
  resetCascadeTierSessionState();
6358
6576
  runtime.resetForSession(sessionStartMs);
6577
+ sessionGenerationAtStart = runtime.sessionGeneration;
6359
6578
  logLatency({
6360
6579
  type: "phase",
6361
6580
  phase: "session_start_runtime_reset",
@@ -6398,7 +6617,7 @@ async function handleSessionStart(deps) {
6398
6617
  const cwd = ctxCwd ?? process.cwd();
6399
6618
  const projectDataDir = getProjectDataDir(cwd);
6400
6619
  for (const migration of drainProjectDataDirMigrations()) {
6401
- const targetName = path12.basename(migration.to);
6620
+ const targetName = path13.basename(migration.to);
6402
6621
  const hash = targetName.match(/([0-9a-f]{8})$/)?.[1] ?? "unknown";
6403
6622
  if (migration.outcome === "ephemeral") {
6404
6623
  recordDegradationOnce({
@@ -6417,11 +6636,11 @@ async function handleSessionStart(deps) {
6417
6636
  const globalDir = getGlobalPiLensDir();
6418
6637
  void sweepAtomicWriteStages([
6419
6638
  projectDataDir,
6420
- path12.join(projectDataDir, "cache"),
6421
- path12.join(projectDataDir, "sessions"),
6639
+ path13.join(projectDataDir, "cache"),
6640
+ path13.join(projectDataDir, "sessions"),
6422
6641
  globalDir,
6423
- path12.join(globalDir, "bin"),
6424
- path12.join(globalDir, "tools")
6642
+ path13.join(globalDir, "bin"),
6643
+ path13.join(globalDir, "tools")
6425
6644
  ]).catch(() => {
6426
6645
  });
6427
6646
  void sweepDeadEphemeralDataDirs({ isPidAlive: realIsPidAlive }).catch(() => {
@@ -6570,7 +6789,7 @@ async function handleSessionStart(deps) {
6570
6789
  if (allowBootstrapTasks && !getFlag("no-lsp")) {
6571
6790
  const cleaned = cleanStaleTsBuildInfo(ctxCwd ?? process.cwd());
6572
6791
  if (cleaned.length > 0) {
6573
- notify(`\u{1F9F9} Deleted stale TypeScript build cache (${cleaned.map((f) => path12.basename(f)).join(", ")}) \u2014 phantom errors suppressed.`, "warning");
6792
+ notify(`\u{1F9F9} Deleted stale TypeScript build cache (${cleaned.map((f) => path13.basename(f)).join(", ")}) \u2014 phantom errors suppressed.`, "warning");
6574
6793
  dbg(`session_start: cleaned stale tsbuildinfo: ${cleaned.join(", ")}`);
6575
6794
  }
6576
6795
  }
@@ -6655,7 +6874,7 @@ async function handleSessionStart(deps) {
6655
6874
  const startupScanSource = cachedStartupScan ? "snapshot" : "computed";
6656
6875
  let startupScan;
6657
6876
  if (cachedStartupScan) {
6658
- startupScan = { ...cachedStartupScan, cwd: path12.resolve(cwd) };
6877
+ startupScan = { ...cachedStartupScan, cwd: path13.resolve(cwd) };
6659
6878
  } else {
6660
6879
  const scanContextStartedAt = Date.now();
6661
6880
  startupScan = resolveStartupScanContext(cwd);
@@ -6760,7 +6979,10 @@ async function handleSessionStart(deps) {
6760
6979
  tools.push("Rust (cargo)");
6761
6980
  log(`Active tools: ${tools.join(", ")}`);
6762
6981
  dbg(`session_start tools: ${tools.join(", ")}`);
6763
- const agentStartupGuidance = SESSION_START_GUIDANCE;
6982
+ const agentStartupGuidance = renderSessionStartGuidance({
6983
+ disabledToolNames: deps.disabledToolNames,
6984
+ requirePreExistingFixes: deps.globalConfig?.contextInjection?.requirePreExistingFixes
6985
+ });
6764
6986
  runtime.projectRulesScan = scanProjectRules(analysisRoot);
6765
6987
  saveRuntimeProjectSnapshot({
6766
6988
  cwd: snapshotRoot,
@@ -6792,10 +7014,13 @@ async function handleSessionStart(deps) {
6792
7014
  } else if (!startupScan.canWarmCaches) {
6793
7015
  dbg(`session_start: skipping heavy scans (${startupScan.reason ?? "unknown"})`);
6794
7016
  dbg(`session_start: skipping TODO scan (${startupScan.reason ?? "unknown"})`);
6795
- if (startupScan.reason === "too-many-source-files" || startupScan.reason === "too-many-entries") {
6796
- const overrideHint = startupScan.reason === "too-many-entries" ? ` (set PI_LENS_STARTUP_SCAN_MAX_ENTRIES=<n> to override the ${getStartupScanMaxEntries()}-entry cap)` : "";
6797
- notify(`\u{1F4E6} Project-size limits disabled background warm scans (heavy scans, TODO scan, LSP pre-warm)${overrideHint}.`, "warning");
6798
- }
7017
+ notifyStartupWarmSkip({
7018
+ scan: startupScan,
7019
+ analysisRoot,
7020
+ notify,
7021
+ runtime,
7022
+ sessionGeneration
7023
+ });
6799
7024
  } else {
6800
7025
  scheduleStartupScans(deps, runtime, sessionGeneration, analysisRoot, snapshotRoot, languageProfile, dbg);
6801
7026
  }
@@ -6857,18 +7082,18 @@ function emitSmellsSessionStartLine(dbg, sessionStartMs) {
6857
7082
 
6858
7083
  // dist/clients/runtime-turn.js
6859
7084
  import * as fs17 from "node:fs";
6860
- import * as path23 from "node:path";
7085
+ import * as path24 from "node:path";
6861
7086
 
6862
7087
  // dist/clients/actionable-warnings.js
6863
7088
  import { createHash as createHash2 } from "node:crypto";
6864
7089
  import * as fs12 from "node:fs";
6865
- import * as path14 from "node:path";
7090
+ import * as path15 from "node:path";
6866
7091
 
6867
7092
  // dist/clients/actionable-warnings-logger.js
6868
- import * as path13 from "node:path";
7093
+ import * as path14 from "node:path";
6869
7094
  var AW_LOG_DIR = getGlobalPiLensLogDir();
6870
- var AW_LOG_FILE = path13.join(AW_LOG_DIR, "actionable-warnings.log");
6871
- var AW_LOG_BACKUP_FILE = path13.join(AW_LOG_DIR, "actionable-warnings.log.1");
7095
+ var AW_LOG_FILE = path14.join(AW_LOG_DIR, "actionable-warnings.log");
7096
+ var AW_LOG_BACKUP_FILE = path14.join(AW_LOG_DIR, "actionable-warnings.log.1");
6872
7097
  var MAX_LOG_BYTES2 = Math.max(128 * 1024, Number.parseInt(process.env.PI_LENS_AW_LOG_MAX_BYTES ?? "1048576", 10) || 1048576);
6873
7098
  var writer2 = createNdjsonLogger({
6874
7099
  filePath: AW_LOG_FILE,
@@ -6903,7 +7128,7 @@ function createActionableWarningId(args) {
6903
7128
  });
6904
7129
  }
6905
7130
  function legacyActionableWarningId(args) {
6906
- const rel = path14.relative(args.cwd, args.filePath).replace(/\\/g, "/");
7131
+ const rel = path15.relative(args.cwd, args.filePath).replace(/\\/g, "/");
6907
7132
  const legacyRelativeFile = rel && !rel.startsWith("..") ? rel : normalizeMapKey(args.filePath);
6908
7133
  const parts = [
6909
7134
  legacyRelativeFile,
@@ -6949,7 +7174,7 @@ function deserializeSuppressionState(contents) {
6949
7174
  }
6950
7175
  }
6951
7176
  function readSuppressionState(cwd) {
6952
- const statePath = path14.join(getProjectDataDir(cwd), "cache", "actionable-warning-state.json");
7177
+ const statePath = path15.join(getProjectDataDir(cwd), "cache", "actionable-warning-state.json");
6953
7178
  try {
6954
7179
  return deserializeSuppressionState(fs12.readFileSync(statePath, "utf8"));
6955
7180
  } catch {
@@ -6957,8 +7182,8 @@ function readSuppressionState(cwd) {
6957
7182
  }
6958
7183
  }
6959
7184
  function updateWarningState(cwd, warnings) {
6960
- const statePath = path14.join(getProjectDataDir(cwd), "cache", "actionable-warning-state.json");
6961
- fs12.mkdirSync(path14.dirname(statePath), { recursive: true });
7185
+ const statePath = path15.join(getProjectDataDir(cwd), "cache", "actionable-warning-state.json");
7186
+ fs12.mkdirSync(path15.dirname(statePath), { recursive: true });
6962
7187
  const hook = beforeWarningStateLockForTests;
6963
7188
  beforeWarningStateLockForTests = null;
6964
7189
  hook?.();
@@ -7103,7 +7328,7 @@ function boundedNumber(value, fallback) {
7103
7328
  return value !== void 0 && Number.isFinite(value) && value > 0 ? value : fallback;
7104
7329
  }
7105
7330
  async function buildActionableWarningsReport(args) {
7106
- const cwd = path14.resolve(args.cwd);
7331
+ const cwd = path15.resolve(args.cwd);
7107
7332
  const records = [...args.dispatchWarnings];
7108
7333
  const lspService = getLSPService();
7109
7334
  const observedAtByPath = /* @__PURE__ */ new Map();
@@ -7125,7 +7350,7 @@ async function buildActionableWarningsReport(args) {
7125
7350
  const eligible = [];
7126
7351
  let outsideRoot = 0;
7127
7352
  for (const file of args.files) {
7128
- const filePath = path14.resolve(cwd, file);
7353
+ const filePath = path15.resolve(cwd, file);
7129
7354
  if (normalizeMapKey(filePath) === normalizeMapKey(cwd) || !isUnderDir(filePath, cwd)) {
7130
7355
  outsideRoot++;
7131
7356
  logActionableWarningsEvent({
@@ -7235,7 +7460,7 @@ async function buildActionableWarningsReport(args) {
7235
7460
  }
7236
7461
  };
7237
7462
  const deferredWork = (async () => {
7238
- await new Promise((resolve30) => setTimeout(resolve30, 0));
7463
+ await new Promise((resolve32) => setTimeout(resolve32, 0));
7239
7464
  const deferredRecords = [...carried];
7240
7465
  const deferredDeadline = Date.now() + ACTIONABLE_WARNINGS_DEFERRED_BUDGET_MS;
7241
7466
  deferredDeps.deadlineAt = deferredDeadline;
@@ -7612,7 +7837,7 @@ function publishActionableWarningsReport(cacheManager2, cwd, report, opts = {})
7612
7837
  const reason = `${merged.droppedFiles.length} carried-forward entries: findings are LOST on this channel; ${cause} (files: ${merged.droppedFiles.slice(0, 3).join(", ")}${merged.droppedFiles.length > 3 ? ", ..." : ""})`;
7613
7838
  incrementDegradationCount({
7614
7839
  kind: "actionable-warnings-inband-superseded",
7615
- subject: `${path14.resolve(cwd)}:inband-carry-superseded`,
7840
+ subject: `${path15.resolve(cwd)}:inband-carry-superseded`,
7616
7841
  reason
7617
7842
  });
7618
7843
  opts.dbg?.(`actionable_warnings: in-band publish dropped ${merged.droppedFiles.length} superseded carried-forward file entry/entries (${merged.droppedFiles.slice(0, 3).join(", ")}${merged.droppedFiles.length > 3 ? ", ..." : ""})`);
@@ -7664,7 +7889,7 @@ function writeDeferredActionableWarningsReport(args) {
7664
7889
  if (droppedFiles.length > 0) {
7665
7890
  incrementDegradationCount({
7666
7891
  kind: "actionable-warnings-deferred-superseded",
7667
- subject: `${path14.resolve(args.cwd)}:deferred-file-superseded`,
7892
+ subject: `${path15.resolve(args.cwd)}:deferred-file-superseded`,
7668
7893
  reason: `${droppedFiles.length} changed file(s): warnings are LOST; unchanged files were merged into the persisted report (files: ${droppedFiles.slice(0, 3).join(", ")}${droppedFiles.length > 3 ? ", ..." : ""})`
7669
7894
  });
7670
7895
  args.dbg?.(`turn_end: deferred actionable-warnings dropped ${droppedFiles.length} superseded file entry/entries`);
@@ -7673,7 +7898,7 @@ function writeDeferredActionableWarningsReport(args) {
7673
7898
  return { mergedFiles, droppedFiles: droppedFiles.length };
7674
7899
  }
7675
7900
  function getActionableWarningsHistoryPath(cwd) {
7676
- return path14.join(getProjectDataDir(cwd), "actionable-warnings.jsonl");
7901
+ return path15.join(getProjectDataDir(cwd), "actionable-warnings.jsonl");
7677
7902
  }
7678
7903
  function appendActionableWarningsHistory(cwd, report) {
7679
7904
  const entries = [];
@@ -7712,7 +7937,7 @@ function appendActionableWarningsHistory(cwd, report) {
7712
7937
  return;
7713
7938
  const historyPath = getActionableWarningsHistoryPath(cwd);
7714
7939
  try {
7715
- fs12.mkdirSync(path14.dirname(historyPath), { recursive: true });
7940
+ fs12.mkdirSync(path15.dirname(historyPath), { recursive: true });
7716
7941
  fs12.appendFileSync(historyPath, `${entries.map((entry) => JSON.stringify(entry)).join("\n")}
7717
7942
  `, "utf8");
7718
7943
  } catch {
@@ -7751,7 +7976,7 @@ ${fileList}${more}` : void 0,
7751
7976
 
7752
7977
  // dist/clients/code-quality-warnings.js
7753
7978
  import * as fs13 from "node:fs";
7754
- import * as path15 from "node:path";
7979
+ import * as path16 from "node:path";
7755
7980
  var TIER_BUDGET_ORDER = [
7756
7981
  "warning",
7757
7982
  "info",
@@ -7765,7 +7990,7 @@ function lineInModifiedRanges2(line, ranges) {
7765
7990
  return ranges.some((range) => line >= range.start - 2 && line <= range.end + 2);
7766
7991
  }
7767
7992
  function buildCodeQualityWarningsReport(args) {
7768
- const cwd = path15.resolve(args.cwd);
7993
+ const cwd = path16.resolve(args.cwd);
7769
7994
  const maxWarnings = Math.max(1, args.maxWarnings ?? 50);
7770
7995
  const byId = /* @__PURE__ */ new Map();
7771
7996
  for (const warning of args.warnings) {
@@ -7829,7 +8054,7 @@ function writeCodeQualityWarningsReport(cacheManager2, cwd, report) {
7829
8054
  cacheManager2.writeCache("code-quality-warnings", report, cwd);
7830
8055
  }
7831
8056
  function getCodeQualityWarningsHistoryPath(cwd) {
7832
- return path15.join(getProjectDataDir(cwd), "code-quality-warnings.jsonl");
8057
+ return path16.join(getProjectDataDir(cwd), "code-quality-warnings.jsonl");
7833
8058
  }
7834
8059
  function appendCodeQualityWarningsHistory(cwd, report) {
7835
8060
  const warnings = report.files.flatMap((file) => file.warnings.map((warning) => ({
@@ -7854,7 +8079,7 @@ function appendCodeQualityWarningsHistory(cwd, report) {
7854
8079
  return;
7855
8080
  const historyPath = getCodeQualityWarningsHistoryPath(cwd);
7856
8081
  try {
7857
- fs13.mkdirSync(path15.dirname(historyPath), { recursive: true });
8082
+ fs13.mkdirSync(path16.dirname(historyPath), { recursive: true });
7858
8083
  fs13.appendFileSync(historyPath, `${warnings.map((entry) => JSON.stringify(entry)).join("\n")}
7859
8084
  `, "utf8");
7860
8085
  } catch {
@@ -7886,7 +8111,7 @@ function formatCodeQualityWarningsAdvisory(report, cwd, host = "pi") {
7886
8111
  }
7887
8112
 
7888
8113
  // dist/clients/git-guard.js
7889
- import * as path16 from "node:path";
8114
+ import * as path17 from "node:path";
7890
8115
 
7891
8116
  // dist/clients/read-guard-tool-lines.js
7892
8117
  var MAX_FAILURE_TRACKER_SIZE = 200;
@@ -7961,7 +8186,7 @@ function readBytes(filePath) {
7961
8186
 
7962
8187
  // dist/clients/git-guard.js
7963
8188
  function resolveGuardPath(filePath, cwd) {
7964
- return path16.resolve(cwd, filePath);
8189
+ return path17.resolve(cwd, filePath);
7965
8190
  }
7966
8191
  function guardPathKey(filePath, cwd) {
7967
8192
  return advisoryPathKey(filePath, cwd);
@@ -8084,7 +8309,9 @@ function resyncGitGuardAfterInlinePolicy(args) {
8084
8309
  return;
8085
8310
  args.runtime.updateGitGuardStatus(false, "");
8086
8311
  }
8087
- function syncGitGuardRecord(runtime, cacheManager2, cwd, editedFilePath) {
8312
+ function syncGitGuardRecord(runtime, cacheManager2, cwd, editedFilePath, analysisRootMode = "session") {
8313
+ if (!canWriteAnalysisRoot(analysisRootMode))
8314
+ return;
8088
8315
  const entries = (runtime.getInlineBlockersSnapshot?.() ?? []).filter((entry) => !entry.policySuppressed);
8089
8316
  const inspection = cacheManager2.inspectCache("turn-end-findings", cwd);
8090
8317
  const existing = cacheRecord(cacheManager2, cwd);
@@ -8278,10 +8505,10 @@ function dedupeSecretFindings(findings) {
8278
8505
  }
8279
8506
 
8280
8507
  // dist/clients/project-diagnostics/runner-adapters/govulncheck.js
8281
- import * as path17 from "node:path";
8508
+ import * as path18 from "node:path";
8282
8509
  function govulncheckFindingToProjectDiagnostic(cwd, finding) {
8283
8510
  const frame = finding.trace?.find((f) => f.filename);
8284
- const filePath = frame?.filename ? path17.isAbsolute(frame.filename) ? frame.filename : path17.resolve(cwd, frame.filename) : cwd;
8511
+ const filePath = frame?.filename ? path18.isAbsolute(frame.filename) ? frame.filename : path18.resolve(cwd, frame.filename) : cwd;
8285
8512
  const fix = finding.fixedVersion ? ` (fixed in ${finding.fixedVersion})` : "";
8286
8513
  return {
8287
8514
  filePath,
@@ -8354,12 +8581,12 @@ var govulncheckLane = {
8354
8581
  };
8355
8582
 
8356
8583
  // dist/clients/project-diagnostics/runner-adapters/gitleaks.js
8357
- import * as path18 from "node:path";
8584
+ import * as path19 from "node:path";
8358
8585
  function gitleaksFindingToProjectDiagnostic(cwd, finding) {
8359
8586
  const statusSuffix = finding.pathStatus ? ` [git: ${finding.pathStatus}]` : "";
8360
8587
  const isBlocking2 = finding.pathStatus !== "scratch" && finding.pathStatus !== "ignored" && finding.pathStatus !== "nested-repository";
8361
8588
  return {
8362
- filePath: path18.isAbsolute(finding.file) ? finding.file : path18.resolve(cwd, finding.file),
8589
+ filePath: path19.isAbsolute(finding.file) ? finding.file : path19.resolve(cwd, finding.file),
8363
8590
  line: finding.startLine,
8364
8591
  severity: isBlocking2 ? "error" : "info",
8365
8592
  semantic: isBlocking2 ? "blocking" : "none",
@@ -8377,9 +8604,9 @@ function gitleaksResultToProjectDiagnostics(cwd, result) {
8377
8604
  }
8378
8605
 
8379
8606
  // dist/clients/project-diagnostics/runner-adapters/trivy.js
8380
- import * as path19 from "node:path";
8607
+ import * as path20 from "node:path";
8381
8608
  function trivyFindingToProjectDiagnostic(cwd, finding) {
8382
- const filePath = finding.target ? path19.isAbsolute(finding.target) ? finding.target : path19.resolve(cwd, finding.target) : cwd;
8609
+ const filePath = finding.target ? path20.isAbsolute(finding.target) ? finding.target : path20.resolve(cwd, finding.target) : cwd;
8383
8610
  const version = finding.installedVersion ? `@${finding.installedVersion}` : "";
8384
8611
  const fix = finding.fixedVersion ? ` (fixed in ${finding.fixedVersion})` : "";
8385
8612
  return {
@@ -8396,7 +8623,7 @@ function trivyFindingToProjectDiagnostic(cwd, finding) {
8396
8623
  }
8397
8624
  function trivySecretFindingToProjectDiagnostic(cwd, finding) {
8398
8625
  return {
8399
- filePath: path19.isAbsolute(finding.file) ? finding.file : path19.resolve(cwd, finding.file),
8626
+ filePath: path20.isAbsolute(finding.file) ? finding.file : path20.resolve(cwd, finding.file),
8400
8627
  line: finding.line,
8401
8628
  severity: "error",
8402
8629
  semantic: "blocking",
@@ -8610,7 +8837,7 @@ function stillReportedParked(parked, current, keyOf, offeredKeys) {
8610
8837
  }
8611
8838
 
8612
8839
  // dist/clients/knip-scan-roots.js
8613
- import { resolve as resolve11 } from "node:path";
8840
+ import { resolve as resolve13 } from "node:path";
8614
8841
  var MAX_KNIP_ROOTS_PER_TURN = 3;
8615
8842
  function resolveKnipScanRoots(sessionCwd, modifiedFiles) {
8616
8843
  const session = resolveGitCheckout(sessionCwd);
@@ -8620,7 +8847,7 @@ function resolveKnipScanRoots(sessionCwd, modifiedFiles) {
8620
8847
  let sessionOwnsEdit = false;
8621
8848
  const linked = [];
8622
8849
  for (const file of modifiedFiles) {
8623
- const owner = resolveLinkedWorktreeOwner(session, resolve11(sessionCwd, file));
8850
+ const owner = resolveLinkedWorktreeOwner(session, resolve13(sessionCwd, file));
8624
8851
  if (owner === null) {
8625
8852
  sessionOwnsEdit = true;
8626
8853
  } else if (!linked.some((known) => pathsEqual(known.root, owner.root))) {
@@ -8636,7 +8863,7 @@ function resolveKnipScanRoots(sessionCwd, modifiedFiles) {
8636
8863
  }
8637
8864
 
8638
8865
  // dist/clients/project-diagnostics/runner-adapters/dead-code.js
8639
- import * as path20 from "node:path";
8866
+ import * as path21 from "node:path";
8640
8867
  function deadCodeRunnerId(language) {
8641
8868
  return `dead-code-${language}`;
8642
8869
  }
@@ -8652,7 +8879,7 @@ function deadCodeMessage(issue) {
8652
8879
  function deadCodeIssueToProjectDiagnostic(cwd, issue, language) {
8653
8880
  const blocking = issue.category === "unlisted";
8654
8881
  return {
8655
- filePath: issue.file ? path20.isAbsolute(issue.file) ? issue.file : path20.resolve(cwd, issue.file) : cwd,
8882
+ filePath: issue.file ? path21.isAbsolute(issue.file) ? issue.file : path21.resolve(cwd, issue.file) : cwd,
8656
8883
  line: issue.line,
8657
8884
  severity: blocking ? "error" : "warning",
8658
8885
  semantic: blocking ? "blocking" : "warning",
@@ -8676,11 +8903,11 @@ function deadCodeResultToProjectDiagnostics(cwd, result) {
8676
8903
  }
8677
8904
 
8678
8905
  // dist/clients/project-diagnostics/runner-adapters/knip.js
8679
- import * as path21 from "node:path";
8906
+ import * as path22 from "node:path";
8680
8907
  function knipIssueFile(cwd, issue) {
8681
8908
  if (!issue.file)
8682
8909
  return cwd;
8683
- return path21.isAbsolute(issue.file) ? issue.file : path21.resolve(cwd, issue.file);
8910
+ return path22.isAbsolute(issue.file) ? issue.file : path22.resolve(cwd, issue.file);
8684
8911
  }
8685
8912
  function knipIssueMessage(issue) {
8686
8913
  if (issue.type === "unlisted") {
@@ -9025,7 +9252,7 @@ async function runPersistentReverify(args) {
9025
9252
 
9026
9253
  // dist/clients/inline-blocker-dispositions.js
9027
9254
  import * as fs16 from "node:fs";
9028
- import * as path22 from "node:path";
9255
+ import * as path23 from "node:path";
9029
9256
  function readCurrentContent(filePath) {
9030
9257
  try {
9031
9258
  return fs16.readFileSync(filePath, "utf-8");
@@ -9053,7 +9280,7 @@ function applyInlineBlockerPolicy(record, cwd) {
9053
9280
  }
9054
9281
  const byFile = /* @__PURE__ */ new Map();
9055
9282
  for (const diagnostic of diagnostics) {
9056
- const owner = path22.resolve(cwd, diagnostic.filePath);
9283
+ const owner = path23.resolve(cwd, diagnostic.filePath);
9057
9284
  const group = byFile.get(owner);
9058
9285
  if (group)
9059
9286
  group.push(diagnostic);
@@ -9192,21 +9419,6 @@ var TEST_RUNNER_MAX_TARGETS = 12;
9192
9419
  var TURN_END_SELECTION_ROOT_ROWS = 8;
9193
9420
  var TEST_RUNNER_BATCH_BUDGET_MS = 2e4;
9194
9421
  var TEST_RUNNER_MAX_DEFERRALS = 2;
9195
- var TEST_RUNNER_MAX_PERSISTED_TARGETS = 64;
9196
- function deferralEntryKey(entry) {
9197
- return `${entry.sessionId ?? ""}\0${normalizeMapKey(path23.resolve(entry.testFile))}`;
9198
- }
9199
- function mergeDeferredTargets(existing, incoming) {
9200
- const byKey = /* @__PURE__ */ new Map();
9201
- for (const entry of [...existing, ...incoming]) {
9202
- const key = deferralEntryKey(entry);
9203
- const prior = byKey.get(key);
9204
- if (prior && (prior.attempts ?? 0) >= (entry.attempts ?? 0))
9205
- continue;
9206
- byKey.set(key, entry);
9207
- }
9208
- return [...byKey.values()];
9209
- }
9210
9422
  async function runTestTargetsBounded(args) {
9211
9423
  const results = [];
9212
9424
  if (args.targets.length === 0)
@@ -9249,7 +9461,7 @@ async function runTestTargetsBounded(args) {
9249
9461
  });
9250
9462
  void pool.catch(() => {
9251
9463
  });
9252
- await new Promise((resolve30) => {
9464
+ await new Promise((resolve32) => {
9253
9465
  let settled = false;
9254
9466
  const finish = (reason) => {
9255
9467
  if (settled)
@@ -9261,7 +9473,7 @@ async function runTestTargetsBounded(args) {
9261
9473
  batchAbort.abort();
9262
9474
  clearTimeout(timer);
9263
9475
  args.signal?.removeEventListener("abort", onAbort);
9264
- resolve30();
9476
+ resolve32();
9265
9477
  };
9266
9478
  const onAbort = () => finish("abort");
9267
9479
  const timer = setTimeout(() => finish("budget"), budgetMs);
@@ -9495,22 +9707,26 @@ async function handleTurnEnd(deps) {
9495
9707
  dbg(`turn_end: retaining newer turn state (dispatch=${turnIndexAtDispatch}, current=${runtime.turnIndex})`);
9496
9708
  return;
9497
9709
  }
9498
- cacheManager2.clearTurnState(cwd, currentOwner);
9710
+ cacheManager2.clearTurnState(cwd, currentOwner, isSecondarySession ? sessionId : void 0);
9499
9711
  };
9500
9712
  void updateHeartbeat().catch(() => {
9501
9713
  });
9502
9714
  const cwd = ctxCwd ?? process.cwd();
9503
- let turnState = cacheManager2.readTurnState(cwd);
9715
+ const isSecondarySession = deps.sessionScope?.role === "secondary";
9716
+ const sessionRole = isSecondarySession ? "secondary" : "primary";
9717
+ const allowProjectWideFindings = admitsProjectWideFindings(sessionRole);
9718
+ const turnStateSessionId = isSecondarySession ? sessionId : void 0;
9719
+ let turnState = cacheManager2.readTurnState(cwd, turnStateSessionId);
9504
9720
  const currentOwner = {
9505
9721
  ...owner ?? {
9506
9722
  kind: "pi",
9507
- id: runtime.telemetrySessionId,
9723
+ id: sessionId ?? runtime.telemetrySessionId,
9508
9724
  pid: process.pid,
9509
9725
  lastSeen: (/* @__PURE__ */ new Date()).toISOString()
9510
9726
  },
9511
9727
  sessionStartedAt: owner?.sessionStartedAt ?? runtime.sessionStartedAt
9512
9728
  };
9513
- const access = cacheManager2.getTurnStateAccess(cwd, currentOwner);
9729
+ const access = isSecondarySession ? "owned" : cacheManager2.getTurnStateAccess(cwd, currentOwner);
9514
9730
  const gateOwnerLabel = turnState.owner ? `${turnState.owner.kind}:${turnState.owner.id}` : turnState.sessionId ? `legacy:${turnState.sessionId}` : "none";
9515
9731
  const sameProcessPiSessionHandoff = access === "foreign-live" && currentOwner.kind === "pi" && turnState.owner?.kind === "pi" && turnState.owner.pid === process.pid && turnState.owner.id !== currentOwner.id;
9516
9732
  if (access === "foreign-live" && !sameProcessPiSessionHandoff) {
@@ -9520,9 +9736,10 @@ async function handleTurnEnd(deps) {
9520
9736
  if (access === "available" && (turnState.files || turnState.owner || turnState.sessionId)) {
9521
9737
  dbg("turn_end: evicting stale turn-state owner");
9522
9738
  clearOwnedTurnState();
9523
- turnState = cacheManager2.readTurnState(cwd);
9739
+ turnState = cacheManager2.readTurnState(cwd, turnStateSessionId);
9524
9740
  }
9525
- const files = Object.keys(turnState.files);
9741
+ const ownFiles = Object.keys(turnState.files);
9742
+ const files = ownFiles;
9526
9743
  let widgetDemotedFindingsRetired = 0;
9527
9744
  for (const wPath of drainRenderedDependencyDriftFilePaths()) {
9528
9745
  const deliveryCount = incrementWidgetDependencyDriftDelivery(wPath);
@@ -9538,7 +9755,7 @@ async function handleTurnEnd(deps) {
9538
9755
  }
9539
9756
  }
9540
9757
  }
9541
- if (files.length === 0 && !runtime.hasCascadeRuns()) {
9758
+ if (files.length === 0 && !runtime.hasCascadeRuns() && !isSecondarySession) {
9542
9759
  if (getFlag("lens-guard") && !runtime.gitGuardHasBlockers) {
9543
9760
  const guardRecord = cacheManager2.readCache("turn-end-findings", cwd)?.data;
9544
9761
  if (guardRecord?.sessionId === runtime.telemetrySessionId && guardRecord.testFailures !== true) {
@@ -9570,7 +9787,7 @@ async function handleTurnEnd(deps) {
9570
9787
  dbg("turn_end: cancelled pending LSP idle reset (active editing)");
9571
9788
  }
9572
9789
  dbg(`turn_end: ${files.length} file(s) modified, cycles: ${turnState.turnCycles}/${turnState.maxCycles}, access: ${access}, owner: ${gateOwnerLabel}`);
9573
- if (cacheManager2.isMaxCyclesExceeded(cwd)) {
9790
+ if (cacheManager2.isMaxCyclesExceeded(cwd, turnStateSessionId)) {
9574
9791
  dbg("turn_end: max cycles exceeded, clearing state and forcing through");
9575
9792
  clearOwnedTurnState();
9576
9793
  runtime.fixedThisTurn.clear();
@@ -9613,7 +9830,14 @@ async function handleTurnEnd(deps) {
9613
9830
  const projectDiagnosticsDelta = [];
9614
9831
  const projectDiagnosticsSources = /* @__PURE__ */ new Set();
9615
9832
  const runnerFindingsStart = Date.now();
9616
- const pendingRunnerFindings = await drainPendingRunnerFindings(0);
9833
+ const runnerDrainSignal = deps.signal;
9834
+ const deliverySessionId = sessionId ?? runtime.telemetrySessionId;
9835
+ const pendingRunnerFindings = await bounded(drainPendingRunnerFindings(0, runtime.sessionScope.capture(), deliverySessionId), {
9836
+ ms: HOOK_WALL_BUDGET_MS.turn_end,
9837
+ signal: runnerDrainSignal,
9838
+ hook: "turn_end",
9839
+ label: "drainPendingRunnerFindings"
9840
+ }) ?? [];
9617
9841
  let runnerFindingsDelivered = 0;
9618
9842
  let runnerFindingsStale = 0;
9619
9843
  let runnerFindingsFailed = 0;
@@ -9838,7 +10062,16 @@ ${policy.body}`);
9838
10062
  });
9839
10063
  }
9840
10064
  const cascadeSettleStart = Date.now();
9841
- const { settled, timedOut } = await runtime.settleCascadeRuns(cascadeSettleWaitMs(), { trackTurnEndClock: true, generation: holdScope });
10065
+ const cascadeSettleSignal = deps.signal;
10066
+ const { settled, timedOut } = await bounded(runtime.settleCascadeRuns(cascadeSettleWaitMs(), {
10067
+ trackTurnEndClock: true,
10068
+ generation: holdScope
10069
+ }), {
10070
+ ms: HOOK_WALL_BUDGET_MS.turn_end,
10071
+ signal: cascadeSettleSignal,
10072
+ hook: "turn_end",
10073
+ label: "settleCascadeRuns"
10074
+ }) ?? { settled: 0, timedOut: 0 };
9842
10075
  logLatency({
9843
10076
  type: "phase",
9844
10077
  toolName: "turn_end",
@@ -9855,11 +10088,11 @@ ${policy.body}`);
9855
10088
  const changedSince = runtime.getFilesChangedSince(originSeq);
9856
10089
  if (changedSince.length > 0) {
9857
10090
  const changedSet = new Set(changedSince);
9858
- const primaryKey = normalizeMapKey(path23.resolve(run.filePath));
10091
+ const primaryKey = normalizeMapKey(path24.resolve(run.filePath));
9859
10092
  const neighborKeys = [
9860
10093
  ...(run.result?.neighbors ?? []).map((n) => n.filePath),
9861
10094
  ...run.selectedNeighborPaths ?? []
9862
- ].map((filePath) => normalizeMapKey(path23.resolve(filePath)));
10095
+ ].map((filePath) => normalizeMapKey(path24.resolve(filePath)));
9863
10096
  const supersededByOwnFile = changedSet.has(primaryKey) || neighborKeys.some((k) => changedSet.has(k));
9864
10097
  if (supersededByOwnFile) {
9865
10098
  logCascade({
@@ -10565,15 +10798,15 @@ ${suffix}`;
10565
10798
  const govGate = scannerGates.govulncheck;
10566
10799
  const gitleaksGate = scannerGates.gitleaks;
10567
10800
  const trivySecretsGate = scannerGates["trivy-secrets"];
10568
- const govDelivery = govulncheckLane.render(govulncheckLane.gate({ govulncheck: govGate }, laneCtx), laneCtx);
10801
+ const govDelivery = allowProjectWideFindings ? govulncheckLane.render(govulncheckLane.gate({ govulncheck: govGate }, laneCtx), laneCtx) : {};
10569
10802
  for (const [store, count] of Object.entries(govDelivery.dispositionSuppressed ?? {})) {
10570
10803
  recordDispositionSuppressed(store, count);
10571
10804
  }
10572
10805
  advisoryParts.push(...govDelivery.advisoryParts ?? []);
10573
- const secretsDelivery = secretsLane.render(secretsLane.gate({
10806
+ const secretsDelivery = allowProjectWideFindings ? secretsLane.render(secretsLane.gate({
10574
10807
  gitleaks: gitleaksGate,
10575
10808
  "trivy-secrets": trivySecretsGate
10576
- }, laneCtx), laneCtx);
10809
+ }, laneCtx), laneCtx) : {};
10577
10810
  for (const [store, count] of Object.entries(secretsDelivery.dispositionSuppressed ?? {})) {
10578
10811
  recordDispositionSuppressed(store, count);
10579
10812
  }
@@ -10640,16 +10873,22 @@ ${suffix}`;
10640
10873
  }
10641
10874
  const t3 = Date.now();
10642
10875
  let madgeStats;
10643
- if (getFlag("lens-turn-end-madge") && await depChecker.ensureAvailable()) {
10644
- const madgeFiles = cacheManager2.getFilesForMadge(cwd);
10876
+ const madgeAvailabilitySignal = deps.signal;
10877
+ if (getFlag("lens-turn-end-madge") && await bounded(depChecker.ensureAvailable(), {
10878
+ ms: HOOK_WALL_BUDGET_MS.turn_end,
10879
+ signal: madgeAvailabilitySignal,
10880
+ hook: "turn_end",
10881
+ label: "madge.ensureAvailable"
10882
+ })) {
10883
+ const madgeFiles = cacheManager2.getFilesForMadge(cwd, turnStateSessionId);
10645
10884
  if (madgeFiles.length > 0) {
10646
10885
  dbg(`turn_end: madge checking ${madgeFiles.length} file(s) for circular deps`);
10647
- const absFiles = madgeFiles.map((file) => path23.resolve(cwd, file));
10886
+ const absFiles = madgeFiles.map((file) => path24.resolve(cwd, file));
10648
10887
  const batch = await depChecker.checkFilesBatch(absFiles, cwd);
10649
10888
  const depResults = batch.results;
10650
10889
  madgeStats = batch.stats;
10651
10890
  for (const file of madgeFiles) {
10652
- const absPath = path23.resolve(cwd, file);
10891
+ const absPath = path24.resolve(cwd, file);
10653
10892
  const depResult = depResults.get(absPath);
10654
10893
  if (!depResult)
10655
10894
  continue;
@@ -10685,7 +10924,7 @@ ${suffix}`;
10685
10924
  }
10686
10925
  for (const result of cascadeResults) {
10687
10926
  for (const neighbor of result.neighbors) {
10688
- const abs = path23.isAbsolute(neighbor.filePath) ? neighbor.filePath : resolveRunnerPath(cwd, neighbor.filePath);
10927
+ const abs = path24.isAbsolute(neighbor.filePath) ? neighbor.filePath : resolveRunnerPath(cwd, neighbor.filePath);
10689
10928
  const key = normalizeMapKey(abs);
10690
10929
  if (seenCandidateKeys.has(key))
10691
10930
  continue;
@@ -10697,20 +10936,17 @@ ${suffix}`;
10697
10936
  const carriedDeferred = priorTestCache?.deferredTargets ?? [];
10698
10937
  const turnSessionId = sessionId ?? runtime.telemetrySessionId;
10699
10938
  const isThisSession = (entry) => entry.sessionId === turnSessionId;
10700
- const entryPathKey = (entry) => normalizeMapKey(path23.resolve(cwd, entry.testFile));
10939
+ const entryPathKey = (entry) => normalizeMapKey(path24.resolve(cwd, entry.testFile));
10701
10940
  const retiredCarry = (priorTestCache?.retiredTargets ?? []).filter(isThisSession);
10702
10941
  const retiredKeys = new Set(retiredCarry.map(entryPathKey));
10703
10942
  const resolvedDeferralKeys = /* @__PURE__ */ new Set();
10704
10943
  const heldDeferred = [];
10705
10944
  const boundPersisted = (entries, label) => {
10706
- const ordered = [
10707
- ...entries.filter((entry) => !isThisSession(entry)),
10708
- ...entries.filter(isThisSession)
10709
- ];
10710
- if (ordered.length <= TEST_RUNNER_MAX_PERSISTED_TARGETS)
10711
- return ordered;
10712
- dbg(`turn_end: ${label} test target list held ${ordered.length} entries, bounded to the newest ${TEST_RUNNER_MAX_PERSISTED_TARGETS} (oldest foreign-session rows dropped)`);
10713
- return ordered.slice(-TEST_RUNNER_MAX_PERSISTED_TARGETS);
10945
+ const bounded2 = boundDeferredTargets(entries, TEST_RUNNER_MAX_PERSISTED_TARGETS, isThisSession);
10946
+ if (bounded2.length !== entries.length) {
10947
+ dbg(`turn_end: ${label} test target list held ${entries.length} entries, bounded to the newest ${TEST_RUNNER_MAX_PERSISTED_TARGETS} (oldest foreign-session rows dropped)`);
10948
+ }
10949
+ return bounded2;
10714
10950
  };
10715
10951
  const writeTestFindings = (record, ownDeferred) => {
10716
10952
  const live = cacheManager2.readCache("test-runner-findings", cwd)?.data;
@@ -10734,7 +10970,7 @@ ${suffix}`;
10734
10970
  let redispatchedDeferred = 0;
10735
10971
  let retiredThisTurn = 0;
10736
10972
  for (const carried of carriedDeferred) {
10737
- const testFile = path23.resolve(cwd, carried.testFile);
10973
+ const testFile = path24.resolve(cwd, carried.testFile);
10738
10974
  const carriedKey = normalizeMapKey(testFile);
10739
10975
  if (!isThisSession(carried)) {
10740
10976
  foreignDeferred++;
@@ -10759,9 +10995,9 @@ ${suffix}`;
10759
10995
  incrementDegradationCount({
10760
10996
  kind: "test-runner-batch-capped",
10761
10997
  subject: `${cwd}:deferral-exhausted`,
10762
- reason: `test target was cut after ${attempts} turn(s) at the turn-end budget and is retired for this session \u2014 too slow for a per-turn batch; run it explicitly (target: ${path23.relative(cwd, testFile)})`
10998
+ reason: `test target was cut after ${attempts} turn(s) at the turn-end budget and is retired for this session \u2014 too slow for a per-turn batch; run it explicitly (target: ${path24.relative(cwd, testFile)})`
10763
10999
  });
10764
- dbg(`turn_end: retiring deferred test target ${path23.relative(cwd, testFile)} after ${attempts} cut batch(es) \u2014 too slow for the turn-end budget, run it explicitly`);
11000
+ dbg(`turn_end: retiring deferred test target ${path24.relative(cwd, testFile)} after ${attempts} cut batch(es) \u2014 too slow for the turn-end budget, run it explicitly`);
10765
11001
  }
10766
11002
  continue;
10767
11003
  }
@@ -10795,7 +11031,7 @@ ${suffix}`;
10795
11031
  testRoot: carriedRoot,
10796
11032
  deferralAttempts: attempts
10797
11033
  });
10798
- dbg(`turn_end: re-running deferred test target ${path23.relative(cwd, testFile)} (${carried.runner}) from the previous turn's cut batch`);
11034
+ dbg(`turn_end: re-running deferred test target ${path24.relative(cwd, testFile)} (${carried.runner}) from the previous turn's cut batch`);
10799
11035
  }
10800
11036
  if (deadDeferred > 0) {
10801
11037
  dbg(`turn_end: dropped ${deadDeferred} deferred test target(s) that no longer resolve (missing file, excluded, or unknown runner)`);
@@ -10839,17 +11075,17 @@ ${suffix}`;
10839
11075
  seen.add(targetKey);
10840
11076
  if (retiredKeys.has(targetKey)) {
10841
11077
  retiredSkips++;
10842
- dbg(`turn_end: ${display} \u2192 test target retired earlier this session (outran the turn-end batch budget), skipping spawn (${path23.relative(cwd, target.testFile)})`);
11078
+ dbg(`turn_end: ${display} \u2192 test target retired earlier this session (outran the turn-end batch budget), skipping spawn (${path24.relative(cwd, target.testFile)})`);
10843
11079
  continue;
10844
11080
  }
10845
11081
  if (isExcludedTestTarget(target.testFile, testRoot)) {
10846
11082
  excludedTargets++;
10847
- dbg(`turn_end: ${display} \u2192 test target excluded by the built-in turn-end policy, skipping spawn (${path23.relative(cwd, target.testFile)})`);
11083
+ dbg(`turn_end: ${display} \u2192 test target excluded by the built-in turn-end policy, skipping spawn (${path24.relative(cwd, target.testFile)})`);
10848
11084
  continue;
10849
11085
  }
10850
11086
  if (!fs17.existsSync(target.testFile)) {
10851
11087
  missingTargetFiles++;
10852
- dbg(`turn_end: ${display} \u2192 test file missing, skipping spawn (${path23.relative(cwd, target.testFile)})`);
11088
+ dbg(`turn_end: ${display} \u2192 test file missing, skipping spawn (${path24.relative(cwd, target.testFile)})`);
10853
11089
  continue;
10854
11090
  }
10855
11091
  if (targets.length >= TEST_RUNNER_MAX_TARGETS) {
@@ -10857,7 +11093,7 @@ ${suffix}`;
10857
11093
  continue;
10858
11094
  }
10859
11095
  targets.push({ ...target, sourceFile: abs, testRoot });
10860
- dbg(`turn_end: ${display} \u2192 test ${target.runner} ${path23.relative(cwd, target.testFile)} (${target.strategy}${isNeighbor ? ", cascade-neighbor" : ""})`);
11096
+ dbg(`turn_end: ${display} \u2192 test ${target.runner} ${path24.relative(cwd, target.testFile)} (${target.strategy}${isNeighbor ? ", cascade-neighbor" : ""})`);
10861
11097
  } else if (!target) {
10862
11098
  noTestFile++;
10863
11099
  dbg(`turn_end: ${display} \u2192 no test file found${isNeighbor ? " (cascade-neighbor)" : ""}`);
@@ -10989,6 +11225,7 @@ ${suffix}`;
10989
11225
  {
10990
11226
  file: result.value.file,
10991
11227
  sourceFile: target.sourceFile,
11228
+ runner: target.runner,
10992
11229
  fileSeq: target.fileSeqAtRun === void 0 ? {
10993
11230
  state: "unknown",
10994
11231
  reason: "sequence-unavailable"
@@ -11020,7 +11257,7 @@ ${suffix}`;
11020
11257
  });
11021
11258
  dbg(`turn_end: test batch stopped early (${stopReason ?? "incomplete"}), ${deferred.length} target(s) deferred to the next turn`);
11022
11259
  }
11023
- const deferralNote = deferred.length > 0 ? `${deferred.length} test target(s) did not finish within the turn-end batch budget and are deferred to the next turn (they run first): ${deferredTargets.map((t) => path23.relative(cwd, t.testFile)).join(", ")}` : "";
11260
+ const deferralNote = deferred.length > 0 ? `${deferred.length} test target(s) did not finish within the turn-end batch budget and are deferred to the next turn (they run first): ${deferredTargets.map((t) => path24.relative(cwd, t.testFile)).join(", ")}` : "";
11024
11261
  const supersededByNewerGeneration = (label) => {
11025
11262
  const current = cacheManager2.readCache("test-runner-findings", cwd)?.data;
11026
11263
  const currentGeneration = current?.testRunGeneration;
@@ -11072,7 +11309,7 @@ ${suffix}`;
11072
11309
  const { file, runner, passed, failed, duration, error } = r.value;
11073
11310
  if (failed > 0)
11074
11311
  hasRealFailure = true;
11075
- const shortFile = path23.basename(file);
11312
+ const shortFile = path24.basename(file);
11076
11313
  const elapsed = formatRunDurationMs(duration);
11077
11314
  const verdict = failed > 0 ? "FAIL" : "PASS";
11078
11315
  const summary = isRunnerErrorResult(r.value) ? passed > 0 ? `error: ${error} (${passed} passed before)` : `error: ${error}` : `${verdict} ${passed}p/${failed}f (${elapsed})`;
@@ -11236,8 +11473,8 @@ ${suffix}`;
11236
11473
  } else {
11237
11474
  const callGraphStart = Date.now();
11238
11475
  try {
11239
- const { impact, formatImpact, parseSymbolKey } = await import("../chunk-FJNKANOS.js");
11240
- const { callGraphImpactToProjectDiagnostics } = await import("../chunk-47D2BO4S.js");
11476
+ const { impact, formatImpact, parseSymbolKey } = await import("../chunk-BKE7E7TC.js");
11477
+ const { callGraphImpactToProjectDiagnostics } = await import("../chunk-6GFRNVYR.js");
11241
11478
  const impactLines = [];
11242
11479
  let callGraphDispositionSuppressed = 0;
11243
11480
  const impactFindings = [];
@@ -11544,13 +11781,17 @@ ${impactLines.join("\n")}`;
11544
11781
  }
11545
11782
  });
11546
11783
  }
11547
- cacheManager2.incrementTurnCycle(cwd, currentOwner);
11784
+ cacheManager2.incrementTurnCycle(cwd, currentOwner, turnStateSessionId);
11548
11785
  const lateAuxStart = Date.now();
11549
- const { drainedPairs, lateAuxCapEvicted } = holdScope.guardedWrite("turn-end:late-aux", () => ({
11786
+ const lateAuxDrain = isSecondarySession ? {
11787
+ drainedPairs: drainPendingAuxiliaryCoverage(),
11788
+ lateAuxCapEvicted: drainPendingAuxCapEvictedCount()
11789
+ } : holdScope.guardedWrite("turn-end:late-aux", () => ({
11550
11790
  drainedPairs: drainPendingAuxiliaryCoverage(),
11551
11791
  lateAuxCapEvicted: drainPendingAuxCapEvictedCount()
11552
11792
  })) ?? { drainedPairs: [], lateAuxCapEvicted: 0 };
11553
- const rearmLateAux = (pair) => holdScope.guardedWrite(
11793
+ const { drainedPairs, lateAuxCapEvicted } = lateAuxDrain;
11794
+ const rearmLateAux = (pair) => isSecondarySession ? (rearmPendingAuxiliaryCoverage(pair), true) : holdScope.guardedWrite(
11554
11795
  // One subject for the store (#4168 F3): the row's count carries how many.
11555
11796
  "turn-end:late-aux-rearm",
11556
11797
  () => {
@@ -11830,7 +12071,7 @@ ${lines.join("\n")}`;
11830
12071
  }
11831
12072
  });
11832
12073
  }
11833
- const unresolvedKeys = new Set(unresolvedBlockers.map((record) => normalizeMapKey(path23.resolve(record.filePath))));
12074
+ const unresolvedKeys = new Set(unresolvedBlockers.map((record) => normalizeMapKey(path24.resolve(record.filePath))));
11834
12075
  const formatResolvedTail = (dropped) => `\u2026 and ${dropped} more`;
11835
12076
  const resolvedTailMax = formatResolvedTail(Number.MAX_SAFE_INTEGER).length;
11836
12077
  const resolvedShareChars = Math.floor(RUNTIME_CONFIG.turnEnd.maxChars * RESOLVED_SHARE_OF_CHARS) - resolvedTailMax;
@@ -11855,7 +12096,7 @@ ${lines.join("\n")}`;
11855
12096
  let resolvedChars = 0;
11856
12097
  let resolvedCount = 0;
11857
12098
  const { files: resolvedBlockerFileList, dropped: resolvedBlockerFilesDropped } = runtime.consumeResolvedBlockerFiles((entry) => {
11858
- if (unresolvedKeys.has(normalizeMapKey(path23.resolve(entry.filePath))))
12099
+ if (unresolvedKeys.has(normalizeMapKey(path24.resolve(entry.filePath))))
11859
12100
  return true;
11860
12101
  const cost = formatResolvedLine(entry).length + 1;
11861
12102
  if (resolvedCount > 0 && (resolvedCount >= resolvedLineBudget || resolvedChars + cost > resolvedCharBudget))
@@ -11951,7 +12192,7 @@ ${raw}`
11951
12192
  projectSeqStart: runtime.turnStartProjectSeq,
11952
12193
  projectSeqEnd: runtime.projectSeq,
11953
12194
  fileSeqByPath: Object.fromEntries(runtime.getFileSeqEntries().map(([filePath, seq]) => [
11954
- normalizeMapKey(path23.resolve(filePath)),
12195
+ normalizeMapKey(path24.resolve(filePath)),
11955
12196
  seq
11956
12197
  ])),
11957
12198
  fileContentHashes: {},
@@ -11968,7 +12209,7 @@ ${raw}`
11968
12209
  settleHolds(false);
11969
12210
  const fileSeqByPath2 = {};
11970
12211
  for (const [filePath, seq] of runtime.getFileSeqEntries()) {
11971
- fileSeqByPath2[normalizeMapKey(path23.resolve(filePath))] = seq;
12212
+ fileSeqByPath2[normalizeMapKey(path24.resolve(filePath))] = seq;
11972
12213
  }
11973
12214
  if (getFlag("lens-guard")) {
11974
12215
  const existingGuard = cacheManager2.readCache("turn-end-findings", cwd)?.data;
@@ -12186,10 +12427,10 @@ var TurnEndQueue = class {
12186
12427
  if (this.pending.length >= 1) {
12187
12428
  return Promise.reject(new Error("turn_end queue is busy"));
12188
12429
  }
12189
- return new Promise((resolve30, reject) => {
12430
+ return new Promise((resolve32, reject) => {
12190
12431
  const item = {
12191
12432
  work,
12192
- resolve: resolve30,
12433
+ resolve: resolve32,
12193
12434
  reject,
12194
12435
  timer: setTimeout(() => {
12195
12436
  const index = this.pending.indexOf(item);
@@ -12233,7 +12474,7 @@ async function runTurnEndNowImpl(cwd, files = [], deferredDelivery = false) {
12233
12474
  const host = createMcpHost(void 0, cwd);
12234
12475
  let registered = 0;
12235
12476
  for (const file of files) {
12236
- const abs = path24.isAbsolute(file) ? file : path24.resolve(cwd, file);
12477
+ const abs = path25.isAbsolute(file) ? file : path25.resolve(cwd, file);
12237
12478
  let lineCount3 = 1;
12238
12479
  try {
12239
12480
  lineCount3 = fs18.readFileSync(abs, "utf8").split("\n").length;
@@ -12377,7 +12618,7 @@ function acknowledgeTurnEnd(cwd, deliveryId) {
12377
12618
 
12378
12619
  // dist/clients/module-report.js
12379
12620
  import * as fs19 from "node:fs";
12380
- import * as path25 from "node:path";
12621
+ import * as path26 from "node:path";
12381
12622
 
12382
12623
  // dist/clients/middle-man-analysis.js
12383
12624
  var SELF_TOKEN = {
@@ -12926,7 +13167,7 @@ function blastReadArgs(graph, normalizedFile) {
12926
13167
  return { path: normalizedFile, offset: 1, limit };
12927
13168
  }
12928
13169
  async function computeBlastRadius(graph, normalizedPath, projectRoot, maxDepth) {
12929
- const { computeTransitiveImpact } = await import("../chunk-34U7PLGE.js");
13170
+ const { computeTransitiveImpact } = await import("../chunk-4UNXBNE2.js");
12930
13171
  const result = computeTransitiveImpact(graph, normalizedPath, { maxDepth });
12931
13172
  const byFile = /* @__PURE__ */ new Map();
12932
13173
  for (const hit of result.hits) {
@@ -13516,7 +13757,7 @@ async function moduleReport(file, cwd, options) {
13516
13757
  const startedAt = Date.now();
13517
13758
  const maxRefs = normalizeMaxRefsPerSymbol(options?.maxRefsPerSymbol);
13518
13759
  const maxCallGraphEntries = Math.min(CALL_GRAPH_MAX_ENTRY_CAP, Math.max(1, Math.floor(options?.maxCallGraphEntries ?? CALL_GRAPH_DEFAULT_ENTRY_CAP)));
13519
- const absPath = path25.resolve(cwd, file);
13760
+ const absPath = path26.resolve(cwd, file);
13520
13761
  const normalizedPath = normalizeMapKey(absPath);
13521
13762
  let content;
13522
13763
  try {
@@ -13543,7 +13784,7 @@ async function moduleReport(file, cwd, options) {
13543
13784
  let callGraphIdentity;
13544
13785
  let revisionDriftNote;
13545
13786
  try {
13546
- const { getCachedReviewGraph, getReviewGraphCacheIdentity, getReviewGraphSizeSkipVerdict, getReviewGraphRevisionDrift, formatReviewGraphRevisionDriftNote: formatReviewGraphRevisionDriftNote2 } = await import("../chunk-VD4JQCQC.js");
13787
+ const { getCachedReviewGraph, getReviewGraphCacheIdentity, getReviewGraphSizeSkipVerdict, getReviewGraphRevisionDrift, formatReviewGraphRevisionDriftNote: formatReviewGraphRevisionDriftNote2 } = await import("../chunk-2ZGIZPDG.js");
13547
13788
  graph = getCachedReviewGraph(cwd);
13548
13789
  graphFileCap = getReviewGraphSizeSkipVerdict(cwd)?.maxFileCount;
13549
13790
  const drift = graph ? getReviewGraphRevisionDrift(cwd) : void 0;
@@ -13833,7 +14074,7 @@ function suggestSimilarNames(candidates, target) {
13833
14074
  async function readSymbol(file, symbolName, cwd, options = {}) {
13834
14075
  const { kind: symbolKindFilter } = options;
13835
14076
  const startedAt = Date.now();
13836
- const absPath = path25.resolve(cwd, file);
14077
+ const absPath = path26.resolve(cwd, file);
13837
14078
  const log = (found) => {
13838
14079
  logLatency({
13839
14080
  type: "phase",
@@ -14008,7 +14249,7 @@ function enclosingOutline(selected, symbols, callbacks, filters, filePath) {
14008
14249
  }
14009
14250
  async function readEnclosing(file, line, cwd, options) {
14010
14251
  const startedAt = Date.now();
14011
- const absPath = path25.resolve(cwd, file);
14252
+ const absPath = path26.resolve(cwd, file);
14012
14253
  const targetLine = Math.max(1, Math.floor(line));
14013
14254
  const log = (found) => {
14014
14255
  logLatency({
@@ -14184,7 +14425,7 @@ async function readEnclosing(file, line, cwd, options) {
14184
14425
  }
14185
14426
 
14186
14427
  // dist/clients/project-report.js
14187
- import * as path26 from "node:path";
14428
+ import * as path27 from "node:path";
14188
14429
  var DEFAULT_LIMIT = 10;
14189
14430
  var STALE_THRESHOLD_MS = 15 * 6e4;
14190
14431
  var LOW_COVERAGE_THRESHOLD = 0.8;
@@ -14327,7 +14568,7 @@ function roleFor(graph, filePath) {
14327
14568
  return top.length > 0 ? top.map((s) => s.name).join(", ") : void 0;
14328
14569
  }
14329
14570
  async function computeHubs(graph, degrees, cwd, limit, focusTerms) {
14330
- const { computeTransitiveImpact } = await import("../chunk-34U7PLGE.js");
14571
+ const { computeTransitiveImpact } = await import("../chunk-4UNXBNE2.js");
14331
14572
  const ranked = [...graph.fileNodes.entries()].map(([filePath, fileNodeId]) => ({
14332
14573
  filePath,
14333
14574
  fileNodeId,
@@ -14565,7 +14806,7 @@ function computeDeadWeight(graph, degrees, entryPointFiles, cwd, limit) {
14565
14806
  }
14566
14807
  var inFlightGraphBuilds = /* @__PURE__ */ new Set();
14567
14808
  function triggerBackgroundGraphBuild(cwd, deps) {
14568
- const key = normalizeMapKey(path26.resolve(cwd));
14809
+ const key = normalizeMapKey(path27.resolve(cwd));
14569
14810
  if (inFlightGraphBuilds.has(key))
14570
14811
  return "already_running";
14571
14812
  const deduped = deps.isGraphBuildInFlight(key, []);
@@ -14579,7 +14820,7 @@ async function projectReport(cwd, options) {
14579
14820
  const limit = clampLimit(options?.limit);
14580
14821
  const focusTerms = normalizeFocus2(options?.focus);
14581
14822
  const view = options?.view;
14582
- const { getCachedReviewGraph, getReviewGraphSizeSkipVerdict, getLastReviewGraphBuildAttempt, buildOrUpdateGraph, isGraphBuildInFlight, getReviewGraphRevisionDrift } = await import("../chunk-VD4JQCQC.js");
14823
+ const { getCachedReviewGraph, getReviewGraphSizeSkipVerdict, getLastReviewGraphBuildAttempt, buildOrUpdateGraph, isGraphBuildInFlight, getReviewGraphRevisionDrift } = await import("../chunk-2ZGIZPDG.js");
14583
14824
  let graph;
14584
14825
  try {
14585
14826
  graph = getCachedReviewGraph(cwd);
@@ -14603,7 +14844,7 @@ async function projectReport(cwd, options) {
14603
14844
  };
14604
14845
  }
14605
14846
  const previousAttempt = getLastReviewGraphBuildAttempt(cwd);
14606
- const { FactStore: FactStore2 } = await import("../chunk-YSNFMJKN.js");
14847
+ const { FactStore: FactStore2 } = await import("../chunk-QGQ5CCCG.js");
14607
14848
  const trigger = triggerBackgroundGraphBuild(cwd, {
14608
14849
  buildOrUpdateGraph,
14609
14850
  isGraphBuildInFlight,
@@ -14705,7 +14946,7 @@ function renderCompactProjectReport(report) {
14705
14946
 
14706
14947
  // dist/clients/effective-config.js
14707
14948
  import * as os3 from "node:os";
14708
- import * as path27 from "node:path";
14949
+ import * as path28 from "node:path";
14709
14950
  function isEffectiveFileViewError(file) {
14710
14951
  return "error" in file;
14711
14952
  }
@@ -14779,10 +15020,10 @@ async function effectiveConfig(options = {}) {
14779
15020
  globalConfigPath: getPiLensGlobalConfigPath(),
14780
15021
  homeDir
14781
15022
  });
14782
- const requestedFile = options.file === void 0 ? void 0 : path27.resolve(cwd, options.file);
15023
+ const requestedFile = options.file === void 0 ? void 0 : path28.resolve(cwd, options.file);
14783
15024
  const fileOutsideCwd = requestedFile !== void 0 && !isSameOrWithin(cwd, requestedFile);
14784
15025
  const absolute = fileOutsideCwd ? void 0 : requestedFile;
14785
- const resolution = resolveAt(absolute === void 0 ? cwd : path27.dirname(absolute));
15026
+ const resolution = resolveAt(absolute === void 0 ? cwd : path28.dirname(absolute));
14786
15027
  const resolved = {
14787
15028
  value: resolution.value,
14788
15029
  provenance: resolution.provenance
@@ -14816,7 +15057,7 @@ async function effectiveConfig(options = {}) {
14816
15057
  // `{ absolute, lspConfig }` struct (#2520): `absolute` is already
14817
15058
  // this branch's narrowed local, so the struct's own field was
14818
15059
  // carrying the same fact twice.
14819
- registerLSPConfig(lspConfigOf(resolution.value))
15060
+ registerLSPConfig(lspConfigOf(resolution.value, void 0, resolution.provenance))
14820
15061
  )
14821
15062
  }
14822
15063
  };
@@ -14944,10 +15185,10 @@ function symbolSearchFileMatchesLang(file, lang) {
14944
15185
  const exts = extensionsForLanguageToken(lang);
14945
15186
  if (exts.length === 0)
14946
15187
  return false;
14947
- return exts.includes(path28.extname(file).toLowerCase());
15188
+ return exts.includes(path29.extname(file).toLowerCase());
14948
15189
  }
14949
15190
  function fileMatchesPathGlobs(file, cwd, globs) {
14950
- const rel = path28.relative(cwd, file).split(path28.sep).join("/");
15191
+ const rel = path29.relative(cwd, file).split(path29.sep).join("/");
14951
15192
  const options = { dot: true, nocase: process.platform === "win32" };
14952
15193
  return globs.some((raw) => {
14953
15194
  const pattern = raw.split("\\").join("/");
@@ -14969,7 +15210,7 @@ function buildSymbolSearchFileFilter(cwd, options) {
14969
15210
  }
14970
15211
  function annotateSymbolSearchHitsWithGraph(hits, centrality, graph) {
14971
15212
  for (const hit of hits) {
14972
- const normalized = normalizeMapKey(path28.resolve(hit.file));
15213
+ const normalized = normalizeMapKey(path29.resolve(hit.file));
14973
15214
  const fanIn = centrality.get(hit.file) ?? 0;
14974
15215
  let complexity;
14975
15216
  for (const symbolId of graph.symbolNodesByFile.get(normalized) ?? []) {
@@ -15015,14 +15256,14 @@ async function symbolSearch(query, cwd, limit = 20, options = {}) {
15015
15256
  let centrality;
15016
15257
  let results;
15017
15258
  try {
15018
- centrality = centralityFromReverseDeps(index, snapshot?.reverseDeps, (file) => normalizeMapKey(path28.resolve(file)));
15259
+ centrality = centralityFromReverseDeps(index, snapshot?.reverseDeps, (file) => normalizeMapKey(path29.resolve(file)));
15019
15260
  const fileFilter = buildSymbolSearchFileFilter(cwd, options);
15020
15261
  results = searchWordIndex(index, query, { limit, centrality, fileFilter });
15021
15262
  } finally {
15022
15263
  warmLease.release();
15023
15264
  }
15024
15265
  const hits = results.map(toSymbolSearchHit);
15025
- const { getCachedReviewGraph } = await import("../chunk-VD4JQCQC.js");
15266
+ const { getCachedReviewGraph } = await import("../chunk-2ZGIZPDG.js");
15026
15267
  const graph = getCachedReviewGraph(cwd);
15027
15268
  if (graph)
15028
15269
  annotateSymbolSearchHitsWithGraph(hits, centrality, graph);
@@ -15039,10 +15280,10 @@ async function symbolSearch(query, cwd, limit = 20, options = {}) {
15039
15280
  import { Type } from "typebox";
15040
15281
 
15041
15282
  // dist/clients/ast-grep-tool-logger.js
15042
- import * as path29 from "node:path";
15283
+ import * as path30 from "node:path";
15043
15284
  var AG_LOG_DIR = getGlobalPiLensLogDir();
15044
- var AG_LOG_FILE = path29.join(AG_LOG_DIR, "ast-grep-tools.log");
15045
- var AG_LOG_BACKUP_FILE = path29.join(AG_LOG_DIR, "ast-grep-tools.log.1");
15285
+ var AG_LOG_FILE = path30.join(AG_LOG_DIR, "ast-grep-tools.log");
15286
+ var AG_LOG_BACKUP_FILE = path30.join(AG_LOG_DIR, "ast-grep-tools.log.1");
15046
15287
  var MAX_LOG_BYTES3 = Math.max(128 * 1024, Number.parseInt(process.env.PI_LENS_AST_GREP_LOG_MAX_BYTES ?? "1048576", 10) || 1048576);
15047
15288
  var writer3 = createNdjsonLogger({
15048
15289
  filePath: AG_LOG_FILE,
@@ -15219,6 +15460,11 @@ function canonicalLanguage(lang) {
15219
15460
  }
15220
15461
 
15221
15462
  // dist/tools/shared.js
15463
+ function resolveAstGrepPaths(paths, cwd) {
15464
+ const base = cwd || ".";
15465
+ const requested = paths?.length ? paths : [base];
15466
+ return requested.map((p) => resolveHostToolPath(p, base));
15467
+ }
15222
15468
  var LANGUAGES = [
15223
15469
  "bash",
15224
15470
  "c",
@@ -15355,7 +15601,7 @@ function createAstGrepReplaceTool(astGrepClient2, captureLineage) {
15355
15601
  details: {}
15356
15602
  };
15357
15603
  }
15358
- const searchPaths = paths?.length ? paths : [ctx.cwd || "."];
15604
+ const searchPaths = resolveAstGrepPaths(paths, ctx.cwd);
15359
15605
  if (hasStructuralIntent({
15360
15606
  insideKind,
15361
15607
  hasKind,
@@ -15388,7 +15634,7 @@ function createAstGrepReplaceTool(astGrepClient2, captureLineage) {
15388
15634
  details: {}
15389
15635
  };
15390
15636
  }
15391
- const ruleResult = await astGrepClient2.replaceWithRule(ruleYaml, searchPaths, applyFlag);
15637
+ const ruleResult = await astGrepClient2.replaceWithRule(ruleYaml, searchPaths, applyFlag, { lineage, toolCallId: _toolCallId });
15392
15638
  if (ruleResult.stalePreview) {
15393
15639
  logOutcome("error", { errorRaw: "stale_preview" });
15394
15640
  return {
@@ -15427,7 +15673,7 @@ function createAstGrepReplaceTool(astGrepClient2, captureLineage) {
15427
15673
  }
15428
15674
  };
15429
15675
  }
15430
- const result = await astGrepClient2.replace(pattern, rewrite, lang, searchPaths, applyFlag, { strictness, lineage });
15676
+ const result = await astGrepClient2.replace(pattern, rewrite, lang, searchPaths, applyFlag, { strictness, lineage, toolCallId: _toolCallId });
15431
15677
  if (result.stalePreview) {
15432
15678
  logOutcome("error", { errorRaw: "stale_preview" });
15433
15679
  return {
@@ -15975,7 +16221,7 @@ function createAstGrepSearchTool(astGrepClient2) {
15975
16221
  details: {}
15976
16222
  };
15977
16223
  }
15978
- const searchPaths = paths?.length ? paths : [ctx.cwd || "."];
16224
+ const searchPaths = resolveAstGrepPaths(paths, ctx.cwd);
15979
16225
  const PAGE_SIZE = Math.max(1, Math.min(MAX_PAGE_SIZE, Number.isFinite(maxMatches) ? Math.floor(maxMatches) : DEFAULT_PAGE_SIZE));
15980
16226
  let effectiveRule = hasRawRule ? rawRule : void 0;
15981
16227
  if (!effectiveRule && (hasNodeKind || hasStructuralIntent({
@@ -16167,7 +16413,7 @@ ${hint}` : `${output}${paginationNote}`;
16167
16413
  // dist/tools/lens-diagnostics.js
16168
16414
  import { promises as fs23 } from "node:fs";
16169
16415
  import * as fsSync from "node:fs";
16170
- import * as path36 from "node:path";
16416
+ import * as path37 from "node:path";
16171
16417
 
16172
16418
  // dist/clients/project-diagnostics/extractors.js
16173
16419
  var WARM_TRIGGER = {
@@ -16227,12 +16473,12 @@ function reasonFromAvailabilityVerdict(tool, verdict) {
16227
16473
  // dist/clients/project-diagnostics/fresh-fetch.js
16228
16474
  import * as fs21 from "node:fs";
16229
16475
  import * as os4 from "node:os";
16230
- import * as path34 from "node:path";
16476
+ import * as path35 from "node:path";
16231
16477
 
16232
16478
  // dist/clients/project-diagnostics/runner-adapters/jscpd.js
16233
- import * as path30 from "node:path";
16479
+ import * as path31 from "node:path";
16234
16480
  function resolveClonePath(cwd, file) {
16235
- return path30.isAbsolute(file) ? file : path30.resolve(cwd, file);
16481
+ return path31.isAbsolute(file) ? file : path31.resolve(cwd, file);
16236
16482
  }
16237
16483
  function jscpdCloneToProjectDiagnostics(cwd, clone) {
16238
16484
  const a = resolveClonePath(cwd, clone.fileA);
@@ -16245,7 +16491,7 @@ function jscpdCloneToProjectDiagnostics(cwd, clone) {
16245
16491
  tool: "jscpd",
16246
16492
  runner: "jscpd",
16247
16493
  rule: "jscpd:duplicate",
16248
- message: `Duplicate code (${clone.lines} lines) \u2014 also at ${path30.relative(cwd, otherFile)}:${otherLine}`,
16494
+ message: `Duplicate code (${clone.lines} lines) \u2014 also at ${path31.relative(cwd, otherFile)}:${otherLine}`,
16249
16495
  source: "project-scan"
16250
16496
  });
16251
16497
  return [
@@ -16260,16 +16506,16 @@ function jscpdResultToProjectDiagnostics(cwd, result) {
16260
16506
  }
16261
16507
 
16262
16508
  // dist/clients/project-diagnostics/runner-adapters/madge.js
16263
- import * as path31 from "node:path";
16509
+ import * as path32 from "node:path";
16264
16510
  function resolveCyclePath(cwd, file) {
16265
- return path31.isAbsolute(file) ? file : path31.resolve(cwd, file);
16511
+ return path32.isAbsolute(file) ? file : path32.resolve(cwd, file);
16266
16512
  }
16267
16513
  function cycleMembers(dep) {
16268
16514
  return dep.path.length > 0 ? dep.path : [dep.file];
16269
16515
  }
16270
16516
  function circularDepToProjectDiagnostics(cwd, dep) {
16271
16517
  const members = cycleMembers(dep);
16272
- const rendered = `${members.map((f) => path31.basename(f)).join(" \u2192 ")} \u2192 ${path31.basename(members[0] ?? "")}`;
16518
+ const rendered = `${members.map((f) => path32.basename(f)).join(" \u2192 ")} \u2192 ${path32.basename(members[0] ?? "")}`;
16273
16519
  return members.map((file) => ({
16274
16520
  filePath: resolveCyclePath(cwd, file),
16275
16521
  severity: "warning",
@@ -16295,7 +16541,7 @@ function circularDepsToProjectDiagnostics(cwd, circular) {
16295
16541
  }
16296
16542
 
16297
16543
  // dist/clients/project-diagnostics/runner-adapters/opengrep.js
16298
- import * as path32 from "node:path";
16544
+ import * as path33 from "node:path";
16299
16545
  function severityFor(raw) {
16300
16546
  switch (raw.toUpperCase()) {
16301
16547
  case "ERROR":
@@ -16310,7 +16556,7 @@ function opengrepFindingToProjectDiagnostic(cwd, finding) {
16310
16556
  const { severity, blocking } = severityFor(finding.severity);
16311
16557
  const cweSuffix = finding.cwe?.length ? ` (${finding.cwe[0]})` : "";
16312
16558
  return {
16313
- filePath: path32.isAbsolute(finding.path) ? finding.path : path32.resolve(cwd, finding.path),
16559
+ filePath: path33.isAbsolute(finding.path) ? finding.path : path33.resolve(cwd, finding.path),
16314
16560
  line: finding.startLine,
16315
16561
  column: finding.startCol,
16316
16562
  severity,
@@ -16330,7 +16576,7 @@ function opengrepResultToProjectDiagnostics(cwd, result) {
16330
16576
 
16331
16577
  // dist/clients/project-diagnostics/runner-adapters/runner-findings.js
16332
16578
  import * as fs20 from "node:fs";
16333
- import * as path33 from "node:path";
16579
+ import * as path34 from "node:path";
16334
16580
  function failureMessage(failure) {
16335
16581
  const firstLine2 = failure.message.split("\n")[0]?.slice(0, 300) ?? "";
16336
16582
  return firstLine2 ? `${failure.name}: ${firstLine2}` : failure.name;
@@ -16396,7 +16642,7 @@ function testRunnerFindingsToProjectDiagnostics(cache, cwd, runtime) {
16396
16642
  return [];
16397
16643
  const validation = cwd ? validateAdvisoryProvenance(cache, cwd, runtime) : { status: "unknown", reasons: ["validation-context-missing"] };
16398
16644
  const root = cwd ?? process.cwd();
16399
- const missingKeys = new Set((cache.provenance?.files ?? []).filter((file) => !fs20.existsSync(path33.resolve(root, file.path))).map((file) => advisoryPathKey(file.path, root)));
16645
+ const missingKeys = new Set((cache.provenance?.files ?? []).filter((file) => !fs20.existsSync(path34.resolve(root, file.path))).map((file) => advisoryPathKey(file.path, root)));
16400
16646
  const historical = cache.superseded === true || cache.stale === true || validation.status !== "current";
16401
16647
  return cache.results.filter((result) => !missingKeys.has(advisoryPathKey(result.file, root))).flatMap((result) => testResultToProjectDiagnostics(result, historical)).map((diagnostic) => historical ? {
16402
16648
  ...diagnostic,
@@ -16426,7 +16672,7 @@ function laneObservedAtMs(scannedAt, startedAtMs) {
16426
16672
  return parseScannedAtMs(scannedAt) ?? startedAtMs;
16427
16673
  }
16428
16674
  async function fetchFreshProjectDiagnostics(cacheManager2, cwd, clients, signal, options = {}) {
16429
- const requestedRoot = options.analysisRoot === void 0 ? cwd : path34.resolve(cwd, options.analysisRoot);
16675
+ const requestedRoot = options.analysisRoot === void 0 ? cwd : path35.resolve(cwd, options.analysisRoot);
16430
16676
  let analysisRoot;
16431
16677
  let analysisRootValidation;
16432
16678
  if (options.analysisRoot !== void 0) {
@@ -16593,7 +16839,7 @@ async function fetchFreshProjectDiagnostics(cacheManager2, cwd, clients, signal,
16593
16839
  markCold("jscpd", reasonFromAvailabilityVerdict("jscpd", clients.jscpdClient.getAvailabilityVerdict?.()));
16594
16840
  return;
16595
16841
  }
16596
- const isTsProject = fs21.existsSync(path34.join(analysisRoot, "tsconfig.json"));
16842
+ const isTsProject = fs21.existsSync(path35.join(analysisRoot, "tsconfig.json"));
16597
16843
  const scannerKey = isTsProject ? "jscpd-ts" : "jscpd";
16598
16844
  const startMs = Date.now();
16599
16845
  const result = await clients.jscpdClient.scan(analysisRoot, void 0, void 0, isTsProject);
@@ -16824,12 +17070,12 @@ async function fetchFreshProjectDiagnostics(cacheManager2, cwd, clients, signal,
16824
17070
  const allSettled = Promise.all(tasks).then(() => "completed").catch(() => "completed");
16825
17071
  const outcome = signal ? await Promise.race([
16826
17072
  allSettled,
16827
- new Promise((resolve30) => {
17073
+ new Promise((resolve32) => {
16828
17074
  if (signal.aborted) {
16829
- resolve30("aborted");
17075
+ resolve32("aborted");
16830
17076
  return;
16831
17077
  }
16832
- signal.addEventListener("abort", () => resolve30("aborted"), {
17078
+ signal.addEventListener("abort", () => resolve32("aborted"), {
16833
17079
  once: true
16834
17080
  });
16835
17081
  })
@@ -16910,7 +17156,7 @@ function makeProgressReporter(onUpdate, label, throttleMs = 250) {
16910
17156
 
16911
17157
  // dist/tools/lsp-diagnostics.js
16912
17158
  import * as fs22 from "node:fs";
16913
- import * as path35 from "node:path";
17159
+ import * as path36 from "node:path";
16914
17160
  var MAX_FILES = 100;
16915
17161
  var MAX_BATCH_FILES = 100;
16916
17162
  var MAX_DIAGNOSTICS = 200;
@@ -16993,11 +17239,11 @@ async function collectFiles(dir, extensions, maxFiles, isIgnored = () => false)
16993
17239
  return;
16994
17240
  if (entry.isSymbolicLink())
16995
17241
  continue;
16996
- const full = path35.join(current, entry.name);
17242
+ const full = path36.join(current, entry.name);
16997
17243
  if (entry.isDirectory()) {
16998
17244
  if (!isExcludedDirName(entry.name) && !isIgnored(full, true))
16999
17245
  await walk(full);
17000
- } else if (entry.isFile() && extensions.includes(path35.extname(full))) {
17246
+ } else if (entry.isFile() && extensions.includes(path36.extname(full))) {
17001
17247
  if (isIgnored(full, false))
17002
17248
  continue;
17003
17249
  files.push(full);
@@ -17091,7 +17337,7 @@ function createLspDiagnosticsTool(nextWriteIndex, onConfirmedNoBlockers, getServ
17091
17337
  details: {}
17092
17338
  };
17093
17339
  }
17094
- const absPath = normalizeMapKey(path35.resolve(cwd, rawPath));
17340
+ const absPath = normalizeMapKey(path36.resolve(cwd, rawPath));
17095
17341
  let stat;
17096
17342
  try {
17097
17343
  stat = fs22.statSync(absPath);
@@ -17272,7 +17518,7 @@ function coveredSourcesForCheck(file, serverScope, unconfirmedServerIds) {
17272
17518
  return [];
17273
17519
  }
17274
17520
  for (const server of servers) {
17275
- const auxiliary = server.role === "auxiliary";
17521
+ const auxiliary = isAuxiliary(server);
17276
17522
  if (auxiliary && serverScope === "primary")
17277
17523
  continue;
17278
17524
  if (unconfirmed.has(server.id))
@@ -17748,12 +17994,12 @@ async function collectBatchDiagnostics(files, severity, lspService, options) {
17748
17994
  });
17749
17995
  const result = options.signal ? await Promise.race([
17750
17996
  bounded2,
17751
- new Promise((resolve30) => {
17997
+ new Promise((resolve32) => {
17752
17998
  if (options.signal?.aborted) {
17753
- resolve30(void 0);
17999
+ resolve32(void 0);
17754
18000
  return;
17755
18001
  }
17756
- options.signal?.addEventListener("abort", () => resolve30(void 0), {
18002
+ options.signal?.addEventListener("abort", () => resolve32(void 0), {
17757
18003
  once: true
17758
18004
  });
17759
18005
  })
@@ -17812,7 +18058,7 @@ async function expandPathsToFiles(cwd, rawPaths) {
17812
18058
  const directories = [];
17813
18059
  const seenFileKeys = /* @__PURE__ */ new Set();
17814
18060
  for (const entry of rawPaths) {
17815
- const abs = normalizeMapKey(path35.resolve(cwd, entry));
18061
+ const abs = normalizeMapKey(path36.resolve(cwd, entry));
17816
18062
  let stat;
17817
18063
  try {
17818
18064
  stat = fs22.statSync(abs);
@@ -18016,7 +18262,7 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
18016
18262
  outcome: result.outcome,
18017
18263
  reason: result.tooLargeReason ?? result.inconclusiveReason ?? result.error ?? result.unavailable
18018
18264
  }));
18019
- const tooLargeLines = renderBatchOutcomeLines(outcomeDetails, (file) => path35.relative(absPath, file));
18265
+ const tooLargeLines = renderBatchOutcomeLines(outcomeDetails, (file) => path36.relative(absPath, file));
18020
18266
  let text;
18021
18267
  if (total === 0) {
18022
18268
  const cleanLine = unconfirmed > 0 ? `${clean} clean \xB7 ${unconfirmed} unconfirmed: ${unconfirmedReasonClause(unconfirmed, timedOut, rootFallbackReasons)} NOT the same as 0 diagnostics.` : "No diagnostics found.";
@@ -18048,7 +18294,7 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
18048
18294
  ];
18049
18295
  const toRelative = (d) => ({
18050
18296
  ...d,
18051
- file: path35.relative(absPath, d.file)
18297
+ file: path36.relative(absPath, d.file)
18052
18298
  });
18053
18299
  if (primaryDisplay.length > 0) {
18054
18300
  lines.push(`Primary findings (${primaryDisplay.length}):`);
@@ -18079,7 +18325,7 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
18079
18325
  filesScanned: filesToProcess.length,
18080
18326
  capped: wasCapped,
18081
18327
  diagnostics: display.map((d) => ({
18082
- file: path35.relative(absPath, d.file),
18328
+ file: path36.relative(absPath, d.file),
18083
18329
  line: d.line,
18084
18330
  character: d.character,
18085
18331
  severity: d.severity,
@@ -18170,7 +18416,7 @@ async function findFullScanBindingMismatches(results) {
18170
18416
  sinceYield += 1;
18171
18417
  if (sinceYield >= FULL_SCAN_BINDING_YIELD_EVERY) {
18172
18418
  sinceYield = 0;
18173
- await new Promise((resolve30) => setImmediate(resolve30));
18419
+ await new Promise((resolve32) => setImmediate(resolve32));
18174
18420
  }
18175
18421
  }
18176
18422
  return mismatched;
@@ -18209,7 +18455,7 @@ function createLensDiagnosticsTool(cacheManager2, getCwd, getLspService = getLSP
18209
18455
  const files2 = details.filesChecked ?? details.filesScanned ?? 0;
18210
18456
  const noun = count === 1 ? "diagnostic" : "diagnostics";
18211
18457
  const filePath = typeof details?.filePath === "string" ? details.filePath : Array.isArray(args?.paths) && args.paths.length === 1 && typeof args.paths[0] === "string" ? args.paths[0] : void 0;
18212
- const singleFile = (details?.mode === "file" || files2 === 1) && typeof filePath === "string" ? ` ${path36.basename(filePath)}` : "";
18458
+ const singleFile = (details?.mode === "file" || files2 === 1) && typeof filePath === "string" ? ` ${path37.basename(filePath)}` : "";
18213
18459
  const scope = files2 > 1 ? ` across ${files2} files` : singleFile;
18214
18460
  if (isError)
18215
18461
  return `lens_diagnostics lsp \u2014 ${text.split("\n")[0] ?? "error"}`;
@@ -18455,7 +18701,7 @@ function appendProjectDiagnosticsDeltaLines(groups, cwd, report, severity, inclu
18455
18701
  const diagnostics = [...gated.live, ...gated.stale];
18456
18702
  const byFile = /* @__PURE__ */ new Map();
18457
18703
  for (const diagnostic of diagnostics) {
18458
- const filePath = path36.resolve(diagnostic.filePath);
18704
+ const filePath = path37.resolve(diagnostic.filePath);
18459
18705
  const bucket = byFile.get(filePath) ?? [];
18460
18706
  bucket.push(diagnostic);
18461
18707
  byFile.set(filePath, bucket);
@@ -18515,7 +18761,10 @@ function filterDeltaReportDispositions(report, cwd, policyMap) {
18515
18761
  if (!report?.diagnostics.length)
18516
18762
  return report;
18517
18763
  const kept = report.diagnostics.filter((d) => applyCachedDispositions([projectDiagnosticToWidget(d, d.filePath)], cwd, d.filePath).length === 1);
18518
- const policyKept = applyRulePolicy(kept, policyMap);
18764
+ const policyKept = applyRulePolicy(kept, policyMap, {
18765
+ root: cwd,
18766
+ filePath: cwd
18767
+ });
18519
18768
  if (policyKept.length === report.diagnostics.length)
18520
18769
  return report;
18521
18770
  return { ...report, diagnostics: policyKept };
@@ -18579,7 +18828,7 @@ function applyDeltaFreshnessGate(files, cwd, generatedAt) {
18579
18828
  })).filter((file) => file.warnings.length > 0);
18580
18829
  }
18581
18830
  function getDeltaFileGroup(groups, cwd, filePath) {
18582
- const rel = path36.relative(cwd, filePath);
18831
+ const rel = path37.relative(cwd, filePath);
18583
18832
  let group = groups.get(rel);
18584
18833
  if (!group) {
18585
18834
  group = {
@@ -18607,7 +18856,7 @@ function formatDeltaMode(cacheManager2, cwd, severity, pathsScope) {
18607
18856
  // #2504 r4 (F1): carried through, so a merged report's older
18608
18857
  // entries keep their own age all the way to the freshness gate.
18609
18858
  generatedAt: file.generatedAt,
18610
- warnings: applyRulePolicy(applyCachedDispositions(file.warnings ?? [], cwd, file.filePath), policyMap)
18859
+ warnings: applyRulePolicy(applyCachedDispositions(file.warnings ?? [], cwd, file.filePath), policyMap, { root: cwd, filePath: file.filePath })
18611
18860
  })).filter((file) => file.warnings.length > 0);
18612
18861
  const actionableFiles = applyDeltaFreshnessGate(visibleWarningFiles(actionable?.files), cwd, actionable?.generatedAt);
18613
18862
  const qualityFiles = applyDeltaFreshnessGate(visibleWarningFiles(quality?.files), cwd, quality?.generatedAt);
@@ -18676,7 +18925,7 @@ function formatDeltaMode(cacheManager2, cwd, severity, pathsScope) {
18676
18925
  let text = `No ${severity === "all" ? "" : severity + " "}issues in the current turn delta.`;
18677
18926
  const carried = getFileDiagnosticSummaries().filter((f) => includeFile(f.filePath)).map((f) => ({
18678
18927
  ...f,
18679
- diagnostics: applyRulePolicy(applyCachedDispositions(f.diagnostics, cwd, f.filePath), policyMap)
18928
+ diagnostics: applyRulePolicy(applyCachedDispositions(f.diagnostics, cwd, f.filePath), policyMap, { root: cwd, filePath: f.filePath })
18680
18929
  })).filter((f) => f.diagnostics.length > 0);
18681
18930
  const carriedIssues = carried.reduce((n, f) => n + f.diagnostics.length, 0);
18682
18931
  if (carried.length > 0) {
@@ -18703,7 +18952,7 @@ Summary (turn delta): ${aw} actionable warning${aw === 1 ? "" : "s"} \xB7 ${cq}
18703
18952
  function createCurrentIgnoreFilter(cwd) {
18704
18953
  try {
18705
18954
  const matcher = getProjectIgnoreMatcher(cwd);
18706
- return (filePath) => !matcher.isIgnored(path36.resolve(filePath), false);
18955
+ return (filePath) => !matcher.isIgnored(path37.resolve(filePath), false);
18707
18956
  } catch {
18708
18957
  return () => true;
18709
18958
  }
@@ -18714,7 +18963,7 @@ function resolvePathsScope(cwd, rawPaths) {
18714
18963
  if (rawPaths.length > MAX_PATHS_ENTRIES) {
18715
18964
  throw new Error(`paths has ${rawPaths.length} entries, over the ${MAX_PATHS_ENTRIES} cap. Narrow the list or omit paths to scan without a restriction.`);
18716
18965
  }
18717
- const entries = rawPaths.filter((entry) => typeof entry === "string" && entry.trim().length > 0).map((entry) => path36.resolve(path36.isAbsolute(entry) ? entry : path36.resolve(cwd, entry)));
18966
+ const entries = rawPaths.filter((entry) => typeof entry === "string" && entry.trim().length > 0).map((entry) => path37.resolve(path37.isAbsolute(entry) ? entry : path37.resolve(cwd, entry)));
18718
18967
  const missing = [];
18719
18968
  const fileEntries = [];
18720
18969
  const existingEntries = [];
@@ -18737,9 +18986,9 @@ function resolvePathsScope(cwd, rawPaths) {
18737
18986
  }
18738
18987
  const fileKeys = new Set(fileEntries.map((f) => normalizeFilePath(f)));
18739
18988
  const dirPrefixes = dirEntries.map((d) => normalizeFilePath(d));
18740
- const includeExplicitFile = (filePath) => fileKeys.has(normalizeFilePath(path36.resolve(filePath)));
18989
+ const includeExplicitFile = (filePath) => fileKeys.has(normalizeFilePath(path37.resolve(filePath)));
18741
18990
  const includeFile = (filePath) => {
18742
- const key = normalizeFilePath(path36.resolve(filePath));
18991
+ const key = normalizeFilePath(path37.resolve(filePath));
18743
18992
  if (fileKeys.has(key))
18744
18993
  return true;
18745
18994
  return dirPrefixes.some((prefix) => key === prefix || key.startsWith(`${prefix}/`));
@@ -18760,7 +19009,7 @@ function pathsScopeCacheOnlyNote(scope) {
18760
19009
  function pathsScopeMissingNote(scope) {
18761
19010
  if (!scope || scope.missing.length === 0)
18762
19011
  return "";
18763
- const shown2 = scope.missing.slice(0, 10).map((p) => path36.basename(p));
19012
+ const shown2 = scope.missing.slice(0, 10).map((p) => path37.basename(p));
18764
19013
  const more = scope.missing.length > shown2.length ? ` (+${scope.missing.length - shown2.length} more)` : "";
18765
19014
  return `
18766
19015
 
@@ -18792,12 +19041,15 @@ function filterProjectDiagnosticsDeltaReport(report, includeFile) {
18792
19041
  diagnostics: report.diagnostics.filter((d) => includeFile(d.filePath))
18793
19042
  };
18794
19043
  }
18795
- function applyProjectRulePolicy(value, policyMap) {
19044
+ function applyProjectRulePolicy(value, policyMap, root) {
18796
19045
  if (!value)
18797
19046
  return void 0;
18798
19047
  return {
18799
19048
  ...value,
18800
- diagnostics: applyRulePolicy(value.diagnostics, policyMap)
19049
+ diagnostics: applyRulePolicy(value.diagnostics, policyMap, {
19050
+ root,
19051
+ filePath: value.diagnostics[0]?.filePath ?? root
19052
+ })
18801
19053
  };
18802
19054
  }
18803
19055
  function isErrorLike(d) {
@@ -18888,7 +19140,7 @@ function projectDiagnosticToWidget(diagnostic, filePath) {
18888
19140
  }
18889
19141
  function diagnosticDedupKey(filePath, diagnostic) {
18890
19142
  const ruleId = normalizeRuleId(diagnostic.rule ?? diagnostic.tool ?? "");
18891
- return [path36.resolve(filePath), diagnostic.line ?? "?", ruleId].join(":");
19143
+ return [path37.resolve(filePath), diagnostic.line ?? "?", ruleId].join(":");
18892
19144
  }
18893
19145
  function runnerIdOf(diagnostic) {
18894
19146
  return diagnostic.tool ?? diagnostic.rule?.split(":", 1)[0] ?? "";
@@ -18902,8 +19154,8 @@ function runnerRetirementDecision(diagnostic, filePath, authoritativeRunnerIds,
18902
19154
  const realFilePath = realpathOrResolve(filePath);
18903
19155
  for (const entry of coverage) {
18904
19156
  const root = entry.root;
18905
- const relative9 = path36.relative(root, realFilePath);
18906
- const underRoot = relative9 === "" || !relative9.startsWith("..") && !path36.isAbsolute(relative9);
19157
+ const relative9 = path37.relative(root, realFilePath);
19158
+ const underRoot = relative9 === "" || !relative9.startsWith("..") && !path37.isAbsolute(relative9);
18907
19159
  if (!underRoot)
18908
19160
  continue;
18909
19161
  if (entry.files.has(realFilePath)) {
@@ -19027,7 +19279,7 @@ function mergeDiagnosticsWithWidgetSummaries(widgetSummaries, lspResults, projec
19027
19279
  const byFile = /* @__PURE__ */ new Map();
19028
19280
  const seen = /* @__PURE__ */ new Set();
19029
19281
  for (const summary of widgetSummaries) {
19030
- const filePath = path36.resolve(summary.filePath);
19282
+ const filePath = path37.resolve(summary.filePath);
19031
19283
  if (authoritativeLspFiles?.has(filePath))
19032
19284
  continue;
19033
19285
  const retained = summary.diagnostics ?? [];
@@ -19055,7 +19307,7 @@ function mergeDiagnosticsWithWidgetSummaries(widgetSummaries, lspResults, projec
19055
19307
  byFile.set(filePath, summarizeDiagnostics(filePath, diagnostics, existing?.hasFinalSnapshot ?? true));
19056
19308
  };
19057
19309
  for (const result of lspResults) {
19058
- const filePath = path36.resolve(result.filePath);
19310
+ const filePath = path37.resolve(result.filePath);
19059
19311
  for (const diagnostic of result.diagnostics ?? []) {
19060
19312
  addDiagnostic(filePath, {
19061
19313
  ...lspDiagnosticToWidget(diagnostic),
@@ -19075,12 +19327,12 @@ function mergeDiagnosticsWithWidgetSummaries(widgetSummaries, lspResults, projec
19075
19327
  });
19076
19328
  }
19077
19329
  for (const diagnostic of projectSnapshot?.diagnostics ?? []) {
19078
- addDiagnostic(path36.resolve(diagnostic.filePath), projectDiagnosticToWidget(diagnostic, diagnostic.filePath));
19330
+ addDiagnostic(path37.resolve(diagnostic.filePath), projectDiagnosticToWidget(diagnostic, diagnostic.filePath));
19079
19331
  }
19080
19332
  for (const diagnostic of deltaDiagnostics) {
19081
19333
  const widget = projectDiagnosticToWidget(diagnostic, diagnostic.filePath);
19082
19334
  const observedAt = widget.observedAt ?? deltaObservedAt;
19083
- addDiagnostic(path36.resolve(diagnostic.filePath), {
19335
+ addDiagnostic(path37.resolve(diagnostic.filePath), {
19084
19336
  ...widget,
19085
19337
  ...observedAt !== void 0 && { observedAt }
19086
19338
  });
@@ -19095,7 +19347,10 @@ async function applyInlineSuppressionsToSummaries(summaries, cwd, policyMap) {
19095
19347
  try {
19096
19348
  content = await fs23.readFile(summary.filePath, "utf8");
19097
19349
  } catch {
19098
- const policyKept2 = applyRulePolicy(summary.diagnostics, policyMap);
19350
+ const policyKept2 = applyRulePolicy(summary.diagnostics, policyMap, {
19351
+ root: cwd,
19352
+ filePath: summary.filePath
19353
+ });
19099
19354
  return policyKept2.length === summary.diagnostics.length ? summary : summarizeDiagnostics(summary.filePath, policyKept2, summary.hasFinalSnapshot);
19100
19355
  }
19101
19356
  const { kept: policyKept } = applyFindingPolicy(summary.diagnostics, {
@@ -19280,20 +19535,20 @@ async function formatFullMode(cwd, severity, lspService, cacheManager2, options
19280
19535
  result2.observedAt
19281
19536
  );
19282
19537
  if (retired === true) {
19283
- authoritativeLspFiles.add(path36.resolve(result2.filePath));
19538
+ authoritativeLspFiles.add(path37.resolve(result2.filePath));
19284
19539
  } else {
19285
- unreconciledFiles.add(path36.resolve(result2.filePath));
19540
+ unreconciledFiles.add(path37.resolve(result2.filePath));
19286
19541
  recordDegradationOnce({
19287
19542
  kind: "diagnostic-retained-unreconciled",
19288
- subject: `${path36.resolve(result2.filePath)}:lsp`,
19543
+ subject: `${path37.resolve(result2.filePath)}:lsp`,
19289
19544
  reason: "confirmed diagnostic result could not replace retained widget state"
19290
19545
  });
19291
19546
  }
19292
19547
  } catch {
19293
- unreconciledFiles.add(path36.resolve(result2.filePath));
19548
+ unreconciledFiles.add(path37.resolve(result2.filePath));
19294
19549
  recordDegradationOnce({
19295
19550
  kind: "diagnostic-retained-unreconciled",
19296
- subject: `${path36.resolve(result2.filePath)}:lsp`,
19551
+ subject: `${path37.resolve(result2.filePath)}:lsp`,
19297
19552
  reason: "confirmed diagnostic result failed during widget reconciliation"
19298
19553
  });
19299
19554
  }
@@ -19303,9 +19558,9 @@ async function formatFullMode(cwd, severity, lspService, cacheManager2, options
19303
19558
  const authoritativeRunnerIds = new Set(extracted.analyzed ?? []);
19304
19559
  const authoritativeRunnerCoverage = extracted.authoritativeCoverage ?? [];
19305
19560
  const foldedProjectSnapshot = foldExtraDiagnosticsIntoSnapshot(scannedSnapshot, extracted.diagnostics.filter((d) => includeFile(d.filePath)), extracted.runners, cwd);
19306
- const projectSnapshot = applyProjectRulePolicy(foldedProjectSnapshot, policyMap);
19307
- const projectDelta = applyProjectRulePolicy(filterProjectDiagnosticsDeltaReport(loadProjectDiagnosticsDeltaReport(cwd), includeFile), policyMap);
19308
- const authoritativeRetiredCount = getFileDiagnosticSummaries().filter((summary) => includeFile(summary.filePath) && authoritativeLspFiles.has(path36.resolve(summary.filePath)) && (summary.diagnostics?.length ?? 0) > 0).length;
19561
+ const projectSnapshot = applyProjectRulePolicy(foldedProjectSnapshot, policyMap, cwd);
19562
+ const projectDelta = applyProjectRulePolicy(filterProjectDiagnosticsDeltaReport(loadProjectDiagnosticsDeltaReport(cwd), includeFile), policyMap, cwd);
19563
+ const authoritativeRetiredCount = getFileDiagnosticSummaries().filter((summary) => includeFile(summary.filePath) && authoritativeLspFiles.has(path37.resolve(summary.filePath)) && (summary.diagnostics?.length ?? 0) > 0).length;
19309
19564
  if (authoritativeRetiredCount > 0) {
19310
19565
  logLatency({
19311
19566
  type: "phase",
@@ -19360,7 +19615,7 @@ async function formatFullMode(cwd, severity, lspService, cacheManager2, options
19360
19615
  }
19361
19616
  let summaries = await applyInlineSuppressionsToSummaries(mergeDiagnosticsWithWidgetSummaries(getFileDiagnosticSummaries().filter((summary) => includeFile(summary.filePath)), confirmedLspResults, projectSnapshot, projectDelta, authoritativeLspFiles, authoritativeRunnerIds, authoritativeRunnerCoverage), cwd, policyMap);
19362
19617
  if (unreconciledFiles.size > 0) {
19363
- summaries = summaries.map((summary) => unreconciledFiles.has(path36.resolve(summary.filePath)) ? summarizeDiagnostics(summary.filePath, markUnreconciledFindings(summary.diagnostics), summary.hasFinalSnapshot) : summary);
19618
+ summaries = summaries.map((summary) => unreconciledFiles.has(path37.resolve(summary.filePath)) ? summarizeDiagnostics(summary.filePath, markUnreconciledFindings(summary.diagnostics), summary.hasFinalSnapshot) : summary);
19364
19619
  }
19365
19620
  const parsedProjectScanObservedAt = projectSnapshot?.scannedAt ? Date.parse(projectSnapshot.scannedAt) : void 0;
19366
19621
  const projectScanObservedAt = parsedProjectScanObservedAt !== void 0 && Number.isFinite(parsedProjectScanObservedAt) ? parsedProjectScanObservedAt : void 0;
@@ -19579,7 +19834,10 @@ async function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummari
19579
19834
  const dispositioned = isFullMode ? summaries : summaries.map((s) => {
19580
19835
  const diagnostics = s.diagnostics ?? [];
19581
19836
  const kept = applyCachedDispositions(diagnostics, cwd, s.filePath);
19582
- const policyKept = applyRulePolicy(kept, policyMap);
19837
+ const policyKept = applyRulePolicy(kept, policyMap, {
19838
+ root: cwd,
19839
+ filePath: s.filePath
19840
+ });
19583
19841
  if (policyKept.length === kept.length && kept.length === diagnostics.length)
19584
19842
  return s;
19585
19843
  return summarizeDiagnostics(s.filePath, policyKept, s.hasFinalSnapshot);
@@ -19624,7 +19882,7 @@ async function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummari
19624
19882
  let totalWarnings = 0;
19625
19883
  let totalAdvisories = 0;
19626
19884
  for (const s of rendered) {
19627
- const rel = path36.relative(cwd, s.filePath);
19885
+ const rel = path37.relative(cwd, s.filePath);
19628
19886
  const parts = [];
19629
19887
  if (s.blocking > 0)
19630
19888
  parts.push(`\u{1F534} ${s.blocking} blocking`);
@@ -19692,7 +19950,7 @@ Summary (${visibleSummaries.length} files diagnosed this session):`,
19692
19950
 
19693
19951
  // dist/tools/lsp-navigation.js
19694
19952
  import * as nodeFs2 from "node:fs";
19695
- import * as path37 from "node:path";
19953
+ import * as path38 from "node:path";
19696
19954
  import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL2 } from "node:url";
19697
19955
 
19698
19956
  // dist/tools/lsp-structured-output.js
@@ -20402,7 +20660,7 @@ function collectSearchReadsForOperation(operation, result, callHierarchyItem) {
20402
20660
  }
20403
20661
  function formatCapabilities(snapshots, filePath) {
20404
20662
  if (snapshots.length === 0) {
20405
- return filePath ? `No active LSP server for ${path37.basename(filePath)}. Open/touch the file first or run another LSP operation to start the server.` : "No active LSP servers in this session.";
20663
+ return filePath ? `No active LSP server for ${path38.basename(filePath)}. Open/touch the file first or run another LSP operation to start the server.` : "No active LSP servers in this session.";
20406
20664
  }
20407
20665
  const rows = [
20408
20666
  ["definition", (s) => !!s.operationSupport.definition],
@@ -20432,7 +20690,7 @@ function formatCapabilities(snapshots, filePath) {
20432
20690
  ];
20433
20691
  const lines = [];
20434
20692
  for (const snapshot of snapshots) {
20435
- const label = filePath ? `${snapshot.serverId} (${path37.basename(filePath)})` : `${snapshot.serverId} (${snapshot.root})`;
20693
+ const label = filePath ? `${snapshot.serverId} (${path38.basename(filePath)})` : `${snapshot.serverId} (${snapshot.root})`;
20436
20694
  lines.push(label);
20437
20695
  for (const [name, supported, note] of rows) {
20438
20696
  const suffix = note ? ` (${note})` : "";
@@ -20723,7 +20981,12 @@ function createLspNavigationTool(getFlag, mutationDeps) {
20723
20981
  const mutationSource = operation === "executeCommand" ? "lsp-execute-command" : "lsp-rename";
20724
20982
  mutationContext = {
20725
20983
  cwd,
20984
+ sessionId: ctx.sessionManager?.getSessionId?.(),
20726
20985
  correlationId: newLspMutationCorrelationId(_toolCallId),
20986
+ // #4187 R4-1: the call whose `tool_call` licensed the paths it
20987
+ // named; the workspace edit's other files (a rename's importers)
20988
+ // are unlicensed, so they end an authorship instead of advancing it.
20989
+ toolCallId: _toolCallId,
20727
20990
  tool: `lsp_navigation:${operation}`,
20728
20991
  source: mutationSource,
20729
20992
  ...mutationDeps,
@@ -20767,7 +21030,7 @@ function createLspNavigationTool(getFlag, mutationDeps) {
20767
21030
  resultCount: 0
20768
21031
  });
20769
21032
  }
20770
- const filePath = rawPath ? path37.isAbsolute(rawPath) ? rawPath : path37.resolve(ctx.cwd || ".", rawPath) : "";
21033
+ const filePath = rawPath ? path38.isAbsolute(rawPath) ? rawPath : path38.resolve(ctx.cwd || ".", rawPath) : "";
20771
21034
  let filePathIsDirectory = false;
20772
21035
  if (filePath) {
20773
21036
  try {
@@ -20803,7 +21066,7 @@ function createLspNavigationTool(getFlag, mutationDeps) {
20803
21066
  content: [
20804
21067
  {
20805
21068
  type: "text",
20806
- text: `No LSP server available for ${path37.basename(filePath)}. Check that the language server is installed.`
21069
+ text: `No LSP server available for ${path38.basename(filePath)}. Check that the language server is installed.`
20807
21070
  }
20808
21071
  ],
20809
21072
  isError: true
@@ -20904,7 +21167,7 @@ ${JSON.stringify(result2)}`
20904
21167
  content: [
20905
21168
  {
20906
21169
  type: "text",
20907
- text: `No LSP server available for ${path37.basename(filePath)}. Check that the language server is installed.`
21170
+ text: `No LSP server available for ${path38.basename(filePath)}. Check that the language server is installed.`
20908
21171
  }
20909
21172
  ],
20910
21173
  isError: true
@@ -20923,7 +21186,7 @@ ${JSON.stringify(result2)}`
20923
21186
  content: [
20924
21187
  {
20925
21188
  type: "text",
20926
- text: `LSP server for ${path37.basename(filePath)} does not advertise support for ${operation}`
21189
+ text: `LSP server for ${path38.basename(filePath)} does not advertise support for ${operation}`
20927
21190
  }
20928
21191
  ],
20929
21192
  isError: true,
@@ -20977,7 +21240,7 @@ ${JSON.stringify(result2)}`
20977
21240
  const msg = err instanceof Error ? err.message : String(err);
20978
21241
  if (rawPath && /No Project/i.test(msg)) {
20979
21242
  await openFileBestEffort(lspService, filePath);
20980
- await new Promise((resolve30) => setTimeout(resolve30, 120));
21243
+ await new Promise((resolve32) => setTimeout(resolve32, 120));
20981
21244
  const retryRaw = await lspService.workspaceSymbol(query ?? "", filePath);
20982
21245
  const retrySymbols = (Array.isArray(retryRaw) ? retryRaw : [retryRaw]).filter((s) => typeof s === "object" && s !== null);
20983
21246
  return dedupeWorkspaceSymbols(retrySymbols);
@@ -21052,7 +21315,7 @@ ${JSON.stringify(result2)}`
21052
21315
  if (!newFilePath || newFilePath.trim().length === 0) {
21053
21316
  throw new Error("__BADINPUT__ newFilePath parameter required for rename_file");
21054
21317
  }
21055
- const resolvedNewFilePath = path37.isAbsolute(newFilePath) ? newFilePath : path37.resolve(ctx.cwd || ".", newFilePath);
21318
+ const resolvedNewFilePath = path38.isAbsolute(newFilePath) ? newFilePath : path38.resolve(ctx.cwd || ".", newFilePath);
21056
21319
  const result2 = await lspService.renameFile(filePath, resolvedNewFilePath, {
21057
21320
  cwd: ctx.cwd || ".",
21058
21321
  apply: apply ?? false,
@@ -21196,7 +21459,7 @@ ${JSON.stringify(result2)}`
21196
21459
  }, { operation, filePath, failureKind: "lsp_error", resultCount: 0 });
21197
21460
  }
21198
21461
  const isEmpty = !result || Array.isArray(result) && result.length === 0;
21199
- const fileCtx = filePath ? " at " + path37.basename(filePath) : "";
21462
+ const fileCtx = filePath ? " at " + path38.basename(filePath) : "";
21200
21463
  const lineCtx = line ? ":" + line + ":" + character : "";
21201
21464
  let output = isEmpty ? "No results for " + operation + fileCtx + lineCtx : JSON.stringify(result);
21202
21465
  if (isEmpty && operation === "workspaceSymbol" && !rawPath) {
@@ -21438,11 +21701,11 @@ function resolveDefaultCwd() {
21438
21701
  const fromArg = process.argv.find((arg) => arg.startsWith("--cwd="))?.slice("--cwd=".length);
21439
21702
  return fromArg ?? process.env.PI_LENS_MCP_CWD ?? process.cwd();
21440
21703
  }
21441
- var DEFAULT_CWD = path38.resolve(resolveDefaultCwd());
21704
+ var DEFAULT_CWD = path39.resolve(resolveDefaultCwd());
21442
21705
  var lspReadyCwds = /* @__PURE__ */ new Set();
21443
21706
  var SERVER_FILE = fileURLToPath3(import.meta.url);
21444
- var SERVER_DIR = path38.dirname(SERVER_FILE);
21445
- var WORKER_PATH = path38.join(SERVER_DIR, "worker.js");
21707
+ var SERVER_DIR = path39.dirname(SERVER_FILE);
21708
+ var WORKER_PATH = path39.join(SERVER_DIR, "worker.js");
21446
21709
  var REBUILD_SCRIPT = resolveRebuildScript(SERVER_FILE);
21447
21710
  var STALENESS_STAT_PATH = process.env.PI_LENS_MCP_STALENESS_STAT_PATH ?? SERVER_FILE;
21448
21711
  var BUILD_STAMP = computeBuildStamp(STALENESS_STAT_PATH);
@@ -21464,7 +21727,7 @@ function isWarmBuildStale() {
21464
21727
  function findRepoRoot(start) {
21465
21728
  let dir = start;
21466
21729
  for (let depth = 0; depth < 6; depth++) {
21467
- const pkgPath = path38.join(dir, "package.json");
21730
+ const pkgPath = path39.join(dir, "package.json");
21468
21731
  if (fs25.existsSync(pkgPath)) {
21469
21732
  try {
21470
21733
  const pkg = JSON.parse(fs25.readFileSync(pkgPath, "utf8"));
@@ -21473,16 +21736,16 @@ function findRepoRoot(start) {
21473
21736
  } catch {
21474
21737
  }
21475
21738
  }
21476
- const parent = path38.dirname(dir);
21739
+ const parent = path39.dirname(dir);
21477
21740
  if (parent === dir)
21478
21741
  break;
21479
21742
  dir = parent;
21480
21743
  }
21481
- return path38.resolve(start, "..", "..");
21744
+ return path39.resolve(start, "..", "..");
21482
21745
  }
21483
- var REPO_ROOT = process.env.PI_LENS_MCP_REPO_ROOT ? path38.resolve(process.env.PI_LENS_MCP_REPO_ROOT) : findRepoRoot(SERVER_DIR);
21746
+ var REPO_ROOT = process.env.PI_LENS_MCP_REPO_ROOT ? path39.resolve(process.env.PI_LENS_MCP_REPO_ROOT) : findRepoRoot(SERVER_DIR);
21484
21747
  async function ensureReady(cwd) {
21485
- const normalized = path38.resolve(cwd);
21748
+ const normalized = path39.resolve(cwd);
21486
21749
  if (!shouldInitializeSessionRoot(normalized, lspReadyCwds)) {
21487
21750
  return;
21488
21751
  }
@@ -21528,14 +21791,14 @@ function getAutoSessionStatus() {
21528
21791
  }
21529
21792
  var IPC_PATH = ipcPathForCwd(DEFAULT_CWD);
21530
21793
  function rejectForeignTurnCwd(route, cwd) {
21531
- const message = `${route} cwd outside this server's workspace: ${path38.resolve(cwd)} not within ${DEFAULT_CWD}`;
21794
+ const message = `${route} cwd outside this server's workspace: ${path39.resolve(cwd)} not within ${DEFAULT_CWD}`;
21532
21795
  console.error(`[pi-lens-mcp] rejected ${message}`);
21533
21796
  return message;
21534
21797
  }
21535
21798
  function isWithinServerWorkspace(cwd) {
21536
- const target = path38.resolve(cwd);
21537
- const rel = path38.relative(DEFAULT_CWD, target);
21538
- return !rel.startsWith("..") && !path38.isAbsolute(rel);
21799
+ const target = path39.resolve(cwd);
21800
+ const rel = path39.relative(DEFAULT_CWD, target);
21801
+ return !rel.startsWith("..") && !path39.isAbsolute(rel);
21539
21802
  }
21540
21803
  function startIpcServer() {
21541
21804
  if (process.platform !== "win32") {
@@ -21685,6 +21948,12 @@ function graphStalenessNote(builtAtIso, label) {
21685
21948
  return void 0;
21686
21949
  return `${label} last updated ${formatRelativeAge(ageMs)}; run pilens_analyze on recently-changed files, pilens_session_start, or pilens_rebuild to refresh it.`;
21687
21950
  }
21951
+ setProjectTrustState("trusted");
21952
+ logExtension({
21953
+ subsystem: "project-trust",
21954
+ level: "debug",
21955
+ message: "mcp: workspace trust belongs to the MCP client; pi trust not consulted"
21956
+ });
21688
21957
  var cacheManager = new CacheManager();
21689
21958
  var isLensGuardEnabled = () => Boolean(createMcpHost(void 0, DEFAULT_CWD).getFlag("lens-guard"));
21690
21959
  var lensDiagnosticsTool = createLensDiagnosticsTool(
@@ -22024,7 +22293,7 @@ function enabledToolsForCwd(cwd) {
22024
22293
  }
22025
22294
  function formatAnalyze(result, cwd, mode, servedBy) {
22026
22295
  const lspNote = result.lsp ? ` \xB7 lsp ${result.lsp.diagnosticCount} (${result.lsp.status}${result.lsp.failureKind ? `: ${result.lsp.failureKind}` : ""}, ${result.lsp.durationMs}ms)` : "";
22027
- const summary = `${path38.relative(cwd, result.filePath) || result.filePath} [${mode}] \u2014 ${result.counts.blockers} blocking, ${result.counts.warnings} warning(s), ${result.counts.advisories} advisory(ies), ${result.counts.diagnostics} total` + (result.latency ? ` \xB7 ${result.latency.totalDurationMs}ms` : "") + lspNote + (result.counts.fixed > 0 ? ` \xB7 ${result.counts.fixed} auto-fixed` : "") + (servedBy ? `
22296
+ const summary = `${path39.relative(cwd, result.filePath) || result.filePath} [${mode}] \u2014 ${result.counts.blockers} blocking, ${result.counts.warnings} warning(s), ${result.counts.advisories} advisory(ies), ${result.counts.diagnostics} total` + (result.latency ? ` \xB7 ${result.latency.totalDurationMs}ms` : "") + lspNote + (result.counts.fixed > 0 ? ` \xB7 ${result.counts.fixed} auto-fixed` : "") + (servedBy ? `
22028
22297
 
22029
22298
  servedBy: ${servedBy}` : "");
22030
22299
  return renderToolText(summary, servedBy ? { ...result, servedBy } : result);
@@ -22158,7 +22427,7 @@ ${outcome.output}`, {
22158
22427
  }
22159
22428
  const { deduped, byRule, byFile } = summarizeScan(snapshot.diagnostics);
22160
22429
  const topRules = Object.entries(byRule).sort((a, b) => b[1] - a[1]);
22161
- const topFiles = Object.entries(byFile).sort((a, b) => b[1] - a[1]).slice(0, 15).map(([file, count]) => ({ file: path38.relative(cwd, file), count }));
22430
+ const topFiles = Object.entries(byFile).sort((a, b) => b[1] - a[1]).slice(0, 15).map(([file, count]) => ({ file: path39.relative(cwd, file), count }));
22162
22431
  const summaryLines = [
22163
22432
  `Scanned ${snapshot.filesScanned} file(s) [${snapshot.runners.join(", ")}] \u2192 ${deduped.length} unique diagnostic(s)` + (snapshot.diagnostics.length !== deduped.length ? ` (${snapshot.diagnostics.length} raw, ${snapshot.diagnostics.length - deduped.length} duplicate)` : ""),
22164
22433
  ...topRules.slice(0, 12).map(([rule, count]) => ` ${count}\xD7 ${rule}`)
@@ -22211,7 +22480,7 @@ ${stalenessNote}` : ""), {
22211
22480
  }
22212
22481
  const lines = [
22213
22482
  `Top ${results.length} file(s) for "${query}":`,
22214
- ...results.map((result, i) => ` ${i + 1}. ${path38.relative(cwd, result.file)} (score ${result.score.toFixed(2)}, ${result.hits} hit(s), line ${result.startLine})`),
22483
+ ...results.map((result, i) => ` ${i + 1}. ${path39.relative(cwd, result.file)} (score ${result.score.toFixed(2)}, ${result.hits} hit(s), line ${result.startLine})`),
22215
22484
  ...stalenessNote ? ["", stalenessNote] : [],
22216
22485
  ...coverage ? [
22217
22486
  `Index covers ${coverage.files} files${coverage.truncated ? " (capped \u2014 results may be incomplete)" : ""}.`
@@ -22221,7 +22490,7 @@ ${stalenessNote}` : ""), {
22221
22490
  query,
22222
22491
  coverage,
22223
22492
  results: results.map((result) => {
22224
- const relFile = path38.relative(cwd, result.file);
22493
+ const relFile = path39.relative(cwd, result.file);
22225
22494
  return {
22226
22495
  file: relFile,
22227
22496
  score: result.score,
@@ -22260,12 +22529,12 @@ ${stalenessNote}` : ""), {
22260
22529
  });
22261
22530
  if (!report.available) {
22262
22531
  return {
22263
- ...renderToolText(`No module report for ${path38.relative(cwd, path38.resolve(cwd, file))} \u2014 not a symbol-bearing file, or unreadable.`, report, true),
22532
+ ...renderToolText(`No module report for ${path39.relative(cwd, path39.resolve(cwd, file))} \u2014 not a symbol-bearing file, or unreadable.`, report, true),
22264
22533
  isError: true
22265
22534
  };
22266
22535
  }
22267
22536
  const graphStaleness = graphStalenessNote(report.graphBuiltAt, "Review graph");
22268
- const summary = `${path38.relative(cwd, report.path) || report.path} [${report.staleness}] \u2014 ${report.summary.symbols} symbol(s), ${report.summary.exports} exported, ${report.api.length} in public API` + (graphStaleness ? `
22537
+ const summary = `${path39.relative(cwd, report.path) || report.path} [${report.staleness}] \u2014 ${report.summary.symbols} symbol(s), ${report.summary.exports} exported, ${report.api.length} in public API` + (graphStaleness ? `
22269
22538
 
22270
22539
  ${graphStaleness}` : "");
22271
22540
  if (view === "compact") {
@@ -22326,7 +22595,7 @@ ${graphStaleness}` : "");
22326
22595
  if (!result.found) {
22327
22596
  const suggestionSuffix = result.suggestions?.length ? ` Did you mean: ${result.suggestions.join(", ")}?` : " Use pilens_module_report to list symbols.";
22328
22597
  return {
22329
- ...renderToolText(`Symbol "${symbol}" not found in ${path38.basename(file)}.${suggestionSuffix}`, {
22598
+ ...renderToolText(`Symbol "${symbol}" not found in ${path39.basename(file)}.${suggestionSuffix}`, {
22330
22599
  found: false,
22331
22600
  ...result.suggestions ? { suggestions: result.suggestions } : {}
22332
22601
  }),
@@ -22335,7 +22604,7 @@ ${graphStaleness}` : "");
22335
22604
  }
22336
22605
  const sigSuffix = result.signature ? ` ${result.signature}` : "";
22337
22606
  const ambiguityNote = result.ambiguous ? ` (${result.ambiguous.count} matches \u2014 returned the ${result.kind}; pass \`kind\` to disambiguate: ${result.ambiguous.kinds.join(", ")})` : "";
22338
- const header = `${result.kind} ${result.name}${ambiguityNote}${sigSuffix} ${path38.relative(cwd, result.path)}:${result.startLine}-${result.endLine}`;
22607
+ const header = `${result.kind} ${result.name}${ambiguityNote}${sigSuffix} ${path39.relative(cwd, result.path)}:${result.startLine}-${result.endLine}`;
22339
22608
  return {
22340
22609
  content: [
22341
22610
  {
@@ -22373,14 +22642,14 @@ ${result.source ?? ""}`
22373
22642
 
22374
22643
  Nested outline:
22375
22644
  ${JSON.stringify(result.outline)}` : "";
22376
- const text = result.error ? `Could not read enclosing range in ${path38.basename(file)}:${result.line}: ${result.error}${warningSuffix}${outlineSuffix}` : `No enclosing symbol/callback found in ${path38.basename(file)}:${result.line}.${warningSuffix}`;
22645
+ const text = result.error ? `Could not read enclosing range in ${path39.basename(file)}:${result.line}: ${result.error}${warningSuffix}${outlineSuffix}` : `No enclosing symbol/callback found in ${path39.basename(file)}:${result.line}.${warningSuffix}`;
22377
22646
  return {
22378
22647
  ...renderToolText(text, { found: false, line: result.line }),
22379
22648
  isError: true
22380
22649
  };
22381
22650
  }
22382
22651
  const range = result.partial ? `${result.startLine}-${result.endLine} (partial of ${result.enclosingStartLine}-${result.enclosingEndLine})` : `${result.startLine}-${result.endLine}`;
22383
- const header = `${result.kind} ${result.name} ${path38.relative(cwd, result.path)}:${range}`;
22652
+ const header = `${result.kind} ${result.name} ${path39.relative(cwd, result.path)}:${range}`;
22384
22653
  return {
22385
22654
  content: [
22386
22655
  {
@@ -22415,7 +22684,7 @@ ${result.source ?? ""}`
22415
22684
  ]),
22416
22685
  ...renderLspBrokenStatusLines(brokenServers),
22417
22686
  ...renderDegradationLines(degradations),
22418
- last ? `Last dispatch: ${path38.basename(last.filePath)} \u2014 ${last.totalDurationMs}ms, ${last.totalDiagnostics} diagnostic(s)` : "Last dispatch: none yet",
22687
+ last ? `Last dispatch: ${path39.basename(last.filePath)} \u2014 ${last.totalDurationMs}ms, ${last.totalDiagnostics} diagnostic(s)` : "Last dispatch: none yet",
22419
22688
  `Diagnostics this session: ${stats.totalShown} shown \xB7 ${stats.totalAutoFixed} auto-fixed \xB7 ${stats.totalUnresolved} unresolved`,
22420
22689
  autoSession ? `Auto session_start: ${autoSession.succeeded ? "succeeded" : autoSession.error ? "FAILED" : autoSession.attempted ? "in progress" : "not yet attempted"}${autoSession.firedAt ? ` (fired ${autoSession.firedAt})` : ""}${autoSession.error ? ` \u2014 ${autoSession.error}` : ""}` : "Auto session_start: disabled (PI_LENS_MCP_AUTO_SESSION not set)",
22421
22690
  turnEnd ? `Stop-hook turn-end: ${turnEnd.ran} ran \xB7 ${turnEnd.skipped} skipped${turnEnd.lastRunAt ? ` (last ran ${turnEnd.lastRunAt})` : ""}${turnEnd.lastSkipReason ? ` \u2014 last skip: ${turnEnd.lastSkipReason}${turnEnd.lastSkipAt ? ` at ${turnEnd.lastSkipAt}` : ""}` : ""}` : "Stop-hook turn-end: no activity recorded (hook not installed, or no Stop yet)",
@@ -22481,7 +22750,7 @@ ${result.source ?? ""}`
22481
22750
  const limit = typeof args.limit === "number" && Number.isFinite(args.limit) ? Math.max(1, Math.floor(args.limit)) : 5;
22482
22751
  const fileFilter = typeof args.file === "string" ? args.file : void 0;
22483
22752
  const recent = recentLatency(limit, fileFilter);
22484
- const summary = recent.length === 0 ? "No dispatch latency reports yet." : recent.map((report) => `${path38.basename(report.filePath)}: ${report.totalDurationMs}ms (${report.totalDiagnostics} diag${report.stoppedEarly ? ", stopped early" : ""})`).join("\n");
22753
+ const summary = recent.length === 0 ? "No dispatch latency reports yet." : recent.map((report) => `${path39.basename(report.filePath)}: ${report.totalDurationMs}ms (${report.totalDiagnostics} diag${report.stoppedEarly ? ", stopped early" : ""})`).join("\n");
22485
22754
  return renderToolText(summary, recent);
22486
22755
  }
22487
22756
  if (name === "pilens_session_start") {