pi-lens 4.4.1 → 4.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/config/dependency-cruiser-eager-allowlist.json +3 -1
  3. package/dist/{chunk-BJF25SUG.js → chunk-23WOO7TX.js} +11 -10
  4. package/dist/{chunk-WATC3IQR.js → chunk-2B73ZPOK.js} +3 -3
  5. package/dist/{chunk-N2IRDBZI.js → chunk-2BBLFX75.js} +101 -11
  6. package/dist/{chunk-TA5L5KWQ.js → chunk-2CD5RS6M.js} +2 -2
  7. package/dist/{chunk-3TTPKMEF.js → chunk-2VUQMUMT.js} +38 -37
  8. package/dist/{chunk-5JHXUETW.js → chunk-3EFKIGZN.js} +3 -3
  9. package/dist/chunk-3JNKMP3B.js +17 -0
  10. package/dist/{chunk-6P54TMWL.js → chunk-3VPVG5SU.js} +6 -6
  11. package/dist/{chunk-ZETYB3XG.js → chunk-3W7IEVVL.js} +44 -28
  12. package/dist/chunk-3YN6V5TV.js +85 -0
  13. package/dist/chunk-466Z26MG.js +30 -0
  14. package/dist/{chunk-PCHDGUKT.js → chunk-4UNXBNE2.js} +12 -11
  15. package/dist/{chunk-J7WJZ6W7.js → chunk-4W4VLAPJ.js} +12 -11
  16. package/dist/{chunk-F5VLZHUW.js → chunk-52FUZI6K.js} +4 -2
  17. package/dist/{chunk-H5EV3VMF.js → chunk-5NGR67U5.js} +2 -2
  18. package/dist/{chunk-CBD2B5CP.js → chunk-5UCAH366.js} +21 -20
  19. package/dist/{chunk-BHWHXMQ2.js → chunk-67WMNWEE.js} +9 -8
  20. package/dist/{chunk-USHG4DLH.js → chunk-6C7JWPCP.js} +7 -7
  21. package/dist/{chunk-VPWKF3GN.js → chunk-6GFRNVYR.js} +11 -10
  22. package/dist/{chunk-3JL6IFD7.js → chunk-6SDEP3AJ.js} +9 -8
  23. package/dist/{chunk-NJF2X5XF.js → chunk-6STA6Z7I.js} +19 -17
  24. package/dist/{chunk-KJCWXUUS.js → chunk-724UL2TJ.js} +6 -4
  25. package/dist/{chunk-MQA2YBAH.js → chunk-76MWC2K6.js} +3 -3
  26. package/dist/{chunk-YLN4K6WD.js → chunk-77M37OKR.js} +10 -8
  27. package/dist/{chunk-XHSCVSOL.js → chunk-7DYBLXYA.js} +3 -3
  28. package/dist/{chunk-OC7LYAU5.js → chunk-AB3YYG6N.js} +24 -10
  29. package/dist/{chunk-LD7WFTRX.js → chunk-ACXEQZOU.js} +16 -16
  30. package/dist/{chunk-VSMJK3ZJ.js → chunk-AOHZU2BM.js} +1 -1
  31. package/dist/{chunk-T4QZ6FEH.js → chunk-AW7R2GWG.js} +65 -8
  32. package/dist/{chunk-GAYGGICG.js → chunk-BKE7E7TC.js} +10 -9
  33. package/dist/{chunk-EUT3B22Y.js → chunk-BRGDSBJE.js} +13 -12
  34. package/dist/{chunk-T54VJRAD.js → chunk-C34ZXZ7O.js} +55 -11
  35. package/dist/{chunk-UGSMCQ4D.js → chunk-CHOW74WE.js} +1 -1
  36. package/dist/{chunk-H2W2AXD3.js → chunk-CMMHNUZ2.js} +66 -58
  37. package/dist/chunk-CSHFH5IN.js +17 -0
  38. package/dist/chunk-EC47DWFA.js +56 -0
  39. package/dist/{chunk-KJN4IOEJ.js → chunk-EJIUXPBX.js} +1 -1
  40. package/dist/{chunk-IQZXOZ7S.js → chunk-EU63SDCG.js} +720 -334
  41. package/dist/{chunk-LNOMA3MP.js → chunk-FCSOVF55.js} +107 -3
  42. package/dist/{chunk-CFYISHUW.js → chunk-FLKWCIZH.js} +8 -5
  43. package/dist/{chunk-OYTMZXRF.js → chunk-FMSO6RDZ.js} +16 -14
  44. package/dist/{chunk-IUKPRZER.js → chunk-GAEGEPOC.js} +2 -2
  45. package/dist/{chunk-6XX5HCLK.js → chunk-GBPP5P7X.js} +4 -4
  46. package/dist/{chunk-TQFBZVCG.js → chunk-GXW6K4YJ.js} +1 -1
  47. package/dist/{chunk-ZZUFZUQZ.js → chunk-H6MF6EVN.js} +331 -107
  48. package/dist/{chunk-3JSEWUWI.js → chunk-IBGIOBEE.js} +3 -3
  49. package/dist/chunk-IZSHAIJ7.js +26 -0
  50. package/dist/{chunk-7ELY2CSM.js → chunk-JAU72XNY.js} +16 -14
  51. package/dist/chunk-JMYYOTQG.js +77 -0
  52. package/dist/{chunk-ELVWJYRV.js → chunk-JSQFTSC6.js} +12 -11
  53. package/dist/{chunk-J6DFZDFA.js → chunk-KBG47GHP.js} +4 -4
  54. package/dist/chunk-KDERNTCQ.js +28 -0
  55. package/dist/chunk-KSX3TXSZ.js +69 -0
  56. package/dist/{chunk-XGUL4SLQ.js → chunk-L3BLXZG7.js} +2 -2
  57. package/dist/chunk-LOP6GRPH.js +31 -0
  58. package/dist/{chunk-MTDRLSI7.js → chunk-M6HSLBWY.js} +1064 -986
  59. package/dist/chunk-MQLUAEOC.js +162 -0
  60. package/dist/{chunk-3MRXIJ6I.js → chunk-MRXKYPCS.js} +2 -2
  61. package/dist/{chunk-D65GOCJB.js → chunk-MXGY6KPT.js} +19 -16
  62. package/dist/{chunk-G3RLJYA2.js → chunk-N2H3ZPZY.js} +47 -1
  63. package/dist/{chunk-HREZQCT3.js → chunk-N3X6Q4YT.js} +3 -3
  64. package/dist/{chunk-HSIRMK2D.js → chunk-OCAGUNBC.js} +15 -15
  65. package/dist/{chunk-ZOEYVXSV.js → chunk-OXXKQCSV.js} +6 -6
  66. package/dist/{chunk-S6AMWKPC.js → chunk-P7266HLM.js} +2 -2
  67. package/dist/{chunk-64LU2OVH.js → chunk-PHDMFAKM.js} +12 -2
  68. package/dist/{chunk-IMSQR4WO.js → chunk-PNQDPDRC.js} +1 -1
  69. package/dist/{chunk-IUR76K6A.js → chunk-Q7DUK4LX.js} +5 -5
  70. package/dist/{chunk-CULQYFLU.js → chunk-QGQ5CCCG.js} +2 -2
  71. package/dist/{chunk-UEAHAMEI.js → chunk-QHJ6DQ2R.js} +11 -11
  72. package/dist/{chunk-BNKLYOOO.js → chunk-QILIYSJF.js} +1 -1
  73. package/dist/{chunk-VYKKMYVT.js → chunk-QOBUHWHX.js} +11 -6
  74. package/dist/{chunk-3PNFWVHT.js → chunk-QQNQ7SJW.js} +2 -75
  75. package/dist/{chunk-7M4T5N3V.js → chunk-R3PCY3YS.js} +14 -13
  76. package/dist/{chunk-MGIWSROG.js → chunk-RPLINGVB.js} +7 -7
  77. package/dist/{chunk-CMU4RMJB.js → chunk-RWWYJ623.js} +10 -1
  78. package/dist/{chunk-3FERWMCH.js → chunk-RXCOMYPY.js} +27 -26
  79. package/dist/{chunk-7BX5DWSA.js → chunk-SAEFPSB6.js} +11 -11
  80. package/dist/{chunk-N7YNJE5R.js → chunk-SQ4TVJBR.js} +10 -10
  81. package/dist/{chunk-DMS52BEP.js → chunk-SS36ZX4J.js} +9 -7
  82. package/dist/{chunk-JONGANYH.js → chunk-T6CPIZNT.js} +32 -30
  83. package/dist/{chunk-6UYA642J.js → chunk-TYM6OUIT.js} +13 -12
  84. package/dist/{chunk-ALQRYH6O.js → chunk-UXGETRP4.js} +13 -12
  85. package/dist/{chunk-FGALODG3.js → chunk-UZ3TUTRV.js} +1 -1
  86. package/dist/{chunk-J3FZR3YY.js → chunk-VCHXCQFG.js} +3 -3
  87. package/dist/{chunk-3P4PF2BX.js → chunk-VCO24T4L.js} +34 -22
  88. package/dist/{chunk-O7HZX7CA.js → chunk-VRZP76YM.js} +1 -1
  89. package/dist/{chunk-VZHJS6DR.js → chunk-VSSBEUFT.js} +16 -15
  90. package/dist/{chunk-U52SAGRM.js → chunk-VWCXW2E7.js} +1 -1
  91. package/dist/{chunk-WW5BLB74.js → chunk-WOA4LN2R.js} +2 -2
  92. package/dist/{chunk-ZMOMPX2Z.js → chunk-WU567RSC.js} +11 -10
  93. package/dist/{chunk-N77WFUL7.js → chunk-XAGLWMJ4.js} +23 -22
  94. package/dist/{chunk-WSEWKCXU.js → chunk-XCJN5IJU.js} +4 -4
  95. package/dist/{chunk-IR6XFA5P.js → chunk-XIJZVPD5.js} +16 -14
  96. package/dist/{chunk-OYOB3ZFG.js → chunk-XMSXXAHW.js} +2 -2
  97. package/dist/{chunk-34OCDWOS.js → chunk-XTMJ7XDT.js} +1 -1
  98. package/dist/{chunk-Z6BB4JLQ.js → chunk-XVF2VVXP.js} +5 -3
  99. package/dist/{chunk-AFZYMAIV.js → chunk-YIUAVF7E.js} +37 -35
  100. package/dist/{chunk-NFXP47RV.js → chunk-YSZNHOBY.js} +85 -31
  101. package/dist/{chunk-V5MEBST3.js → chunk-YYOIQFRS.js} +21 -21
  102. package/dist/chunk-Z527N6YQ.js +28 -0
  103. package/dist/{chunk-D6YF5HKX.js → chunk-ZANLILYD.js} +22 -2
  104. package/dist/{chunk-2UMYMRI3.js → chunk-ZGDGW2V3.js} +2 -2
  105. package/dist/{chunk-6B5I6ICN.js → chunk-ZHFZWTZV.js} +12 -11
  106. package/dist/{chunk-OEX24ECI.js → chunk-ZWFS3OEF.js} +51 -41
  107. package/dist/{chunk-AXKPQ6ZI.js → chunk-ZXYDQ3TZ.js} +5 -5
  108. package/dist/index.js +73783 -71033
  109. package/dist/mcp/analyze-cli.js +18 -8
  110. package/dist/mcp/cli.js +48 -37
  111. package/dist/mcp/server.js +568 -397
  112. package/dist/mcp/worker.js +67 -55
  113. package/dist/probes/installer.js +20 -19
  114. package/docs/astplayground.md +1 -1
  115. package/docs/configuration.md +29 -0
  116. package/docs/dependencies.md +3 -0
  117. package/docs/features.md +2 -2
  118. package/docs/globalconfig.md +2 -0
  119. package/docs/language-coverage.md +2 -1
  120. package/docs/lsp-capability-matrix.md +29 -7
  121. package/docs/lsp-idle-eviction.md +38 -47
  122. package/docs/pi-lens-fixer.md +4 -0
  123. package/docs/pi-lens-merge-policy.md +17 -4
  124. package/docs/pi-lens-retro.md +2 -1
  125. package/docs/pi-lens-reviewer.md +18 -0
  126. package/docs/pi-lens-subagent.md +7 -0
  127. package/docs/real-harness.md +17 -0
  128. package/docs/schema/pi-lens-config-v1.json +37 -2
  129. package/docs/servercapabilities.md +15 -4
  130. package/docs/settings.md +2 -1
  131. package/package.json +1 -1
  132. package/dist/chunk-2FQ62SPM.js +0 -83
  133. package/dist/chunk-7AXO3ZDF.js +0 -30
  134. package/dist/chunk-KMGPUKFD.js +0 -25
  135. package/dist/chunk-OZJINN4O.js +0 -84
  136. package/dist/chunk-RJFED73B.js +0 -79
  137. package/dist/chunk-RTQNRM5X.js +0 -27
  138. package/dist/chunk-VGEVL5EQ.js +0 -29
  139. package/dist/chunk-WPE5FPGR.js +0 -54
  140. package/dist/chunk-Z7QQA2KT.js +0 -27
@@ -7,35 +7,35 @@ import {
7
7
  deadCodeIssues,
8
8
  formatDeadCodeDelta,
9
9
  stableFindingKey
10
- } from "../chunk-HSIRMK2D.js";
10
+ } from "../chunk-OCAGUNBC.js";
11
11
  import {
12
12
  clearFormatterCache
13
- } from "../chunk-ZETYB3XG.js";
13
+ } from "../chunk-3W7IEVVL.js";
14
14
  import {
15
15
  findNearestProjectRoot,
16
16
  isStartupScanVerdictFresh,
17
17
  resolveStartupScanContext
18
- } from "../chunk-6P54TMWL.js";
18
+ } from "../chunk-3VPVG5SU.js";
19
19
  import {
20
20
  loadCallGraph
21
- } from "../chunk-WW5BLB74.js";
21
+ } from "../chunk-WOA4LN2R.js";
22
+ import {
23
+ isHardFailureSummary
24
+ } from "../chunk-7U2IDNXN.js";
22
25
  import {
23
26
  RUNNERS,
24
27
  formatRunDurationMs,
25
28
  isExcludedTestTarget,
26
29
  isRunnerErrorResult
27
- } from "../chunk-3P4PF2BX.js";
30
+ } from "../chunk-VCO24T4L.js";
28
31
  import {
29
32
  GovulncheckClient
30
- } from "../chunk-N7YNJE5R.js";
33
+ } from "../chunk-SQ4TVJBR.js";
31
34
  import {
32
35
  GitleaksClient,
33
36
  classifyAndFilterFindings
34
- } from "../chunk-DMS52BEP.js";
35
- import "../chunk-2UMYMRI3.js";
36
- import {
37
- isHardFailureSummary
38
- } from "../chunk-7U2IDNXN.js";
37
+ } from "../chunk-SS36ZX4J.js";
38
+ import "../chunk-ZGDGW2V3.js";
39
39
  import {
40
40
  resetManagedToolRefreshSession
41
41
  } from "../chunk-UXVNL3D7.js";
@@ -53,12 +53,13 @@ import {
53
53
  peekTurnEndFindings,
54
54
  snapshotAdvisoryProvenance,
55
55
  validateAdvisoryProvenance
56
- } from "../chunk-J6DFZDFA.js";
56
+ } from "../chunk-KBG47GHP.js";
57
57
  import {
58
58
  CacheManager,
59
59
  DELTA_UNUSED_PROMOTION_NOTE,
60
60
  MCP_TURN_STATE_OWNER_ID,
61
61
  acquireWarmWordIndex,
62
+ admitsProjectWideFindings,
62
63
  analyzeFile,
63
64
  applyFindingPolicy,
64
65
  applyLspFindingPolicy,
@@ -98,56 +99,53 @@ import {
98
99
  resetPendingRunnerFindings,
99
100
  resetPsScriptAnalyzerAvailability,
100
101
  runProviders
101
- } from "../chunk-ZZUFZUQZ.js";
102
+ } from "../chunk-H6MF6EVN.js";
102
103
  import {
103
104
  resetLazyInstallAttempts
104
- } from "../chunk-ZOEYVXSV.js";
105
+ } from "../chunk-OXXKQCSV.js";
105
106
  import {
106
107
  isTestRoleCollateral
107
- } from "../chunk-KJN4IOEJ.js";
108
+ } from "../chunk-EJIUXPBX.js";
109
+ import {
110
+ findingsResult,
111
+ hasUsableResult
112
+ } from "../chunk-2CD5RS6M.js";
108
113
  import "../chunk-FC4RP4I2.js";
114
+ import "../chunk-UZ3TUTRV.js";
115
+ import {
116
+ resetGoAvailability
117
+ } from "../chunk-XMSXXAHW.js";
109
118
  import {
110
119
  TrivyClient
111
- } from "../chunk-YLN4K6WD.js";
112
- import "../chunk-J3FZR3YY.js";
113
- import "../chunk-5JHXUETW.js";
120
+ } from "../chunk-77M37OKR.js";
121
+ import "../chunk-CSHFH5IN.js";
122
+ import "../chunk-VCHXCQFG.js";
123
+ import "../chunk-3EFKIGZN.js";
114
124
  import {
115
125
  resetRustAvailability
116
- } from "../chunk-3MRXIJ6I.js";
117
- import {
118
- findingsResult,
119
- hasUsableResult
120
- } from "../chunk-TA5L5KWQ.js";
121
- import "../chunk-FGALODG3.js";
122
- import "../chunk-OC7LYAU5.js";
126
+ } from "../chunk-MRXKYPCS.js";
127
+ import "../chunk-7DYBLXYA.js";
128
+ import "../chunk-AB3YYG6N.js";
123
129
  import {
124
130
  getSgCommand,
125
131
  resetDispatchAvailabilityState,
126
132
  resolveManagedToolClient
127
- } from "../chunk-NFXP47RV.js";
128
- import "../chunk-MGIWSROG.js";
129
- import "../chunk-WSEWKCXU.js";
130
- import "../chunk-MQA2YBAH.js";
131
- import "../chunk-TQFBZVCG.js";
132
- import "../chunk-WATC3IQR.js";
133
- import {
134
- resetGoAvailability
135
- } from "../chunk-OYOB3ZFG.js";
136
- import "../chunk-XHSCVSOL.js";
133
+ } from "../chunk-YSZNHOBY.js";
134
+ import "../chunk-RPLINGVB.js";
135
+ import "../chunk-XCJN5IJU.js";
136
+ import "../chunk-76MWC2K6.js";
137
+ import "../chunk-GXW6K4YJ.js";
138
+ import "../chunk-2B73ZPOK.js";
137
139
  import {
138
140
  SYMBOL_KIND_NAMES,
139
141
  captureReviewGraphStructuralIr,
140
142
  formatReviewGraphRevisionDriftNote,
141
143
  publishReviewGraphFileIr,
142
144
  reviewGraphIrContentHash
143
- } from "../chunk-V5MEBST3.js";
145
+ } from "../chunk-YYOIQFRS.js";
144
146
  import {
145
147
  buildSymbolId
146
148
  } from "../chunk-3MED75HT.js";
147
- import {
148
- resolveGitCheckout,
149
- resolveLinkedWorktreeOwner
150
- } from "../chunk-IMSQR4WO.js";
151
149
  import {
152
150
  DEPENDENCY_DRIFT_MAX_DELIVERIES,
153
151
  MAX_LATE_AUX_REARMS,
@@ -155,7 +153,6 @@ import {
155
153
  RUNTIME_CONFIG,
156
154
  ReadGuard,
157
155
  STALE_LINE_MARKER,
158
- SWEEP_IDLE_SAFETY_MARGIN_MS,
159
156
  Text,
160
157
  TreeSitterSymbolExtractor,
161
158
  WriteOrderingGuard,
@@ -186,7 +183,6 @@ import {
186
183
  getActiveSessionId,
187
184
  getDisposition,
188
185
  getFileDiagnosticSummaries,
189
- getFullScanWallClockMs,
190
186
  getLSPService,
191
187
  getLspBudgetIdleTimeoutMs,
192
188
  getMutationBridge,
@@ -200,7 +196,6 @@ import {
200
196
  isBlocking,
201
197
  isPendingAuxiliaryPastRearmTtl,
202
198
  isWarmAttached,
203
- isWorkspaceSweepActive,
204
199
  loadLspService,
205
200
  logBusEvent,
206
201
  markWidgetFileBlockersStale,
@@ -230,7 +225,6 @@ import {
230
225
  retireScope,
231
226
  retireWidgetDependencyDriftBlockers,
232
227
  runPerServerGroups,
233
- runWhenWorkspaceSweepIdle,
234
228
  scopeCell,
235
229
  setSessionLanguages,
236
230
  shouldShortenLspIdleTimeout,
@@ -243,22 +237,19 @@ import {
243
237
  widgetDiagnosticUri,
244
238
  workspaceEditTextPaths,
245
239
  writeOrderToken
246
- } from "../chunk-MTDRLSI7.js";
247
- import {
248
- emitBounded,
249
- resetBoundedTelemetry
250
- } from "../chunk-OZJINN4O.js";
240
+ } from "../chunk-M6HSLBWY.js";
251
241
  import {
252
242
  hashText,
253
243
  normalizeMessage,
254
244
  primePersistedMutationAttribution,
255
245
  resetMutationAttribution,
256
246
  stableFindingId
257
- } from "../chunk-3JSEWUWI.js";
247
+ } from "../chunk-IBGIOBEE.js";
258
248
  import {
259
249
  mapWithConcurrency
260
250
  } from "../chunk-XIMYZ3JA.js";
261
251
  import {
252
+ canWriteAnalysisRoot,
262
253
  classifyCascadeWaitTier,
263
254
  customServerSpecsOf,
264
255
  describeRootFallback,
@@ -276,11 +267,32 @@ import {
276
267
  resetLSPCaseSensitivityState,
277
268
  resolveLspCwdForFile,
278
269
  shouldInitializeSessionRoot
279
- } from "../chunk-IQZXOZ7S.js";
270
+ } from "../chunk-EU63SDCG.js";
271
+ import "../chunk-VWCXW2E7.js";
280
272
  import {
281
- hasAmbientPythonEnvironment
282
- } from "../chunk-LNOMA3MP.js";
283
- import "../chunk-U52SAGRM.js";
273
+ emitBounded,
274
+ hasAmbientPythonEnvironment,
275
+ resetBoundedTelemetry
276
+ } from "../chunk-FCSOVF55.js";
277
+ import {
278
+ resolveGitCheckout,
279
+ resolveLinkedWorktreeOwner
280
+ } from "../chunk-PNQDPDRC.js";
281
+ import {
282
+ PROJECT_SNAPSHOT_VERSION,
283
+ getProjectSnapshotLegacyPath,
284
+ getProjectSnapshotPath,
285
+ hydrateRuntimeFromProjectSnapshot,
286
+ hydrateRuntimeFromProjectSnapshotIfIdle,
287
+ isProjectSnapshotFresh,
288
+ isProjectSnapshotMetaStale,
289
+ loadProjectSnapshot,
290
+ loadProjectSnapshotExportsAndRules,
291
+ loadProjectSnapshotWithoutWordIndex,
292
+ readJsonCache,
293
+ readProjectSnapshotMeta,
294
+ saveRuntimeProjectSnapshot
295
+ } from "../chunk-ZXYDQ3TZ.js";
284
296
  import "../chunk-5W3R6LFT.js";
285
297
  import {
286
298
  EXT_TO_LANG,
@@ -294,7 +306,7 @@ import {
294
306
  queryLoader,
295
307
  reportBundledResourceDirHealth,
296
308
  resolveTreeSitterLanguage
297
- } from "../chunk-UEAHAMEI.js";
309
+ } from "../chunk-QHJ6DQ2R.js";
298
310
  import "../chunk-Z54ZSOBZ.js";
299
311
  import {
300
312
  SCAN_LANGUAGE_PRIORITY,
@@ -302,44 +314,35 @@ import {
302
314
  extensionsForLanguageToken,
303
315
  getUserRuleRoot,
304
316
  resolveLanguage
305
- } from "../chunk-KJCWXUUS.js";
317
+ } from "../chunk-724UL2TJ.js";
306
318
  import {
307
319
  dump,
308
320
  resolvePackagePath
309
321
  } from "../chunk-QOZXJ4NA.js";
310
- import "../chunk-6XX5HCLK.js";
311
- import "../chunk-S6AMWKPC.js";
322
+ import "../chunk-GBPP5P7X.js";
323
+ import "../chunk-P7266HLM.js";
324
+ import {
325
+ SWEEP_IDLE_SAFETY_MARGIN_MS,
326
+ getFullScanWallClockMs,
327
+ isWorkspaceSweepActive,
328
+ runWhenWorkspaceSweepIdle
329
+ } from "../chunk-MQLUAEOC.js";
312
330
  import {
313
331
  HOOK_WALL_BUDGET_MS,
314
332
  bounded,
315
333
  combineAbortSignals,
316
334
  withDeadline
317
- } from "../chunk-F5VLZHUW.js";
318
- import {
319
- PROJECT_SNAPSHOT_VERSION,
320
- getProjectSnapshotLegacyPath,
321
- getProjectSnapshotPath,
322
- hydrateRuntimeFromProjectSnapshot,
323
- hydrateRuntimeFromProjectSnapshotIfIdle,
324
- isProjectSnapshotFresh,
325
- isProjectSnapshotMetaStale,
326
- loadProjectSnapshot,
327
- loadProjectSnapshotExportsAndRules,
328
- loadProjectSnapshotWithoutWordIndex,
329
- readJsonCache,
330
- readProjectSnapshotMeta,
331
- saveRuntimeProjectSnapshot
332
- } from "../chunk-AXKPQ6ZI.js";
335
+ } from "../chunk-52FUZI6K.js";
333
336
  import {
334
337
  centralityFromReverseDeps,
335
338
  getWordIndexBuildStatus,
336
339
  logWordIndex,
337
340
  searchWordIndex,
338
341
  triggerBackgroundWordIndexBuild
339
- } from "../chunk-7BX5DWSA.js";
342
+ } from "../chunk-SAEFPSB6.js";
340
343
  import {
341
344
  getProjectDiagnosticsScannerMaxFiles
342
- } from "../chunk-IUKPRZER.js";
345
+ } from "../chunk-GAEGEPOC.js";
343
346
  import {
344
347
  PathKeyedMap
345
348
  } from "../chunk-Z3CCVDUB.js";
@@ -352,30 +355,27 @@ import {
352
355
  readTurnEndStatus,
353
356
  resolveRunnerPath,
354
357
  toRunnerDisplayPath
355
- } from "../chunk-XGUL4SLQ.js";
358
+ } from "../chunk-L3BLXZG7.js";
356
359
  import "../chunk-2BEMIWEW.js";
357
360
  import {
358
361
  commitDurableStore,
359
362
  resetResolvedPathCache,
360
363
  resetZizmorTokenAvailability
361
- } from "../chunk-LD7WFTRX.js";
364
+ } from "../chunk-ACXEQZOU.js";
362
365
  import {
363
366
  resolveToolCwd
364
- } from "../chunk-IUR76K6A.js";
367
+ } from "../chunk-Q7DUK4LX.js";
365
368
  import {
366
369
  _resetPackageManagerCache,
367
370
  findGlobalBinary,
368
371
  pmBinary,
369
372
  resolveNodePackageManager,
370
373
  runScriptArgs
371
- } from "../chunk-T4QZ6FEH.js";
372
- import {
373
- isToolInstallAllowedByTrust,
374
- projectTrustDenialReason
375
- } from "../chunk-D6YF5HKX.js";
374
+ } from "../chunk-AW7R2GWG.js";
376
375
  import {
376
+ getIsolatedNpxSpawnOptions,
377
377
  probeToolAsync
378
- } from "../chunk-CMU4RMJB.js";
378
+ } from "../chunk-RWWYJ623.js";
379
379
  import {
380
380
  classifyProbeFailure,
381
381
  createAvailabilityLatch,
@@ -383,20 +383,20 @@ import {
383
383
  logAvailabilityDecision,
384
384
  resetInstallRetryLatches,
385
385
  startHostStallSampler
386
- } from "../chunk-O7HZX7CA.js";
386
+ } from "../chunk-VRZP76YM.js";
387
387
  import {
388
388
  canRunStartupHeavyScans,
389
389
  detectProjectLanguageProfile,
390
390
  getDefaultStartupTools
391
- } from "../chunk-VYKKMYVT.js";
392
- import "../chunk-BNKLYOOO.js";
391
+ } from "../chunk-QOBUHWHX.js";
392
+ import "../chunk-QILIYSJF.js";
393
393
  import {
394
394
  collectSourceFilesWithBudgetAsync,
395
395
  getSlowFsVerdict,
396
396
  isSlowFs,
397
397
  slowFsDegradationNotice
398
- } from "../chunk-USHG4DLH.js";
399
- import "../chunk-H5EV3VMF.js";
398
+ } from "../chunk-6C7JWPCP.js";
399
+ import "../chunk-5NGR67U5.js";
400
400
  import {
401
401
  displayProjectDataPath,
402
402
  drainProjectDataDirMigrations,
@@ -423,7 +423,7 @@ import {
423
423
  sweepDeadEphemeralDataDirs,
424
424
  updateHeartbeat,
425
425
  withGenerationLockSync
426
- } from "../chunk-T54VJRAD.js";
426
+ } from "../chunk-C34ZXZ7O.js";
427
427
  import {
428
428
  createSingleFlight
429
429
  } from "../chunk-BW5KFIQC.js";
@@ -433,7 +433,7 @@ import {
433
433
  } from "../chunk-QYF5S6P6.js";
434
434
  import {
435
435
  detectFileKind
436
- } from "../chunk-64LU2OVH.js";
436
+ } from "../chunk-PHDMFAKM.js";
437
437
  import {
438
438
  compareKeys,
439
439
  detectFileRole,
@@ -443,12 +443,18 @@ import {
443
443
  provenanceView,
444
444
  resolvePiLensConfig,
445
445
  summarizeConfigResolution
446
- } from "../chunk-N2IRDBZI.js";
446
+ } from "../chunk-2BBLFX75.js";
447
447
  import {
448
448
  LSP_NAMESPACE_KEY,
449
449
  getPiLensGlobalConfigPath,
450
450
  resetWorkspaceTopology
451
- } from "../chunk-Z6BB4JLQ.js";
451
+ } from "../chunk-XVF2VVXP.js";
452
+ import {
453
+ getProjectTrustState,
454
+ isToolInstallAllowedByTrust,
455
+ projectTrustDenialReason,
456
+ setProjectTrustState
457
+ } from "../chunk-ZANLILYD.js";
452
458
  import {
453
459
  DEFAULT_MAX_OUTPUT_BYTES,
454
460
  createBoundedOutputSink,
@@ -469,16 +475,17 @@ import {
469
475
  resolveLensToolEnabled,
470
476
  resolveLensToolName,
471
477
  toolRegistryEntryForMcp
472
- } from "../chunk-7ELY2CSM.js";
478
+ } from "../chunk-JAU72XNY.js";
473
479
  import {
474
480
  createSubsystemLogger,
475
481
  flushExtensionLog,
476
482
  logExtension
477
- } from "../chunk-34OCDWOS.js";
483
+ } from "../chunk-XTMJ7XDT.js";
484
+ import "../chunk-JMYYOTQG.js";
478
485
  import {
479
486
  claimPhaseOncePerSession,
480
487
  logLatency
481
- } from "../chunk-3PNFWVHT.js";
488
+ } from "../chunk-QQNQ7SJW.js";
482
489
  import {
483
490
  beginTurnContext,
484
491
  createNdjsonLogger,
@@ -487,11 +494,11 @@ import {
487
494
  isTestMode,
488
495
  runLogCleanup,
489
496
  setTurnContextSession
490
- } from "../chunk-VSMJK3ZJ.js";
497
+ } from "../chunk-AOHZU2BM.js";
491
498
  import "../chunk-LJDODBBZ.js";
492
499
  import {
493
500
  FactStore
494
- } from "../chunk-UGSMCQ4D.js";
501
+ } from "../chunk-CHOW74WE.js";
495
502
  import {
496
503
  BoundedFifoMap,
497
504
  BoundedLruCache
@@ -506,15 +513,16 @@ import {
506
513
  normalizeMapKey,
507
514
  pathsEqual,
508
515
  realpathOrResolve,
516
+ resolveHostToolPath,
509
517
  toProjectRelativePath,
510
518
  uriToPath
511
- } from "../chunk-G3RLJYA2.js";
519
+ } from "../chunk-N2H3ZPZY.js";
512
520
  import "../chunk-NXL4FFQQ.js";
513
521
 
514
522
  // dist/mcp/server.js
515
523
  import * as fs25 from "node:fs";
516
524
  import * as net from "node:net";
517
- import * as path38 from "node:path";
525
+ import * as path39 from "node:path";
518
526
  import { fileURLToPath as fileURLToPath3 } from "node:url";
519
527
 
520
528
  // dist/tools/render-compact.js
@@ -978,6 +986,7 @@ var SgRunner = class {
978
986
  log;
979
987
  sgPath = null;
980
988
  sgArgsPrefix = [];
989
+ sgSpawnOptions;
981
990
  /**
982
991
  * Availability memo, backed by the shared transient-aware latch (#1476).
983
992
  *
@@ -1059,17 +1068,25 @@ var SgRunner = class {
1059
1068
  this.sweepFallbackCause = "probe-timeout";
1060
1069
  this.sweepHostStallMs = 0;
1061
1070
  this.sweepDurablyMissing = [];
1071
+ const trustedProject = getProjectTrustState() === "trusted";
1062
1072
  const pathCommand = await this.probeCommandCandidates([
1063
- { cmd: "ast-grep", argsPrefix: [] },
1064
- { cmd: "sg", argsPrefix: [] },
1065
- // `npx --no -- ast-grep` starts a Node process before it can answer, so
1073
+ ...trustedProject ? [
1074
+ { cmd: "ast-grep", argsPrefix: [] },
1075
+ { cmd: "sg", argsPrefix: [] }
1076
+ ] : [],
1077
+ // The scoped npx fallback starts a Node process before it can answer, so
1066
1078
  // it is the candidate most likely to blow a 5 s budget on a cold or busy
1067
1079
  // box. Marked a fallback so its timeout cannot veto the install.
1068
- { cmd: "npx", argsPrefix: ["--no", "--", "ast-grep"], fallback: true }
1080
+ {
1081
+ cmd: "npx",
1082
+ argsPrefix: ["--no", "--package", "@ast-grep/cli", "--", "ast-grep"],
1083
+ fallback: true
1084
+ }
1069
1085
  ]);
1070
1086
  if (pathCommand) {
1071
1087
  this.sgPath = pathCommand.cmd;
1072
1088
  this.sgArgsPrefix = pathCommand.argsPrefix;
1089
+ this.sgSpawnOptions = pathCommand.cmd === "npx" ? getIsolatedNpxSpawnOptions() : void 0;
1073
1090
  this.noteAvailable(startedAt, `ast-grep found on PATH: ${pathCommand.cmd}`);
1074
1091
  return true;
1075
1092
  }
@@ -1082,7 +1099,7 @@ var SgRunner = class {
1082
1099
  return true;
1083
1100
  }
1084
1101
  }
1085
- const platformBinary = await this.probePlatformPackageBinary();
1102
+ const platformBinary = trustedProject ? await this.probePlatformPackageBinary() : void 0;
1086
1103
  if (platformBinary) {
1087
1104
  this.sgPath = platformBinary;
1088
1105
  this.sgArgsPrefix = [];
@@ -1115,6 +1132,7 @@ var SgRunner = class {
1115
1132
  if (installed.outcome === "success") {
1116
1133
  this.sgPath = installed.value;
1117
1134
  this.sgArgsPrefix = [];
1135
+ this.sgSpawnOptions = void 0;
1118
1136
  this.noteAvailable(startedAt, `ast-grep auto-installed: ${installed.value}`);
1119
1137
  return true;
1120
1138
  }
@@ -1134,7 +1152,7 @@ var SgRunner = class {
1134
1152
  * sweep already asked, and the sweep returns at the first one that answers,
1135
1153
  * so at this point the flag means "a tier the winner is supposed to lose to
1136
1154
  * never got a fair hearing". Latching that pinned the session to
1137
- * `npx --no -- ast-grep` — a Node start per invocation — over a healthy
1155
+ * `npx` — a Node start per invocation — over a healthy
1138
1156
  * ast-grep on PATH, until the next restart. Provisional instead: served now,
1139
1157
  * re-swept once the stalled tier's cooldown expires.
1140
1158
  *
@@ -1277,7 +1295,10 @@ var SgRunner = class {
1277
1295
  let result;
1278
1296
  let hostStallMs;
1279
1297
  try {
1280
- result = await probeToolAsync(cmd, [...argsPrefix, "--version"], {
1298
+ result = cmd === "npx" ? await safeSpawnAsync(cmd, [...argsPrefix, "--version"], {
1299
+ ...getIsolatedNpxSpawnOptions(),
1300
+ timeout: PROBE_TIMEOUT_MS
1301
+ }) : await probeToolAsync(cmd, [...argsPrefix, "--version"], {
1281
1302
  timeout: PROBE_TIMEOUT_MS
1282
1303
  });
1283
1304
  } finally {
@@ -1364,6 +1385,7 @@ Original error: ${stderr}`;
1364
1385
  async execRaw(args, timeout = DEFAULT_EXEC_TIMEOUT_MS, options = {}) {
1365
1386
  const command = this.getSgCommand();
1366
1387
  const result = await safeSpawnAsync(command.cmd, [...command.argsPrefix, ...args], {
1388
+ ...this.sgSpawnOptions,
1367
1389
  timeout,
1368
1390
  deadlineAt: options.deadlineAt,
1369
1391
  signal: options.signal,
@@ -1392,6 +1414,7 @@ Original error: ${stderr}`;
1392
1414
  const hasBash = Boolean(process.env.MSYSTEM || process.env.GIT_SHELL);
1393
1415
  const useBash = isWindows && hasBash;
1394
1416
  const result = await safeSpawnAsync(useBash ? "bash" : command.cmd, useBash ? buildBashRunArgs(command.cmd, allArgs) : allArgs, {
1417
+ ...this.sgSpawnOptions,
1395
1418
  timeout: DEFAULT_EXEC_TIMEOUT_MS,
1396
1419
  deadlineAt: options.deadlineAt,
1397
1420
  signal: options.signal,
@@ -1540,6 +1563,7 @@ Original error: ${stderr}`;
1540
1563
  ...sgExcludeArgsForProject(dir),
1541
1564
  dir
1542
1565
  ], {
1566
+ ...sgCmd === "npx" ? getIsolatedNpxSpawnOptions() : {},
1543
1567
  timeout,
1544
1568
  deadlineAt: options.deadlineAt,
1545
1569
  signal: options.signal,
@@ -1577,6 +1601,7 @@ Original error: ${stderr}`;
1577
1601
  dir
1578
1602
  ];
1579
1603
  const spawnOptions = {
1604
+ ...sgCmd === "npx" ? getIsolatedNpxSpawnOptions() : {},
1580
1605
  timeout,
1581
1606
  deadlineAt: options.deadlineAt,
1582
1607
  signal: options.signal,
@@ -2278,7 +2303,7 @@ message: found
2278
2303
  };
2279
2304
 
2280
2305
  // dist/clients/lens-engine.js
2281
- import * as path28 from "node:path";
2306
+ import * as path29 from "node:path";
2282
2307
 
2283
2308
  // dist/clients/project-diagnostics/scanner.js
2284
2309
  import * as fs6 from "node:fs";
@@ -2725,7 +2750,7 @@ function resolveRebuildScript(serverFilePath) {
2725
2750
  return serverFilePath.replace(/\\/g, "/").includes("/dist/") ? "build:dist" : "build";
2726
2751
  }
2727
2752
  function analyzeFileFresh(workerPath, file, cwd, options = {}, timeoutMs = 12e4) {
2728
- return new Promise((resolve31) => {
2753
+ return new Promise((resolve32) => {
2729
2754
  const args = [workerPath, `--file=${file}`, `--cwd=${cwd}`];
2730
2755
  if (options.flags)
2731
2756
  args.push(`--flags=${JSON.stringify(options.flags)}`);
@@ -2735,7 +2760,7 @@ function analyzeFileFresh(workerPath, file, cwd, options = {}, timeoutMs = 12e4)
2735
2760
  stdio: ["ignore", "pipe", "pipe"]
2736
2761
  });
2737
2762
  } catch (err) {
2738
- resolve31({
2763
+ resolve32({
2739
2764
  error: `failed to fork worker: ${err instanceof Error ? err.message : String(err)}`
2740
2765
  });
2741
2766
  return;
@@ -2748,7 +2773,7 @@ function analyzeFileFresh(workerPath, file, cwd, options = {}, timeoutMs = 12e4)
2748
2773
  return;
2749
2774
  settled = true;
2750
2775
  clearTimeout(timer);
2751
- resolve31(outcome);
2776
+ resolve32(outcome);
2752
2777
  };
2753
2778
  const timer = setTimeout(() => {
2754
2779
  child.kill();
@@ -2856,7 +2881,7 @@ function summarizeScan(diagnostics) {
2856
2881
  // dist/clients/mcp/session.js
2857
2882
  import * as crypto from "node:crypto";
2858
2883
  import * as fs18 from "node:fs";
2859
- import * as path24 from "node:path";
2884
+ import * as path25 from "node:path";
2860
2885
 
2861
2886
  // dist/clients/bootstrap.js
2862
2887
  var residentClients = null;
@@ -2913,29 +2938,29 @@ async function buildBootstrapClients() {
2913
2938
  }
2914
2939
  }
2915
2940
  const [ruffClient, biomeClient, knipClient, todoScanner, jscpdClient, depChecker, testRunnerClient, metricsClient, complexityClient, goClient, govulncheckClient, gitleaksClient, trivyClient, opengrepClient, rustClient, agentBehaviorClient2, deadCodeClients] = await Promise.all([
2916
- load("ruff", async () => new (await import("../chunk-JONGANYH.js")).RuffClient()),
2917
- load("biome", async () => new (await import("../chunk-OEX24ECI.js")).BiomeClient()),
2918
- load("knip", async () => new (await import("../chunk-AFZYMAIV.js")).KnipClient()),
2919
- load("todo", async () => new (await import("../chunk-ELVWJYRV.js")).TodoScanner()),
2920
- load("jscpd", async () => new (await import("../chunk-H2W2AXD3.js")).JscpdClient()),
2921
- load("dependency-checker", async () => new (await import("../chunk-WPE5FPGR.js")).DependencyChecker()),
2922
- load("test-runner", async () => new (await import("../chunk-N77WFUL7.js")).TestRunnerClient()),
2923
- load("metrics", async () => new (await import("../chunk-HREZQCT3.js")).MetricsClient()),
2924
- load("complexity", async () => new (await import("../chunk-VZHJS6DR.js")).ComplexityClient()),
2941
+ load("ruff", async () => new (await import("../chunk-T6CPIZNT.js")).RuffClient()),
2942
+ load("biome", async () => new (await import("../chunk-ZWFS3OEF.js")).BiomeClient()),
2943
+ load("knip", async () => new (await import("../chunk-YIUAVF7E.js")).KnipClient()),
2944
+ load("todo", async () => new (await import("../chunk-JSQFTSC6.js")).TodoScanner()),
2945
+ load("jscpd", async () => new (await import("../chunk-CMMHNUZ2.js")).JscpdClient()),
2946
+ load("dependency-checker", async () => new (await import("../chunk-EC47DWFA.js")).DependencyChecker()),
2947
+ load("test-runner", async () => new (await import("../chunk-XAGLWMJ4.js")).TestRunnerClient()),
2948
+ load("metrics", async () => new (await import("../chunk-N3X6Q4YT.js")).MetricsClient()),
2949
+ load("complexity", async () => new (await import("../chunk-VSSBEUFT.js")).ComplexityClient()),
2925
2950
  // The process-wide singleton, NOT a fresh instance (#2455 fix round 4,
2926
2951
  // F2). `handleSessionStart` reads this object for its "Active tools"
2927
2952
  // line while `resetGoAvailability` re-arms the singleton's latch; a
2928
2953
  // second instance here meant the reset never reached the surface a
2929
2954
  // user actually sees.
2930
- load("go", async () => (await import("../chunk-Z7QQA2KT.js")).goClient),
2931
- load("govulncheck", async () => new (await import("../chunk-RTQNRM5X.js")).GovulncheckClient()),
2932
- load("gitleaks", async () => new (await import("../chunk-IR6XFA5P.js")).GitleaksClient()),
2933
- load("trivy", async () => new (await import("../chunk-OYTMZXRF.js")).TrivyClient()),
2934
- load("opengrep", async () => new (await import("../chunk-D65GOCJB.js")).OpengrepClient()),
2955
+ load("go", async () => (await import("../chunk-Z527N6YQ.js")).goClient),
2956
+ load("govulncheck", async () => new (await import("../chunk-KDERNTCQ.js")).GovulncheckClient()),
2957
+ load("gitleaks", async () => new (await import("../chunk-XIJZVPD5.js")).GitleaksClient()),
2958
+ load("trivy", async () => new (await import("../chunk-FMSO6RDZ.js")).TrivyClient()),
2959
+ load("opengrep", async () => new (await import("../chunk-MXGY6KPT.js")).OpengrepClient()),
2935
2960
  // The process-wide singleton, for the same reason as "go" above.
2936
- load("rust", async () => (await import("../chunk-EUT3B22Y.js")).rustClient),
2937
- load("agent-behavior", async () => (await import("../chunk-BJF25SUG.js")).agentBehaviorClient),
2938
- loadList("dead-code", async () => (await import("../chunk-NJF2X5XF.js")).getDeadCodeClients())
2961
+ load("rust", async () => (await import("../chunk-BRGDSBJE.js")).rustClient),
2962
+ load("agent-behavior", async () => (await import("../chunk-23WOO7TX.js")).agentBehaviorClient),
2963
+ loadList("dead-code", async () => (await import("../chunk-6STA6Z7I.js")).getDeadCodeClients())
2939
2964
  ]);
2940
2965
  if (failures.length > 0)
2941
2966
  await logBootstrapFailures(failures);
@@ -3022,7 +3047,7 @@ function resetAnalyzerBootstrapSessionState() {
3022
3047
  }
3023
3048
 
3024
3049
  // dist/clients/formatters-lazy.js
3025
- var lazyFormatters = createLazyImport(() => import("../chunk-3FERWMCH.js"));
3050
+ var lazyFormatters = createLazyImport(() => import("../chunk-RXCOMYPY.js"));
3026
3051
  function warmFormatters() {
3027
3052
  return lazyFormatters.get();
3028
3053
  }
@@ -4061,7 +4086,9 @@ var RuntimeCoordinator = class {
4061
4086
  * session; it is therefore required. `filePath` is the edited file the
4062
4087
  * compute belongs to, the subject of a dropped admission's ledger row.
4063
4088
  */
4064
- appendCascadePromise(p, generation, filePath) {
4089
+ appendCascadePromise(p, generation, filePath, analysisRootMode = "session") {
4090
+ if (!canWriteAnalysisRoot(analysisRootMode))
4091
+ return;
4065
4092
  if (generation.guardedWrite(filePath, () => true) === void 0)
4066
4093
  return;
4067
4094
  if (this._pendingCascadeRuns.length < MAX_PENDING_CASCADE_RUNS) {
@@ -4123,8 +4150,8 @@ var RuntimeCoordinator = class {
4123
4150
  });
4124
4151
  return entry;
4125
4152
  });
4126
- const timeout = new Promise((resolve31) => {
4127
- setTimeout(resolve31, maxWaitMs).unref?.();
4153
+ const timeout = new Promise((resolve32) => {
4154
+ setTimeout(resolve32, maxWaitMs).unref?.();
4128
4155
  });
4129
4156
  await Promise.race([
4130
4157
  Promise.allSettled(tracked.map((t) => t.promise)),
@@ -4830,7 +4857,9 @@ var RuntimeCoordinator = class {
4830
4857
  * for a same-kind re-touch. Callers publish each kind's first transition
4831
4858
  * without spamming repeated edits before `agent_end`.
4832
4859
  */
4833
- deferMutation(filePath, cwd, toolName, turnStateCwd, kind, ownerSessionId, originCwd, readGuardBranchEpoch = this.readGuard.currentBranchEpoch) {
4860
+ deferMutation(filePath, cwd, toolName, turnStateCwd, kind, ownerSessionId, originCwd, readGuardBranchEpoch = this.readGuard.currentBranchEpoch, analysisRootMode = "session") {
4861
+ if (!canWriteAnalysisRoot(analysisRootMode))
4862
+ return false;
4834
4863
  const key = path8.resolve(filePath);
4835
4864
  const now = Date.now();
4836
4865
  const resolvedOriginCwd = originCwd ?? turnStateCwd;
@@ -4865,8 +4894,8 @@ var RuntimeCoordinator = class {
4865
4894
  });
4866
4895
  return true;
4867
4896
  }
4868
- deferFormat(filePath, cwd, toolName, turnStateCwd, ownerSessionId, originCwd) {
4869
- return this.deferMutation(filePath, cwd, toolName, turnStateCwd, "format", ownerSessionId, originCwd);
4897
+ deferFormat(filePath, cwd, toolName, turnStateCwd, ownerSessionId, originCwd, analysisRootMode = "session") {
4898
+ return this.deferMutation(filePath, cwd, toolName, turnStateCwd, "format", ownerSessionId, originCwd, void 0, analysisRootMode);
4870
4899
  }
4871
4900
  /** Return the ownership identity of a queued format record for bus events. */
4872
4901
  deferredFormatIdentity(filePath) {
@@ -5005,7 +5034,7 @@ var RuntimeCoordinator = class {
5005
5034
 
5006
5035
  // dist/clients/runtime-session.js
5007
5036
  import * as nodeFs from "node:fs";
5008
- import * as path12 from "node:path";
5037
+ import * as path13 from "node:path";
5009
5038
 
5010
5039
  // dist/clients/message-end-attribution.js
5011
5040
  var lastStableSessionId;
@@ -5283,6 +5312,38 @@ function emitSituationalDeadWeight() {
5283
5312
  });
5284
5313
  }
5285
5314
 
5315
+ // dist/clients/deferred-test-targets.js
5316
+ import * as path12 from "node:path";
5317
+ var TEST_RUNNER_MAX_PERSISTED_TARGETS = 64;
5318
+ function deferralEntryKey(entry) {
5319
+ return `${entry.sessionId ?? ""}\0${normalizeMapKey(path12.resolve(entry.testFile))}`;
5320
+ }
5321
+ function mergeDeferredTargets(existing, incoming) {
5322
+ const byKey = /* @__PURE__ */ new Map();
5323
+ for (const entry of [...existing, ...incoming]) {
5324
+ const key = deferralEntryKey(entry);
5325
+ const prior = byKey.get(key);
5326
+ if (prior && (prior.attempts ?? 0) >= (entry.attempts ?? 0))
5327
+ continue;
5328
+ byKey.set(key, entry);
5329
+ }
5330
+ return [...byKey.values()];
5331
+ }
5332
+ function boundDeferredTargets(entries, max, isOwn) {
5333
+ const foreign = [];
5334
+ const own = [];
5335
+ for (const entry of entries) {
5336
+ if (isOwn(entry))
5337
+ own.push(entry);
5338
+ else
5339
+ foreign.push(entry);
5340
+ }
5341
+ const ordered = [...foreign, ...own];
5342
+ if (ordered.length <= max)
5343
+ return ordered;
5344
+ return ordered.slice(-max);
5345
+ }
5346
+
5286
5347
  // dist/clients/test-runner-delivery.js
5287
5348
  var pending = /* @__PURE__ */ new Map();
5288
5349
  function resetTestRunnerDelivery() {
@@ -5339,7 +5400,7 @@ function logHostReadyDelay(deps, cwd) {
5339
5400
  });
5340
5401
  }
5341
5402
  function resolveSnapshotRoot(cwd) {
5342
- const resolvedCwd = path12.resolve(cwd);
5403
+ const resolvedCwd = path13.resolve(cwd);
5343
5404
  const nearest = findNearestProjectRoot(resolvedCwd);
5344
5405
  if (!nearest || isAtOrAboveHomeDir(nearest)) {
5345
5406
  return resolvedCwd;
@@ -5442,8 +5503,8 @@ async function readSequenceWithBudget(args) {
5442
5503
  const { snapshotRoot, base, cwd, runtime, sessionGeneration, dbg, onDeferredSequenceResolved } = args;
5443
5504
  const readPromise = readLatestProjectSequenceAsync(snapshotRoot, base);
5444
5505
  let timeoutHandle;
5445
- const timeoutPromise = new Promise((resolve31) => {
5446
- timeoutHandle = setTimeout(() => resolve31({ timedOut: true }), sequenceReadBudgetMs());
5506
+ const timeoutPromise = new Promise((resolve32) => {
5507
+ timeoutHandle = setTimeout(() => resolve32({ timedOut: true }), sequenceReadBudgetMs());
5447
5508
  timeoutHandle.unref();
5448
5509
  });
5449
5510
  const raced = await Promise.race([
@@ -5512,7 +5573,7 @@ async function igniteWarmFiles(cwd, warmFiles, runtime, sessionGeneration, dbg)
5512
5573
  for (const relPath of warmFiles) {
5513
5574
  if (!runtime.isCurrentSession(sessionGeneration))
5514
5575
  return;
5515
- const filePath = path12.isAbsolute(relPath) ? relPath : path12.resolve(cwd, relPath);
5576
+ const filePath = path13.isAbsolute(relPath) ? relPath : path13.resolve(cwd, relPath);
5516
5577
  if (!nodeFs.existsSync(filePath)) {
5517
5578
  dbg(`session_start lsp-warm: not found: ${relPath}`);
5518
5579
  errors++;
@@ -5543,8 +5604,8 @@ async function igniteDominantLanguageWarm(cwd, runtime, sessionGeneration, dbg)
5543
5604
  if (!runtime.isCurrentSession(sessionGeneration))
5544
5605
  return;
5545
5606
  const lspService = (await loadLspService()).getLSPService();
5546
- const { collectSourceFilesAsync } = await import("../chunk-J7WJZ6W7.js");
5547
- const { CODE_KINDS, detectFileKind: detectFileKind2 } = await import("../chunk-CFYISHUW.js");
5607
+ const { collectSourceFilesAsync } = await import("../chunk-4W4VLAPJ.js");
5608
+ const { CODE_KINDS, detectFileKind: detectFileKind2 } = await import("../chunk-FLKWCIZH.js");
5548
5609
  const files = await collectSourceFilesAsync(cwd, {
5549
5610
  inspectGeneratedHeaders: false
5550
5611
  });
@@ -5578,7 +5639,7 @@ async function igniteDominantLanguageWarm(cwd, runtime, sessionGeneration, dbg)
5578
5639
  clientScope: "primary",
5579
5640
  maxClientWaitMs: 2e3
5580
5641
  });
5581
- dbg(`session_start lsp-warm: dominant=${kind} via ${path12.basename(sample)}`);
5642
+ dbg(`session_start lsp-warm: dominant=${kind} via ${path13.basename(sample)}`);
5582
5643
  return;
5583
5644
  }
5584
5645
  dbg("session_start lsp-warm: no dominant-language LSP file found to warm");
@@ -5610,7 +5671,7 @@ function scheduleManagedToolRefresh(dbg) {
5610
5671
  const timer = setTimeout(() => {
5611
5672
  void (async () => {
5612
5673
  try {
5613
- const refresh = await import("../chunk-CBD2B5CP.js");
5674
+ const refresh = await import("../chunk-5UCAH366.js");
5614
5675
  const outcome = await refresh.runManagedToolRefresh();
5615
5676
  if (outcome.skipped) {
5616
5677
  dbg(`session_start tool-refresh: skipped (${outcome.skipped})`);
@@ -5627,7 +5688,7 @@ function scheduleManagedToolRefresh(dbg) {
5627
5688
  timer.unref?.();
5628
5689
  }
5629
5690
  async function probePrettierInstall(ensureTool, dbg, analysisRoot) {
5630
- const pkgPath = path12.join(analysisRoot, "package.json");
5691
+ const pkgPath = path13.join(analysisRoot, "package.json");
5631
5692
  try {
5632
5693
  const raw = await nodeFs.promises.readFile(pkgPath, "utf-8");
5633
5694
  const pkg = JSON.parse(raw);
@@ -5657,7 +5718,7 @@ function scanOneTodoFile(scanner, filePath, items) {
5657
5718
  async function collectTodoBaselineItems(scanner, analysisRoot, stillCurrent) {
5658
5719
  const items = [];
5659
5720
  try {
5660
- const { getSourceFilesAsync } = await import("../chunk-KMGPUKFD.js");
5721
+ const { getSourceFilesAsync } = await import("../chunk-IZSHAIJ7.js");
5661
5722
  const files = await getSourceFilesAsync(analysisRoot, true);
5662
5723
  if (!stillCurrent())
5663
5724
  return items;
@@ -5686,7 +5747,7 @@ async function buildOrRefreshWordIndex(args) {
5686
5747
  const snapshot = loadProjectSnapshot(snapshotRoot);
5687
5748
  const snapshotLoadMs = Date.now() - snapshotLoadStartMs;
5688
5749
  if (snapshot?.wordIndex) {
5689
- const { deserializeWordIndex, refreshWordIndexIncrementally, countWordIndexPostingEntries: countWordIndexPostingEntries2, estimateWordIndexResidentBytes: estimateWordIndexResidentBytes2 } = await import("../chunk-ZMOMPX2Z.js");
5750
+ const { deserializeWordIndex, refreshWordIndexIncrementally, countWordIndexPostingEntries: countWordIndexPostingEntries2, estimateWordIndexResidentBytes: estimateWordIndexResidentBytes2 } = await import("../chunk-WU567RSC.js");
5690
5751
  const deserializeStartMs = Date.now();
5691
5752
  const index = deserializeWordIndex(snapshot.wordIndex);
5692
5753
  const deserializeMs = Date.now() - deserializeStartMs;
@@ -5758,7 +5819,7 @@ async function buildOrRefreshWordIndex(args) {
5758
5819
  }
5759
5820
  }
5760
5821
  }
5761
- const { buildWordIndexAsync, collectWordIndexDocs, countWordIndexPostingEntries, estimateWordIndexResidentBytes } = await import("../chunk-ZMOMPX2Z.js");
5822
+ const { buildWordIndexAsync, collectWordIndexDocs, countWordIndexPostingEntries, estimateWordIndexResidentBytes } = await import("../chunk-WU567RSC.js");
5762
5823
  const docs = await collectWordIndexDocs(analysisRoot, () => runtime.isCurrentSession(sessionGeneration), rebuildPreflightFiles);
5763
5824
  if (!runtime.isCurrentSession(sessionGeneration))
5764
5825
  return;
@@ -5828,7 +5889,7 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
5828
5889
  const queuedAt = Date.now();
5829
5890
  dbg(`session_start task ${name}: scheduled`);
5830
5891
  runtime.markStartupScanInFlight(name, sessionGeneration);
5831
- const completion = new Promise((resolve31) => {
5892
+ const completion = new Promise((resolve32) => {
5832
5893
  const fire = () => {
5833
5894
  const startedAt = Date.now();
5834
5895
  dbg(`session_start task ${name}: start queuedMs=${startedAt - queuedAt}`);
@@ -5840,7 +5901,7 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
5840
5901
  }).finally(() => {
5841
5902
  runtime.clearStartupScanInFlight(name, sessionGeneration);
5842
5903
  dbg(`session_start task ${name}: end`);
5843
- resolve31();
5904
+ resolve32();
5844
5905
  });
5845
5906
  };
5846
5907
  const delay = taskDeferMsByName[name] ?? 0;
@@ -5917,7 +5978,7 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
5917
5978
  if (await jscpdClient.ensureAvailable()) {
5918
5979
  if (!runtime.isCurrentSession(sessionGeneration))
5919
5980
  return;
5920
- const isTsProject = nodeFs.existsSync(path12.join(analysisRoot, "tsconfig.json"));
5981
+ const isTsProject = nodeFs.existsSync(path13.join(analysisRoot, "tsconfig.json"));
5921
5982
  const scannerKey = isTsProject ? "jscpd-ts" : "jscpd";
5922
5983
  const cached = cacheManager2.readCache(scannerKey, analysisRoot);
5923
5984
  if (cached) {
@@ -6123,9 +6184,9 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
6123
6184
  });
6124
6185
  let callGraphIdentity;
6125
6186
  const callGraphTask = runTask("call-graph", async () => {
6126
- const { FactStore: FactStore2 } = await import("../chunk-CULQYFLU.js");
6127
- const { buildOrUpdateGraph, extractSymbolsAndRefsFromGraph, getReviewGraphCacheIdentity } = await import("../chunk-3TTPKMEF.js");
6128
- const { buildCallGraph, saveCallGraph, loadCallGraph: loadCallGraph2 } = await import("../chunk-GAYGGICG.js");
6187
+ const { FactStore: FactStore2 } = await import("../chunk-QGQ5CCCG.js");
6188
+ const { buildOrUpdateGraph, extractSymbolsAndRefsFromGraph, getReviewGraphCacheIdentity } = await import("../chunk-2VUQMUMT.js");
6189
+ const { buildCallGraph, saveCallGraph, loadCallGraph: loadCallGraph2 } = await import("../chunk-BKE7E7TC.js");
6129
6190
  if (!runtime.isCurrentSession(sessionGeneration))
6130
6191
  return;
6131
6192
  const startMs = Date.now();
@@ -6166,7 +6227,7 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
6166
6227
  return;
6167
6228
  if (!runtime.callGraph)
6168
6229
  return;
6169
- const { buildCodebaseModel, saveCodebaseModel, DEFAULT_CODEBASE_MODEL_TOKEN_BUDGET } = await import("../chunk-6UYA642J.js");
6230
+ const { buildCodebaseModel, saveCodebaseModel, DEFAULT_CODEBASE_MODEL_TOKEN_BUDGET } = await import("../chunk-TYM6OUIT.js");
6170
6231
  if (!callGraphIdentity)
6171
6232
  return;
6172
6233
  const model = buildCodebaseModel(runtime.callGraph, analysisRoot, DEFAULT_CODEBASE_MODEL_TOKEN_BUDGET, callGraphIdentity);
@@ -6239,9 +6300,76 @@ function scheduleDeferredToolProbesWithClients(deps, languageProfile, startupDef
6239
6300
  dbg(`session_start tools (deferred probes complete, ${Date.now() - warmStart}ms): ${summary}`);
6240
6301
  })();
6241
6302
  }
6242
- var SESSION_START_GUIDANCE = [
6243
- "\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."
6244
- ];
6303
+ function readSubstituteGuidance(isEnabled) {
6304
+ const units = [];
6305
+ if (isEnabled("symbol_search")) {
6306
+ units.push({ name: "symbol_search", role: "ranked identifier search" });
6307
+ }
6308
+ if (isEnabled("module_report")) {
6309
+ units.push({
6310
+ name: "module_report",
6311
+ role: "navigable outline/callback handles"
6312
+ });
6313
+ }
6314
+ const readSymbol2 = isEnabled("read_symbol");
6315
+ const readEnclosing2 = isEnabled("read_enclosing");
6316
+ if (readSymbol2 && readEnclosing2) {
6317
+ units.push({
6318
+ name: "read_symbol/read_enclosing",
6319
+ role: "exact body reads"
6320
+ });
6321
+ } else if (readSymbol2) {
6322
+ units.push({ name: "read_symbol", role: "exact body reads" });
6323
+ } else if (readEnclosing2) {
6324
+ units.push({ name: "read_enclosing", role: "exact body reads" });
6325
+ }
6326
+ if (units.length === 0)
6327
+ return void 0;
6328
+ const roles = units.map((unit) => unit.role);
6329
+ const roleTail = roles.length === 1 ? roles[0] : roles.length === 2 ? `${roles[0]}, then ${roles[1]}` : `${roles[0]}, then ${roles[1]} + ${roles[2]}`;
6330
+ return `\u2022 ${units.map((unit) => unit.name).join(" \u2192 ")} \u2014 ${roleTail}; cheaper than reading a whole file before editing.`;
6331
+ }
6332
+ function situationalGuidance(isEnabled) {
6333
+ const active = [];
6334
+ for (const name of [
6335
+ "lsp_navigation",
6336
+ "ast_grep_search",
6337
+ "ast_grep_replace"
6338
+ ]) {
6339
+ if (isEnabled(name))
6340
+ active.push(name);
6341
+ }
6342
+ if (active.length === 0)
6343
+ return void 0;
6344
+ let text = `\u2022 Situational (activate via pi_lens_activate_tools): ${active.join(", ")}.`;
6345
+ if (isEnabled("ast_grep_search")) {
6346
+ text += " Use ast_grep_search with dump=true to inspect AST nodes.";
6347
+ }
6348
+ return text;
6349
+ }
6350
+ function renderSessionStartGuidance(options = {}) {
6351
+ const disabled = new Set(options.disabledToolNames ?? []);
6352
+ const isEnabled = (name) => !disabled.has(name);
6353
+ const requirePreExistingFixes = options.requirePreExistingFixes !== false;
6354
+ 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.");
6355
+ const bullets = [];
6356
+ if (isEnabled("lens_diagnostics")) {
6357
+ 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)).");
6358
+ }
6359
+ const readSubstitute = readSubstituteGuidance(isEnabled);
6360
+ if (readSubstitute)
6361
+ bullets.push(readSubstitute);
6362
+ const situational = situationalGuidance(isEnabled);
6363
+ if (situational)
6364
+ bullets.push(situational);
6365
+ const lines = [header];
6366
+ if (bullets.length > 0) {
6367
+ lines.push("Key tools (see each tool's own description for args):");
6368
+ lines.push(...bullets);
6369
+ }
6370
+ return [lines.join("\n")];
6371
+ }
6372
+ var SESSION_START_GUIDANCE = renderSessionStartGuidance();
6245
6373
  async function handleSessionStart(deps) {
6246
6374
  resetDegradationLedger();
6247
6375
  let sessionGenerationAtStart = deps.runtime.sessionGeneration;
@@ -6283,8 +6411,8 @@ async function handleSessionStart(deps) {
6283
6411
  try {
6284
6412
  warmupDbg("warmup: starting background warmup");
6285
6413
  const scanContextStartedAt = Date.now();
6286
- const startupScanModule = await import("../chunk-6B5I6ICN.js");
6287
- const languageProfileModule = await import("../chunk-7M4T5N3V.js");
6414
+ const startupScanModule = await import("../chunk-ZHFZWTZV.js");
6415
+ const languageProfileModule = await import("../chunk-R3PCY3YS.js");
6288
6416
  const warmupSnapshotRoot = resolveSnapshotRoot(warmupCwd);
6289
6417
  const cachedSnapshot = loadProjectSnapshot(warmupSnapshotRoot);
6290
6418
  warmupOwnSnapshotRead = cachedSnapshot ? {
@@ -6296,7 +6424,7 @@ async function handleSessionStart(deps) {
6296
6424
  const cachedVerdict = cachedSnapshot?.startupScan;
6297
6425
  let scan;
6298
6426
  if (cachedVerdict && startupScanModule.isStartupScanVerdictFresh(cachedVerdict)) {
6299
- scan = { ...cachedVerdict, cwd: path12.resolve(warmupCwd) };
6427
+ scan = { ...cachedVerdict, cwd: path13.resolve(warmupCwd) };
6300
6428
  warmupDbg(`warmup: scan-context reused from cache (canWarm=${scan.canWarmCaches}${scan.reason ? `, reason=${scan.reason}` : ""})`);
6301
6429
  } else {
6302
6430
  scan = await startupScanModule.resolveStartupScanContextAsync(warmupCwd);
@@ -6490,7 +6618,7 @@ async function handleSessionStart(deps) {
6490
6618
  const cwd = ctxCwd ?? process.cwd();
6491
6619
  const projectDataDir = getProjectDataDir(cwd);
6492
6620
  for (const migration of drainProjectDataDirMigrations()) {
6493
- const targetName = path12.basename(migration.to);
6621
+ const targetName = path13.basename(migration.to);
6494
6622
  const hash = targetName.match(/([0-9a-f]{8})$/)?.[1] ?? "unknown";
6495
6623
  if (migration.outcome === "ephemeral") {
6496
6624
  recordDegradationOnce({
@@ -6509,11 +6637,11 @@ async function handleSessionStart(deps) {
6509
6637
  const globalDir = getGlobalPiLensDir();
6510
6638
  void sweepAtomicWriteStages([
6511
6639
  projectDataDir,
6512
- path12.join(projectDataDir, "cache"),
6513
- path12.join(projectDataDir, "sessions"),
6640
+ path13.join(projectDataDir, "cache"),
6641
+ path13.join(projectDataDir, "sessions"),
6514
6642
  globalDir,
6515
- path12.join(globalDir, "bin"),
6516
- path12.join(globalDir, "tools")
6643
+ path13.join(globalDir, "bin"),
6644
+ path13.join(globalDir, "tools")
6517
6645
  ]).catch(() => {
6518
6646
  });
6519
6647
  void sweepDeadEphemeralDataDirs({ isPidAlive: realIsPidAlive }).catch(() => {
@@ -6662,7 +6790,7 @@ async function handleSessionStart(deps) {
6662
6790
  if (allowBootstrapTasks && !getFlag("no-lsp")) {
6663
6791
  const cleaned = cleanStaleTsBuildInfo(ctxCwd ?? process.cwd());
6664
6792
  if (cleaned.length > 0) {
6665
- notify(`\u{1F9F9} Deleted stale TypeScript build cache (${cleaned.map((f) => path12.basename(f)).join(", ")}) \u2014 phantom errors suppressed.`, "warning");
6793
+ notify(`\u{1F9F9} Deleted stale TypeScript build cache (${cleaned.map((f) => path13.basename(f)).join(", ")}) \u2014 phantom errors suppressed.`, "warning");
6666
6794
  dbg(`session_start: cleaned stale tsbuildinfo: ${cleaned.join(", ")}`);
6667
6795
  }
6668
6796
  }
@@ -6747,7 +6875,7 @@ async function handleSessionStart(deps) {
6747
6875
  const startupScanSource = cachedStartupScan ? "snapshot" : "computed";
6748
6876
  let startupScan;
6749
6877
  if (cachedStartupScan) {
6750
- startupScan = { ...cachedStartupScan, cwd: path12.resolve(cwd) };
6878
+ startupScan = { ...cachedStartupScan, cwd: path13.resolve(cwd) };
6751
6879
  } else {
6752
6880
  const scanContextStartedAt = Date.now();
6753
6881
  startupScan = resolveStartupScanContext(cwd);
@@ -6852,7 +6980,10 @@ async function handleSessionStart(deps) {
6852
6980
  tools.push("Rust (cargo)");
6853
6981
  log(`Active tools: ${tools.join(", ")}`);
6854
6982
  dbg(`session_start tools: ${tools.join(", ")}`);
6855
- const agentStartupGuidance = SESSION_START_GUIDANCE;
6983
+ const agentStartupGuidance = renderSessionStartGuidance({
6984
+ disabledToolNames: deps.disabledToolNames,
6985
+ requirePreExistingFixes: deps.globalConfig?.contextInjection?.requirePreExistingFixes
6986
+ });
6856
6987
  runtime.projectRulesScan = scanProjectRules(analysisRoot);
6857
6988
  saveRuntimeProjectSnapshot({
6858
6989
  cwd: snapshotRoot,
@@ -6952,18 +7083,18 @@ function emitSmellsSessionStartLine(dbg, sessionStartMs) {
6952
7083
 
6953
7084
  // dist/clients/runtime-turn.js
6954
7085
  import * as fs17 from "node:fs";
6955
- import * as path23 from "node:path";
7086
+ import * as path24 from "node:path";
6956
7087
 
6957
7088
  // dist/clients/actionable-warnings.js
6958
7089
  import { createHash as createHash2 } from "node:crypto";
6959
7090
  import * as fs12 from "node:fs";
6960
- import * as path14 from "node:path";
7091
+ import * as path15 from "node:path";
6961
7092
 
6962
7093
  // dist/clients/actionable-warnings-logger.js
6963
- import * as path13 from "node:path";
7094
+ import * as path14 from "node:path";
6964
7095
  var AW_LOG_DIR = getGlobalPiLensLogDir();
6965
- var AW_LOG_FILE = path13.join(AW_LOG_DIR, "actionable-warnings.log");
6966
- var AW_LOG_BACKUP_FILE = path13.join(AW_LOG_DIR, "actionable-warnings.log.1");
7096
+ var AW_LOG_FILE = path14.join(AW_LOG_DIR, "actionable-warnings.log");
7097
+ var AW_LOG_BACKUP_FILE = path14.join(AW_LOG_DIR, "actionable-warnings.log.1");
6967
7098
  var MAX_LOG_BYTES2 = Math.max(128 * 1024, Number.parseInt(process.env.PI_LENS_AW_LOG_MAX_BYTES ?? "1048576", 10) || 1048576);
6968
7099
  var writer2 = createNdjsonLogger({
6969
7100
  filePath: AW_LOG_FILE,
@@ -6998,7 +7129,7 @@ function createActionableWarningId(args) {
6998
7129
  });
6999
7130
  }
7000
7131
  function legacyActionableWarningId(args) {
7001
- const rel = path14.relative(args.cwd, args.filePath).replace(/\\/g, "/");
7132
+ const rel = path15.relative(args.cwd, args.filePath).replace(/\\/g, "/");
7002
7133
  const legacyRelativeFile = rel && !rel.startsWith("..") ? rel : normalizeMapKey(args.filePath);
7003
7134
  const parts = [
7004
7135
  legacyRelativeFile,
@@ -7044,7 +7175,7 @@ function deserializeSuppressionState(contents) {
7044
7175
  }
7045
7176
  }
7046
7177
  function readSuppressionState(cwd) {
7047
- const statePath = path14.join(getProjectDataDir(cwd), "cache", "actionable-warning-state.json");
7178
+ const statePath = path15.join(getProjectDataDir(cwd), "cache", "actionable-warning-state.json");
7048
7179
  try {
7049
7180
  return deserializeSuppressionState(fs12.readFileSync(statePath, "utf8"));
7050
7181
  } catch {
@@ -7052,8 +7183,8 @@ function readSuppressionState(cwd) {
7052
7183
  }
7053
7184
  }
7054
7185
  function updateWarningState(cwd, warnings) {
7055
- const statePath = path14.join(getProjectDataDir(cwd), "cache", "actionable-warning-state.json");
7056
- fs12.mkdirSync(path14.dirname(statePath), { recursive: true });
7186
+ const statePath = path15.join(getProjectDataDir(cwd), "cache", "actionable-warning-state.json");
7187
+ fs12.mkdirSync(path15.dirname(statePath), { recursive: true });
7057
7188
  const hook = beforeWarningStateLockForTests;
7058
7189
  beforeWarningStateLockForTests = null;
7059
7190
  hook?.();
@@ -7198,7 +7329,7 @@ function boundedNumber(value, fallback) {
7198
7329
  return value !== void 0 && Number.isFinite(value) && value > 0 ? value : fallback;
7199
7330
  }
7200
7331
  async function buildActionableWarningsReport(args) {
7201
- const cwd = path14.resolve(args.cwd);
7332
+ const cwd = path15.resolve(args.cwd);
7202
7333
  const records = [...args.dispatchWarnings];
7203
7334
  const lspService = getLSPService();
7204
7335
  const observedAtByPath = /* @__PURE__ */ new Map();
@@ -7220,7 +7351,7 @@ async function buildActionableWarningsReport(args) {
7220
7351
  const eligible = [];
7221
7352
  let outsideRoot = 0;
7222
7353
  for (const file of args.files) {
7223
- const filePath = path14.resolve(cwd, file);
7354
+ const filePath = path15.resolve(cwd, file);
7224
7355
  if (normalizeMapKey(filePath) === normalizeMapKey(cwd) || !isUnderDir(filePath, cwd)) {
7225
7356
  outsideRoot++;
7226
7357
  logActionableWarningsEvent({
@@ -7330,7 +7461,7 @@ async function buildActionableWarningsReport(args) {
7330
7461
  }
7331
7462
  };
7332
7463
  const deferredWork = (async () => {
7333
- await new Promise((resolve31) => setTimeout(resolve31, 0));
7464
+ await new Promise((resolve32) => setTimeout(resolve32, 0));
7334
7465
  const deferredRecords = [...carried];
7335
7466
  const deferredDeadline = Date.now() + ACTIONABLE_WARNINGS_DEFERRED_BUDGET_MS;
7336
7467
  deferredDeps.deadlineAt = deferredDeadline;
@@ -7707,7 +7838,7 @@ function publishActionableWarningsReport(cacheManager2, cwd, report, opts = {})
7707
7838
  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 ? ", ..." : ""})`;
7708
7839
  incrementDegradationCount({
7709
7840
  kind: "actionable-warnings-inband-superseded",
7710
- subject: `${path14.resolve(cwd)}:inband-carry-superseded`,
7841
+ subject: `${path15.resolve(cwd)}:inband-carry-superseded`,
7711
7842
  reason
7712
7843
  });
7713
7844
  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 ? ", ..." : ""})`);
@@ -7759,7 +7890,7 @@ function writeDeferredActionableWarningsReport(args) {
7759
7890
  if (droppedFiles.length > 0) {
7760
7891
  incrementDegradationCount({
7761
7892
  kind: "actionable-warnings-deferred-superseded",
7762
- subject: `${path14.resolve(args.cwd)}:deferred-file-superseded`,
7893
+ subject: `${path15.resolve(args.cwd)}:deferred-file-superseded`,
7763
7894
  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 ? ", ..." : ""})`
7764
7895
  });
7765
7896
  args.dbg?.(`turn_end: deferred actionable-warnings dropped ${droppedFiles.length} superseded file entry/entries`);
@@ -7768,7 +7899,7 @@ function writeDeferredActionableWarningsReport(args) {
7768
7899
  return { mergedFiles, droppedFiles: droppedFiles.length };
7769
7900
  }
7770
7901
  function getActionableWarningsHistoryPath(cwd) {
7771
- return path14.join(getProjectDataDir(cwd), "actionable-warnings.jsonl");
7902
+ return path15.join(getProjectDataDir(cwd), "actionable-warnings.jsonl");
7772
7903
  }
7773
7904
  function appendActionableWarningsHistory(cwd, report) {
7774
7905
  const entries = [];
@@ -7807,7 +7938,7 @@ function appendActionableWarningsHistory(cwd, report) {
7807
7938
  return;
7808
7939
  const historyPath = getActionableWarningsHistoryPath(cwd);
7809
7940
  try {
7810
- fs12.mkdirSync(path14.dirname(historyPath), { recursive: true });
7941
+ fs12.mkdirSync(path15.dirname(historyPath), { recursive: true });
7811
7942
  fs12.appendFileSync(historyPath, `${entries.map((entry) => JSON.stringify(entry)).join("\n")}
7812
7943
  `, "utf8");
7813
7944
  } catch {
@@ -7846,7 +7977,7 @@ ${fileList}${more}` : void 0,
7846
7977
 
7847
7978
  // dist/clients/code-quality-warnings.js
7848
7979
  import * as fs13 from "node:fs";
7849
- import * as path15 from "node:path";
7980
+ import * as path16 from "node:path";
7850
7981
  var TIER_BUDGET_ORDER = [
7851
7982
  "warning",
7852
7983
  "info",
@@ -7860,7 +7991,7 @@ function lineInModifiedRanges2(line, ranges) {
7860
7991
  return ranges.some((range) => line >= range.start - 2 && line <= range.end + 2);
7861
7992
  }
7862
7993
  function buildCodeQualityWarningsReport(args) {
7863
- const cwd = path15.resolve(args.cwd);
7994
+ const cwd = path16.resolve(args.cwd);
7864
7995
  const maxWarnings = Math.max(1, args.maxWarnings ?? 50);
7865
7996
  const byId = /* @__PURE__ */ new Map();
7866
7997
  for (const warning of args.warnings) {
@@ -7924,7 +8055,7 @@ function writeCodeQualityWarningsReport(cacheManager2, cwd, report) {
7924
8055
  cacheManager2.writeCache("code-quality-warnings", report, cwd);
7925
8056
  }
7926
8057
  function getCodeQualityWarningsHistoryPath(cwd) {
7927
- return path15.join(getProjectDataDir(cwd), "code-quality-warnings.jsonl");
8058
+ return path16.join(getProjectDataDir(cwd), "code-quality-warnings.jsonl");
7928
8059
  }
7929
8060
  function appendCodeQualityWarningsHistory(cwd, report) {
7930
8061
  const warnings = report.files.flatMap((file) => file.warnings.map((warning) => ({
@@ -7949,7 +8080,7 @@ function appendCodeQualityWarningsHistory(cwd, report) {
7949
8080
  return;
7950
8081
  const historyPath = getCodeQualityWarningsHistoryPath(cwd);
7951
8082
  try {
7952
- fs13.mkdirSync(path15.dirname(historyPath), { recursive: true });
8083
+ fs13.mkdirSync(path16.dirname(historyPath), { recursive: true });
7953
8084
  fs13.appendFileSync(historyPath, `${warnings.map((entry) => JSON.stringify(entry)).join("\n")}
7954
8085
  `, "utf8");
7955
8086
  } catch {
@@ -7981,7 +8112,7 @@ function formatCodeQualityWarningsAdvisory(report, cwd, host = "pi") {
7981
8112
  }
7982
8113
 
7983
8114
  // dist/clients/git-guard.js
7984
- import * as path16 from "node:path";
8115
+ import * as path17 from "node:path";
7985
8116
 
7986
8117
  // dist/clients/read-guard-tool-lines.js
7987
8118
  var MAX_FAILURE_TRACKER_SIZE = 200;
@@ -8056,7 +8187,7 @@ function readBytes(filePath) {
8056
8187
 
8057
8188
  // dist/clients/git-guard.js
8058
8189
  function resolveGuardPath(filePath, cwd) {
8059
- return path16.resolve(cwd, filePath);
8190
+ return path17.resolve(cwd, filePath);
8060
8191
  }
8061
8192
  function guardPathKey(filePath, cwd) {
8062
8193
  return advisoryPathKey(filePath, cwd);
@@ -8179,7 +8310,9 @@ function resyncGitGuardAfterInlinePolicy(args) {
8179
8310
  return;
8180
8311
  args.runtime.updateGitGuardStatus(false, "");
8181
8312
  }
8182
- function syncGitGuardRecord(runtime, cacheManager2, cwd, editedFilePath) {
8313
+ function syncGitGuardRecord(runtime, cacheManager2, cwd, editedFilePath, analysisRootMode = "session") {
8314
+ if (!canWriteAnalysisRoot(analysisRootMode))
8315
+ return;
8183
8316
  const entries = (runtime.getInlineBlockersSnapshot?.() ?? []).filter((entry) => !entry.policySuppressed);
8184
8317
  const inspection = cacheManager2.inspectCache("turn-end-findings", cwd);
8185
8318
  const existing = cacheRecord(cacheManager2, cwd);
@@ -8373,10 +8506,10 @@ function dedupeSecretFindings(findings) {
8373
8506
  }
8374
8507
 
8375
8508
  // dist/clients/project-diagnostics/runner-adapters/govulncheck.js
8376
- import * as path17 from "node:path";
8509
+ import * as path18 from "node:path";
8377
8510
  function govulncheckFindingToProjectDiagnostic(cwd, finding) {
8378
8511
  const frame = finding.trace?.find((f) => f.filename);
8379
- const filePath = frame?.filename ? path17.isAbsolute(frame.filename) ? frame.filename : path17.resolve(cwd, frame.filename) : cwd;
8512
+ const filePath = frame?.filename ? path18.isAbsolute(frame.filename) ? frame.filename : path18.resolve(cwd, frame.filename) : cwd;
8380
8513
  const fix = finding.fixedVersion ? ` (fixed in ${finding.fixedVersion})` : "";
8381
8514
  return {
8382
8515
  filePath,
@@ -8449,12 +8582,12 @@ var govulncheckLane = {
8449
8582
  };
8450
8583
 
8451
8584
  // dist/clients/project-diagnostics/runner-adapters/gitleaks.js
8452
- import * as path18 from "node:path";
8585
+ import * as path19 from "node:path";
8453
8586
  function gitleaksFindingToProjectDiagnostic(cwd, finding) {
8454
8587
  const statusSuffix = finding.pathStatus ? ` [git: ${finding.pathStatus}]` : "";
8455
8588
  const isBlocking2 = finding.pathStatus !== "scratch" && finding.pathStatus !== "ignored" && finding.pathStatus !== "nested-repository";
8456
8589
  return {
8457
- filePath: path18.isAbsolute(finding.file) ? finding.file : path18.resolve(cwd, finding.file),
8590
+ filePath: path19.isAbsolute(finding.file) ? finding.file : path19.resolve(cwd, finding.file),
8458
8591
  line: finding.startLine,
8459
8592
  severity: isBlocking2 ? "error" : "info",
8460
8593
  semantic: isBlocking2 ? "blocking" : "none",
@@ -8472,9 +8605,9 @@ function gitleaksResultToProjectDiagnostics(cwd, result) {
8472
8605
  }
8473
8606
 
8474
8607
  // dist/clients/project-diagnostics/runner-adapters/trivy.js
8475
- import * as path19 from "node:path";
8608
+ import * as path20 from "node:path";
8476
8609
  function trivyFindingToProjectDiagnostic(cwd, finding) {
8477
- const filePath = finding.target ? path19.isAbsolute(finding.target) ? finding.target : path19.resolve(cwd, finding.target) : cwd;
8610
+ const filePath = finding.target ? path20.isAbsolute(finding.target) ? finding.target : path20.resolve(cwd, finding.target) : cwd;
8478
8611
  const version = finding.installedVersion ? `@${finding.installedVersion}` : "";
8479
8612
  const fix = finding.fixedVersion ? ` (fixed in ${finding.fixedVersion})` : "";
8480
8613
  return {
@@ -8491,7 +8624,7 @@ function trivyFindingToProjectDiagnostic(cwd, finding) {
8491
8624
  }
8492
8625
  function trivySecretFindingToProjectDiagnostic(cwd, finding) {
8493
8626
  return {
8494
- filePath: path19.isAbsolute(finding.file) ? finding.file : path19.resolve(cwd, finding.file),
8627
+ filePath: path20.isAbsolute(finding.file) ? finding.file : path20.resolve(cwd, finding.file),
8495
8628
  line: finding.line,
8496
8629
  severity: "error",
8497
8630
  semantic: "blocking",
@@ -8705,7 +8838,7 @@ function stillReportedParked(parked, current, keyOf, offeredKeys) {
8705
8838
  }
8706
8839
 
8707
8840
  // dist/clients/knip-scan-roots.js
8708
- import { resolve as resolve12 } from "node:path";
8841
+ import { resolve as resolve13 } from "node:path";
8709
8842
  var MAX_KNIP_ROOTS_PER_TURN = 3;
8710
8843
  function resolveKnipScanRoots(sessionCwd, modifiedFiles) {
8711
8844
  const session = resolveGitCheckout(sessionCwd);
@@ -8715,7 +8848,7 @@ function resolveKnipScanRoots(sessionCwd, modifiedFiles) {
8715
8848
  let sessionOwnsEdit = false;
8716
8849
  const linked = [];
8717
8850
  for (const file of modifiedFiles) {
8718
- const owner = resolveLinkedWorktreeOwner(session, resolve12(sessionCwd, file));
8851
+ const owner = resolveLinkedWorktreeOwner(session, resolve13(sessionCwd, file));
8719
8852
  if (owner === null) {
8720
8853
  sessionOwnsEdit = true;
8721
8854
  } else if (!linked.some((known) => pathsEqual(known.root, owner.root))) {
@@ -8731,7 +8864,7 @@ function resolveKnipScanRoots(sessionCwd, modifiedFiles) {
8731
8864
  }
8732
8865
 
8733
8866
  // dist/clients/project-diagnostics/runner-adapters/dead-code.js
8734
- import * as path20 from "node:path";
8867
+ import * as path21 from "node:path";
8735
8868
  function deadCodeRunnerId(language) {
8736
8869
  return `dead-code-${language}`;
8737
8870
  }
@@ -8747,7 +8880,7 @@ function deadCodeMessage(issue) {
8747
8880
  function deadCodeIssueToProjectDiagnostic(cwd, issue, language) {
8748
8881
  const blocking = issue.category === "unlisted";
8749
8882
  return {
8750
- filePath: issue.file ? path20.isAbsolute(issue.file) ? issue.file : path20.resolve(cwd, issue.file) : cwd,
8883
+ filePath: issue.file ? path21.isAbsolute(issue.file) ? issue.file : path21.resolve(cwd, issue.file) : cwd,
8751
8884
  line: issue.line,
8752
8885
  severity: blocking ? "error" : "warning",
8753
8886
  semantic: blocking ? "blocking" : "warning",
@@ -8771,11 +8904,11 @@ function deadCodeResultToProjectDiagnostics(cwd, result) {
8771
8904
  }
8772
8905
 
8773
8906
  // dist/clients/project-diagnostics/runner-adapters/knip.js
8774
- import * as path21 from "node:path";
8907
+ import * as path22 from "node:path";
8775
8908
  function knipIssueFile(cwd, issue) {
8776
8909
  if (!issue.file)
8777
8910
  return cwd;
8778
- return path21.isAbsolute(issue.file) ? issue.file : path21.resolve(cwd, issue.file);
8911
+ return path22.isAbsolute(issue.file) ? issue.file : path22.resolve(cwd, issue.file);
8779
8912
  }
8780
8913
  function knipIssueMessage(issue) {
8781
8914
  if (issue.type === "unlisted") {
@@ -9120,7 +9253,7 @@ async function runPersistentReverify(args) {
9120
9253
 
9121
9254
  // dist/clients/inline-blocker-dispositions.js
9122
9255
  import * as fs16 from "node:fs";
9123
- import * as path22 from "node:path";
9256
+ import * as path23 from "node:path";
9124
9257
  function readCurrentContent(filePath) {
9125
9258
  try {
9126
9259
  return fs16.readFileSync(filePath, "utf-8");
@@ -9148,7 +9281,7 @@ function applyInlineBlockerPolicy(record, cwd) {
9148
9281
  }
9149
9282
  const byFile = /* @__PURE__ */ new Map();
9150
9283
  for (const diagnostic of diagnostics) {
9151
- const owner = path22.resolve(cwd, diagnostic.filePath);
9284
+ const owner = path23.resolve(cwd, diagnostic.filePath);
9152
9285
  const group = byFile.get(owner);
9153
9286
  if (group)
9154
9287
  group.push(diagnostic);
@@ -9287,21 +9420,6 @@ var TEST_RUNNER_MAX_TARGETS = 12;
9287
9420
  var TURN_END_SELECTION_ROOT_ROWS = 8;
9288
9421
  var TEST_RUNNER_BATCH_BUDGET_MS = 2e4;
9289
9422
  var TEST_RUNNER_MAX_DEFERRALS = 2;
9290
- var TEST_RUNNER_MAX_PERSISTED_TARGETS = 64;
9291
- function deferralEntryKey(entry) {
9292
- return `${entry.sessionId ?? ""}\0${normalizeMapKey(path23.resolve(entry.testFile))}`;
9293
- }
9294
- function mergeDeferredTargets(existing, incoming) {
9295
- const byKey = /* @__PURE__ */ new Map();
9296
- for (const entry of [...existing, ...incoming]) {
9297
- const key = deferralEntryKey(entry);
9298
- const prior = byKey.get(key);
9299
- if (prior && (prior.attempts ?? 0) >= (entry.attempts ?? 0))
9300
- continue;
9301
- byKey.set(key, entry);
9302
- }
9303
- return [...byKey.values()];
9304
- }
9305
9423
  async function runTestTargetsBounded(args) {
9306
9424
  const results = [];
9307
9425
  if (args.targets.length === 0)
@@ -9344,7 +9462,7 @@ async function runTestTargetsBounded(args) {
9344
9462
  });
9345
9463
  void pool.catch(() => {
9346
9464
  });
9347
- await new Promise((resolve31) => {
9465
+ await new Promise((resolve32) => {
9348
9466
  let settled = false;
9349
9467
  const finish = (reason) => {
9350
9468
  if (settled)
@@ -9356,7 +9474,7 @@ async function runTestTargetsBounded(args) {
9356
9474
  batchAbort.abort();
9357
9475
  clearTimeout(timer);
9358
9476
  args.signal?.removeEventListener("abort", onAbort);
9359
- resolve31();
9477
+ resolve32();
9360
9478
  };
9361
9479
  const onAbort = () => finish("abort");
9362
9480
  const timer = setTimeout(() => finish("budget"), budgetMs);
@@ -9590,22 +9708,26 @@ async function handleTurnEnd(deps) {
9590
9708
  dbg(`turn_end: retaining newer turn state (dispatch=${turnIndexAtDispatch}, current=${runtime.turnIndex})`);
9591
9709
  return;
9592
9710
  }
9593
- cacheManager2.clearTurnState(cwd, currentOwner);
9711
+ cacheManager2.clearTurnState(cwd, currentOwner, isSecondarySession ? sessionId : void 0);
9594
9712
  };
9595
9713
  void updateHeartbeat().catch(() => {
9596
9714
  });
9597
9715
  const cwd = ctxCwd ?? process.cwd();
9598
- let turnState = cacheManager2.readTurnState(cwd);
9716
+ const isSecondarySession = deps.sessionScope?.role === "secondary";
9717
+ const sessionRole = isSecondarySession ? "secondary" : "primary";
9718
+ const allowProjectWideFindings = admitsProjectWideFindings(sessionRole);
9719
+ const turnStateSessionId = isSecondarySession ? sessionId : void 0;
9720
+ let turnState = cacheManager2.readTurnState(cwd, turnStateSessionId);
9599
9721
  const currentOwner = {
9600
9722
  ...owner ?? {
9601
9723
  kind: "pi",
9602
- id: runtime.telemetrySessionId,
9724
+ id: sessionId ?? runtime.telemetrySessionId,
9603
9725
  pid: process.pid,
9604
9726
  lastSeen: (/* @__PURE__ */ new Date()).toISOString()
9605
9727
  },
9606
9728
  sessionStartedAt: owner?.sessionStartedAt ?? runtime.sessionStartedAt
9607
9729
  };
9608
- const access = cacheManager2.getTurnStateAccess(cwd, currentOwner);
9730
+ const access = isSecondarySession ? "owned" : cacheManager2.getTurnStateAccess(cwd, currentOwner);
9609
9731
  const gateOwnerLabel = turnState.owner ? `${turnState.owner.kind}:${turnState.owner.id}` : turnState.sessionId ? `legacy:${turnState.sessionId}` : "none";
9610
9732
  const sameProcessPiSessionHandoff = access === "foreign-live" && currentOwner.kind === "pi" && turnState.owner?.kind === "pi" && turnState.owner.pid === process.pid && turnState.owner.id !== currentOwner.id;
9611
9733
  if (access === "foreign-live" && !sameProcessPiSessionHandoff) {
@@ -9615,9 +9737,10 @@ async function handleTurnEnd(deps) {
9615
9737
  if (access === "available" && (turnState.files || turnState.owner || turnState.sessionId)) {
9616
9738
  dbg("turn_end: evicting stale turn-state owner");
9617
9739
  clearOwnedTurnState();
9618
- turnState = cacheManager2.readTurnState(cwd);
9740
+ turnState = cacheManager2.readTurnState(cwd, turnStateSessionId);
9619
9741
  }
9620
- const files = Object.keys(turnState.files);
9742
+ const ownFiles = Object.keys(turnState.files);
9743
+ const files = ownFiles;
9621
9744
  let widgetDemotedFindingsRetired = 0;
9622
9745
  for (const wPath of drainRenderedDependencyDriftFilePaths()) {
9623
9746
  const deliveryCount = incrementWidgetDependencyDriftDelivery(wPath);
@@ -9633,7 +9756,7 @@ async function handleTurnEnd(deps) {
9633
9756
  }
9634
9757
  }
9635
9758
  }
9636
- if (files.length === 0 && !runtime.hasCascadeRuns()) {
9759
+ if (files.length === 0 && !runtime.hasCascadeRuns() && !isSecondarySession) {
9637
9760
  if (getFlag("lens-guard") && !runtime.gitGuardHasBlockers) {
9638
9761
  const guardRecord = cacheManager2.readCache("turn-end-findings", cwd)?.data;
9639
9762
  if (guardRecord?.sessionId === runtime.telemetrySessionId && guardRecord.testFailures !== true) {
@@ -9665,7 +9788,7 @@ async function handleTurnEnd(deps) {
9665
9788
  dbg("turn_end: cancelled pending LSP idle reset (active editing)");
9666
9789
  }
9667
9790
  dbg(`turn_end: ${files.length} file(s) modified, cycles: ${turnState.turnCycles}/${turnState.maxCycles}, access: ${access}, owner: ${gateOwnerLabel}`);
9668
- if (cacheManager2.isMaxCyclesExceeded(cwd)) {
9791
+ if (cacheManager2.isMaxCyclesExceeded(cwd, turnStateSessionId)) {
9669
9792
  dbg("turn_end: max cycles exceeded, clearing state and forcing through");
9670
9793
  clearOwnedTurnState();
9671
9794
  runtime.fixedThisTurn.clear();
@@ -9708,7 +9831,14 @@ async function handleTurnEnd(deps) {
9708
9831
  const projectDiagnosticsDelta = [];
9709
9832
  const projectDiagnosticsSources = /* @__PURE__ */ new Set();
9710
9833
  const runnerFindingsStart = Date.now();
9711
- const pendingRunnerFindings = await drainPendingRunnerFindings(0);
9834
+ const runnerDrainSignal = deps.signal;
9835
+ const deliverySessionId = sessionId ?? runtime.telemetrySessionId;
9836
+ const pendingRunnerFindings = await bounded(drainPendingRunnerFindings(0, runtime.sessionScope.capture(), deliverySessionId), {
9837
+ ms: HOOK_WALL_BUDGET_MS.turn_end,
9838
+ signal: runnerDrainSignal,
9839
+ hook: "turn_end",
9840
+ label: "drainPendingRunnerFindings"
9841
+ }) ?? [];
9712
9842
  let runnerFindingsDelivered = 0;
9713
9843
  let runnerFindingsStale = 0;
9714
9844
  let runnerFindingsFailed = 0;
@@ -9933,7 +10063,16 @@ ${policy.body}`);
9933
10063
  });
9934
10064
  }
9935
10065
  const cascadeSettleStart = Date.now();
9936
- const { settled, timedOut } = await runtime.settleCascadeRuns(cascadeSettleWaitMs(), { trackTurnEndClock: true, generation: holdScope });
10066
+ const cascadeSettleSignal = deps.signal;
10067
+ const { settled, timedOut } = await bounded(runtime.settleCascadeRuns(cascadeSettleWaitMs(), {
10068
+ trackTurnEndClock: true,
10069
+ generation: holdScope
10070
+ }), {
10071
+ ms: HOOK_WALL_BUDGET_MS.turn_end,
10072
+ signal: cascadeSettleSignal,
10073
+ hook: "turn_end",
10074
+ label: "settleCascadeRuns"
10075
+ }) ?? { settled: 0, timedOut: 0 };
9937
10076
  logLatency({
9938
10077
  type: "phase",
9939
10078
  toolName: "turn_end",
@@ -9950,11 +10089,11 @@ ${policy.body}`);
9950
10089
  const changedSince = runtime.getFilesChangedSince(originSeq);
9951
10090
  if (changedSince.length > 0) {
9952
10091
  const changedSet = new Set(changedSince);
9953
- const primaryKey = normalizeMapKey(path23.resolve(run.filePath));
10092
+ const primaryKey = normalizeMapKey(path24.resolve(run.filePath));
9954
10093
  const neighborKeys = [
9955
10094
  ...(run.result?.neighbors ?? []).map((n) => n.filePath),
9956
10095
  ...run.selectedNeighborPaths ?? []
9957
- ].map((filePath) => normalizeMapKey(path23.resolve(filePath)));
10096
+ ].map((filePath) => normalizeMapKey(path24.resolve(filePath)));
9958
10097
  const supersededByOwnFile = changedSet.has(primaryKey) || neighborKeys.some((k) => changedSet.has(k));
9959
10098
  if (supersededByOwnFile) {
9960
10099
  logCascade({
@@ -10660,15 +10799,15 @@ ${suffix}`;
10660
10799
  const govGate = scannerGates.govulncheck;
10661
10800
  const gitleaksGate = scannerGates.gitleaks;
10662
10801
  const trivySecretsGate = scannerGates["trivy-secrets"];
10663
- const govDelivery = govulncheckLane.render(govulncheckLane.gate({ govulncheck: govGate }, laneCtx), laneCtx);
10802
+ const govDelivery = allowProjectWideFindings ? govulncheckLane.render(govulncheckLane.gate({ govulncheck: govGate }, laneCtx), laneCtx) : {};
10664
10803
  for (const [store, count] of Object.entries(govDelivery.dispositionSuppressed ?? {})) {
10665
10804
  recordDispositionSuppressed(store, count);
10666
10805
  }
10667
10806
  advisoryParts.push(...govDelivery.advisoryParts ?? []);
10668
- const secretsDelivery = secretsLane.render(secretsLane.gate({
10807
+ const secretsDelivery = allowProjectWideFindings ? secretsLane.render(secretsLane.gate({
10669
10808
  gitleaks: gitleaksGate,
10670
10809
  "trivy-secrets": trivySecretsGate
10671
- }, laneCtx), laneCtx);
10810
+ }, laneCtx), laneCtx) : {};
10672
10811
  for (const [store, count] of Object.entries(secretsDelivery.dispositionSuppressed ?? {})) {
10673
10812
  recordDispositionSuppressed(store, count);
10674
10813
  }
@@ -10735,16 +10874,22 @@ ${suffix}`;
10735
10874
  }
10736
10875
  const t3 = Date.now();
10737
10876
  let madgeStats;
10738
- if (getFlag("lens-turn-end-madge") && await depChecker.ensureAvailable()) {
10739
- const madgeFiles = cacheManager2.getFilesForMadge(cwd);
10877
+ const madgeAvailabilitySignal = deps.signal;
10878
+ if (getFlag("lens-turn-end-madge") && await bounded(depChecker.ensureAvailable(), {
10879
+ ms: HOOK_WALL_BUDGET_MS.turn_end,
10880
+ signal: madgeAvailabilitySignal,
10881
+ hook: "turn_end",
10882
+ label: "madge.ensureAvailable"
10883
+ })) {
10884
+ const madgeFiles = cacheManager2.getFilesForMadge(cwd, turnStateSessionId);
10740
10885
  if (madgeFiles.length > 0) {
10741
10886
  dbg(`turn_end: madge checking ${madgeFiles.length} file(s) for circular deps`);
10742
- const absFiles = madgeFiles.map((file) => path23.resolve(cwd, file));
10887
+ const absFiles = madgeFiles.map((file) => path24.resolve(cwd, file));
10743
10888
  const batch = await depChecker.checkFilesBatch(absFiles, cwd);
10744
10889
  const depResults = batch.results;
10745
10890
  madgeStats = batch.stats;
10746
10891
  for (const file of madgeFiles) {
10747
- const absPath = path23.resolve(cwd, file);
10892
+ const absPath = path24.resolve(cwd, file);
10748
10893
  const depResult = depResults.get(absPath);
10749
10894
  if (!depResult)
10750
10895
  continue;
@@ -10780,7 +10925,7 @@ ${suffix}`;
10780
10925
  }
10781
10926
  for (const result of cascadeResults) {
10782
10927
  for (const neighbor of result.neighbors) {
10783
- const abs = path23.isAbsolute(neighbor.filePath) ? neighbor.filePath : resolveRunnerPath(cwd, neighbor.filePath);
10928
+ const abs = path24.isAbsolute(neighbor.filePath) ? neighbor.filePath : resolveRunnerPath(cwd, neighbor.filePath);
10784
10929
  const key = normalizeMapKey(abs);
10785
10930
  if (seenCandidateKeys.has(key))
10786
10931
  continue;
@@ -10792,20 +10937,17 @@ ${suffix}`;
10792
10937
  const carriedDeferred = priorTestCache?.deferredTargets ?? [];
10793
10938
  const turnSessionId = sessionId ?? runtime.telemetrySessionId;
10794
10939
  const isThisSession = (entry) => entry.sessionId === turnSessionId;
10795
- const entryPathKey = (entry) => normalizeMapKey(path23.resolve(cwd, entry.testFile));
10940
+ const entryPathKey = (entry) => normalizeMapKey(path24.resolve(cwd, entry.testFile));
10796
10941
  const retiredCarry = (priorTestCache?.retiredTargets ?? []).filter(isThisSession);
10797
10942
  const retiredKeys = new Set(retiredCarry.map(entryPathKey));
10798
10943
  const resolvedDeferralKeys = /* @__PURE__ */ new Set();
10799
10944
  const heldDeferred = [];
10800
10945
  const boundPersisted = (entries, label) => {
10801
- const ordered = [
10802
- ...entries.filter((entry) => !isThisSession(entry)),
10803
- ...entries.filter(isThisSession)
10804
- ];
10805
- if (ordered.length <= TEST_RUNNER_MAX_PERSISTED_TARGETS)
10806
- return ordered;
10807
- 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)`);
10808
- return ordered.slice(-TEST_RUNNER_MAX_PERSISTED_TARGETS);
10946
+ const bounded2 = boundDeferredTargets(entries, TEST_RUNNER_MAX_PERSISTED_TARGETS, isThisSession);
10947
+ if (bounded2.length !== entries.length) {
10948
+ 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)`);
10949
+ }
10950
+ return bounded2;
10809
10951
  };
10810
10952
  const writeTestFindings = (record, ownDeferred) => {
10811
10953
  const live = cacheManager2.readCache("test-runner-findings", cwd)?.data;
@@ -10829,7 +10971,7 @@ ${suffix}`;
10829
10971
  let redispatchedDeferred = 0;
10830
10972
  let retiredThisTurn = 0;
10831
10973
  for (const carried of carriedDeferred) {
10832
- const testFile = path23.resolve(cwd, carried.testFile);
10974
+ const testFile = path24.resolve(cwd, carried.testFile);
10833
10975
  const carriedKey = normalizeMapKey(testFile);
10834
10976
  if (!isThisSession(carried)) {
10835
10977
  foreignDeferred++;
@@ -10854,9 +10996,9 @@ ${suffix}`;
10854
10996
  incrementDegradationCount({
10855
10997
  kind: "test-runner-batch-capped",
10856
10998
  subject: `${cwd}:deferral-exhausted`,
10857
- 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)})`
10999
+ 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)})`
10858
11000
  });
10859
- 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`);
11001
+ 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`);
10860
11002
  }
10861
11003
  continue;
10862
11004
  }
@@ -10890,7 +11032,7 @@ ${suffix}`;
10890
11032
  testRoot: carriedRoot,
10891
11033
  deferralAttempts: attempts
10892
11034
  });
10893
- dbg(`turn_end: re-running deferred test target ${path23.relative(cwd, testFile)} (${carried.runner}) from the previous turn's cut batch`);
11035
+ dbg(`turn_end: re-running deferred test target ${path24.relative(cwd, testFile)} (${carried.runner}) from the previous turn's cut batch`);
10894
11036
  }
10895
11037
  if (deadDeferred > 0) {
10896
11038
  dbg(`turn_end: dropped ${deadDeferred} deferred test target(s) that no longer resolve (missing file, excluded, or unknown runner)`);
@@ -10934,17 +11076,17 @@ ${suffix}`;
10934
11076
  seen.add(targetKey);
10935
11077
  if (retiredKeys.has(targetKey)) {
10936
11078
  retiredSkips++;
10937
- dbg(`turn_end: ${display} \u2192 test target retired earlier this session (outran the turn-end batch budget), skipping spawn (${path23.relative(cwd, target.testFile)})`);
11079
+ dbg(`turn_end: ${display} \u2192 test target retired earlier this session (outran the turn-end batch budget), skipping spawn (${path24.relative(cwd, target.testFile)})`);
10938
11080
  continue;
10939
11081
  }
10940
11082
  if (isExcludedTestTarget(target.testFile, testRoot)) {
10941
11083
  excludedTargets++;
10942
- dbg(`turn_end: ${display} \u2192 test target excluded by the built-in turn-end policy, skipping spawn (${path23.relative(cwd, target.testFile)})`);
11084
+ dbg(`turn_end: ${display} \u2192 test target excluded by the built-in turn-end policy, skipping spawn (${path24.relative(cwd, target.testFile)})`);
10943
11085
  continue;
10944
11086
  }
10945
11087
  if (!fs17.existsSync(target.testFile)) {
10946
11088
  missingTargetFiles++;
10947
- dbg(`turn_end: ${display} \u2192 test file missing, skipping spawn (${path23.relative(cwd, target.testFile)})`);
11089
+ dbg(`turn_end: ${display} \u2192 test file missing, skipping spawn (${path24.relative(cwd, target.testFile)})`);
10948
11090
  continue;
10949
11091
  }
10950
11092
  if (targets.length >= TEST_RUNNER_MAX_TARGETS) {
@@ -10952,7 +11094,7 @@ ${suffix}`;
10952
11094
  continue;
10953
11095
  }
10954
11096
  targets.push({ ...target, sourceFile: abs, testRoot });
10955
- dbg(`turn_end: ${display} \u2192 test ${target.runner} ${path23.relative(cwd, target.testFile)} (${target.strategy}${isNeighbor ? ", cascade-neighbor" : ""})`);
11097
+ dbg(`turn_end: ${display} \u2192 test ${target.runner} ${path24.relative(cwd, target.testFile)} (${target.strategy}${isNeighbor ? ", cascade-neighbor" : ""})`);
10956
11098
  } else if (!target) {
10957
11099
  noTestFile++;
10958
11100
  dbg(`turn_end: ${display} \u2192 no test file found${isNeighbor ? " (cascade-neighbor)" : ""}`);
@@ -11084,6 +11226,7 @@ ${suffix}`;
11084
11226
  {
11085
11227
  file: result.value.file,
11086
11228
  sourceFile: target.sourceFile,
11229
+ runner: target.runner,
11087
11230
  fileSeq: target.fileSeqAtRun === void 0 ? {
11088
11231
  state: "unknown",
11089
11232
  reason: "sequence-unavailable"
@@ -11115,7 +11258,7 @@ ${suffix}`;
11115
11258
  });
11116
11259
  dbg(`turn_end: test batch stopped early (${stopReason ?? "incomplete"}), ${deferred.length} target(s) deferred to the next turn`);
11117
11260
  }
11118
- 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(", ")}` : "";
11261
+ 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(", ")}` : "";
11119
11262
  const supersededByNewerGeneration = (label) => {
11120
11263
  const current = cacheManager2.readCache("test-runner-findings", cwd)?.data;
11121
11264
  const currentGeneration = current?.testRunGeneration;
@@ -11167,7 +11310,7 @@ ${suffix}`;
11167
11310
  const { file, runner, passed, failed, duration, error } = r.value;
11168
11311
  if (failed > 0)
11169
11312
  hasRealFailure = true;
11170
- const shortFile = path23.basename(file);
11313
+ const shortFile = path24.basename(file);
11171
11314
  const elapsed = formatRunDurationMs(duration);
11172
11315
  const verdict = failed > 0 ? "FAIL" : "PASS";
11173
11316
  const summary = isRunnerErrorResult(r.value) ? passed > 0 ? `error: ${error} (${passed} passed before)` : `error: ${error}` : `${verdict} ${passed}p/${failed}f (${elapsed})`;
@@ -11331,8 +11474,8 @@ ${suffix}`;
11331
11474
  } else {
11332
11475
  const callGraphStart = Date.now();
11333
11476
  try {
11334
- const { impact, formatImpact, parseSymbolKey } = await import("../chunk-GAYGGICG.js");
11335
- const { callGraphImpactToProjectDiagnostics } = await import("../chunk-VPWKF3GN.js");
11477
+ const { impact, formatImpact, parseSymbolKey } = await import("../chunk-BKE7E7TC.js");
11478
+ const { callGraphImpactToProjectDiagnostics } = await import("../chunk-6GFRNVYR.js");
11336
11479
  const impactLines = [];
11337
11480
  let callGraphDispositionSuppressed = 0;
11338
11481
  const impactFindings = [];
@@ -11639,13 +11782,17 @@ ${impactLines.join("\n")}`;
11639
11782
  }
11640
11783
  });
11641
11784
  }
11642
- cacheManager2.incrementTurnCycle(cwd, currentOwner);
11785
+ cacheManager2.incrementTurnCycle(cwd, currentOwner, turnStateSessionId);
11643
11786
  const lateAuxStart = Date.now();
11644
- const { drainedPairs, lateAuxCapEvicted } = holdScope.guardedWrite("turn-end:late-aux", () => ({
11787
+ const lateAuxDrain = isSecondarySession ? {
11788
+ drainedPairs: drainPendingAuxiliaryCoverage(),
11789
+ lateAuxCapEvicted: drainPendingAuxCapEvictedCount()
11790
+ } : holdScope.guardedWrite("turn-end:late-aux", () => ({
11645
11791
  drainedPairs: drainPendingAuxiliaryCoverage(),
11646
11792
  lateAuxCapEvicted: drainPendingAuxCapEvictedCount()
11647
11793
  })) ?? { drainedPairs: [], lateAuxCapEvicted: 0 };
11648
- const rearmLateAux = (pair) => holdScope.guardedWrite(
11794
+ const { drainedPairs, lateAuxCapEvicted } = lateAuxDrain;
11795
+ const rearmLateAux = (pair) => isSecondarySession ? (rearmPendingAuxiliaryCoverage(pair), true) : holdScope.guardedWrite(
11649
11796
  // One subject for the store (#4168 F3): the row's count carries how many.
11650
11797
  "turn-end:late-aux-rearm",
11651
11798
  () => {
@@ -11925,7 +12072,7 @@ ${lines.join("\n")}`;
11925
12072
  }
11926
12073
  });
11927
12074
  }
11928
- const unresolvedKeys = new Set(unresolvedBlockers.map((record) => normalizeMapKey(path23.resolve(record.filePath))));
12075
+ const unresolvedKeys = new Set(unresolvedBlockers.map((record) => normalizeMapKey(path24.resolve(record.filePath))));
11929
12076
  const formatResolvedTail = (dropped) => `\u2026 and ${dropped} more`;
11930
12077
  const resolvedTailMax = formatResolvedTail(Number.MAX_SAFE_INTEGER).length;
11931
12078
  const resolvedShareChars = Math.floor(RUNTIME_CONFIG.turnEnd.maxChars * RESOLVED_SHARE_OF_CHARS) - resolvedTailMax;
@@ -11950,7 +12097,7 @@ ${lines.join("\n")}`;
11950
12097
  let resolvedChars = 0;
11951
12098
  let resolvedCount = 0;
11952
12099
  const { files: resolvedBlockerFileList, dropped: resolvedBlockerFilesDropped } = runtime.consumeResolvedBlockerFiles((entry) => {
11953
- if (unresolvedKeys.has(normalizeMapKey(path23.resolve(entry.filePath))))
12100
+ if (unresolvedKeys.has(normalizeMapKey(path24.resolve(entry.filePath))))
11954
12101
  return true;
11955
12102
  const cost = formatResolvedLine(entry).length + 1;
11956
12103
  if (resolvedCount > 0 && (resolvedCount >= resolvedLineBudget || resolvedChars + cost > resolvedCharBudget))
@@ -12046,7 +12193,7 @@ ${raw}`
12046
12193
  projectSeqStart: runtime.turnStartProjectSeq,
12047
12194
  projectSeqEnd: runtime.projectSeq,
12048
12195
  fileSeqByPath: Object.fromEntries(runtime.getFileSeqEntries().map(([filePath, seq]) => [
12049
- normalizeMapKey(path23.resolve(filePath)),
12196
+ normalizeMapKey(path24.resolve(filePath)),
12050
12197
  seq
12051
12198
  ])),
12052
12199
  fileContentHashes: {},
@@ -12063,7 +12210,7 @@ ${raw}`
12063
12210
  settleHolds(false);
12064
12211
  const fileSeqByPath2 = {};
12065
12212
  for (const [filePath, seq] of runtime.getFileSeqEntries()) {
12066
- fileSeqByPath2[normalizeMapKey(path23.resolve(filePath))] = seq;
12213
+ fileSeqByPath2[normalizeMapKey(path24.resolve(filePath))] = seq;
12067
12214
  }
12068
12215
  if (getFlag("lens-guard")) {
12069
12216
  const existingGuard = cacheManager2.readCache("turn-end-findings", cwd)?.data;
@@ -12281,10 +12428,10 @@ var TurnEndQueue = class {
12281
12428
  if (this.pending.length >= 1) {
12282
12429
  return Promise.reject(new Error("turn_end queue is busy"));
12283
12430
  }
12284
- return new Promise((resolve31, reject) => {
12431
+ return new Promise((resolve32, reject) => {
12285
12432
  const item = {
12286
12433
  work,
12287
- resolve: resolve31,
12434
+ resolve: resolve32,
12288
12435
  reject,
12289
12436
  timer: setTimeout(() => {
12290
12437
  const index = this.pending.indexOf(item);
@@ -12328,7 +12475,7 @@ async function runTurnEndNowImpl(cwd, files = [], deferredDelivery = false) {
12328
12475
  const host = createMcpHost(void 0, cwd);
12329
12476
  let registered = 0;
12330
12477
  for (const file of files) {
12331
- const abs = path24.isAbsolute(file) ? file : path24.resolve(cwd, file);
12478
+ const abs = path25.isAbsolute(file) ? file : path25.resolve(cwd, file);
12332
12479
  let lineCount3 = 1;
12333
12480
  try {
12334
12481
  lineCount3 = fs18.readFileSync(abs, "utf8").split("\n").length;
@@ -12472,7 +12619,7 @@ function acknowledgeTurnEnd(cwd, deliveryId) {
12472
12619
 
12473
12620
  // dist/clients/module-report.js
12474
12621
  import * as fs19 from "node:fs";
12475
- import * as path25 from "node:path";
12622
+ import * as path26 from "node:path";
12476
12623
 
12477
12624
  // dist/clients/middle-man-analysis.js
12478
12625
  var SELF_TOKEN = {
@@ -13021,7 +13168,7 @@ function blastReadArgs(graph, normalizedFile) {
13021
13168
  return { path: normalizedFile, offset: 1, limit };
13022
13169
  }
13023
13170
  async function computeBlastRadius(graph, normalizedPath, projectRoot, maxDepth) {
13024
- const { computeTransitiveImpact } = await import("../chunk-PCHDGUKT.js");
13171
+ const { computeTransitiveImpact } = await import("../chunk-4UNXBNE2.js");
13025
13172
  const result = computeTransitiveImpact(graph, normalizedPath, { maxDepth });
13026
13173
  const byFile = /* @__PURE__ */ new Map();
13027
13174
  for (const hit of result.hits) {
@@ -13611,7 +13758,7 @@ async function moduleReport(file, cwd, options) {
13611
13758
  const startedAt = Date.now();
13612
13759
  const maxRefs = normalizeMaxRefsPerSymbol(options?.maxRefsPerSymbol);
13613
13760
  const maxCallGraphEntries = Math.min(CALL_GRAPH_MAX_ENTRY_CAP, Math.max(1, Math.floor(options?.maxCallGraphEntries ?? CALL_GRAPH_DEFAULT_ENTRY_CAP)));
13614
- const absPath = path25.resolve(cwd, file);
13761
+ const absPath = path26.resolve(cwd, file);
13615
13762
  const normalizedPath = normalizeMapKey(absPath);
13616
13763
  let content;
13617
13764
  try {
@@ -13638,7 +13785,7 @@ async function moduleReport(file, cwd, options) {
13638
13785
  let callGraphIdentity;
13639
13786
  let revisionDriftNote;
13640
13787
  try {
13641
- const { getCachedReviewGraph, getReviewGraphCacheIdentity, getReviewGraphSizeSkipVerdict, getReviewGraphRevisionDrift, formatReviewGraphRevisionDriftNote: formatReviewGraphRevisionDriftNote2 } = await import("../chunk-3TTPKMEF.js");
13788
+ const { getCachedReviewGraph, getReviewGraphCacheIdentity, getReviewGraphSizeSkipVerdict, getReviewGraphRevisionDrift, formatReviewGraphRevisionDriftNote: formatReviewGraphRevisionDriftNote2 } = await import("../chunk-2VUQMUMT.js");
13642
13789
  graph = getCachedReviewGraph(cwd);
13643
13790
  graphFileCap = getReviewGraphSizeSkipVerdict(cwd)?.maxFileCount;
13644
13791
  const drift = graph ? getReviewGraphRevisionDrift(cwd) : void 0;
@@ -13928,7 +14075,7 @@ function suggestSimilarNames(candidates, target) {
13928
14075
  async function readSymbol(file, symbolName, cwd, options = {}) {
13929
14076
  const { kind: symbolKindFilter } = options;
13930
14077
  const startedAt = Date.now();
13931
- const absPath = path25.resolve(cwd, file);
14078
+ const absPath = path26.resolve(cwd, file);
13932
14079
  const log = (found) => {
13933
14080
  logLatency({
13934
14081
  type: "phase",
@@ -14103,7 +14250,7 @@ function enclosingOutline(selected, symbols, callbacks, filters, filePath) {
14103
14250
  }
14104
14251
  async function readEnclosing(file, line, cwd, options) {
14105
14252
  const startedAt = Date.now();
14106
- const absPath = path25.resolve(cwd, file);
14253
+ const absPath = path26.resolve(cwd, file);
14107
14254
  const targetLine = Math.max(1, Math.floor(line));
14108
14255
  const log = (found) => {
14109
14256
  logLatency({
@@ -14279,7 +14426,7 @@ async function readEnclosing(file, line, cwd, options) {
14279
14426
  }
14280
14427
 
14281
14428
  // dist/clients/project-report.js
14282
- import * as path26 from "node:path";
14429
+ import * as path27 from "node:path";
14283
14430
  var DEFAULT_LIMIT = 10;
14284
14431
  var STALE_THRESHOLD_MS = 15 * 6e4;
14285
14432
  var LOW_COVERAGE_THRESHOLD = 0.8;
@@ -14422,7 +14569,7 @@ function roleFor(graph, filePath) {
14422
14569
  return top.length > 0 ? top.map((s) => s.name).join(", ") : void 0;
14423
14570
  }
14424
14571
  async function computeHubs(graph, degrees, cwd, limit, focusTerms) {
14425
- const { computeTransitiveImpact } = await import("../chunk-PCHDGUKT.js");
14572
+ const { computeTransitiveImpact } = await import("../chunk-4UNXBNE2.js");
14426
14573
  const ranked = [...graph.fileNodes.entries()].map(([filePath, fileNodeId]) => ({
14427
14574
  filePath,
14428
14575
  fileNodeId,
@@ -14660,7 +14807,7 @@ function computeDeadWeight(graph, degrees, entryPointFiles, cwd, limit) {
14660
14807
  }
14661
14808
  var inFlightGraphBuilds = /* @__PURE__ */ new Set();
14662
14809
  function triggerBackgroundGraphBuild(cwd, deps) {
14663
- const key = normalizeMapKey(path26.resolve(cwd));
14810
+ const key = normalizeMapKey(path27.resolve(cwd));
14664
14811
  if (inFlightGraphBuilds.has(key))
14665
14812
  return "already_running";
14666
14813
  const deduped = deps.isGraphBuildInFlight(key, []);
@@ -14674,7 +14821,7 @@ async function projectReport(cwd, options) {
14674
14821
  const limit = clampLimit(options?.limit);
14675
14822
  const focusTerms = normalizeFocus2(options?.focus);
14676
14823
  const view = options?.view;
14677
- const { getCachedReviewGraph, getReviewGraphSizeSkipVerdict, getLastReviewGraphBuildAttempt, buildOrUpdateGraph, isGraphBuildInFlight, getReviewGraphRevisionDrift } = await import("../chunk-3TTPKMEF.js");
14824
+ const { getCachedReviewGraph, getReviewGraphSizeSkipVerdict, getLastReviewGraphBuildAttempt, buildOrUpdateGraph, isGraphBuildInFlight, getReviewGraphRevisionDrift } = await import("../chunk-2VUQMUMT.js");
14678
14825
  let graph;
14679
14826
  try {
14680
14827
  graph = getCachedReviewGraph(cwd);
@@ -14698,7 +14845,7 @@ async function projectReport(cwd, options) {
14698
14845
  };
14699
14846
  }
14700
14847
  const previousAttempt = getLastReviewGraphBuildAttempt(cwd);
14701
- const { FactStore: FactStore2 } = await import("../chunk-CULQYFLU.js");
14848
+ const { FactStore: FactStore2 } = await import("../chunk-QGQ5CCCG.js");
14702
14849
  const trigger = triggerBackgroundGraphBuild(cwd, {
14703
14850
  buildOrUpdateGraph,
14704
14851
  isGraphBuildInFlight,
@@ -14800,7 +14947,7 @@ function renderCompactProjectReport(report) {
14800
14947
 
14801
14948
  // dist/clients/effective-config.js
14802
14949
  import * as os3 from "node:os";
14803
- import * as path27 from "node:path";
14950
+ import * as path28 from "node:path";
14804
14951
  function isEffectiveFileViewError(file) {
14805
14952
  return "error" in file;
14806
14953
  }
@@ -14874,10 +15021,10 @@ async function effectiveConfig(options = {}) {
14874
15021
  globalConfigPath: getPiLensGlobalConfigPath(),
14875
15022
  homeDir
14876
15023
  });
14877
- const requestedFile = options.file === void 0 ? void 0 : path27.resolve(cwd, options.file);
15024
+ const requestedFile = options.file === void 0 ? void 0 : path28.resolve(cwd, options.file);
14878
15025
  const fileOutsideCwd = requestedFile !== void 0 && !isSameOrWithin(cwd, requestedFile);
14879
15026
  const absolute = fileOutsideCwd ? void 0 : requestedFile;
14880
- const resolution = resolveAt(absolute === void 0 ? cwd : path27.dirname(absolute));
15027
+ const resolution = resolveAt(absolute === void 0 ? cwd : path28.dirname(absolute));
14881
15028
  const resolved = {
14882
15029
  value: resolution.value,
14883
15030
  provenance: resolution.provenance
@@ -14911,7 +15058,7 @@ async function effectiveConfig(options = {}) {
14911
15058
  // `{ absolute, lspConfig }` struct (#2520): `absolute` is already
14912
15059
  // this branch's narrowed local, so the struct's own field was
14913
15060
  // carrying the same fact twice.
14914
- registerLSPConfig(lspConfigOf(resolution.value))
15061
+ registerLSPConfig(lspConfigOf(resolution.value, void 0, resolution.provenance))
14915
15062
  )
14916
15063
  }
14917
15064
  };
@@ -15039,10 +15186,10 @@ function symbolSearchFileMatchesLang(file, lang) {
15039
15186
  const exts = extensionsForLanguageToken(lang);
15040
15187
  if (exts.length === 0)
15041
15188
  return false;
15042
- return exts.includes(path28.extname(file).toLowerCase());
15189
+ return exts.includes(path29.extname(file).toLowerCase());
15043
15190
  }
15044
15191
  function fileMatchesPathGlobs(file, cwd, globs) {
15045
- const rel = path28.relative(cwd, file).split(path28.sep).join("/");
15192
+ const rel = path29.relative(cwd, file).split(path29.sep).join("/");
15046
15193
  const options = { dot: true, nocase: process.platform === "win32" };
15047
15194
  return globs.some((raw) => {
15048
15195
  const pattern = raw.split("\\").join("/");
@@ -15064,7 +15211,7 @@ function buildSymbolSearchFileFilter(cwd, options) {
15064
15211
  }
15065
15212
  function annotateSymbolSearchHitsWithGraph(hits, centrality, graph) {
15066
15213
  for (const hit of hits) {
15067
- const normalized = normalizeMapKey(path28.resolve(hit.file));
15214
+ const normalized = normalizeMapKey(path29.resolve(hit.file));
15068
15215
  const fanIn = centrality.get(hit.file) ?? 0;
15069
15216
  let complexity;
15070
15217
  for (const symbolId of graph.symbolNodesByFile.get(normalized) ?? []) {
@@ -15110,14 +15257,14 @@ async function symbolSearch(query, cwd, limit = 20, options = {}) {
15110
15257
  let centrality;
15111
15258
  let results;
15112
15259
  try {
15113
- centrality = centralityFromReverseDeps(index, snapshot?.reverseDeps, (file) => normalizeMapKey(path28.resolve(file)));
15260
+ centrality = centralityFromReverseDeps(index, snapshot?.reverseDeps, (file) => normalizeMapKey(path29.resolve(file)));
15114
15261
  const fileFilter = buildSymbolSearchFileFilter(cwd, options);
15115
15262
  results = searchWordIndex(index, query, { limit, centrality, fileFilter });
15116
15263
  } finally {
15117
15264
  warmLease.release();
15118
15265
  }
15119
15266
  const hits = results.map(toSymbolSearchHit);
15120
- const { getCachedReviewGraph } = await import("../chunk-3TTPKMEF.js");
15267
+ const { getCachedReviewGraph } = await import("../chunk-2VUQMUMT.js");
15121
15268
  const graph = getCachedReviewGraph(cwd);
15122
15269
  if (graph)
15123
15270
  annotateSymbolSearchHitsWithGraph(hits, centrality, graph);
@@ -15134,10 +15281,10 @@ async function symbolSearch(query, cwd, limit = 20, options = {}) {
15134
15281
  import { Type } from "typebox";
15135
15282
 
15136
15283
  // dist/clients/ast-grep-tool-logger.js
15137
- import * as path29 from "node:path";
15284
+ import * as path30 from "node:path";
15138
15285
  var AG_LOG_DIR = getGlobalPiLensLogDir();
15139
- var AG_LOG_FILE = path29.join(AG_LOG_DIR, "ast-grep-tools.log");
15140
- var AG_LOG_BACKUP_FILE = path29.join(AG_LOG_DIR, "ast-grep-tools.log.1");
15286
+ var AG_LOG_FILE = path30.join(AG_LOG_DIR, "ast-grep-tools.log");
15287
+ var AG_LOG_BACKUP_FILE = path30.join(AG_LOG_DIR, "ast-grep-tools.log.1");
15141
15288
  var MAX_LOG_BYTES3 = Math.max(128 * 1024, Number.parseInt(process.env.PI_LENS_AST_GREP_LOG_MAX_BYTES ?? "1048576", 10) || 1048576);
15142
15289
  var writer3 = createNdjsonLogger({
15143
15290
  filePath: AG_LOG_FILE,
@@ -15314,6 +15461,11 @@ function canonicalLanguage(lang) {
15314
15461
  }
15315
15462
 
15316
15463
  // dist/tools/shared.js
15464
+ function resolveAstGrepPaths(paths, cwd) {
15465
+ const base = cwd || ".";
15466
+ const requested = paths?.length ? paths : [base];
15467
+ return requested.map((p) => resolveHostToolPath(p, base));
15468
+ }
15317
15469
  var LANGUAGES = [
15318
15470
  "bash",
15319
15471
  "c",
@@ -15450,7 +15602,7 @@ function createAstGrepReplaceTool(astGrepClient2, captureLineage) {
15450
15602
  details: {}
15451
15603
  };
15452
15604
  }
15453
- const searchPaths = paths?.length ? paths : [ctx.cwd || "."];
15605
+ const searchPaths = resolveAstGrepPaths(paths, ctx.cwd);
15454
15606
  if (hasStructuralIntent({
15455
15607
  insideKind,
15456
15608
  hasKind,
@@ -16070,7 +16222,7 @@ function createAstGrepSearchTool(astGrepClient2) {
16070
16222
  details: {}
16071
16223
  };
16072
16224
  }
16073
- const searchPaths = paths?.length ? paths : [ctx.cwd || "."];
16225
+ const searchPaths = resolveAstGrepPaths(paths, ctx.cwd);
16074
16226
  const PAGE_SIZE = Math.max(1, Math.min(MAX_PAGE_SIZE, Number.isFinite(maxMatches) ? Math.floor(maxMatches) : DEFAULT_PAGE_SIZE));
16075
16227
  let effectiveRule = hasRawRule ? rawRule : void 0;
16076
16228
  if (!effectiveRule && (hasNodeKind || hasStructuralIntent({
@@ -16262,7 +16414,7 @@ ${hint}` : `${output}${paginationNote}`;
16262
16414
  // dist/tools/lens-diagnostics.js
16263
16415
  import { promises as fs23 } from "node:fs";
16264
16416
  import * as fsSync from "node:fs";
16265
- import * as path36 from "node:path";
16417
+ import * as path37 from "node:path";
16266
16418
 
16267
16419
  // dist/clients/project-diagnostics/extractors.js
16268
16420
  var WARM_TRIGGER = {
@@ -16322,12 +16474,12 @@ function reasonFromAvailabilityVerdict(tool, verdict) {
16322
16474
  // dist/clients/project-diagnostics/fresh-fetch.js
16323
16475
  import * as fs21 from "node:fs";
16324
16476
  import * as os4 from "node:os";
16325
- import * as path34 from "node:path";
16477
+ import * as path35 from "node:path";
16326
16478
 
16327
16479
  // dist/clients/project-diagnostics/runner-adapters/jscpd.js
16328
- import * as path30 from "node:path";
16480
+ import * as path31 from "node:path";
16329
16481
  function resolveClonePath(cwd, file) {
16330
- return path30.isAbsolute(file) ? file : path30.resolve(cwd, file);
16482
+ return path31.isAbsolute(file) ? file : path31.resolve(cwd, file);
16331
16483
  }
16332
16484
  function jscpdCloneToProjectDiagnostics(cwd, clone) {
16333
16485
  const a = resolveClonePath(cwd, clone.fileA);
@@ -16340,7 +16492,7 @@ function jscpdCloneToProjectDiagnostics(cwd, clone) {
16340
16492
  tool: "jscpd",
16341
16493
  runner: "jscpd",
16342
16494
  rule: "jscpd:duplicate",
16343
- message: `Duplicate code (${clone.lines} lines) \u2014 also at ${path30.relative(cwd, otherFile)}:${otherLine}`,
16495
+ message: `Duplicate code (${clone.lines} lines) \u2014 also at ${path31.relative(cwd, otherFile)}:${otherLine}`,
16344
16496
  source: "project-scan"
16345
16497
  });
16346
16498
  return [
@@ -16355,16 +16507,16 @@ function jscpdResultToProjectDiagnostics(cwd, result) {
16355
16507
  }
16356
16508
 
16357
16509
  // dist/clients/project-diagnostics/runner-adapters/madge.js
16358
- import * as path31 from "node:path";
16510
+ import * as path32 from "node:path";
16359
16511
  function resolveCyclePath(cwd, file) {
16360
- return path31.isAbsolute(file) ? file : path31.resolve(cwd, file);
16512
+ return path32.isAbsolute(file) ? file : path32.resolve(cwd, file);
16361
16513
  }
16362
16514
  function cycleMembers(dep) {
16363
16515
  return dep.path.length > 0 ? dep.path : [dep.file];
16364
16516
  }
16365
16517
  function circularDepToProjectDiagnostics(cwd, dep) {
16366
16518
  const members = cycleMembers(dep);
16367
- const rendered = `${members.map((f) => path31.basename(f)).join(" \u2192 ")} \u2192 ${path31.basename(members[0] ?? "")}`;
16519
+ const rendered = `${members.map((f) => path32.basename(f)).join(" \u2192 ")} \u2192 ${path32.basename(members[0] ?? "")}`;
16368
16520
  return members.map((file) => ({
16369
16521
  filePath: resolveCyclePath(cwd, file),
16370
16522
  severity: "warning",
@@ -16390,7 +16542,7 @@ function circularDepsToProjectDiagnostics(cwd, circular) {
16390
16542
  }
16391
16543
 
16392
16544
  // dist/clients/project-diagnostics/runner-adapters/opengrep.js
16393
- import * as path32 from "node:path";
16545
+ import * as path33 from "node:path";
16394
16546
  function severityFor(raw) {
16395
16547
  switch (raw.toUpperCase()) {
16396
16548
  case "ERROR":
@@ -16405,7 +16557,7 @@ function opengrepFindingToProjectDiagnostic(cwd, finding) {
16405
16557
  const { severity, blocking } = severityFor(finding.severity);
16406
16558
  const cweSuffix = finding.cwe?.length ? ` (${finding.cwe[0]})` : "";
16407
16559
  return {
16408
- filePath: path32.isAbsolute(finding.path) ? finding.path : path32.resolve(cwd, finding.path),
16560
+ filePath: path33.isAbsolute(finding.path) ? finding.path : path33.resolve(cwd, finding.path),
16409
16561
  line: finding.startLine,
16410
16562
  column: finding.startCol,
16411
16563
  severity,
@@ -16425,7 +16577,7 @@ function opengrepResultToProjectDiagnostics(cwd, result) {
16425
16577
 
16426
16578
  // dist/clients/project-diagnostics/runner-adapters/runner-findings.js
16427
16579
  import * as fs20 from "node:fs";
16428
- import * as path33 from "node:path";
16580
+ import * as path34 from "node:path";
16429
16581
  function failureMessage(failure) {
16430
16582
  const firstLine2 = failure.message.split("\n")[0]?.slice(0, 300) ?? "";
16431
16583
  return firstLine2 ? `${failure.name}: ${firstLine2}` : failure.name;
@@ -16491,7 +16643,7 @@ function testRunnerFindingsToProjectDiagnostics(cache, cwd, runtime) {
16491
16643
  return [];
16492
16644
  const validation = cwd ? validateAdvisoryProvenance(cache, cwd, runtime) : { status: "unknown", reasons: ["validation-context-missing"] };
16493
16645
  const root = cwd ?? process.cwd();
16494
- const missingKeys = new Set((cache.provenance?.files ?? []).filter((file) => !fs20.existsSync(path33.resolve(root, file.path))).map((file) => advisoryPathKey(file.path, root)));
16646
+ const missingKeys = new Set((cache.provenance?.files ?? []).filter((file) => !fs20.existsSync(path34.resolve(root, file.path))).map((file) => advisoryPathKey(file.path, root)));
16495
16647
  const historical = cache.superseded === true || cache.stale === true || validation.status !== "current";
16496
16648
  return cache.results.filter((result) => !missingKeys.has(advisoryPathKey(result.file, root))).flatMap((result) => testResultToProjectDiagnostics(result, historical)).map((diagnostic) => historical ? {
16497
16649
  ...diagnostic,
@@ -16521,7 +16673,7 @@ function laneObservedAtMs(scannedAt, startedAtMs) {
16521
16673
  return parseScannedAtMs(scannedAt) ?? startedAtMs;
16522
16674
  }
16523
16675
  async function fetchFreshProjectDiagnostics(cacheManager2, cwd, clients, signal, options = {}) {
16524
- const requestedRoot = options.analysisRoot === void 0 ? cwd : path34.resolve(cwd, options.analysisRoot);
16676
+ const requestedRoot = options.analysisRoot === void 0 ? cwd : path35.resolve(cwd, options.analysisRoot);
16525
16677
  let analysisRoot;
16526
16678
  let analysisRootValidation;
16527
16679
  if (options.analysisRoot !== void 0) {
@@ -16688,7 +16840,7 @@ async function fetchFreshProjectDiagnostics(cacheManager2, cwd, clients, signal,
16688
16840
  markCold("jscpd", reasonFromAvailabilityVerdict("jscpd", clients.jscpdClient.getAvailabilityVerdict?.()));
16689
16841
  return;
16690
16842
  }
16691
- const isTsProject = fs21.existsSync(path34.join(analysisRoot, "tsconfig.json"));
16843
+ const isTsProject = fs21.existsSync(path35.join(analysisRoot, "tsconfig.json"));
16692
16844
  const scannerKey = isTsProject ? "jscpd-ts" : "jscpd";
16693
16845
  const startMs = Date.now();
16694
16846
  const result = await clients.jscpdClient.scan(analysisRoot, void 0, void 0, isTsProject);
@@ -16919,12 +17071,12 @@ async function fetchFreshProjectDiagnostics(cacheManager2, cwd, clients, signal,
16919
17071
  const allSettled = Promise.all(tasks).then(() => "completed").catch(() => "completed");
16920
17072
  const outcome = signal ? await Promise.race([
16921
17073
  allSettled,
16922
- new Promise((resolve31) => {
17074
+ new Promise((resolve32) => {
16923
17075
  if (signal.aborted) {
16924
- resolve31("aborted");
17076
+ resolve32("aborted");
16925
17077
  return;
16926
17078
  }
16927
- signal.addEventListener("abort", () => resolve31("aborted"), {
17079
+ signal.addEventListener("abort", () => resolve32("aborted"), {
16928
17080
  once: true
16929
17081
  });
16930
17082
  })
@@ -17005,7 +17157,7 @@ function makeProgressReporter(onUpdate, label, throttleMs = 250) {
17005
17157
 
17006
17158
  // dist/tools/lsp-diagnostics.js
17007
17159
  import * as fs22 from "node:fs";
17008
- import * as path35 from "node:path";
17160
+ import * as path36 from "node:path";
17009
17161
  var MAX_FILES = 100;
17010
17162
  var MAX_BATCH_FILES = 100;
17011
17163
  var MAX_DIAGNOSTICS = 200;
@@ -17088,11 +17240,11 @@ async function collectFiles(dir, extensions, maxFiles, isIgnored = () => false)
17088
17240
  return;
17089
17241
  if (entry.isSymbolicLink())
17090
17242
  continue;
17091
- const full = path35.join(current, entry.name);
17243
+ const full = path36.join(current, entry.name);
17092
17244
  if (entry.isDirectory()) {
17093
17245
  if (!isExcludedDirName(entry.name) && !isIgnored(full, true))
17094
17246
  await walk(full);
17095
- } else if (entry.isFile() && extensions.includes(path35.extname(full))) {
17247
+ } else if (entry.isFile() && extensions.includes(path36.extname(full))) {
17096
17248
  if (isIgnored(full, false))
17097
17249
  continue;
17098
17250
  files.push(full);
@@ -17186,7 +17338,7 @@ function createLspDiagnosticsTool(nextWriteIndex, onConfirmedNoBlockers, getServ
17186
17338
  details: {}
17187
17339
  };
17188
17340
  }
17189
- const absPath = normalizeMapKey(path35.resolve(cwd, rawPath));
17341
+ const absPath = normalizeMapKey(path36.resolve(cwd, rawPath));
17190
17342
  let stat;
17191
17343
  try {
17192
17344
  stat = fs22.statSync(absPath);
@@ -17843,12 +17995,12 @@ async function collectBatchDiagnostics(files, severity, lspService, options) {
17843
17995
  });
17844
17996
  const result = options.signal ? await Promise.race([
17845
17997
  bounded2,
17846
- new Promise((resolve31) => {
17998
+ new Promise((resolve32) => {
17847
17999
  if (options.signal?.aborted) {
17848
- resolve31(void 0);
18000
+ resolve32(void 0);
17849
18001
  return;
17850
18002
  }
17851
- options.signal?.addEventListener("abort", () => resolve31(void 0), {
18003
+ options.signal?.addEventListener("abort", () => resolve32(void 0), {
17852
18004
  once: true
17853
18005
  });
17854
18006
  })
@@ -17907,7 +18059,7 @@ async function expandPathsToFiles(cwd, rawPaths) {
17907
18059
  const directories = [];
17908
18060
  const seenFileKeys = /* @__PURE__ */ new Set();
17909
18061
  for (const entry of rawPaths) {
17910
- const abs = normalizeMapKey(path35.resolve(cwd, entry));
18062
+ const abs = normalizeMapKey(path36.resolve(cwd, entry));
17911
18063
  let stat;
17912
18064
  try {
17913
18065
  stat = fs22.statSync(abs);
@@ -18111,7 +18263,7 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
18111
18263
  outcome: result.outcome,
18112
18264
  reason: result.tooLargeReason ?? result.inconclusiveReason ?? result.error ?? result.unavailable
18113
18265
  }));
18114
- const tooLargeLines = renderBatchOutcomeLines(outcomeDetails, (file) => path35.relative(absPath, file));
18266
+ const tooLargeLines = renderBatchOutcomeLines(outcomeDetails, (file) => path36.relative(absPath, file));
18115
18267
  let text;
18116
18268
  if (total === 0) {
18117
18269
  const cleanLine = unconfirmed > 0 ? `${clean} clean \xB7 ${unconfirmed} unconfirmed: ${unconfirmedReasonClause(unconfirmed, timedOut, rootFallbackReasons)} NOT the same as 0 diagnostics.` : "No diagnostics found.";
@@ -18143,7 +18295,7 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
18143
18295
  ];
18144
18296
  const toRelative = (d) => ({
18145
18297
  ...d,
18146
- file: path35.relative(absPath, d.file)
18298
+ file: path36.relative(absPath, d.file)
18147
18299
  });
18148
18300
  if (primaryDisplay.length > 0) {
18149
18301
  lines.push(`Primary findings (${primaryDisplay.length}):`);
@@ -18174,7 +18326,7 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
18174
18326
  filesScanned: filesToProcess.length,
18175
18327
  capped: wasCapped,
18176
18328
  diagnostics: display.map((d) => ({
18177
- file: path35.relative(absPath, d.file),
18329
+ file: path36.relative(absPath, d.file),
18178
18330
  line: d.line,
18179
18331
  character: d.character,
18180
18332
  severity: d.severity,
@@ -18265,7 +18417,7 @@ async function findFullScanBindingMismatches(results) {
18265
18417
  sinceYield += 1;
18266
18418
  if (sinceYield >= FULL_SCAN_BINDING_YIELD_EVERY) {
18267
18419
  sinceYield = 0;
18268
- await new Promise((resolve31) => setImmediate(resolve31));
18420
+ await new Promise((resolve32) => setImmediate(resolve32));
18269
18421
  }
18270
18422
  }
18271
18423
  return mismatched;
@@ -18304,7 +18456,7 @@ function createLensDiagnosticsTool(cacheManager2, getCwd, getLspService = getLSP
18304
18456
  const files2 = details.filesChecked ?? details.filesScanned ?? 0;
18305
18457
  const noun = count === 1 ? "diagnostic" : "diagnostics";
18306
18458
  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;
18307
- const singleFile = (details?.mode === "file" || files2 === 1) && typeof filePath === "string" ? ` ${path36.basename(filePath)}` : "";
18459
+ const singleFile = (details?.mode === "file" || files2 === 1) && typeof filePath === "string" ? ` ${path37.basename(filePath)}` : "";
18308
18460
  const scope = files2 > 1 ? ` across ${files2} files` : singleFile;
18309
18461
  if (isError)
18310
18462
  return `lens_diagnostics lsp \u2014 ${text.split("\n")[0] ?? "error"}`;
@@ -18550,7 +18702,7 @@ function appendProjectDiagnosticsDeltaLines(groups, cwd, report, severity, inclu
18550
18702
  const diagnostics = [...gated.live, ...gated.stale];
18551
18703
  const byFile = /* @__PURE__ */ new Map();
18552
18704
  for (const diagnostic of diagnostics) {
18553
- const filePath = path36.resolve(diagnostic.filePath);
18705
+ const filePath = path37.resolve(diagnostic.filePath);
18554
18706
  const bucket = byFile.get(filePath) ?? [];
18555
18707
  bucket.push(diagnostic);
18556
18708
  byFile.set(filePath, bucket);
@@ -18610,7 +18762,10 @@ function filterDeltaReportDispositions(report, cwd, policyMap) {
18610
18762
  if (!report?.diagnostics.length)
18611
18763
  return report;
18612
18764
  const kept = report.diagnostics.filter((d) => applyCachedDispositions([projectDiagnosticToWidget(d, d.filePath)], cwd, d.filePath).length === 1);
18613
- const policyKept = applyRulePolicy(kept, policyMap);
18765
+ const policyKept = applyRulePolicy(kept, policyMap, {
18766
+ root: cwd,
18767
+ filePath: cwd
18768
+ });
18614
18769
  if (policyKept.length === report.diagnostics.length)
18615
18770
  return report;
18616
18771
  return { ...report, diagnostics: policyKept };
@@ -18674,7 +18829,7 @@ function applyDeltaFreshnessGate(files, cwd, generatedAt) {
18674
18829
  })).filter((file) => file.warnings.length > 0);
18675
18830
  }
18676
18831
  function getDeltaFileGroup(groups, cwd, filePath) {
18677
- const rel = path36.relative(cwd, filePath);
18832
+ const rel = path37.relative(cwd, filePath);
18678
18833
  let group = groups.get(rel);
18679
18834
  if (!group) {
18680
18835
  group = {
@@ -18702,7 +18857,7 @@ function formatDeltaMode(cacheManager2, cwd, severity, pathsScope) {
18702
18857
  // #2504 r4 (F1): carried through, so a merged report's older
18703
18858
  // entries keep their own age all the way to the freshness gate.
18704
18859
  generatedAt: file.generatedAt,
18705
- warnings: applyRulePolicy(applyCachedDispositions(file.warnings ?? [], cwd, file.filePath), policyMap)
18860
+ warnings: applyRulePolicy(applyCachedDispositions(file.warnings ?? [], cwd, file.filePath), policyMap, { root: cwd, filePath: file.filePath })
18706
18861
  })).filter((file) => file.warnings.length > 0);
18707
18862
  const actionableFiles = applyDeltaFreshnessGate(visibleWarningFiles(actionable?.files), cwd, actionable?.generatedAt);
18708
18863
  const qualityFiles = applyDeltaFreshnessGate(visibleWarningFiles(quality?.files), cwd, quality?.generatedAt);
@@ -18771,7 +18926,7 @@ function formatDeltaMode(cacheManager2, cwd, severity, pathsScope) {
18771
18926
  let text = `No ${severity === "all" ? "" : severity + " "}issues in the current turn delta.`;
18772
18927
  const carried = getFileDiagnosticSummaries().filter((f) => includeFile(f.filePath)).map((f) => ({
18773
18928
  ...f,
18774
- diagnostics: applyRulePolicy(applyCachedDispositions(f.diagnostics, cwd, f.filePath), policyMap)
18929
+ diagnostics: applyRulePolicy(applyCachedDispositions(f.diagnostics, cwd, f.filePath), policyMap, { root: cwd, filePath: f.filePath })
18775
18930
  })).filter((f) => f.diagnostics.length > 0);
18776
18931
  const carriedIssues = carried.reduce((n, f) => n + f.diagnostics.length, 0);
18777
18932
  if (carried.length > 0) {
@@ -18798,7 +18953,7 @@ Summary (turn delta): ${aw} actionable warning${aw === 1 ? "" : "s"} \xB7 ${cq}
18798
18953
  function createCurrentIgnoreFilter(cwd) {
18799
18954
  try {
18800
18955
  const matcher = getProjectIgnoreMatcher(cwd);
18801
- return (filePath) => !matcher.isIgnored(path36.resolve(filePath), false);
18956
+ return (filePath) => !matcher.isIgnored(path37.resolve(filePath), false);
18802
18957
  } catch {
18803
18958
  return () => true;
18804
18959
  }
@@ -18809,7 +18964,7 @@ function resolvePathsScope(cwd, rawPaths) {
18809
18964
  if (rawPaths.length > MAX_PATHS_ENTRIES) {
18810
18965
  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.`);
18811
18966
  }
18812
- const entries = rawPaths.filter((entry) => typeof entry === "string" && entry.trim().length > 0).map((entry) => path36.resolve(path36.isAbsolute(entry) ? entry : path36.resolve(cwd, entry)));
18967
+ const entries = rawPaths.filter((entry) => typeof entry === "string" && entry.trim().length > 0).map((entry) => path37.resolve(path37.isAbsolute(entry) ? entry : path37.resolve(cwd, entry)));
18813
18968
  const missing = [];
18814
18969
  const fileEntries = [];
18815
18970
  const existingEntries = [];
@@ -18832,9 +18987,9 @@ function resolvePathsScope(cwd, rawPaths) {
18832
18987
  }
18833
18988
  const fileKeys = new Set(fileEntries.map((f) => normalizeFilePath(f)));
18834
18989
  const dirPrefixes = dirEntries.map((d) => normalizeFilePath(d));
18835
- const includeExplicitFile = (filePath) => fileKeys.has(normalizeFilePath(path36.resolve(filePath)));
18990
+ const includeExplicitFile = (filePath) => fileKeys.has(normalizeFilePath(path37.resolve(filePath)));
18836
18991
  const includeFile = (filePath) => {
18837
- const key = normalizeFilePath(path36.resolve(filePath));
18992
+ const key = normalizeFilePath(path37.resolve(filePath));
18838
18993
  if (fileKeys.has(key))
18839
18994
  return true;
18840
18995
  return dirPrefixes.some((prefix) => key === prefix || key.startsWith(`${prefix}/`));
@@ -18855,7 +19010,7 @@ function pathsScopeCacheOnlyNote(scope) {
18855
19010
  function pathsScopeMissingNote(scope) {
18856
19011
  if (!scope || scope.missing.length === 0)
18857
19012
  return "";
18858
- const shown2 = scope.missing.slice(0, 10).map((p) => path36.basename(p));
19013
+ const shown2 = scope.missing.slice(0, 10).map((p) => path37.basename(p));
18859
19014
  const more = scope.missing.length > shown2.length ? ` (+${scope.missing.length - shown2.length} more)` : "";
18860
19015
  return `
18861
19016
 
@@ -18887,12 +19042,15 @@ function filterProjectDiagnosticsDeltaReport(report, includeFile) {
18887
19042
  diagnostics: report.diagnostics.filter((d) => includeFile(d.filePath))
18888
19043
  };
18889
19044
  }
18890
- function applyProjectRulePolicy(value, policyMap) {
19045
+ function applyProjectRulePolicy(value, policyMap, root) {
18891
19046
  if (!value)
18892
19047
  return void 0;
18893
19048
  return {
18894
19049
  ...value,
18895
- diagnostics: applyRulePolicy(value.diagnostics, policyMap)
19050
+ diagnostics: applyRulePolicy(value.diagnostics, policyMap, {
19051
+ root,
19052
+ filePath: value.diagnostics[0]?.filePath ?? root
19053
+ })
18896
19054
  };
18897
19055
  }
18898
19056
  function isErrorLike(d) {
@@ -18983,7 +19141,7 @@ function projectDiagnosticToWidget(diagnostic, filePath) {
18983
19141
  }
18984
19142
  function diagnosticDedupKey(filePath, diagnostic) {
18985
19143
  const ruleId = normalizeRuleId(diagnostic.rule ?? diagnostic.tool ?? "");
18986
- return [path36.resolve(filePath), diagnostic.line ?? "?", ruleId].join(":");
19144
+ return [path37.resolve(filePath), diagnostic.line ?? "?", ruleId].join(":");
18987
19145
  }
18988
19146
  function runnerIdOf(diagnostic) {
18989
19147
  return diagnostic.tool ?? diagnostic.rule?.split(":", 1)[0] ?? "";
@@ -18997,8 +19155,8 @@ function runnerRetirementDecision(diagnostic, filePath, authoritativeRunnerIds,
18997
19155
  const realFilePath = realpathOrResolve(filePath);
18998
19156
  for (const entry of coverage) {
18999
19157
  const root = entry.root;
19000
- const relative9 = path36.relative(root, realFilePath);
19001
- const underRoot = relative9 === "" || !relative9.startsWith("..") && !path36.isAbsolute(relative9);
19158
+ const relative9 = path37.relative(root, realFilePath);
19159
+ const underRoot = relative9 === "" || !relative9.startsWith("..") && !path37.isAbsolute(relative9);
19002
19160
  if (!underRoot)
19003
19161
  continue;
19004
19162
  if (entry.files.has(realFilePath)) {
@@ -19122,7 +19280,7 @@ function mergeDiagnosticsWithWidgetSummaries(widgetSummaries, lspResults, projec
19122
19280
  const byFile = /* @__PURE__ */ new Map();
19123
19281
  const seen = /* @__PURE__ */ new Set();
19124
19282
  for (const summary of widgetSummaries) {
19125
- const filePath = path36.resolve(summary.filePath);
19283
+ const filePath = path37.resolve(summary.filePath);
19126
19284
  if (authoritativeLspFiles?.has(filePath))
19127
19285
  continue;
19128
19286
  const retained = summary.diagnostics ?? [];
@@ -19150,7 +19308,7 @@ function mergeDiagnosticsWithWidgetSummaries(widgetSummaries, lspResults, projec
19150
19308
  byFile.set(filePath, summarizeDiagnostics(filePath, diagnostics, existing?.hasFinalSnapshot ?? true));
19151
19309
  };
19152
19310
  for (const result of lspResults) {
19153
- const filePath = path36.resolve(result.filePath);
19311
+ const filePath = path37.resolve(result.filePath);
19154
19312
  for (const diagnostic of result.diagnostics ?? []) {
19155
19313
  addDiagnostic(filePath, {
19156
19314
  ...lspDiagnosticToWidget(diagnostic),
@@ -19170,12 +19328,12 @@ function mergeDiagnosticsWithWidgetSummaries(widgetSummaries, lspResults, projec
19170
19328
  });
19171
19329
  }
19172
19330
  for (const diagnostic of projectSnapshot?.diagnostics ?? []) {
19173
- addDiagnostic(path36.resolve(diagnostic.filePath), projectDiagnosticToWidget(diagnostic, diagnostic.filePath));
19331
+ addDiagnostic(path37.resolve(diagnostic.filePath), projectDiagnosticToWidget(diagnostic, diagnostic.filePath));
19174
19332
  }
19175
19333
  for (const diagnostic of deltaDiagnostics) {
19176
19334
  const widget = projectDiagnosticToWidget(diagnostic, diagnostic.filePath);
19177
19335
  const observedAt = widget.observedAt ?? deltaObservedAt;
19178
- addDiagnostic(path36.resolve(diagnostic.filePath), {
19336
+ addDiagnostic(path37.resolve(diagnostic.filePath), {
19179
19337
  ...widget,
19180
19338
  ...observedAt !== void 0 && { observedAt }
19181
19339
  });
@@ -19190,7 +19348,10 @@ async function applyInlineSuppressionsToSummaries(summaries, cwd, policyMap) {
19190
19348
  try {
19191
19349
  content = await fs23.readFile(summary.filePath, "utf8");
19192
19350
  } catch {
19193
- const policyKept2 = applyRulePolicy(summary.diagnostics, policyMap);
19351
+ const policyKept2 = applyRulePolicy(summary.diagnostics, policyMap, {
19352
+ root: cwd,
19353
+ filePath: summary.filePath
19354
+ });
19194
19355
  return policyKept2.length === summary.diagnostics.length ? summary : summarizeDiagnostics(summary.filePath, policyKept2, summary.hasFinalSnapshot);
19195
19356
  }
19196
19357
  const { kept: policyKept } = applyFindingPolicy(summary.diagnostics, {
@@ -19375,20 +19536,20 @@ async function formatFullMode(cwd, severity, lspService, cacheManager2, options
19375
19536
  result2.observedAt
19376
19537
  );
19377
19538
  if (retired === true) {
19378
- authoritativeLspFiles.add(path36.resolve(result2.filePath));
19539
+ authoritativeLspFiles.add(path37.resolve(result2.filePath));
19379
19540
  } else {
19380
- unreconciledFiles.add(path36.resolve(result2.filePath));
19541
+ unreconciledFiles.add(path37.resolve(result2.filePath));
19381
19542
  recordDegradationOnce({
19382
19543
  kind: "diagnostic-retained-unreconciled",
19383
- subject: `${path36.resolve(result2.filePath)}:lsp`,
19544
+ subject: `${path37.resolve(result2.filePath)}:lsp`,
19384
19545
  reason: "confirmed diagnostic result could not replace retained widget state"
19385
19546
  });
19386
19547
  }
19387
19548
  } catch {
19388
- unreconciledFiles.add(path36.resolve(result2.filePath));
19549
+ unreconciledFiles.add(path37.resolve(result2.filePath));
19389
19550
  recordDegradationOnce({
19390
19551
  kind: "diagnostic-retained-unreconciled",
19391
- subject: `${path36.resolve(result2.filePath)}:lsp`,
19552
+ subject: `${path37.resolve(result2.filePath)}:lsp`,
19392
19553
  reason: "confirmed diagnostic result failed during widget reconciliation"
19393
19554
  });
19394
19555
  }
@@ -19398,9 +19559,9 @@ async function formatFullMode(cwd, severity, lspService, cacheManager2, options
19398
19559
  const authoritativeRunnerIds = new Set(extracted.analyzed ?? []);
19399
19560
  const authoritativeRunnerCoverage = extracted.authoritativeCoverage ?? [];
19400
19561
  const foldedProjectSnapshot = foldExtraDiagnosticsIntoSnapshot(scannedSnapshot, extracted.diagnostics.filter((d) => includeFile(d.filePath)), extracted.runners, cwd);
19401
- const projectSnapshot = applyProjectRulePolicy(foldedProjectSnapshot, policyMap);
19402
- const projectDelta = applyProjectRulePolicy(filterProjectDiagnosticsDeltaReport(loadProjectDiagnosticsDeltaReport(cwd), includeFile), policyMap);
19403
- const authoritativeRetiredCount = getFileDiagnosticSummaries().filter((summary) => includeFile(summary.filePath) && authoritativeLspFiles.has(path36.resolve(summary.filePath)) && (summary.diagnostics?.length ?? 0) > 0).length;
19562
+ const projectSnapshot = applyProjectRulePolicy(foldedProjectSnapshot, policyMap, cwd);
19563
+ const projectDelta = applyProjectRulePolicy(filterProjectDiagnosticsDeltaReport(loadProjectDiagnosticsDeltaReport(cwd), includeFile), policyMap, cwd);
19564
+ const authoritativeRetiredCount = getFileDiagnosticSummaries().filter((summary) => includeFile(summary.filePath) && authoritativeLspFiles.has(path37.resolve(summary.filePath)) && (summary.diagnostics?.length ?? 0) > 0).length;
19404
19565
  if (authoritativeRetiredCount > 0) {
19405
19566
  logLatency({
19406
19567
  type: "phase",
@@ -19455,7 +19616,7 @@ async function formatFullMode(cwd, severity, lspService, cacheManager2, options
19455
19616
  }
19456
19617
  let summaries = await applyInlineSuppressionsToSummaries(mergeDiagnosticsWithWidgetSummaries(getFileDiagnosticSummaries().filter((summary) => includeFile(summary.filePath)), confirmedLspResults, projectSnapshot, projectDelta, authoritativeLspFiles, authoritativeRunnerIds, authoritativeRunnerCoverage), cwd, policyMap);
19457
19618
  if (unreconciledFiles.size > 0) {
19458
- summaries = summaries.map((summary) => unreconciledFiles.has(path36.resolve(summary.filePath)) ? summarizeDiagnostics(summary.filePath, markUnreconciledFindings(summary.diagnostics), summary.hasFinalSnapshot) : summary);
19619
+ summaries = summaries.map((summary) => unreconciledFiles.has(path37.resolve(summary.filePath)) ? summarizeDiagnostics(summary.filePath, markUnreconciledFindings(summary.diagnostics), summary.hasFinalSnapshot) : summary);
19459
19620
  }
19460
19621
  const parsedProjectScanObservedAt = projectSnapshot?.scannedAt ? Date.parse(projectSnapshot.scannedAt) : void 0;
19461
19622
  const projectScanObservedAt = parsedProjectScanObservedAt !== void 0 && Number.isFinite(parsedProjectScanObservedAt) ? parsedProjectScanObservedAt : void 0;
@@ -19674,7 +19835,10 @@ async function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummari
19674
19835
  const dispositioned = isFullMode ? summaries : summaries.map((s) => {
19675
19836
  const diagnostics = s.diagnostics ?? [];
19676
19837
  const kept = applyCachedDispositions(diagnostics, cwd, s.filePath);
19677
- const policyKept = applyRulePolicy(kept, policyMap);
19838
+ const policyKept = applyRulePolicy(kept, policyMap, {
19839
+ root: cwd,
19840
+ filePath: s.filePath
19841
+ });
19678
19842
  if (policyKept.length === kept.length && kept.length === diagnostics.length)
19679
19843
  return s;
19680
19844
  return summarizeDiagnostics(s.filePath, policyKept, s.hasFinalSnapshot);
@@ -19719,7 +19883,7 @@ async function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummari
19719
19883
  let totalWarnings = 0;
19720
19884
  let totalAdvisories = 0;
19721
19885
  for (const s of rendered) {
19722
- const rel = path36.relative(cwd, s.filePath);
19886
+ const rel = path37.relative(cwd, s.filePath);
19723
19887
  const parts = [];
19724
19888
  if (s.blocking > 0)
19725
19889
  parts.push(`\u{1F534} ${s.blocking} blocking`);
@@ -19787,7 +19951,7 @@ Summary (${visibleSummaries.length} files diagnosed this session):`,
19787
19951
 
19788
19952
  // dist/tools/lsp-navigation.js
19789
19953
  import * as nodeFs2 from "node:fs";
19790
- import * as path37 from "node:path";
19954
+ import * as path38 from "node:path";
19791
19955
  import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL2 } from "node:url";
19792
19956
 
19793
19957
  // dist/tools/lsp-structured-output.js
@@ -20497,7 +20661,7 @@ function collectSearchReadsForOperation(operation, result, callHierarchyItem) {
20497
20661
  }
20498
20662
  function formatCapabilities(snapshots, filePath) {
20499
20663
  if (snapshots.length === 0) {
20500
- 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.";
20664
+ 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.";
20501
20665
  }
20502
20666
  const rows = [
20503
20667
  ["definition", (s) => !!s.operationSupport.definition],
@@ -20527,7 +20691,7 @@ function formatCapabilities(snapshots, filePath) {
20527
20691
  ];
20528
20692
  const lines = [];
20529
20693
  for (const snapshot of snapshots) {
20530
- const label = filePath ? `${snapshot.serverId} (${path37.basename(filePath)})` : `${snapshot.serverId} (${snapshot.root})`;
20694
+ const label = filePath ? `${snapshot.serverId} (${path38.basename(filePath)})` : `${snapshot.serverId} (${snapshot.root})`;
20531
20695
  lines.push(label);
20532
20696
  for (const [name, supported, note] of rows) {
20533
20697
  const suffix = note ? ` (${note})` : "";
@@ -20818,6 +20982,7 @@ function createLspNavigationTool(getFlag, mutationDeps) {
20818
20982
  const mutationSource = operation === "executeCommand" ? "lsp-execute-command" : "lsp-rename";
20819
20983
  mutationContext = {
20820
20984
  cwd,
20985
+ sessionId: ctx.sessionManager?.getSessionId?.(),
20821
20986
  correlationId: newLspMutationCorrelationId(_toolCallId),
20822
20987
  // #4187 R4-1: the call whose `tool_call` licensed the paths it
20823
20988
  // named; the workspace edit's other files (a rename's importers)
@@ -20866,7 +21031,7 @@ function createLspNavigationTool(getFlag, mutationDeps) {
20866
21031
  resultCount: 0
20867
21032
  });
20868
21033
  }
20869
- const filePath = rawPath ? path37.isAbsolute(rawPath) ? rawPath : path37.resolve(ctx.cwd || ".", rawPath) : "";
21034
+ const filePath = rawPath ? path38.isAbsolute(rawPath) ? rawPath : path38.resolve(ctx.cwd || ".", rawPath) : "";
20870
21035
  let filePathIsDirectory = false;
20871
21036
  if (filePath) {
20872
21037
  try {
@@ -20902,7 +21067,7 @@ function createLspNavigationTool(getFlag, mutationDeps) {
20902
21067
  content: [
20903
21068
  {
20904
21069
  type: "text",
20905
- text: `No LSP server available for ${path37.basename(filePath)}. Check that the language server is installed.`
21070
+ text: `No LSP server available for ${path38.basename(filePath)}. Check that the language server is installed.`
20906
21071
  }
20907
21072
  ],
20908
21073
  isError: true
@@ -21003,7 +21168,7 @@ ${JSON.stringify(result2)}`
21003
21168
  content: [
21004
21169
  {
21005
21170
  type: "text",
21006
- text: `No LSP server available for ${path37.basename(filePath)}. Check that the language server is installed.`
21171
+ text: `No LSP server available for ${path38.basename(filePath)}. Check that the language server is installed.`
21007
21172
  }
21008
21173
  ],
21009
21174
  isError: true
@@ -21022,7 +21187,7 @@ ${JSON.stringify(result2)}`
21022
21187
  content: [
21023
21188
  {
21024
21189
  type: "text",
21025
- text: `LSP server for ${path37.basename(filePath)} does not advertise support for ${operation}`
21190
+ text: `LSP server for ${path38.basename(filePath)} does not advertise support for ${operation}`
21026
21191
  }
21027
21192
  ],
21028
21193
  isError: true,
@@ -21076,7 +21241,7 @@ ${JSON.stringify(result2)}`
21076
21241
  const msg = err instanceof Error ? err.message : String(err);
21077
21242
  if (rawPath && /No Project/i.test(msg)) {
21078
21243
  await openFileBestEffort(lspService, filePath);
21079
- await new Promise((resolve31) => setTimeout(resolve31, 120));
21244
+ await new Promise((resolve32) => setTimeout(resolve32, 120));
21080
21245
  const retryRaw = await lspService.workspaceSymbol(query ?? "", filePath);
21081
21246
  const retrySymbols = (Array.isArray(retryRaw) ? retryRaw : [retryRaw]).filter((s) => typeof s === "object" && s !== null);
21082
21247
  return dedupeWorkspaceSymbols(retrySymbols);
@@ -21151,7 +21316,7 @@ ${JSON.stringify(result2)}`
21151
21316
  if (!newFilePath || newFilePath.trim().length === 0) {
21152
21317
  throw new Error("__BADINPUT__ newFilePath parameter required for rename_file");
21153
21318
  }
21154
- const resolvedNewFilePath = path37.isAbsolute(newFilePath) ? newFilePath : path37.resolve(ctx.cwd || ".", newFilePath);
21319
+ const resolvedNewFilePath = path38.isAbsolute(newFilePath) ? newFilePath : path38.resolve(ctx.cwd || ".", newFilePath);
21155
21320
  const result2 = await lspService.renameFile(filePath, resolvedNewFilePath, {
21156
21321
  cwd: ctx.cwd || ".",
21157
21322
  apply: apply ?? false,
@@ -21295,7 +21460,7 @@ ${JSON.stringify(result2)}`
21295
21460
  }, { operation, filePath, failureKind: "lsp_error", resultCount: 0 });
21296
21461
  }
21297
21462
  const isEmpty = !result || Array.isArray(result) && result.length === 0;
21298
- const fileCtx = filePath ? " at " + path37.basename(filePath) : "";
21463
+ const fileCtx = filePath ? " at " + path38.basename(filePath) : "";
21299
21464
  const lineCtx = line ? ":" + line + ":" + character : "";
21300
21465
  let output = isEmpty ? "No results for " + operation + fileCtx + lineCtx : JSON.stringify(result);
21301
21466
  if (isEmpty && operation === "workspaceSymbol" && !rawPath) {
@@ -21537,11 +21702,11 @@ function resolveDefaultCwd() {
21537
21702
  const fromArg = process.argv.find((arg) => arg.startsWith("--cwd="))?.slice("--cwd=".length);
21538
21703
  return fromArg ?? process.env.PI_LENS_MCP_CWD ?? process.cwd();
21539
21704
  }
21540
- var DEFAULT_CWD = path38.resolve(resolveDefaultCwd());
21705
+ var DEFAULT_CWD = path39.resolve(resolveDefaultCwd());
21541
21706
  var lspReadyCwds = /* @__PURE__ */ new Set();
21542
21707
  var SERVER_FILE = fileURLToPath3(import.meta.url);
21543
- var SERVER_DIR = path38.dirname(SERVER_FILE);
21544
- var WORKER_PATH = path38.join(SERVER_DIR, "worker.js");
21708
+ var SERVER_DIR = path39.dirname(SERVER_FILE);
21709
+ var WORKER_PATH = path39.join(SERVER_DIR, "worker.js");
21545
21710
  var REBUILD_SCRIPT = resolveRebuildScript(SERVER_FILE);
21546
21711
  var STALENESS_STAT_PATH = process.env.PI_LENS_MCP_STALENESS_STAT_PATH ?? SERVER_FILE;
21547
21712
  var BUILD_STAMP = computeBuildStamp(STALENESS_STAT_PATH);
@@ -21563,7 +21728,7 @@ function isWarmBuildStale() {
21563
21728
  function findRepoRoot(start) {
21564
21729
  let dir = start;
21565
21730
  for (let depth = 0; depth < 6; depth++) {
21566
- const pkgPath = path38.join(dir, "package.json");
21731
+ const pkgPath = path39.join(dir, "package.json");
21567
21732
  if (fs25.existsSync(pkgPath)) {
21568
21733
  try {
21569
21734
  const pkg = JSON.parse(fs25.readFileSync(pkgPath, "utf8"));
@@ -21572,16 +21737,16 @@ function findRepoRoot(start) {
21572
21737
  } catch {
21573
21738
  }
21574
21739
  }
21575
- const parent = path38.dirname(dir);
21740
+ const parent = path39.dirname(dir);
21576
21741
  if (parent === dir)
21577
21742
  break;
21578
21743
  dir = parent;
21579
21744
  }
21580
- return path38.resolve(start, "..", "..");
21745
+ return path39.resolve(start, "..", "..");
21581
21746
  }
21582
- var REPO_ROOT = process.env.PI_LENS_MCP_REPO_ROOT ? path38.resolve(process.env.PI_LENS_MCP_REPO_ROOT) : findRepoRoot(SERVER_DIR);
21747
+ var REPO_ROOT = process.env.PI_LENS_MCP_REPO_ROOT ? path39.resolve(process.env.PI_LENS_MCP_REPO_ROOT) : findRepoRoot(SERVER_DIR);
21583
21748
  async function ensureReady(cwd) {
21584
- const normalized = path38.resolve(cwd);
21749
+ const normalized = path39.resolve(cwd);
21585
21750
  if (!shouldInitializeSessionRoot(normalized, lspReadyCwds)) {
21586
21751
  return;
21587
21752
  }
@@ -21627,14 +21792,14 @@ function getAutoSessionStatus() {
21627
21792
  }
21628
21793
  var IPC_PATH = ipcPathForCwd(DEFAULT_CWD);
21629
21794
  function rejectForeignTurnCwd(route, cwd) {
21630
- const message = `${route} cwd outside this server's workspace: ${path38.resolve(cwd)} not within ${DEFAULT_CWD}`;
21795
+ const message = `${route} cwd outside this server's workspace: ${path39.resolve(cwd)} not within ${DEFAULT_CWD}`;
21631
21796
  console.error(`[pi-lens-mcp] rejected ${message}`);
21632
21797
  return message;
21633
21798
  }
21634
21799
  function isWithinServerWorkspace(cwd) {
21635
- const target = path38.resolve(cwd);
21636
- const rel = path38.relative(DEFAULT_CWD, target);
21637
- return !rel.startsWith("..") && !path38.isAbsolute(rel);
21800
+ const target = path39.resolve(cwd);
21801
+ const rel = path39.relative(DEFAULT_CWD, target);
21802
+ return !rel.startsWith("..") && !path39.isAbsolute(rel);
21638
21803
  }
21639
21804
  function startIpcServer() {
21640
21805
  if (process.platform !== "win32") {
@@ -21784,6 +21949,12 @@ function graphStalenessNote(builtAtIso, label) {
21784
21949
  return void 0;
21785
21950
  return `${label} last updated ${formatRelativeAge(ageMs)}; run pilens_analyze on recently-changed files, pilens_session_start, or pilens_rebuild to refresh it.`;
21786
21951
  }
21952
+ setProjectTrustState("trusted");
21953
+ logExtension({
21954
+ subsystem: "project-trust",
21955
+ level: "debug",
21956
+ message: "mcp: workspace trust belongs to the MCP client; pi trust not consulted"
21957
+ });
21787
21958
  var cacheManager = new CacheManager();
21788
21959
  var isLensGuardEnabled = () => Boolean(createMcpHost(void 0, DEFAULT_CWD).getFlag("lens-guard"));
21789
21960
  var lensDiagnosticsTool = createLensDiagnosticsTool(
@@ -22123,7 +22294,7 @@ function enabledToolsForCwd(cwd) {
22123
22294
  }
22124
22295
  function formatAnalyze(result, cwd, mode, servedBy) {
22125
22296
  const lspNote = result.lsp ? ` \xB7 lsp ${result.lsp.diagnosticCount} (${result.lsp.status}${result.lsp.failureKind ? `: ${result.lsp.failureKind}` : ""}, ${result.lsp.durationMs}ms)` : "";
22126
- 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 ? `
22297
+ 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 ? `
22127
22298
 
22128
22299
  servedBy: ${servedBy}` : "");
22129
22300
  return renderToolText(summary, servedBy ? { ...result, servedBy } : result);
@@ -22257,7 +22428,7 @@ ${outcome.output}`, {
22257
22428
  }
22258
22429
  const { deduped, byRule, byFile } = summarizeScan(snapshot.diagnostics);
22259
22430
  const topRules = Object.entries(byRule).sort((a, b) => b[1] - a[1]);
22260
- const topFiles = Object.entries(byFile).sort((a, b) => b[1] - a[1]).slice(0, 15).map(([file, count]) => ({ file: path38.relative(cwd, file), count }));
22431
+ const topFiles = Object.entries(byFile).sort((a, b) => b[1] - a[1]).slice(0, 15).map(([file, count]) => ({ file: path39.relative(cwd, file), count }));
22261
22432
  const summaryLines = [
22262
22433
  `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)` : ""),
22263
22434
  ...topRules.slice(0, 12).map(([rule, count]) => ` ${count}\xD7 ${rule}`)
@@ -22310,7 +22481,7 @@ ${stalenessNote}` : ""), {
22310
22481
  }
22311
22482
  const lines = [
22312
22483
  `Top ${results.length} file(s) for "${query}":`,
22313
- ...results.map((result, i) => ` ${i + 1}. ${path38.relative(cwd, result.file)} (score ${result.score.toFixed(2)}, ${result.hits} hit(s), line ${result.startLine})`),
22484
+ ...results.map((result, i) => ` ${i + 1}. ${path39.relative(cwd, result.file)} (score ${result.score.toFixed(2)}, ${result.hits} hit(s), line ${result.startLine})`),
22314
22485
  ...stalenessNote ? ["", stalenessNote] : [],
22315
22486
  ...coverage ? [
22316
22487
  `Index covers ${coverage.files} files${coverage.truncated ? " (capped \u2014 results may be incomplete)" : ""}.`
@@ -22320,7 +22491,7 @@ ${stalenessNote}` : ""), {
22320
22491
  query,
22321
22492
  coverage,
22322
22493
  results: results.map((result) => {
22323
- const relFile = path38.relative(cwd, result.file);
22494
+ const relFile = path39.relative(cwd, result.file);
22324
22495
  return {
22325
22496
  file: relFile,
22326
22497
  score: result.score,
@@ -22359,12 +22530,12 @@ ${stalenessNote}` : ""), {
22359
22530
  });
22360
22531
  if (!report.available) {
22361
22532
  return {
22362
- ...renderToolText(`No module report for ${path38.relative(cwd, path38.resolve(cwd, file))} \u2014 not a symbol-bearing file, or unreadable.`, report, true),
22533
+ ...renderToolText(`No module report for ${path39.relative(cwd, path39.resolve(cwd, file))} \u2014 not a symbol-bearing file, or unreadable.`, report, true),
22363
22534
  isError: true
22364
22535
  };
22365
22536
  }
22366
22537
  const graphStaleness = graphStalenessNote(report.graphBuiltAt, "Review graph");
22367
- 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 ? `
22538
+ 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 ? `
22368
22539
 
22369
22540
  ${graphStaleness}` : "");
22370
22541
  if (view === "compact") {
@@ -22425,7 +22596,7 @@ ${graphStaleness}` : "");
22425
22596
  if (!result.found) {
22426
22597
  const suggestionSuffix = result.suggestions?.length ? ` Did you mean: ${result.suggestions.join(", ")}?` : " Use pilens_module_report to list symbols.";
22427
22598
  return {
22428
- ...renderToolText(`Symbol "${symbol}" not found in ${path38.basename(file)}.${suggestionSuffix}`, {
22599
+ ...renderToolText(`Symbol "${symbol}" not found in ${path39.basename(file)}.${suggestionSuffix}`, {
22429
22600
  found: false,
22430
22601
  ...result.suggestions ? { suggestions: result.suggestions } : {}
22431
22602
  }),
@@ -22434,7 +22605,7 @@ ${graphStaleness}` : "");
22434
22605
  }
22435
22606
  const sigSuffix = result.signature ? ` ${result.signature}` : "";
22436
22607
  const ambiguityNote = result.ambiguous ? ` (${result.ambiguous.count} matches \u2014 returned the ${result.kind}; pass \`kind\` to disambiguate: ${result.ambiguous.kinds.join(", ")})` : "";
22437
- const header = `${result.kind} ${result.name}${ambiguityNote}${sigSuffix} ${path38.relative(cwd, result.path)}:${result.startLine}-${result.endLine}`;
22608
+ const header = `${result.kind} ${result.name}${ambiguityNote}${sigSuffix} ${path39.relative(cwd, result.path)}:${result.startLine}-${result.endLine}`;
22438
22609
  return {
22439
22610
  content: [
22440
22611
  {
@@ -22472,14 +22643,14 @@ ${result.source ?? ""}`
22472
22643
 
22473
22644
  Nested outline:
22474
22645
  ${JSON.stringify(result.outline)}` : "";
22475
- 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}`;
22646
+ 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}`;
22476
22647
  return {
22477
22648
  ...renderToolText(text, { found: false, line: result.line }),
22478
22649
  isError: true
22479
22650
  };
22480
22651
  }
22481
22652
  const range = result.partial ? `${result.startLine}-${result.endLine} (partial of ${result.enclosingStartLine}-${result.enclosingEndLine})` : `${result.startLine}-${result.endLine}`;
22482
- const header = `${result.kind} ${result.name} ${path38.relative(cwd, result.path)}:${range}`;
22653
+ const header = `${result.kind} ${result.name} ${path39.relative(cwd, result.path)}:${range}`;
22483
22654
  return {
22484
22655
  content: [
22485
22656
  {
@@ -22514,7 +22685,7 @@ ${result.source ?? ""}`
22514
22685
  ]),
22515
22686
  ...renderLspBrokenStatusLines(brokenServers),
22516
22687
  ...renderDegradationLines(degradations),
22517
- last ? `Last dispatch: ${path38.basename(last.filePath)} \u2014 ${last.totalDurationMs}ms, ${last.totalDiagnostics} diagnostic(s)` : "Last dispatch: none yet",
22688
+ last ? `Last dispatch: ${path39.basename(last.filePath)} \u2014 ${last.totalDurationMs}ms, ${last.totalDiagnostics} diagnostic(s)` : "Last dispatch: none yet",
22518
22689
  `Diagnostics this session: ${stats.totalShown} shown \xB7 ${stats.totalAutoFixed} auto-fixed \xB7 ${stats.totalUnresolved} unresolved`,
22519
22690
  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)",
22520
22691
  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)",
@@ -22580,7 +22751,7 @@ ${result.source ?? ""}`
22580
22751
  const limit = typeof args.limit === "number" && Number.isFinite(args.limit) ? Math.max(1, Math.floor(args.limit)) : 5;
22581
22752
  const fileFilter = typeof args.file === "string" ? args.file : void 0;
22582
22753
  const recent = recentLatency(limit, fileFilter);
22583
- 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");
22754
+ 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");
22584
22755
  return renderToolText(summary, recent);
22585
22756
  }
22586
22757
  if (name === "pilens_session_start") {