pi-lens 4.4.3 → 4.4.4

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 (143) hide show
  1. package/CHANGELOG.md +281 -0
  2. package/README.md +13 -1
  3. package/dist/{chunk-SS36ZX4J.js → chunk-24AKHFGP.js} +6 -6
  4. package/dist/{chunk-JSQFTSC6.js → chunk-2FN63LPV.js} +11 -11
  5. package/dist/{chunk-5UCAH366.js → chunk-2KXTHF3M.js} +20 -20
  6. package/dist/{chunk-EU63SDCG.js → chunk-3DA2JDOL.js} +367 -281
  7. package/dist/{chunk-RWWYJ623.js → chunk-3KAZU3VR.js} +1 -1
  8. package/dist/chunk-3PTHXJXR.js +28 -0
  9. package/dist/chunk-3QED2P4V.js +31 -0
  10. package/dist/{chunk-N2H3ZPZY.js → chunk-3RKP3FMS.js} +1 -0
  11. package/dist/{chunk-XVF2VVXP.js → chunk-5HSRT26B.js} +2 -2
  12. package/dist/{chunk-MXGY6KPT.js → chunk-5IUSNGOC.js} +15 -15
  13. package/dist/{chunk-EJIUXPBX.js → chunk-5OFV52AM.js} +1 -1
  14. package/dist/{chunk-AW7R2GWG.js → chunk-5VSF4UUQ.js} +12 -7
  15. package/dist/{chunk-6STA6Z7I.js → chunk-5WRPDP2A.js} +18 -18
  16. package/dist/{chunk-YIUAVF7E.js → chunk-6DLWKFEX.js} +35 -34
  17. package/dist/{chunk-WOA4LN2R.js → chunk-7D6L4MTX.js} +2 -2
  18. package/dist/{chunk-GAEGEPOC.js → chunk-7HXE67BN.js} +2 -2
  19. package/dist/chunk-AGSS7277.js +84 -0
  20. package/dist/{chunk-R3PCY3YS.js → chunk-AMERCH6V.js} +13 -13
  21. package/dist/{chunk-WU567RSC.js → chunk-ATKJFDQN.js} +14 -10
  22. package/dist/{chunk-VSSBEUFT.js → chunk-AWSCPCN2.js} +16 -16
  23. package/dist/{chunk-VCHXCQFG.js → chunk-BWD5EPKO.js} +3 -3
  24. package/dist/{chunk-H6MF6EVN.js → chunk-CT4MN5IU.js} +1200 -230
  25. package/dist/{chunk-FCSOVF55.js → chunk-CUHSHSB6.js} +6 -6
  26. package/dist/{chunk-CHOW74WE.js → chunk-CYRKQRPM.js} +7 -10
  27. package/dist/{chunk-Q7DUK4LX.js → chunk-DEL2U6CM.js} +5 -5
  28. package/dist/{chunk-6C7JWPCP.js → chunk-DGUKIYSI.js} +26 -23
  29. package/dist/{chunk-PHDMFAKM.js → chunk-DUO2TU77.js} +2 -2
  30. package/dist/{chunk-MRXKYPCS.js → chunk-EBC36LNN.js} +2 -2
  31. package/dist/{chunk-CSHFH5IN.js → chunk-ED36RF6Y.js} +1 -1
  32. package/dist/{chunk-76MWC2K6.js → chunk-EUXZEFTV.js} +90 -97
  33. package/dist/{chunk-QOZXJ4NA.js → chunk-FM4RDMKT.js} +11 -8
  34. package/dist/{chunk-ACXEQZOU.js → chunk-GAQ7OQG6.js} +31 -28
  35. package/dist/{chunk-XAGLWMJ4.js → chunk-GL5LUMZE.js} +23 -23
  36. package/dist/{chunk-BKE7E7TC.js → chunk-GMX5OS7B.js} +9 -9
  37. package/dist/{chunk-ZHFZWTZV.js → chunk-GSLNCYGG.js} +11 -11
  38. package/dist/{chunk-3W7IEVVL.js → chunk-GSM5UU7A.js} +26 -34
  39. package/dist/{chunk-724UL2TJ.js → chunk-GXFV6MYU.js} +3 -3
  40. package/dist/chunk-HAD7QRQI.js +30 -0
  41. package/dist/{chunk-QOBUHWHX.js → chunk-HRNHZA3N.js} +6 -6
  42. package/dist/{chunk-VWCXW2E7.js → chunk-HWCCCQNX.js} +1 -1
  43. package/dist/{chunk-7DYBLXYA.js → chunk-HYGZEMX6.js} +3 -3
  44. package/dist/chunk-IJYGIDF4.js +186 -0
  45. package/dist/{chunk-4W4VLAPJ.js → chunk-IQWN5JCP.js} +11 -11
  46. package/dist/{chunk-SQ4TVJBR.js → chunk-IUE7SSKV.js} +7 -7
  47. package/dist/{chunk-IBGIOBEE.js → chunk-IWDLU6K3.js} +68 -5
  48. package/dist/{chunk-XCJN5IJU.js → chunk-IYNJ6YPZ.js} +4 -4
  49. package/dist/{chunk-JAU72XNY.js → chunk-J64T55EI.js} +3 -3
  50. package/dist/{chunk-2BBLFX75.js → chunk-J6MPGQTS.js} +5 -5
  51. package/dist/chunk-JORED7EX.js +44 -0
  52. package/dist/{chunk-6SDEP3AJ.js → chunk-JUKMK5Y4.js} +8 -8
  53. package/dist/{chunk-FMSO6RDZ.js → chunk-JW3YWBVB.js} +15 -15
  54. package/dist/{chunk-L3BLXZG7.js → chunk-KAPJINKN.js} +30 -69
  55. package/dist/{chunk-2B73ZPOK.js → chunk-KN35K52N.js} +3 -3
  56. package/dist/{chunk-C34ZXZ7O.js → chunk-KUA4OJ5Y.js} +24 -11
  57. package/dist/chunk-KYUB6QDC.js +72 -0
  58. package/dist/{chunk-67WMNWEE.js → chunk-LLJNQKQW.js} +12 -10
  59. package/dist/chunk-LZSEQW6A.js +57 -0
  60. package/dist/{chunk-ZANLILYD.js → chunk-M2IFVFWU.js} +2 -2
  61. package/dist/chunk-M2UD3LPU.js +88 -0
  62. package/dist/{chunk-XIJZVPD5.js → chunk-MBKQHGA7.js} +15 -15
  63. package/dist/{chunk-P7266HLM.js → chunk-MPNJLASA.js} +2 -2
  64. package/dist/{chunk-6GFRNVYR.js → chunk-MZGQNL3Y.js} +10 -10
  65. package/dist/{chunk-ZWFS3OEF.js → chunk-N5FLEO7W.js} +33 -32
  66. package/dist/{chunk-3JNKMP3B.js → chunk-O4NWQT6I.js} +4 -4
  67. package/dist/{chunk-T6CPIZNT.js → chunk-OHKJ7PQF.js} +33 -32
  68. package/dist/{chunk-VRZP76YM.js → chunk-P63GXC37.js} +4 -1
  69. package/dist/{chunk-XTMJ7XDT.js → chunk-POMLXPMY.js} +1 -1
  70. package/dist/{chunk-QQNQ7SJW.js → chunk-QBFBRV2B.js} +10 -4
  71. package/dist/{chunk-CMMHNUZ2.js → chunk-R3T7CLQ2.js} +60 -38
  72. package/dist/{chunk-23WOO7TX.js → chunk-R7YOCSNV.js} +10 -10
  73. package/dist/{chunk-2CD5RS6M.js → chunk-RKBXZN3A.js} +23 -2
  74. package/dist/{chunk-OCAGUNBC.js → chunk-RKKOYPRA.js} +12 -12
  75. package/dist/{chunk-52FUZI6K.js → chunk-RMLC67LF.js} +1 -1
  76. package/dist/{chunk-XMSXXAHW.js → chunk-ROYSX2S5.js} +2 -2
  77. package/dist/{chunk-M6HSLBWY.js → chunk-RVK72CZX.js} +6507 -7514
  78. package/dist/{chunk-FLKWCIZH.js → chunk-S2HEXIWI.js} +5 -5
  79. package/dist/{chunk-UZ3TUTRV.js → chunk-S67KYG37.js} +1 -1
  80. package/dist/{chunk-VCO24T4L.js → chunk-SD4I7ZO4.js} +14 -14
  81. package/dist/{chunk-Z527N6YQ.js → chunk-SKAMFC7A.js} +12 -12
  82. package/dist/{chunk-MQLUAEOC.js → chunk-SRYEJNES.js} +2 -2
  83. package/dist/{chunk-BRGDSBJE.js → chunk-SYE6T2EB.js} +12 -12
  84. package/dist/{chunk-GXW6K4YJ.js → chunk-T57LYLZT.js} +1 -1
  85. package/dist/{chunk-ZXYDQ3TZ.js → chunk-T7FEBKXN.js} +48 -15
  86. package/dist/{chunk-3EFKIGZN.js → chunk-TIQ2PKDW.js} +3 -3
  87. package/dist/{chunk-QILIYSJF.js → chunk-TLX72JTG.js} +1 -1
  88. package/dist/{chunk-TYM6OUIT.js → chunk-TOY6QKOH.js} +12 -12
  89. package/dist/{chunk-IZSHAIJ7.js → chunk-TRETHTQO.js} +12 -12
  90. package/dist/{chunk-3VPVG5SU.js → chunk-U44DJXQP.js} +10 -6
  91. package/dist/{chunk-PNQDPDRC.js → chunk-UA7ZXYDR.js} +1 -1
  92. package/dist/{chunk-OXXKQCSV.js → chunk-UBOQLVT2.js} +4 -4
  93. package/dist/{chunk-QGQ5CCCG.js → chunk-UMS2MFPZ.js} +2 -2
  94. package/dist/{chunk-QHJ6DQ2R.js → chunk-US7ECLD7.js} +55 -13
  95. package/dist/{chunk-AB3YYG6N.js → chunk-UZ2XCLYS.js} +8 -8
  96. package/dist/chunk-V35JG4JB.js +2667 -0
  97. package/dist/{chunk-N3X6Q4YT.js → chunk-VOOAPROD.js} +3 -3
  98. package/dist/{chunk-SAEFPSB6.js → chunk-W66O5E25.js} +21 -11
  99. package/dist/{chunk-KBG47GHP.js → chunk-WMYE35PX.js} +15 -11
  100. package/dist/{chunk-5NGR67U5.js → chunk-XBJVAEEO.js} +32 -5
  101. package/dist/{chunk-YYOIQFRS.js → chunk-XHJBS436.js} +122 -44
  102. package/dist/{chunk-GBPP5P7X.js → chunk-XTBDCR27.js} +4 -4
  103. package/dist/{chunk-4UNXBNE2.js → chunk-XTFDJI3T.js} +11 -11
  104. package/dist/{chunk-UXGETRP4.js → chunk-XYURS6EN.js} +13 -12
  105. package/dist/{chunk-RPLINGVB.js → chunk-Y6GZHWSJ.js} +6 -6
  106. package/dist/{chunk-2VUQMUMT.js → chunk-YCWYO6TQ.js} +41 -38
  107. package/dist/{chunk-77M37OKR.js → chunk-YH5MVY2U.js} +7 -7
  108. package/dist/{chunk-AOHZU2BM.js → chunk-YNE3RP52.js} +1 -1
  109. package/dist/{chunk-YSZNHOBY.js → chunk-ZHH37KZL.js} +12 -12
  110. package/dist/{chunk-ZGDGW2V3.js → chunk-ZIVJBOO3.js} +2 -2
  111. package/dist/{chunk-RXCOMYPY.js → chunk-ZVUL7QYY.js} +29 -28
  112. package/dist/index.js +11052 -5852
  113. package/dist/mcp/analyze-cli.js +10 -9
  114. package/dist/mcp/cli.js +41 -38
  115. package/dist/mcp/server.js +1120 -474
  116. package/dist/mcp/worker.js +61 -58
  117. package/dist/probes/installer.js +19 -19
  118. package/dist/workers/project-snapshot-persist-worker.js +28 -9
  119. package/dist/workers/review-graph-persist-worker.js +1 -1
  120. package/docs/configuration.md +11 -0
  121. package/docs/engineering-principles.md +32 -0
  122. package/docs/lsp-capability-matrix.md +1 -1
  123. package/docs/lsp-idle-eviction.md +15 -26
  124. package/docs/pi-lens-fixer.md +117 -13
  125. package/docs/pi-lens-merge-policy.md +97 -7
  126. package/docs/pi-lens-monitor.md +8 -2
  127. package/docs/pi-lens-retro.md +4 -1
  128. package/docs/pi-lens-reviewer.md +112 -4
  129. package/docs/pi-lens-subagent.md +22 -14
  130. package/docs/release-qa-baseline.md +3 -1
  131. package/docs/servercapabilities.md +9 -19
  132. package/package.json +7 -5
  133. package/scripts/download-grammars.js +40 -7
  134. package/scripts/lib/latency-log-phases.mjs +95 -0
  135. package/scripts/rpc-load-check.mjs +570 -81
  136. package/dist/chunk-3YN6V5TV.js +0 -85
  137. package/dist/chunk-466Z26MG.js +0 -30
  138. package/dist/chunk-EC47DWFA.js +0 -56
  139. package/dist/chunk-KDERNTCQ.js +0 -28
  140. package/dist/chunk-KSX3TXSZ.js +0 -69
  141. package/dist/chunk-LOP6GRPH.js +0 -31
  142. package/dist/chunk-UOPVJQ4N.js +0 -103
  143. /package/dist/{chunk-FC4RP4I2.js → chunk-6HWVI6MI.js} +0 -0
@@ -1,5 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
3
+ import {
4
+ GitleaksClient,
5
+ classifyAndFilterFindings
6
+ } from "../chunk-24AKHFGP.js";
3
7
  import {
4
8
  DEAD_CODE_DELTA_MAX_SHOWN,
5
9
  deadCodeIssueCount,
@@ -7,18 +11,18 @@ import {
7
11
  deadCodeIssues,
8
12
  formatDeadCodeDelta,
9
13
  stableFindingKey
10
- } from "../chunk-OCAGUNBC.js";
14
+ } from "../chunk-RKKOYPRA.js";
11
15
  import {
12
16
  clearFormatterCache
13
- } from "../chunk-3W7IEVVL.js";
17
+ } from "../chunk-GSM5UU7A.js";
14
18
  import {
15
19
  findNearestProjectRoot,
16
20
  isStartupScanVerdictFresh,
17
21
  resolveStartupScanContext
18
- } from "../chunk-3VPVG5SU.js";
22
+ } from "../chunk-U44DJXQP.js";
19
23
  import {
20
24
  loadCallGraph
21
- } from "../chunk-WOA4LN2R.js";
25
+ } from "../chunk-7D6L4MTX.js";
22
26
  import {
23
27
  isHardFailureSummary
24
28
  } from "../chunk-7U2IDNXN.js";
@@ -27,15 +31,11 @@ import {
27
31
  formatRunDurationMs,
28
32
  isExcludedTestTarget,
29
33
  isRunnerErrorResult
30
- } from "../chunk-VCO24T4L.js";
34
+ } from "../chunk-SD4I7ZO4.js";
31
35
  import {
32
36
  GovulncheckClient
33
- } from "../chunk-SQ4TVJBR.js";
34
- import {
35
- GitleaksClient,
36
- classifyAndFilterFindings
37
- } from "../chunk-SS36ZX4J.js";
38
- import "../chunk-ZGDGW2V3.js";
37
+ } from "../chunk-IUE7SSKV.js";
38
+ import "../chunk-ZIVJBOO3.js";
39
39
  import {
40
40
  resetManagedToolRefreshSession
41
41
  } from "../chunk-UXVNL3D7.js";
@@ -53,7 +53,7 @@ import {
53
53
  peekTurnEndFindings,
54
54
  snapshotAdvisoryProvenance,
55
55
  validateAdvisoryProvenance
56
- } from "../chunk-KBG47GHP.js";
56
+ } from "../chunk-WMYE35PX.js";
57
57
  import {
58
58
  CacheManager,
59
59
  DELTA_UNUSED_PROMOTION_NOTE,
@@ -64,21 +64,27 @@ import {
64
64
  applyFindingPolicy,
65
65
  applyLspFindingPolicy,
66
66
  applyPushedFindingPolicy,
67
+ applyPushedFindingPolicyByFile,
67
68
  applyRulePolicy,
68
69
  canHandle,
69
70
  cascadeCarrySuffix,
70
71
  cascadeSettleWaitMs,
72
+ cascadeStillPresentSince,
73
+ composeCascadeSection,
71
74
  convertLspDiagnostics,
72
75
  createDispatchContext,
73
76
  createMcpHost,
74
77
  demoteInferredProjectDiagnostics,
75
78
  demoteInferredProjectSweepResults,
79
+ dispositionSuppressedLine,
76
80
  drainPendingRunnerFindings,
77
81
  dropStaleRunnerFindings,
78
82
  evaluateAstGrepRules,
79
83
  evaluateRules,
80
84
  filterFindingsByDisposition,
85
+ forgetCascadePendingNeighbor,
81
86
  formatCacheAgeLabel,
87
+ formatCascadeNeighborDiagnostics,
82
88
  formatDiagnostics,
83
89
  getAvailableRunners,
84
90
  getDiagnosticTracker,
@@ -86,75 +92,85 @@ import {
86
92
  getLatencyReports,
87
93
  getToolPlan,
88
94
  inlineBlockerIdentities,
95
+ laneCopyStandsFor,
96
+ listCascadePendingNeighbors,
89
97
  loadProjectRulePolicyMap,
90
98
  loadSg,
91
99
  logCascade,
100
+ markCascadeUnansweredPrimariesDelivered,
92
101
  markUnreconciledFindings,
93
102
  normalizeRuleId,
103
+ owedByContentOnly,
104
+ peekOutstandingCascadeTouches,
94
105
  pendingRunnerFindingsSize,
106
+ readFileContent,
107
+ recordDeliveredCascadeNeighbor,
108
+ renderCascadeResult,
95
109
  requeueRunnerFindings,
96
110
  resetCascadeTierSessionState,
97
111
  resetDispatchBaselines,
98
112
  resetObservedRunnerLatency,
99
113
  resetPendingRunnerFindings,
100
114
  resetPsScriptAnalyzerAvailability,
101
- runProviders
102
- } from "../chunk-H6MF6EVN.js";
115
+ runProviders,
116
+ stampLineHashes
117
+ } from "../chunk-CT4MN5IU.js";
103
118
  import {
104
119
  resetLazyInstallAttempts
105
- } from "../chunk-OXXKQCSV.js";
120
+ } from "../chunk-UBOQLVT2.js";
106
121
  import {
107
122
  isTestRoleCollateral
108
- } from "../chunk-EJIUXPBX.js";
123
+ } from "../chunk-5OFV52AM.js";
109
124
  import {
110
125
  findingsResult,
111
- hasUsableResult
112
- } from "../chunk-2CD5RS6M.js";
113
- import "../chunk-FC4RP4I2.js";
114
- import "../chunk-UZ3TUTRV.js";
126
+ hasUsableResult,
127
+ reachedNoVerdict
128
+ } from "../chunk-RKBXZN3A.js";
129
+ import "../chunk-6HWVI6MI.js";
130
+ import "../chunk-S67KYG37.js";
131
+ import "../chunk-KN35K52N.js";
115
132
  import {
116
133
  resetGoAvailability
117
- } from "../chunk-XMSXXAHW.js";
134
+ } from "../chunk-ROYSX2S5.js";
118
135
  import {
119
136
  TrivyClient
120
- } from "../chunk-77M37OKR.js";
121
- import "../chunk-CSHFH5IN.js";
122
- import "../chunk-VCHXCQFG.js";
123
- import "../chunk-3EFKIGZN.js";
137
+ } from "../chunk-YH5MVY2U.js";
138
+ import {
139
+ scannerReportParentDir
140
+ } from "../chunk-ED36RF6Y.js";
141
+ import "../chunk-BWD5EPKO.js";
142
+ import "../chunk-TIQ2PKDW.js";
124
143
  import {
125
144
  resetRustAvailability
126
- } from "../chunk-MRXKYPCS.js";
127
- import "../chunk-7DYBLXYA.js";
128
- import "../chunk-AB3YYG6N.js";
145
+ } from "../chunk-EBC36LNN.js";
146
+ import "../chunk-HYGZEMX6.js";
147
+ import "../chunk-UZ2XCLYS.js";
129
148
  import {
130
149
  getSgCommand,
131
150
  resetDispatchAvailabilityState,
132
151
  resolveManagedToolClient
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";
152
+ } from "../chunk-ZHH37KZL.js";
153
+ import "../chunk-Y6GZHWSJ.js";
154
+ import "../chunk-IYNJ6YPZ.js";
155
+ import "../chunk-EUXZEFTV.js";
156
+ import "../chunk-T57LYLZT.js";
139
157
  import {
140
158
  SYMBOL_KIND_NAMES,
141
159
  captureReviewGraphStructuralIr,
142
160
  formatReviewGraphRevisionDriftNote,
143
161
  publishReviewGraphFileIr,
144
162
  reviewGraphIrContentHash
145
- } from "../chunk-YYOIQFRS.js";
163
+ } from "../chunk-XHJBS436.js";
146
164
  import {
147
165
  buildSymbolId
148
166
  } from "../chunk-3MED75HT.js";
149
167
  import {
150
- DEPENDENCY_DRIFT_MAX_DELIVERIES,
151
168
  MAX_LATE_AUX_REARMS,
152
169
  PAST_EOF_STALE_MARKER,
153
- RUNTIME_CONFIG,
170
+ PRIMARY_UNAVAILABLE_CAUSE,
154
171
  ReadGuard,
155
172
  STALE_LINE_MARKER,
156
173
  Text,
157
- TreeSitterSymbolExtractor,
158
174
  WriteOrderingGuard,
159
175
  anchorsForDiagnostic,
160
176
  applyAuxiliarySuppressions,
@@ -165,10 +181,12 @@ import {
165
181
  armDeferredLspWork,
166
182
  attemptTsserverSyncDiagnostics,
167
183
  beginScope,
168
- buildScopeKey,
184
+ buildProbeScopeKey,
169
185
  canRearmPendingAuxiliary,
170
186
  classifyDiagnosticTier,
171
187
  clearAllSessions,
188
+ combinePrimaryUnavailableCauses,
189
+ configDisabledAuxiliaryLspServerIds,
172
190
  countRetainedSuppressedRows,
173
191
  createFileTime,
174
192
  createLazyImport,
@@ -178,7 +196,6 @@ import {
178
196
  drainPendingAuxCapEvictedCount,
179
197
  drainPendingAuxiliaryCoverage,
180
198
  drainRenderedDependencyDriftFilePaths,
181
- exceedsLspSyncLimits,
182
199
  findAuxiliaryProfileForSource,
183
200
  getActiveSessionId,
184
201
  getDisposition,
@@ -186,11 +203,14 @@ import {
186
203
  getLSPService,
187
204
  getLspBudgetIdleTimeoutMs,
188
205
  getMutationBridge,
206
+ getSuppressedDiagnostics,
189
207
  getWidgetBlockingFilesForSweep,
208
+ goPackageFilesToAnnounce,
190
209
  groupFilesByPrimaryServer,
191
210
  hasAnyDispositionMarks,
192
211
  hasStrictDispositionMarks,
193
212
  hashDiagnosticContent,
213
+ importTargetsToAnnounce,
194
214
  incrementWidgetDependencyDriftDelivery,
195
215
  isAuxBacklogPublished,
196
216
  isBlocking,
@@ -202,6 +222,7 @@ import {
202
222
  newLspMutationCorrelationId,
203
223
  nextOrderTurn,
204
224
  pendingAuxiliaryCoverageSize,
225
+ primaryUnavailableCause,
205
226
  rearmPendingAuxiliaryCoverage,
206
227
  reconcileCorrelatedScanDiagnostics,
207
228
  reconcileScanDiagnostics,
@@ -218,7 +239,6 @@ import {
218
239
  resetOpaqueMutationState,
219
240
  resetPendingAuxiliaryCoverage,
220
241
  resetWorkspaceDiagnosticsCacheSession,
221
- resolveImportToFiles,
222
242
  resolveLiveBusEmitter,
223
243
  resyncDocumentOnPastEof,
224
244
  retagAuxiliaryDiagnostics,
@@ -229,22 +249,23 @@ import {
229
249
  setSessionLanguages,
230
250
  shouldShortenLspIdleTimeout,
231
251
  summarizeWorkspaceEdit,
232
- sweepInlineBlockerFreshness,
233
- symbolExtractionGrammar,
234
252
  touchCompletedConfirmationPolicy,
235
253
  touchCoverageGap,
236
254
  tryWarmAttachedDiagnostics,
237
255
  widgetDiagnosticUri,
238
256
  workspaceEditTextPaths,
239
257
  writeOrderToken
240
- } from "../chunk-M6HSLBWY.js";
258
+ } from "../chunk-RVK72CZX.js";
241
259
  import {
260
+ ANY_POSITION,
261
+ foldedFindingIdentity,
242
262
  hashText,
243
263
  normalizeMessage,
244
264
  primePersistedMutationAttribution,
245
265
  resetMutationAttribution,
266
+ retainRecordedFindings,
246
267
  stableFindingId
247
- } from "../chunk-IBGIOBEE.js";
268
+ } from "../chunk-IWDLU6K3.js";
248
269
  import {
249
270
  mapWithConcurrency
250
271
  } from "../chunk-XIMYZ3JA.js";
@@ -253,6 +274,7 @@ import {
253
274
  classifyCascadeWaitTier,
254
275
  customServerSpecsOf,
255
276
  describeRootFallback,
277
+ disabledPrimaries,
256
278
  explainServersForFile,
257
279
  getAstGrepRuleFingerprint,
258
280
  getServersForFileWithConfig,
@@ -267,17 +289,17 @@ import {
267
289
  resetLSPCaseSensitivityState,
268
290
  resolveLspCwdForFile,
269
291
  shouldInitializeSessionRoot
270
- } from "../chunk-EU63SDCG.js";
271
- import "../chunk-VWCXW2E7.js";
292
+ } from "../chunk-3DA2JDOL.js";
293
+ import "../chunk-HWCCCQNX.js";
272
294
  import {
273
295
  emitBounded,
274
296
  hasAmbientPythonEnvironment,
275
297
  resetBoundedTelemetry
276
- } from "../chunk-FCSOVF55.js";
298
+ } from "../chunk-CUHSHSB6.js";
277
299
  import {
278
300
  resolveGitCheckout,
279
301
  resolveLinkedWorktreeOwner
280
- } from "../chunk-PNQDPDRC.js";
302
+ } from "../chunk-UA7ZXYDR.js";
281
303
  import {
282
304
  PROJECT_SNAPSHOT_VERSION,
283
305
  getProjectSnapshotLegacyPath,
@@ -292,8 +314,25 @@ import {
292
314
  readJsonCache,
293
315
  readProjectSnapshotMeta,
294
316
  saveRuntimeProjectSnapshot
295
- } from "../chunk-ZXYDQ3TZ.js";
317
+ } from "../chunk-T7FEBKXN.js";
318
+ import {
319
+ SWEEP_IDLE_SAFETY_MARGIN_MS,
320
+ getFullScanWallClockMs,
321
+ isWorkspaceSweepActive,
322
+ runWhenWorkspaceSweepIdle
323
+ } from "../chunk-SRYEJNES.js";
296
324
  import "../chunk-5W3R6LFT.js";
325
+ import {
326
+ DEPENDENCY_DRIFT_MAX_DELIVERIES,
327
+ RUNTIME_CONFIG,
328
+ TreeSitterSymbolExtractor,
329
+ exceedsLspSyncLimits,
330
+ resolveImportToFiles,
331
+ sweepInlineBlockerFreshness,
332
+ symbolExtractionGrammar
333
+ } from "../chunk-V35JG4JB.js";
334
+ import "../chunk-XTBDCR27.js";
335
+ import "../chunk-MPNJLASA.js";
297
336
  import {
298
337
  EXT_TO_LANG,
299
338
  classifyTreeSitterWasmError,
@@ -306,7 +345,7 @@ import {
306
345
  queryLoader,
307
346
  reportBundledResourceDirHealth,
308
347
  resolveTreeSitterLanguage
309
- } from "../chunk-QHJ6DQ2R.js";
348
+ } from "../chunk-US7ECLD7.js";
310
349
  import "../chunk-Z54ZSOBZ.js";
311
350
  import {
312
351
  SCAN_LANGUAGE_PRIORITY,
@@ -314,35 +353,27 @@ import {
314
353
  extensionsForLanguageToken,
315
354
  getUserRuleRoot,
316
355
  resolveLanguage
317
- } from "../chunk-724UL2TJ.js";
356
+ } from "../chunk-GXFV6MYU.js";
318
357
  import {
319
358
  dump,
320
359
  resolvePackagePath
321
- } from "../chunk-QOZXJ4NA.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";
360
+ } from "../chunk-FM4RDMKT.js";
330
361
  import {
331
362
  HOOK_WALL_BUDGET_MS,
332
363
  bounded,
333
364
  combineAbortSignals,
334
365
  withDeadline
335
- } from "../chunk-52FUZI6K.js";
366
+ } from "../chunk-RMLC67LF.js";
336
367
  import {
337
368
  centralityFromReverseDeps,
338
369
  getWordIndexBuildStatus,
339
370
  logWordIndex,
340
371
  searchWordIndex,
341
372
  triggerBackgroundWordIndexBuild
342
- } from "../chunk-SAEFPSB6.js";
373
+ } from "../chunk-W66O5E25.js";
343
374
  import {
344
375
  getProjectDiagnosticsScannerMaxFiles
345
- } from "../chunk-GAEGEPOC.js";
376
+ } from "../chunk-7HXE67BN.js";
346
377
  import {
347
378
  PathKeyedMap
348
379
  } from "../chunk-Z3CCVDUB.js";
@@ -350,32 +381,37 @@ import {
350
381
  WARM_TURN_END_SCHEMA_VERSION,
351
382
  createWarmIpcLineReader,
352
383
  createWarmIpcRequestQueue,
353
- freshnessFromMtime,
354
384
  ipcPathForCwd,
355
385
  readTurnEndStatus,
386
+ unlinkStaleUnixSocket
387
+ } from "../chunk-KAPJINKN.js";
388
+ import {
389
+ freshnessFromMtime,
390
+ hashContentBytes,
356
391
  resolveRunnerPath,
357
392
  toRunnerDisplayPath
358
- } from "../chunk-L3BLXZG7.js";
393
+ } from "../chunk-AGSS7277.js";
359
394
  import "../chunk-2BEMIWEW.js";
395
+ import "../chunk-IJYGIDF4.js";
360
396
  import {
361
397
  commitDurableStore,
362
398
  resetResolvedPathCache,
363
399
  resetZizmorTokenAvailability
364
- } from "../chunk-ACXEQZOU.js";
400
+ } from "../chunk-GAQ7OQG6.js";
365
401
  import {
366
402
  resolveToolCwd
367
- } from "../chunk-Q7DUK4LX.js";
403
+ } from "../chunk-DEL2U6CM.js";
368
404
  import {
369
405
  _resetPackageManagerCache,
370
406
  findGlobalBinary,
371
407
  pmBinary,
372
408
  resolveNodePackageManager,
373
409
  runScriptArgs
374
- } from "../chunk-AW7R2GWG.js";
410
+ } from "../chunk-5VSF4UUQ.js";
375
411
  import {
376
412
  getIsolatedNpxSpawnOptions,
377
413
  probeToolAsync
378
- } from "../chunk-RWWYJ623.js";
414
+ } from "../chunk-3KAZU3VR.js";
379
415
  import {
380
416
  classifyProbeFailure,
381
417
  createAvailabilityLatch,
@@ -383,20 +419,20 @@ import {
383
419
  logAvailabilityDecision,
384
420
  resetInstallRetryLatches,
385
421
  startHostStallSampler
386
- } from "../chunk-VRZP76YM.js";
422
+ } from "../chunk-P63GXC37.js";
387
423
  import {
388
424
  canRunStartupHeavyScans,
389
425
  detectProjectLanguageProfile,
390
426
  getDefaultStartupTools
391
- } from "../chunk-QOBUHWHX.js";
392
- import "../chunk-QILIYSJF.js";
427
+ } from "../chunk-HRNHZA3N.js";
428
+ import "../chunk-TLX72JTG.js";
393
429
  import {
394
430
  collectSourceFilesWithBudgetAsync,
395
431
  getSlowFsVerdict,
396
432
  isSlowFs,
397
433
  slowFsDegradationNotice
398
- } from "../chunk-6C7JWPCP.js";
399
- import "../chunk-5NGR67U5.js";
434
+ } from "../chunk-DGUKIYSI.js";
435
+ import "../chunk-XBJVAEEO.js";
400
436
  import {
401
437
  displayProjectDataPath,
402
438
  drainProjectDataDirMigrations,
@@ -423,7 +459,7 @@ import {
423
459
  sweepDeadEphemeralDataDirs,
424
460
  updateHeartbeat,
425
461
  withGenerationLockSync
426
- } from "../chunk-C34ZXZ7O.js";
462
+ } from "../chunk-KUA4OJ5Y.js";
427
463
  import {
428
464
  createSingleFlight
429
465
  } from "../chunk-BW5KFIQC.js";
@@ -433,7 +469,7 @@ import {
433
469
  } from "../chunk-QYF5S6P6.js";
434
470
  import {
435
471
  detectFileKind
436
- } from "../chunk-PHDMFAKM.js";
472
+ } from "../chunk-DUO2TU77.js";
437
473
  import {
438
474
  compareKeys,
439
475
  detectFileRole,
@@ -443,18 +479,18 @@ import {
443
479
  provenanceView,
444
480
  resolvePiLensConfig,
445
481
  summarizeConfigResolution
446
- } from "../chunk-2BBLFX75.js";
482
+ } from "../chunk-J6MPGQTS.js";
447
483
  import {
448
484
  LSP_NAMESPACE_KEY,
449
485
  getPiLensGlobalConfigPath,
450
486
  resetWorkspaceTopology
451
- } from "../chunk-XVF2VVXP.js";
487
+ } from "../chunk-5HSRT26B.js";
452
488
  import {
453
489
  getProjectTrustState,
454
490
  isToolInstallAllowedByTrust,
455
491
  projectTrustDenialReason,
456
492
  setProjectTrustState
457
- } from "../chunk-ZANLILYD.js";
493
+ } from "../chunk-M2IFVFWU.js";
458
494
  import {
459
495
  DEFAULT_MAX_OUTPUT_BYTES,
460
496
  createBoundedOutputSink,
@@ -475,17 +511,17 @@ import {
475
511
  resolveLensToolEnabled,
476
512
  resolveLensToolName,
477
513
  toolRegistryEntryForMcp
478
- } from "../chunk-JAU72XNY.js";
514
+ } from "../chunk-J64T55EI.js";
479
515
  import {
480
516
  createSubsystemLogger,
481
517
  flushExtensionLog,
482
518
  logExtension
483
- } from "../chunk-XTMJ7XDT.js";
519
+ } from "../chunk-POMLXPMY.js";
484
520
  import "../chunk-JMYYOTQG.js";
485
521
  import {
486
522
  claimPhaseOncePerSession,
487
523
  logLatency
488
- } from "../chunk-QQNQ7SJW.js";
524
+ } from "../chunk-QBFBRV2B.js";
489
525
  import {
490
526
  beginTurnContext,
491
527
  createNdjsonLogger,
@@ -494,11 +530,11 @@ import {
494
530
  isTestMode,
495
531
  runLogCleanup,
496
532
  setTurnContextSession
497
- } from "../chunk-AOHZU2BM.js";
533
+ } from "../chunk-YNE3RP52.js";
498
534
  import "../chunk-LJDODBBZ.js";
499
535
  import {
500
536
  FactStore
501
- } from "../chunk-CHOW74WE.js";
537
+ } from "../chunk-CYRKQRPM.js";
502
538
  import {
503
539
  BoundedFifoMap,
504
540
  BoundedLruCache
@@ -516,13 +552,13 @@ import {
516
552
  resolveHostToolPath,
517
553
  toProjectRelativePath,
518
554
  uriToPath
519
- } from "../chunk-N2H3ZPZY.js";
555
+ } from "../chunk-3RKP3FMS.js";
520
556
  import "../chunk-NXL4FFQQ.js";
521
557
 
522
558
  // dist/mcp/server.js
523
559
  import * as fs25 from "node:fs";
524
560
  import * as net from "node:net";
525
- import * as path39 from "node:path";
561
+ import * as path40 from "node:path";
526
562
  import { fileURLToPath as fileURLToPath3 } from "node:url";
527
563
 
528
564
  // dist/tools/render-compact.js
@@ -787,7 +823,7 @@ function compactRenderResult(summarize) {
787
823
 
788
824
  // dist/clients/ast-grep-client.js
789
825
  import * as fs4 from "node:fs";
790
- import * as os2 from "node:os";
826
+ import * as os from "node:os";
791
827
  import * as path4 from "node:path";
792
828
 
793
829
  // dist/clients/ast-grep-rule-manager.js
@@ -928,7 +964,6 @@ var AstGrepRuleManager = class {
928
964
 
929
965
  // dist/clients/sg-runner.js
930
966
  import * as fs3 from "node:fs";
931
- import * as os from "node:os";
932
967
  import * as path3 from "node:path";
933
968
  function buildBashRunArgs(cmd, allArgs) {
934
969
  return ["-c", '"$0" "$@"', cmd, ...allArgs];
@@ -1357,6 +1392,7 @@ ${result.stderr}`)) {
1357
1392
  case "cwd-unresolvable":
1358
1393
  case "permission-denied":
1359
1394
  case "spawn-failed":
1395
+ case "trust-refused":
1360
1396
  case void 0:
1361
1397
  return result.error ? "cli-failure" : void 0;
1362
1398
  }
@@ -1474,7 +1510,7 @@ Original error: ${stderr}`;
1474
1510
  }
1475
1511
  // --- Shared helpers for temp-dir rule scans ---
1476
1512
  prepareTempScan(ruleId, ruleYaml) {
1477
- const sessionDir = fs3.mkdtempSync(path3.join(os.tmpdir(), `pi-lens-temp-${ruleId}-`));
1513
+ const sessionDir = fs3.mkdtempSync(path3.join(scannerReportParentDir(), `pi-lens-temp-${ruleId}-`));
1478
1514
  try {
1479
1515
  const rulesSubdir = path3.join(sessionDir, "rules");
1480
1516
  const configFile = path3.join(sessionDir, ".sgconfig.yml");
@@ -1953,7 +1989,7 @@ var AstGrepClient = class {
1953
1989
  * Dump the parsed tree-sitter AST for a snippet using ast-grep CLI.
1954
1990
  */
1955
1991
  async dumpAst(source, lang, options = {}) {
1956
- const tmpDir = fs4.mkdtempSync(path4.join(os2.tmpdir(), "pi-lens-ast-dump-"));
1992
+ const tmpDir = fs4.mkdtempSync(path4.join(os.tmpdir(), "pi-lens-ast-dump-"));
1957
1993
  const tmpFile = path4.join(tmpDir, `snippet.${lang.replace(/[^a-z0-9_-]/gi, "") || "txt"}`);
1958
1994
  try {
1959
1995
  fs4.writeFileSync(tmpFile, source, "utf-8");
@@ -1983,7 +2019,7 @@ var AstGrepClient = class {
1983
2019
  if (shapeError)
1984
2020
  return { valid: false, error: shapeError };
1985
2021
  const snippet = validationSnippetFor(lang);
1986
- const tmpDir = fs4.mkdtempSync(path4.join(os2.tmpdir(), "pi-lens-sg-validate-"));
2022
+ const tmpDir = fs4.mkdtempSync(path4.join(os.tmpdir(), "pi-lens-sg-validate-"));
1987
2023
  const tmpFile = path4.join(tmpDir, `snippet.${snippet.ext}`);
1988
2024
  try {
1989
2025
  fs4.writeFileSync(tmpFile, snippet.source, "utf-8");
@@ -2030,7 +2066,7 @@ var AstGrepClient = class {
2030
2066
  return { valid: false, error: shapeError };
2031
2067
  const language = extractRuleLanguage(ruleYaml) ?? "typescript";
2032
2068
  const snippet = validationSnippetFor(language);
2033
- const tmpDir = fs4.mkdtempSync(path4.join(os2.tmpdir(), "pi-lens-sg-rule-"));
2069
+ const tmpDir = fs4.mkdtempSync(path4.join(os.tmpdir(), "pi-lens-sg-rule-"));
2034
2070
  try {
2035
2071
  fs4.writeFileSync(path4.join(tmpDir, `snippet.${snippet.ext}`), snippet.source, "utf-8");
2036
2072
  const result = await this.tempScanDetailed(tmpDir, ruleYaml, options);
@@ -2607,6 +2643,7 @@ async function scanFileMajorRules(cwd, files, signal) {
2607
2643
  } finally {
2608
2644
  facts.dropFileFacts(filePath);
2609
2645
  }
2646
+ await new Promise((resolve32) => setImmediate(resolve32));
2610
2647
  }
2611
2648
  if (wasmAborted) {
2612
2649
  astGrep.length = 0;
@@ -2938,29 +2975,29 @@ async function buildBootstrapClients() {
2938
2975
  }
2939
2976
  }
2940
2977
  const [ruffClient, biomeClient, knipClient, todoScanner, jscpdClient, depChecker, testRunnerClient, metricsClient, complexityClient, goClient, govulncheckClient, gitleaksClient, trivyClient, opengrepClient, rustClient, agentBehaviorClient2, deadCodeClients] = await Promise.all([
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()),
2978
+ load("ruff", async () => new (await import("../chunk-OHKJ7PQF.js")).RuffClient()),
2979
+ load("biome", async () => new (await import("../chunk-N5FLEO7W.js")).BiomeClient()),
2980
+ load("knip", async () => new (await import("../chunk-6DLWKFEX.js")).KnipClient()),
2981
+ load("todo", async () => new (await import("../chunk-2FN63LPV.js")).TodoScanner()),
2982
+ load("jscpd", async () => new (await import("../chunk-R3T7CLQ2.js")).JscpdClient()),
2983
+ load("dependency-checker", async () => new (await import("../chunk-LZSEQW6A.js")).DependencyChecker()),
2984
+ load("test-runner", async () => new (await import("../chunk-GL5LUMZE.js")).TestRunnerClient()),
2985
+ load("metrics", async () => new (await import("../chunk-VOOAPROD.js")).MetricsClient()),
2986
+ load("complexity", async () => new (await import("../chunk-AWSCPCN2.js")).ComplexityClient()),
2950
2987
  // The process-wide singleton, NOT a fresh instance (#2455 fix round 4,
2951
2988
  // F2). `handleSessionStart` reads this object for its "Active tools"
2952
2989
  // line while `resetGoAvailability` re-arms the singleton's latch; a
2953
2990
  // second instance here meant the reset never reached the surface a
2954
2991
  // user actually sees.
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()),
2992
+ load("go", async () => (await import("../chunk-SKAMFC7A.js")).goClient),
2993
+ load("govulncheck", async () => new (await import("../chunk-3PTHXJXR.js")).GovulncheckClient()),
2994
+ load("gitleaks", async () => new (await import("../chunk-MBKQHGA7.js")).GitleaksClient()),
2995
+ load("trivy", async () => new (await import("../chunk-JW3YWBVB.js")).TrivyClient()),
2996
+ load("opengrep", async () => new (await import("../chunk-5IUSNGOC.js")).OpengrepClient()),
2960
2997
  // The process-wide singleton, for the same reason as "go" above.
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())
2998
+ load("rust", async () => (await import("../chunk-SYE6T2EB.js")).rustClient),
2999
+ load("agent-behavior", async () => (await import("../chunk-R7YOCSNV.js")).agentBehaviorClient),
3000
+ loadList("dead-code", async () => (await import("../chunk-5WRPDP2A.js")).getDeadCodeClients())
2964
3001
  ]);
2965
3002
  if (failures.length > 0)
2966
3003
  await logBootstrapFailures(failures);
@@ -3047,7 +3084,7 @@ function resetAnalyzerBootstrapSessionState() {
3047
3084
  }
3048
3085
 
3049
3086
  // dist/clients/formatters-lazy.js
3050
- var lazyFormatters = createLazyImport(() => import("../chunk-RXCOMYPY.js"));
3087
+ var lazyFormatters = createLazyImport(() => import("../chunk-ZVUL7QYY.js"));
3051
3088
  function warmFormatters() {
3052
3089
  return lazyFormatters.get();
3053
3090
  }
@@ -3201,13 +3238,18 @@ function readChangeLogMaxSeq(logPath) {
3201
3238
  }
3202
3239
  var CHANGE_LOG_LOCK = { staleMs: 5e3, waitMs: 500 };
3203
3240
  function appendProjectChangeAllocated(cwd, build) {
3241
+ appendProjectChangesAllocated(cwd, (logMaxSeq) => [build(logMaxSeq)]);
3242
+ }
3243
+ function appendProjectChangesAllocated(cwd, buildAll) {
3204
3244
  const logPath = getProjectChangeLogPath(cwd);
3205
3245
  fs7.mkdirSync(path7.dirname(logPath), { recursive: true });
3206
3246
  readChangeLogMaxSeq(logPath);
3207
3247
  const append = (locked) => {
3208
- const entry = build(readChangeLogMaxSeq(logPath));
3209
- const line = locked ? entry : { ...entry, unlocked: true };
3210
- fs7.appendFileSync(logPath, `${JSON.stringify(line)}
3248
+ const entries = buildAll(readChangeLogMaxSeq(logPath));
3249
+ if (entries.length === 0)
3250
+ return;
3251
+ const payload = entries.map((entry) => JSON.stringify(locked ? entry : { ...entry, unlocked: true })).join("\n");
3252
+ fs7.appendFileSync(logPath, `${payload}
3211
3253
  `, "utf-8");
3212
3254
  };
3213
3255
  const result = withGenerationLockSync(`${logPath}.locks`, CHANGE_LOG_LOCK, () => append(true));
@@ -3228,7 +3270,11 @@ function inlineBlockerSources(blockers) {
3228
3270
  }
3229
3271
  function inlineBlockerLines(blockers, filePath) {
3230
3272
  const own = normalizeEphemeralMapKey(filePath);
3231
- return blockers.filter((d) => normalizeEphemeralMapKey(d.filePath) === own).map((d) => d.line).filter((line) => typeof line === "number");
3273
+ return blockers.filter(
3274
+ // #4509 (N3): a stale entry's line is from older bytes, so it cites
3275
+ // none: the past-EOF sweep would retire the whole record on it.
3276
+ (d) => !d.positionStale && normalizeEphemeralMapKey(d.filePath) === own
3277
+ ).map((d) => d.line).filter((line) => typeof line === "number");
3232
3278
  }
3233
3279
 
3234
3280
  // dist/clients/partial-edit-apply.js
@@ -3414,6 +3460,7 @@ function recordViewIncomplete(cwd, cause) {
3414
3460
  });
3415
3461
  }
3416
3462
  var MAX_RESOLVED_BLOCKER_FILES = 10;
3463
+ var MAX_OWED_ANCHORS = 16;
3417
3464
  var TOOL_CALL_ATTRIBUTION_CAPACITY = 256;
3418
3465
  var TOOL_CALL_ATTRIBUTION_TTL_MS = 5 * 6e4;
3419
3466
  var TurnWarningsBySession = class {
@@ -3463,7 +3510,7 @@ var RuntimeCoordinator = class {
3463
3510
  diagnosticsSurfaced: 0,
3464
3511
  coldSnapshotTouches: 0
3465
3512
  };
3466
- _complexityBaselines = /* @__PURE__ */ new Map();
3513
+ _complexityBaselines = /* @__PURE__ */ new Set();
3467
3514
  _fixedThisTurn = new PathKeyedMap(normalizeMapKey);
3468
3515
  _writtenThisTurn = new PathKeyedMap(normalizeMapKey);
3469
3516
  _autofixDemotedThisTurn = new PathKeyedMap(normalizeMapKey);
@@ -3552,6 +3599,26 @@ var RuntimeCoordinator = class {
3552
3599
  _readWidenings = new BoundedLruCache(TOOL_CALL_ATTRIBUTION_CAPACITY);
3553
3600
  _lspReadWarmState = /* @__PURE__ */ new Map();
3554
3601
  _pendingInlineBlockers = new PathKeyedMap(normalizeMapKey);
3602
+ /**
3603
+ * #4484 round 4: per file, what its last applied inline verdict left owed
3604
+ * (`setAnalysisAnchor`), for a file whose record is gone or has no anchor.
3605
+ * The positions live on the blocker record
3606
+ * (`InlineBlockerRecord.analysisAnchor`). Round 5: this keeps each owing
3607
+ * runner's owed line texts (`owedByContentOnly`, at most
3608
+ * `MAX_OWED_LINE_TEXTS` hashes per runner, 16 KB), not only its id, so
3609
+ * the next write owes it only the lines with those texts, never the whole
3610
+ * file for a skip. Replaced by every applied verdict of the file and
3611
+ * cleared per session.
3612
+ */
3613
+ _owedRunners = new PathKeyedMap(normalizeMapKey);
3614
+ /**
3615
+ * #4484 round 5: for a file with no blocker record, its last verdict's
3616
+ * whole anchor (line hashes too), so the next write still remaps owed
3617
+ * positions exactly (verify probe P23). At most `MAX_OWED_ANCHORS` files,
3618
+ * 400 KB each; an evicted file falls back to `_owedRunners`' owed line
3619
+ * texts, a superset. Cleared per session.
3620
+ */
3621
+ _owedAnchors = new PathKeyedMap(normalizeMapKey, MAX_OWED_ANCHORS);
3555
3622
  /**
3556
3623
  * #3507: one per-path order for BOTH verbs on `_pendingInlineBlockers`,
3557
3624
  * the widget store's guard shape (`widget-state.ts` diagnosticsWriteGuard).
@@ -3640,6 +3707,8 @@ var RuntimeCoordinator = class {
3640
3707
  this._readWidenings.clear();
3641
3708
  this._lspReadWarmState.clear();
3642
3709
  this._pendingInlineBlockers.clear();
3710
+ this._owedRunners.clear();
3711
+ this._owedAnchors.clear();
3643
3712
  this._inlineBlockerWriteOrder.clear();
3644
3713
  this._resolvedBlockerFilesThisTurn.clear();
3645
3714
  this._resolvedBlockerFilesDropped = 0;
@@ -3793,20 +3862,79 @@ var RuntimeCoordinator = class {
3793
3862
  }
3794
3863
  }
3795
3864
  const { projectSeq, fileSeq, key } = logged ?? this.bumpFileSeq(args.filePath);
3865
+ this.recordMutationReceipt({ projectSeq, fileSeq, key }, args.source);
3866
+ return { projectSeq, fileSeq };
3867
+ }
3868
+ /**
3869
+ * #4535: the bulk form of {@link recordProjectMutation}, for a disk change
3870
+ * pi-lens did not author that spans many paths (a publisher's recorded
3871
+ * population). It allocates from the SAME sequence, in ONE locked append:
3872
+ * `appendProjectChangesAllocated` takes the change-log lock once and writes
3873
+ * every line under it, so a 400-path HEAD move costs one lock round-trip
3874
+ * inside a hook budget instead of 400. The lock-failure semantics are the
3875
+ * single writer's: when the lock is unavailable every appended line is
3876
+ * tagged `unlocked` and one `change-log-lock-unavailable` degradation is
3877
+ * recorded for the batch. Receipts are appended per path with the same ring
3878
+ * cap and drop counter as a single mutation.
3879
+ *
3880
+ * Returns the batch's last allocated `projectSeq` (the record's seq after
3881
+ * the batch) and each path's `fileSeq` keyed by the bump's normalized key.
3882
+ * Duplicate paths are the caller's to collapse: each entry bumps.
3883
+ */
3884
+ recordProjectMutations(args) {
3885
+ let logged;
3886
+ if (args.cwd !== void 0 && args.paths.length > 0) {
3887
+ try {
3888
+ const cwd = args.cwd;
3889
+ const paths = args.paths;
3890
+ appendProjectChangesAllocated(cwd, (logMaxSeq) => {
3891
+ const ownSeq = this._projectSeq;
3892
+ const bumps2 = paths.map((filePath) => this.bumpFileSeq(filePath, logMaxSeq));
3893
+ logged = bumps2;
3894
+ if (this._viewMissingThrough > 0) {
3895
+ recordViewIncomplete(cwd, { ownSeq, logMaxSeq });
3896
+ }
3897
+ return bumps2.map((bump, index) => ({
3898
+ seq: bump.projectSeq,
3899
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3900
+ sessionId: this.telemetrySessionId,
3901
+ turnIndex: this.turnIndex,
3902
+ source: args.source,
3903
+ filePath: path8.resolve(paths[index]),
3904
+ fileSeq: bump.fileSeq
3905
+ }));
3906
+ });
3907
+ } catch (err) {
3908
+ args.onAppendError?.(err);
3909
+ }
3910
+ }
3911
+ const bumps = logged ?? args.paths.map((filePath) => this.bumpFileSeq(filePath));
3912
+ const fileSeqs = /* @__PURE__ */ new Map();
3913
+ for (const bump of bumps) {
3914
+ this.recordMutationReceipt({ projectSeq: bump.projectSeq, fileSeq: bump.fileSeq, key: bump.key }, args.source);
3915
+ fileSeqs.set(bump.key, bump.fileSeq);
3916
+ }
3917
+ return { projectSeq: this._projectSeq, fileSeqs };
3918
+ }
3919
+ /**
3920
+ * The receipt ring (§ {@link MutationReceipt}): one entry per bump, oldest
3921
+ * evicted at the cap and counted, never silently incomplete. One writer, so
3922
+ * the single and bulk mutation paths cannot drift apart on attribution.
3923
+ */
3924
+ recordMutationReceipt(bump, source) {
3796
3925
  if (this._mutationReceipts.length >= MAX_MUTATION_RECEIPTS) {
3797
3926
  this._mutationReceipts.shift();
3798
3927
  this._droppedMutationReceipts += 1;
3799
3928
  }
3800
3929
  this._mutationReceipts.push({
3801
- seq: projectSeq,
3930
+ seq: bump.projectSeq,
3802
3931
  // Reuse the bump's normalized key (a realpath syscall: ~200us on
3803
3932
  // Windows, ~1.8us on POSIX since #3098) — never re-derive it here.
3804
- filePath: key,
3805
- source: args.source,
3933
+ filePath: bump.key,
3934
+ source,
3806
3935
  turnIndex: this._turnIndex,
3807
3936
  ts: Date.now()
3808
3937
  });
3809
- return { projectSeq, fileSeq };
3810
3938
  }
3811
3939
  /**
3812
3940
  * Attributed mutations whose seq is strictly after `seq` — the receipt-level
@@ -4275,7 +4403,7 @@ var RuntimeCoordinator = class {
4275
4403
  const existing = this._pendingInlineBlockers.get(key);
4276
4404
  if (existing && !existing.diagnostics)
4277
4405
  return 0;
4278
- const identity = (d) => JSON.stringify([d.tool, d.id, d.line, d.message]);
4406
+ const identity = (d) => JSON.stringify([foldedFindingIdentity(d), d.line, d.column]);
4279
4407
  const known = new Set((existing?.diagnostics ?? []).map(identity));
4280
4408
  const added = blocking.flatMap((d) => known.has(identity(d)) ? [] : [d]);
4281
4409
  if (added.length === 0)
@@ -4311,6 +4439,91 @@ var RuntimeCoordinator = class {
4311
4439
  });
4312
4440
  return added.length;
4313
4441
  }
4442
+ /**
4443
+ * #4484 round 2: the file's recorded blocking diagnostics, which the next
4444
+ * write's pipeline carries forward unless a conclusive observation from
4445
+ * their own source shows them gone. Undefined when nothing is recorded or
4446
+ * the record holds text only.
4447
+ */
4448
+ getInlineBlockerDiagnostics(filePath) {
4449
+ return this._pendingInlineBlockers.get(path8.resolve(filePath))?.diagnostics;
4450
+ }
4451
+ /**
4452
+ * #4484 round 3: the anchor the next write's pipeline aligns to: the line
4453
+ * hashes of the bytes the file's blocker record is about, and the lines
4454
+ * still owed. Round 4: it is the record's own anchor. A file without one
4455
+ * (no record, or a record a collect-later settle started) gets no hashes,
4456
+ * so the pipeline cannot align. Round 5: it gets the owed line texts.
4457
+ */
4458
+ getAnalysisAnchor(filePath) {
4459
+ return this._pendingInlineBlockers.get(filePath)?.analysisAnchor ?? this._owedAnchors.get(filePath) ?? this._owedRunners.get(filePath);
4460
+ }
4461
+ /**
4462
+ * #4484 round 3: store the anchor of an inline verdict that just applied
4463
+ * (`recordInlineBlockers` or `clearInlineBlockers` returned applied), so it
4464
+ * always describes the bytes the record is about. Round 4: it goes on the
4465
+ * record, and lives as long as the record; what is owed goes apart
4466
+ * (`_owedRunners`, by content since round 5), so it outlives a record that
4467
+ * a later retire drops. Round 5: a file with no record keeps its whole
4468
+ * anchor too (`_owedAnchors`, bounded).
4469
+ */
4470
+ setAnalysisAnchor(filePath, anchor) {
4471
+ const existing = this._pendingInlineBlockers.get(filePath);
4472
+ if (existing) {
4473
+ const key = path8.resolve(filePath);
4474
+ const { analysisAnchor: _dropped, ...rest } = existing;
4475
+ this._pendingInlineBlockers.set(key, anchor ? { ...rest, analysisAnchor: anchor } : rest);
4476
+ }
4477
+ const owed = owedByContentOnly(anchor);
4478
+ if (owed)
4479
+ this._owedRunners.set(filePath, owed);
4480
+ else
4481
+ this._owedRunners.delete(filePath);
4482
+ if (owed && anchor?.lineHashes && !existing)
4483
+ this._owedAnchors.set(filePath, anchor);
4484
+ else
4485
+ this._owedAnchors.delete(filePath);
4486
+ }
4487
+ /**
4488
+ * #4484 round 2: a settled collect-later run is the observation that can
4489
+ * retire the blockers its runner left pending while it was in flight. Its
4490
+ * runner's recorded blockers that the run no longer observes leave the
4491
+ * record; blockers from other runners, and from a server the run left
4492
+ * unconfirmed, stay. A blocker is still observed only at its own line, in
4493
+ * range or withheld by the run's edit-range gate (#4484 round 3). The run
4494
+ * must be about the recorded bytes: marked after
4495
+ * they were read (`recordedAtMs`), with the file still holding them
4496
+ * (`recordedHash`). An emptied record is deleted and its file is noted
4497
+ * Resolved for turn end, as a clean write's clear does.
4498
+ *
4499
+ * @returns how many recorded blockers the run retired.
4500
+ */
4501
+ retireUnobservedRunnerBlockers(filePath, run) {
4502
+ const key = path8.resolve(filePath);
4503
+ const existing = this._pendingInlineBlockers.get(key);
4504
+ if (!existing?.diagnostics || run.sha256 === void 0 || existing.recordedHash !== run.sha256 || existing.recordedAtMs === void 0 || run.markedAtMs < existing.recordedAtMs)
4505
+ return 0;
4506
+ const unconfirmed = new Set(run.unconfirmedServerIds);
4507
+ const { kept, retired } = retainRecordedFindings(existing.diagnostics, run.observed, {
4508
+ positionOf: (d) => d.positionStale ? ANY_POSITION : d.line,
4509
+ keepUnobserved: (d) => d.runnerId !== run.runnerId || d.serverId !== void 0 && unconfirmed.has(d.serverId)
4510
+ });
4511
+ if (retired.length === 0)
4512
+ return 0;
4513
+ if (kept.length === 0) {
4514
+ this.noteResolvedBlockerFile(existing, void 0, false);
4515
+ this._pendingInlineBlockers.delete(key);
4516
+ return retired.length;
4517
+ }
4518
+ this._pendingInlineBlockers.set(key, {
4519
+ ...existing,
4520
+ summary: formatDiagnostics(kept, "blocking").trim(),
4521
+ diagnostics: kept,
4522
+ sources: inlineBlockerSources(kept),
4523
+ lines: inlineBlockerLines(kept, existing.filePath)
4524
+ });
4525
+ return retired.length;
4526
+ }
4314
4527
  /**
4315
4528
  * #3218 criterion 2: remember one file whose blocker a clean verdict just
4316
4529
  * retired, for the turn-end "Resolved" line. Called from the two retire
@@ -4348,9 +4561,22 @@ var RuntimeCoordinator = class {
4348
4561
  * counted as `blocker-clear-refused` per file key. A record with no
4349
4562
  * `recordedHash` (#2982: the file was unreadable or over the baseline cap at
4350
4563
  * record time) cannot be matched and is cleared as before.
4564
+ *
4565
+ * #4484: a blocker is pending until a fresh observation no longer holds it,
4566
+ * so a run whose own observation still holds `analysis.blocking` blockers
4567
+ * never clears the record, whatever its authorship or its delta said; it
4568
+ * is refused and counted the same way.
4351
4569
  */
4352
4570
  clearInlineBlockers(filePath, writeIndex, orderTurn = this._writeOrderTurn, analysis) {
4353
4571
  const existing = this._pendingInlineBlockers.get(path8.resolve(filePath));
4572
+ if (existing && (analysis?.blocking ?? 0) > 0) {
4573
+ incrementDegradationCount({
4574
+ kind: "blocker-clear-refused",
4575
+ subject: normalizeMapKey(filePath),
4576
+ reason: "the clearing run still observed a blocker on the file; the record stays until an observation holds none (#4484)"
4577
+ });
4578
+ return false;
4579
+ }
4354
4580
  if (existing && analysis && !analysis.authored && analysis.sha256 !== void 0 && existing.recordedHash === analysis.sha256) {
4355
4581
  incrementDegradationCount({
4356
4582
  kind: "blocker-clear-refused",
@@ -5345,7 +5571,8 @@ function boundDeferredTargets(entries, max, isOwn) {
5345
5571
  }
5346
5572
 
5347
5573
  // dist/clients/test-runner-delivery.js
5348
- var pending = /* @__PURE__ */ new Map();
5574
+ var MAX_PENDING_DELIVERIES = 32;
5575
+ var pending = new BoundedFifoMap(MAX_PENDING_DELIVERIES);
5349
5576
  function resetTestRunnerDelivery() {
5350
5577
  pending.clear();
5351
5578
  }
@@ -5604,8 +5831,8 @@ async function igniteDominantLanguageWarm(cwd, runtime, sessionGeneration, dbg)
5604
5831
  if (!runtime.isCurrentSession(sessionGeneration))
5605
5832
  return;
5606
5833
  const lspService = (await loadLspService()).getLSPService();
5607
- const { collectSourceFilesAsync } = await import("../chunk-4W4VLAPJ.js");
5608
- const { CODE_KINDS, detectFileKind: detectFileKind2 } = await import("../chunk-FLKWCIZH.js");
5834
+ const { collectSourceFilesAsync } = await import("../chunk-IQWN5JCP.js");
5835
+ const { CODE_KINDS, detectFileKind: detectFileKind2 } = await import("../chunk-S2HEXIWI.js");
5609
5836
  const files = await collectSourceFilesAsync(cwd, {
5610
5837
  inspectGeneratedHeaders: false
5611
5838
  });
@@ -5671,7 +5898,7 @@ function scheduleManagedToolRefresh(dbg) {
5671
5898
  const timer = setTimeout(() => {
5672
5899
  void (async () => {
5673
5900
  try {
5674
- const refresh = await import("../chunk-5UCAH366.js");
5901
+ const refresh = await import("../chunk-2KXTHF3M.js");
5675
5902
  const outcome = await refresh.runManagedToolRefresh();
5676
5903
  if (outcome.skipped) {
5677
5904
  dbg(`session_start tool-refresh: skipped (${outcome.skipped})`);
@@ -5718,7 +5945,7 @@ function scanOneTodoFile(scanner, filePath, items) {
5718
5945
  async function collectTodoBaselineItems(scanner, analysisRoot, stillCurrent) {
5719
5946
  const items = [];
5720
5947
  try {
5721
- const { getSourceFilesAsync } = await import("../chunk-IZSHAIJ7.js");
5948
+ const { getSourceFilesAsync } = await import("../chunk-TRETHTQO.js");
5722
5949
  const files = await getSourceFilesAsync(analysisRoot, true);
5723
5950
  if (!stillCurrent())
5724
5951
  return items;
@@ -5747,7 +5974,7 @@ async function buildOrRefreshWordIndex(args) {
5747
5974
  const snapshot = loadProjectSnapshot(snapshotRoot);
5748
5975
  const snapshotLoadMs = Date.now() - snapshotLoadStartMs;
5749
5976
  if (snapshot?.wordIndex) {
5750
- const { deserializeWordIndex, refreshWordIndexIncrementally, countWordIndexPostingEntries: countWordIndexPostingEntries2, estimateWordIndexResidentBytes: estimateWordIndexResidentBytes2 } = await import("../chunk-WU567RSC.js");
5977
+ const { deserializeWordIndex, refreshWordIndexIncrementally, countWordIndexPostingEntries: countWordIndexPostingEntries2, estimateWordIndexResidentBytes: estimateWordIndexResidentBytes2, notifyWordIndexPublished: notifyWordIndexPublished2 } = await import("../chunk-ATKJFDQN.js");
5751
5978
  const deserializeStartMs = Date.now();
5752
5979
  const index = deserializeWordIndex(snapshot.wordIndex);
5753
5980
  const deserializeMs = Date.now() - deserializeStartMs;
@@ -5772,6 +5999,7 @@ async function buildOrRefreshWordIndex(args) {
5772
5999
  });
5773
6000
  } else {
5774
6001
  runtime.wordIndex = index;
6002
+ notifyWordIndexPublished2(snapshotRoot, index, "session_start");
5775
6003
  let snapshotSaveSyncMs = 0;
5776
6004
  if (!isProjectSnapshotFresh(snapshot, effectiveSeq, latestSeq.unlockedThrough) || result.refreshed > 0 || result.dropped > 0 || snapshot.wordIndex.truncated !== index.truncated) {
5777
6005
  const serializeSaveEnqueueStartMs = Date.now();
@@ -5819,7 +6047,7 @@ async function buildOrRefreshWordIndex(args) {
5819
6047
  }
5820
6048
  }
5821
6049
  }
5822
- const { buildWordIndexAsync, collectWordIndexDocs, countWordIndexPostingEntries, estimateWordIndexResidentBytes } = await import("../chunk-WU567RSC.js");
6050
+ const { buildWordIndexAsync, collectWordIndexDocs, countWordIndexPostingEntries, estimateWordIndexResidentBytes, notifyWordIndexPublished } = await import("../chunk-ATKJFDQN.js");
5823
6051
  const docs = await collectWordIndexDocs(analysisRoot, () => runtime.isCurrentSession(sessionGeneration), rebuildPreflightFiles);
5824
6052
  if (!runtime.isCurrentSession(sessionGeneration))
5825
6053
  return;
@@ -5836,6 +6064,7 @@ async function buildOrRefreshWordIndex(args) {
5836
6064
  if (!runtime.isCurrentSession(sessionGeneration))
5837
6065
  return;
5838
6066
  runtime.wordIndex = rebuiltIndex;
6067
+ notifyWordIndexPublished(snapshotRoot, rebuiltIndex, "session_start");
5839
6068
  saveRuntimeProjectSnapshot({ cwd: snapshotRoot, runtime, dbg });
5840
6069
  dbg(`session_start word-index: rebuilt (absent/stale, seq=${effectiveSeq}) ${runtime.wordIndex.docCount} files, ${runtime.wordIndex.postings.size} tokens (${Date.now() - startMs}ms)`);
5841
6070
  logWordIndex({
@@ -5858,6 +6087,11 @@ async function buildOrRefreshWordIndex(args) {
5858
6087
  reused: 0
5859
6088
  };
5860
6089
  }
6090
+ var STARTUP_TASK_DEFER_MS = {
6091
+ "call-graph": 5e3,
6092
+ "codebase-model": 5200,
6093
+ "ast-grep-exports": 5400
6094
+ };
5861
6095
  function scheduleStartupScans(deps, runtime, sessionGeneration, analysisRoot, snapshotRoot, languageProfile, dbg) {
5862
6096
  void (async () => {
5863
6097
  const resolved = await demandBootstrapDeps(deps, "session-start-scans");
@@ -5879,15 +6113,10 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
5879
6113
  reason: "skipped (disabled by config)"
5880
6114
  });
5881
6115
  }
5882
- const taskDeferMsByName = {
5883
- "call-graph": 5e3,
5884
- "codebase-model": 5200,
5885
- "ast-grep exports": 5400,
5886
- "project index": 5400
5887
- };
5888
6116
  const runTask = (name, task) => {
5889
6117
  const queuedAt = Date.now();
5890
- dbg(`session_start task ${name}: scheduled`);
6118
+ const delay = STARTUP_TASK_DEFER_MS[name] ?? 0;
6119
+ dbg(`session_start task ${name}: scheduled deferMs=${delay}`);
5891
6120
  runtime.markStartupScanInFlight(name, sessionGeneration);
5892
6121
  const completion = new Promise((resolve32) => {
5893
6122
  const fire = () => {
@@ -5904,7 +6133,6 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
5904
6133
  resolve32();
5905
6134
  });
5906
6135
  };
5907
- const delay = taskDeferMsByName[name] ?? 0;
5908
6136
  if (delay > 0) {
5909
6137
  setTimeout(fire, delay).unref?.();
5910
6138
  } else {
@@ -5916,7 +6144,7 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
5916
6144
  const canRunJsTsHeavyScans = canRunStartupHeavyScans(languageProfile, "jsts");
5917
6145
  const scanNames = ["todo", "dead-code"];
5918
6146
  if (canRunJsTsHeavyScans) {
5919
- scanNames.push("knip", "jscpd", "ast-grep exports", "project index");
6147
+ scanNames.push("knip", "jscpd", "ast-grep-exports");
5920
6148
  }
5921
6149
  dbg(`session_start: launching background scans (${scanNames.join(", ")})`);
5922
6150
  void runTask("todo", async () => {
@@ -6184,9 +6412,9 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
6184
6412
  });
6185
6413
  let callGraphIdentity;
6186
6414
  const callGraphTask = runTask("call-graph", async () => {
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");
6415
+ const { FactStore: FactStore2 } = await import("../chunk-UMS2MFPZ.js");
6416
+ const { buildOrUpdateGraph, extractSymbolsAndRefsFromGraph, getReviewGraphCacheIdentity } = await import("../chunk-YCWYO6TQ.js");
6417
+ const { buildCallGraph, saveCallGraph, loadCallGraph: loadCallGraph2 } = await import("../chunk-GMX5OS7B.js");
6190
6418
  if (!runtime.isCurrentSession(sessionGeneration))
6191
6419
  return;
6192
6420
  const startMs = Date.now();
@@ -6227,7 +6455,7 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
6227
6455
  return;
6228
6456
  if (!runtime.callGraph)
6229
6457
  return;
6230
- const { buildCodebaseModel, saveCodebaseModel, DEFAULT_CODEBASE_MODEL_TOKEN_BUDGET } = await import("../chunk-TYM6OUIT.js");
6458
+ const { buildCodebaseModel, saveCodebaseModel, DEFAULT_CODEBASE_MODEL_TOKEN_BUDGET } = await import("../chunk-TOY6QKOH.js");
6231
6459
  if (!callGraphIdentity)
6232
6460
  return;
6233
6461
  const model = buildCodebaseModel(runtime.callGraph, analysisRoot, DEFAULT_CODEBASE_MODEL_TOKEN_BUDGET, callGraphIdentity);
@@ -6259,18 +6487,18 @@ function scheduleStartupScansWithClients(deps, runtime, sessionGeneration, analy
6259
6487
  });
6260
6488
  });
6261
6489
  }
6262
- function scheduleDeferredToolProbes(deps, languageProfile, startupDefaults, startupScansWillRun, dbg) {
6490
+ function scheduleDeferredToolProbes(deps, languageProfile, startupDefaults, dbg) {
6263
6491
  void (async () => {
6264
6492
  const resolved = await demandBootstrapDeps(deps, "session-start-tool-probes");
6265
6493
  if (!resolved) {
6266
6494
  dbg("session_start tools: analyzer bootstrap unavailable \u2014 no probes");
6267
6495
  return;
6268
6496
  }
6269
- scheduleDeferredToolProbesWithClients(resolved, languageProfile, startupDefaults, startupScansWillRun, dbg);
6497
+ scheduleDeferredToolProbesWithClients(resolved, languageProfile, startupDefaults, dbg);
6270
6498
  })();
6271
6499
  }
6272
- function scheduleDeferredToolProbesWithClients(deps, languageProfile, startupDefaults, startupScansWillRun, dbg) {
6273
- const { biomeClient, ruffClient, depChecker } = deps;
6500
+ function scheduleDeferredToolProbesWithClients(deps, languageProfile, startupDefaults, dbg) {
6501
+ const { biomeClient, ruffClient } = deps;
6274
6502
  const defaultTools = new Set(startupDefaults);
6275
6503
  const probes = [];
6276
6504
  if (languageProfile.present.jsts && !defaultTools.has("biome")) {
@@ -6279,9 +6507,6 @@ function scheduleDeferredToolProbesWithClients(deps, languageProfile, startupDef
6279
6507
  if (languageProfile.present.python && !defaultTools.has("ruff")) {
6280
6508
  probes.push(["ruff", () => ruffClient.ensureAvailable()]);
6281
6509
  }
6282
- if (startupScansWillRun) {
6283
- probes.push(["madge", () => depChecker.ensureAvailable()]);
6284
- }
6285
6510
  if (probes.length === 0) {
6286
6511
  dbg("session_start tools: no deferred availability probes needed");
6287
6512
  return;
@@ -6300,6 +6525,34 @@ function scheduleDeferredToolProbesWithClients(deps, languageProfile, startupDef
6300
6525
  dbg(`session_start tools (deferred probes complete, ${Date.now() - warmStart}ms): ${summary}`);
6301
6526
  })();
6302
6527
  }
6528
+ function resetSessionScopedState(cwd, sessionStartMs = Date.now()) {
6529
+ resetWorkspaceTopology();
6530
+ resetOpaqueMutationState();
6531
+ resetObservedMutationNet();
6532
+ resetMutationAttribution();
6533
+ primePersistedMutationAttribution(cwd);
6534
+ resetPendingAuxiliaryCoverage();
6535
+ resetSafeSpawnWindowsCommandCache();
6536
+ resetDispatchAvailabilityState();
6537
+ resetObservedRunnerLatency();
6538
+ resetPendingRunnerFindings();
6539
+ resetSpawnTimeoutCooldowns();
6540
+ clearFormatterCache();
6541
+ resetZizmorTokenAvailability();
6542
+ resetGoAvailability();
6543
+ resetRustAvailability();
6544
+ resetPsScriptAnalyzerAvailability();
6545
+ resetInstallRetryLatches();
6546
+ resetLazyInstallAttempts();
6547
+ resetDirectLspCommandAvailability();
6548
+ resetLSPCaseSensitivityState();
6549
+ resetResolvedPathCache();
6550
+ _resetPackageManagerCache();
6551
+ resetManagedToolRefreshSession();
6552
+ resetSmellsSessionState();
6553
+ resetSituationalToolTelemetry();
6554
+ resetWorkspaceDiagnosticsCacheSession(sessionStartMs);
6555
+ }
6303
6556
  function readSubstituteGuidance(isEnabled) {
6304
6557
  const units = [];
6305
6558
  if (isEnabled("symbol_search")) {
@@ -6411,8 +6664,8 @@ async function handleSessionStart(deps) {
6411
6664
  try {
6412
6665
  warmupDbg("warmup: starting background warmup");
6413
6666
  const scanContextStartedAt = Date.now();
6414
- const startupScanModule = await import("../chunk-ZHFZWTZV.js");
6415
- const languageProfileModule = await import("../chunk-R3PCY3YS.js");
6667
+ const startupScanModule = await import("../chunk-GSLNCYGG.js");
6668
+ const languageProfileModule = await import("../chunk-AMERCH6V.js");
6416
6669
  const warmupSnapshotRoot = resolveSnapshotRoot(warmupCwd);
6417
6670
  const cachedSnapshot = loadProjectSnapshot(warmupSnapshotRoot);
6418
6671
  warmupOwnSnapshotRead = cachedSnapshot ? {
@@ -6546,32 +6799,7 @@ async function handleSessionStart(deps) {
6546
6799
  clearAllSessions();
6547
6800
  runtime.complexityBaselines.clear();
6548
6801
  resetDispatchBaselines2(ctxCwd);
6549
- resetWorkspaceTopology();
6550
- resetOpaqueMutationState();
6551
- resetObservedMutationNet();
6552
- resetMutationAttribution();
6553
- primePersistedMutationAttribution(ctxCwd);
6554
- resetPendingAuxiliaryCoverage();
6555
- resetSafeSpawnWindowsCommandCache();
6556
- resetDispatchAvailabilityState();
6557
- resetObservedRunnerLatency();
6558
- resetPendingRunnerFindings();
6559
- resetSpawnTimeoutCooldowns();
6560
- clearFormatterCache();
6561
- resetZizmorTokenAvailability();
6562
- resetGoAvailability();
6563
- resetRustAvailability();
6564
- resetPsScriptAnalyzerAvailability();
6565
- resetInstallRetryLatches();
6566
- resetLazyInstallAttempts();
6567
- resetDirectLspCommandAvailability();
6568
- resetLSPCaseSensitivityState();
6569
- resetResolvedPathCache();
6570
- _resetPackageManagerCache();
6571
- resetManagedToolRefreshSession();
6572
- resetSmellsSessionState();
6573
- resetSituationalToolTelemetry();
6574
- resetWorkspaceDiagnosticsCacheSession(sessionStartMs);
6802
+ resetSessionScopedState(ctxCwd, sessionStartMs);
6575
6803
  deps.bootstrap.peek()?.knipClient.resetSessionState?.();
6576
6804
  resetCascadeTierSessionState();
6577
6805
  runtime.resetForSession(sessionStartMs);
@@ -6958,10 +7186,8 @@ async function handleSessionStart(deps) {
6958
7186
  dbg("session_start: no language defaults selected for pre-install");
6959
7187
  }
6960
7188
  const startupScansEnabled = deps.projectConfig?.startup?.scans?.enabled ?? deps.globalConfig?.startup?.scans?.enabled ?? true;
6961
- const startupScansWillRun = allowBootstrapTasks && startupScan.canWarmCaches && startupScansEnabled;
6962
- const jstsHeavyScansWillRun = startupScansWillRun && canRunStartupHeavyScans(languageProfile, "jsts");
6963
7189
  if (allowBootstrapTasks) {
6964
- scheduleDeferredToolProbes(deps, languageProfile, startupDefaults, jstsHeavyScansWillRun, dbg);
7190
+ scheduleDeferredToolProbes(deps, languageProfile, startupDefaults, dbg);
6965
7191
  scheduleManagedToolRefresh(dbg);
6966
7192
  }
6967
7193
  if (allowBootstrapTasks) {
@@ -7332,6 +7558,7 @@ async function buildActionableWarningsReport(args) {
7332
7558
  const cwd = path15.resolve(args.cwd);
7333
7559
  const records = [...args.dispatchWarnings];
7334
7560
  const lspService = getLSPService();
7561
+ const excludeServerIds = args.getFlag ? configDisabledAuxiliaryLspServerIds(args.getFlag) : void 0;
7335
7562
  const observedAtByPath = /* @__PURE__ */ new Map();
7336
7563
  const buildStartedAt = (/* @__PURE__ */ new Date()).toISOString();
7337
7564
  logActionableWarningsEvent({
@@ -7401,6 +7628,7 @@ async function buildActionableWarningsReport(args) {
7401
7628
  const pullTimeoutMs = boundedNumber(args.lspPullTimeoutMs, ACTIONABLE_WARNINGS_LSP_PULL_TIMEOUT_MS);
7402
7629
  const inBandDeps = {
7403
7630
  lspService,
7631
+ excludeServerIds,
7404
7632
  pullTimeoutMs,
7405
7633
  // #2504 review round 4 (F2). This is the AWAITED hook: the batch
7406
7634
  // budget has to bound what happens INSIDE a file, not only how many
@@ -7448,6 +7676,7 @@ async function buildActionableWarningsReport(args) {
7448
7676
  const loopSignal = combineAbortSignals(args.signal, deferredSignal) ?? deferredSignal;
7449
7677
  const deferredDeps = {
7450
7678
  lspService,
7679
+ excludeServerIds,
7451
7680
  pullTimeoutMs,
7452
7681
  signal: loopSignal,
7453
7682
  // OFF the hook by construction: this loop is the answer to
@@ -7562,7 +7791,7 @@ async function enrichFileFromLsp(cwd, args, target, deps) {
7562
7791
  return out;
7563
7792
  }
7564
7793
  }
7565
- const pulled = await boundedLspCall(() => lspService.getDiagnostics(filePath), deps);
7794
+ const pulled = await boundedLspCall(() => lspService.getDiagnostics(filePath, "full", deps.excludeServerIds), deps);
7566
7795
  if (pulled === void 0) {
7567
7796
  logActionableWarningsEvent({
7568
7797
  event: "lsp_file_skipped",
@@ -8119,8 +8348,8 @@ var MAX_FAILURE_TRACKER_SIZE = 200;
8119
8348
  var recentOldTextFailures = new BoundedFifoMap(MAX_FAILURE_TRACKER_SIZE);
8120
8349
 
8121
8350
  // dist/clients/deferred-runner-blockers.js
8122
- import { createHash as createHash3 } from "node:crypto";
8123
8351
  import * as fs14 from "node:fs";
8352
+ import { resolve as resolve9 } from "node:path";
8124
8353
  function judgeDeferredRunnerFindings(pending2, cwd, options = {}) {
8125
8354
  const findings = pending2.result?.diagnostics ?? [];
8126
8355
  if (findings.length === 0) {
@@ -8161,7 +8390,7 @@ function judgeDeferredRunnerFindings(pending2, cwd, options = {}) {
8161
8390
  };
8162
8391
  }
8163
8392
  function recordDeferredRunnerBlockers(runtime, pending2, survivors, bytes) {
8164
- const blocking = survivors.flatMap((d) => d.semantic === "blocking" ? [d] : []);
8393
+ const blocking = stampLineHashes(withoutLaneCopies(runtime, pending2, survivors).flatMap((d) => d.semantic === "blocking" ? [{ ...d, runnerId: d.runnerId ?? pending2.runnerId }] : []), bytes?.toString("utf-8"), (d) => !d.filePath || resolve9(d.filePath) === resolve9(pending2.filePath));
8165
8394
  if (blocking.length === 0)
8166
8395
  return 0;
8167
8396
  const recorded = runtime.recordDeferredInlineBlockers(pending2.filePath, blocking, {
@@ -8169,7 +8398,7 @@ function recordDeferredRunnerBlockers(runtime, pending2, survivors, bytes) {
8169
8398
  ...bytes ? {
8170
8399
  contentBaseline: {
8171
8400
  size: bytes.byteLength,
8172
- sha256: createHash3("sha256").update(bytes).digest("hex")
8401
+ sha256: hashContentBytes(bytes)
8173
8402
  }
8174
8403
  } : {}
8175
8404
  });
@@ -8177,6 +8406,36 @@ function recordDeferredRunnerBlockers(runtime, pending2, survivors, bytes) {
8177
8406
  runtime.updateGitGuardStatus(false, "");
8178
8407
  return recorded;
8179
8408
  }
8409
+ function retireSettledRunnerBlockers(runtime, pending2, verdict) {
8410
+ const result = pending2.result;
8411
+ if (!result || reachedNoVerdict(result) || result.coveredBy !== void 0 && result.skipReason === "covered-by-primary")
8412
+ return 0;
8413
+ const bytes = readBytes(pending2.filePath);
8414
+ const retired = runtime.retireUnobservedRunnerBlockers(pending2.filePath, {
8415
+ runnerId: pending2.runnerId,
8416
+ // #4484 round 3: what the run's edit-range gate withheld is still
8417
+ // observed, at its own line.
8418
+ observed: [
8419
+ ...withoutLaneCopies(runtime, pending2, verdict.kept),
8420
+ ...result.outOfEditScope ?? []
8421
+ ].flatMap((d) => d.semantic === "blocking" ? [d] : []),
8422
+ markedAtMs: pending2.markedAtMs,
8423
+ sha256: bytes ? hashContentBytes(bytes) : void 0,
8424
+ ...result.unconfirmedServerIds !== void 0 && {
8425
+ unconfirmedServerIds: result.unconfirmedServerIds
8426
+ }
8427
+ });
8428
+ if (retired > 0)
8429
+ runtime.updateGitGuardStatus(false, "");
8430
+ return retired;
8431
+ }
8432
+ function withoutLaneCopies(runtime, pending2, findings) {
8433
+ const lane = pending2.result?.coveredBy;
8434
+ if (lane === void 0)
8435
+ return findings;
8436
+ const standsFor = laneCopyStandsFor((runtime.getInlineBlockerDiagnostics(pending2.filePath) ?? []).flatMap((d) => d.runnerId === lane.runnerId && (lane.serverId === void 0 || d.serverId === lane.serverId) ? [d] : []));
8437
+ return findings.flatMap((d) => standsFor(d) ? [] : [d]);
8438
+ }
8180
8439
  function readBytes(filePath) {
8181
8440
  try {
8182
8441
  return fs14.readFileSync(filePath);
@@ -8838,7 +9097,7 @@ function stillReportedParked(parked, current, keyOf, offeredKeys) {
8838
9097
  }
8839
9098
 
8840
9099
  // dist/clients/knip-scan-roots.js
8841
- import { resolve as resolve13 } from "node:path";
9100
+ import { resolve as resolve14 } from "node:path";
8842
9101
  var MAX_KNIP_ROOTS_PER_TURN = 3;
8843
9102
  function resolveKnipScanRoots(sessionCwd, modifiedFiles) {
8844
9103
  const session = resolveGitCheckout(sessionCwd);
@@ -8848,7 +9107,7 @@ function resolveKnipScanRoots(sessionCwd, modifiedFiles) {
8848
9107
  let sessionOwnsEdit = false;
8849
9108
  const linked = [];
8850
9109
  for (const file of modifiedFiles) {
8851
- const owner = resolveLinkedWorktreeOwner(session, resolve13(sessionCwd, file));
9110
+ const owner = resolveLinkedWorktreeOwner(session, resolve14(sessionCwd, file));
8852
9111
  if (owner === null) {
8853
9112
  sessionOwnsEdit = true;
8854
9113
  } else if (!linked.some((known) => pathsEqual(known.root, owner.root))) {
@@ -9110,7 +9369,7 @@ function selectPersistentReverifyFiles(report, nowMs = Date.now()) {
9110
9369
  } catch {
9111
9370
  continue;
9112
9371
  }
9113
- if (mtimeMs > stamp) {
9372
+ if (freshnessFromMtime({ mtimeMs, referenceMs: stamp, toleranceMs: 0 }).verdict === "stale") {
9114
9373
  skippedChanged += 1;
9115
9374
  continue;
9116
9375
  }
@@ -9501,6 +9760,10 @@ function lateDeadCodeScansOf(scope) {
9501
9760
  function joinedEarlierScan(result, askedAt) {
9502
9761
  return result.scannedAt !== void 0 && Date.parse(result.scannedAt) < askedAt;
9503
9762
  }
9763
+ var KNIP_CARRIED_FILES = "knip-carried-files";
9764
+ function knipCarriedFilesOf(scope) {
9765
+ return scopeCell(scope, KNIP_CARRIED_FILES, () => /* @__PURE__ */ new Map());
9766
+ }
9504
9767
  function withRowForFailureWrite(scan, cacheManager2, cacheKey, cwd, startRow) {
9505
9768
  return scan.then((result) => result.success || startRow?.data.success === true ? { result, row: startRow } : cacheManager2.readCacheAsync(cacheKey, cwd).then((row) => ({ result, row })));
9506
9769
  }
@@ -9844,6 +10107,7 @@ async function handleTurnEnd(deps) {
9844
10107
  let runnerFindingsFailed = 0;
9845
10108
  let runnerFindingsDropped = 0;
9846
10109
  let runnerBlockersRecorded = 0;
10110
+ let runnerBlockersRetired = 0;
9847
10111
  let runnerFindingsDispositionSuppressed = 0;
9848
10112
  const runnerFindingsDeliveredIds = [];
9849
10113
  for (const pending2 of pendingRunnerFindings) {
@@ -9870,6 +10134,7 @@ async function handleTurnEnd(deps) {
9870
10134
  dropStaleRunnerFindings(pending2);
9871
10135
  runnerFindingsDropped += 1;
9872
10136
  }
10137
+ runnerBlockersRetired += retireSettledRunnerBlockers(runtime, pending2, verdict);
9873
10138
  if (verdict.live === 0)
9874
10139
  continue;
9875
10140
  const displayPath = toRunnerDisplayPath(cwd, pending2.filePath);
@@ -9915,6 +10180,7 @@ ${lines.join("\n")}`;
9915
10180
  failed: runnerFindingsFailed,
9916
10181
  dropped: runnerFindingsDropped,
9917
10182
  blockersRecorded: runnerBlockersRecorded,
10183
+ blockersRetired: runnerBlockersRetired,
9918
10184
  dispositionSuppressed: runnerFindingsDispositionSuppressed,
9919
10185
  deliveredIds: runnerFindingsDeliveredIds
9920
10186
  }
@@ -10081,6 +10347,7 @@ ${policy.body}`);
10081
10347
  durationMs: Date.now() - cascadeSettleStart,
10082
10348
  metadata: { settled, timedOut }
10083
10349
  });
10350
+ peekOutstandingCascadeTouches(holdScope);
10084
10351
  const t0 = Date.now();
10085
10352
  const cascadeRuns = (holdScope.guardedWrite("turn-end:cascade-runs", () => runtime.consumeCascadeRuns()) ?? []).filter((run) => {
10086
10353
  const originSeq = run.origin?.projectSeq;
@@ -10133,40 +10400,91 @@ ${policy.body}`);
10133
10400
  }
10134
10401
  }
10135
10402
  const cascadeLogFilePath = cascadeResults[0]?.filePath ?? files[0] ?? cwd;
10136
- if (cascadeResults.length > 0) {
10137
- const seen = /* @__PURE__ */ new Map();
10138
- for (const result of cascadeResults) {
10139
- seen.set(normalizeMapKey(result.filePath), result);
10140
- }
10141
- const neighborOwner = /* @__PURE__ */ new Map();
10142
- for (const result of [...seen.values()].reverse()) {
10143
- const pk = normalizeMapKey(result.filePath);
10144
- for (const n of result.neighbors) {
10145
- const nk = normalizeMapKey(n.filePath);
10146
- if (!neighborOwner.has(nk))
10147
- neighborOwner.set(nk, pk);
10148
- }
10403
+ const cascadeTurn = runtime.turnIndex;
10404
+ const editedKeys = /* @__PURE__ */ new Set();
10405
+ for (const file of files) {
10406
+ const absolute = path24.resolve(cwd, file);
10407
+ editedKeys.add(normalizeMapKey(absolute));
10408
+ forgetCascadePendingNeighbor(absolute);
10409
+ }
10410
+ const seen = /* @__PURE__ */ new Map();
10411
+ for (const result of cascadeResults) {
10412
+ seen.set(normalizeMapKey(result.filePath), result);
10413
+ }
10414
+ const keptResults = new Set(seen.values());
10415
+ const lastObserver = /* @__PURE__ */ new Map();
10416
+ for (const run of [...cascadeRuns].reverse()) {
10417
+ if (run.result && !keptResults.has(run.result))
10418
+ continue;
10419
+ for (const neighborPath of [
10420
+ ...(run.result?.neighbors ?? []).map((n) => n.filePath),
10421
+ ...run.observedNeighborPaths ?? []
10422
+ ]) {
10423
+ const nk = normalizeMapKey(neighborPath);
10424
+ if (!lastObserver.has(nk))
10425
+ lastObserver.set(nk, { run, filePath: neighborPath });
10149
10426
  }
10427
+ }
10428
+ for (const [nk, { run, filePath }] of lastObserver) {
10429
+ const observedClean = run.observedNeighborPaths?.some((p) => normalizeMapKey(p) === nk) && !run.result?.neighbors.some((n) => normalizeMapKey(n.filePath) === nk && n.diagnostics.length > 0);
10430
+ if (observedClean)
10431
+ forgetCascadePendingNeighbor(filePath);
10432
+ }
10433
+ if (cascadeResults.length > 0) {
10150
10434
  const parts = [];
10151
10435
  const sectionRuns = [];
10152
10436
  let injectedNeighborCount = 0;
10153
10437
  let injectedDiagnosticCount = 0;
10154
10438
  let suppressedByOwnership = 0;
10439
+ const sectionObservations = [];
10440
+ const shownUnansweredServerIds = /* @__PURE__ */ new Set();
10441
+ const unansweredLines = [];
10155
10442
  for (const result of seen.values()) {
10156
- const pk = normalizeMapKey(result.filePath);
10157
- const ownsAny = result.neighbors.some((n) => neighborOwner.get(normalizeMapKey(n.filePath)) === pk);
10158
- if (ownsAny && result.formatted) {
10159
- const carryMeta = carriedMetaByResult.get(result);
10160
- const carrySuffix = cascadeCarrySuffix(carryMeta?.carriedTurns, carryMeta?.observedAt);
10161
- parts.push(carrySuffix ? `${result.formatted}
10162
- ${carrySuffix}` : result.formatted);
10163
- if (carrySuffix)
10164
- carriedRunsRendered += 1;
10443
+ const ownedNeighbors = result.neighbors.filter((n) => lastObserver.get(normalizeMapKey(n.filePath))?.run.result === result);
10444
+ const ownsAny = ownedNeighbors.length > 0;
10445
+ let rendered = ownsAny && result.formatted ? renderCascadeResult(result, shownUnansweredServerIds) : void 0;
10446
+ const layout = result.layout;
10447
+ if (rendered && layout) {
10448
+ const since = (n, d) => cascadeStillPresentSince(n.filePath, d, cascadeTurn);
10449
+ const relabel = ownedNeighbors.some((n) => n.diagnostics.some((d) => since(n, d) !== void 0));
10450
+ if (relabel || ownedNeighbors.length < result.neighbors.length) {
10451
+ const block = formatCascadeNeighborDiagnostics(layout.cwd, ownedNeighbors, {
10452
+ noun: layout.noun,
10453
+ includeReason: layout.includeReason,
10454
+ stillPresentSince: since
10455
+ });
10456
+ rendered = {
10457
+ ...rendered,
10458
+ text: composeCascadeSection(block, layout)
10459
+ };
10460
+ }
10461
+ }
10462
+ if (rendered && (rendered.text || rendered.lines.length > 0)) {
10463
+ unansweredLines.push(...rendered.lines);
10464
+ if (rendered.text) {
10465
+ const carryMeta = carriedMetaByResult.get(result);
10466
+ const carrySuffix = cascadeCarrySuffix(carryMeta?.carriedTurns, carryMeta?.observedAt);
10467
+ parts.push(carrySuffix ? `${rendered.text}
10468
+ ${carrySuffix}` : rendered.text);
10469
+ if (carrySuffix)
10470
+ carriedRunsRendered += 1;
10471
+ }
10165
10472
  const sectionRun = runByResult.get(result);
10166
10473
  if (sectionRun)
10167
10474
  sectionRuns.push(sectionRun);
10168
- injectedNeighborCount += result.neighbors.length;
10169
- injectedDiagnosticCount += result.neighbors.reduce((s, n) => s + n.diagnostics.length, 0);
10475
+ for (const n of ownedNeighbors) {
10476
+ if (n.diagnostics.length === 0)
10477
+ continue;
10478
+ if (editedKeys.has(normalizeMapKey(n.filePath)))
10479
+ continue;
10480
+ sectionObservations.push({
10481
+ filePath: n.filePath,
10482
+ diagnostics: n.diagnostics,
10483
+ checkedAt: sectionRun?.checkedAt
10484
+ });
10485
+ }
10486
+ injectedNeighborCount += ownedNeighbors.length;
10487
+ injectedDiagnosticCount += ownedNeighbors.reduce((s, n) => s + n.diagnostics.length, 0);
10170
10488
  } else if (!ownsAny && result.formatted) {
10171
10489
  suppressedByOwnership++;
10172
10490
  }
@@ -10200,11 +10518,20 @@ ${testLines.join("\n")}`;
10200
10518
  parts.push(testSection);
10201
10519
  }
10202
10520
  }
10521
+ if (unansweredLines.length > 0)
10522
+ parts.unshift(unansweredLines.join("\n"));
10203
10523
  if (parts.length > 0) {
10204
10524
  const section = parts.join("\n\n");
10205
10525
  blockerParts.push(section);
10526
+ const shownServerIds = [...shownUnansweredServerIds];
10206
10527
  deliveryHolds.push({
10207
10528
  part: section,
10529
+ onDelivered: () => {
10530
+ markCascadeUnansweredPrimariesDelivered(shownServerIds);
10531
+ for (const o of sectionObservations) {
10532
+ recordDeliveredCascadeNeighbor(o.filePath, o.diagnostics, cascadeTurn, o.checkedAt ?? 0);
10533
+ }
10534
+ },
10208
10535
  onHeld: () => {
10209
10536
  for (const run of sectionRuns)
10210
10537
  runtime.appendCascadeRun(run);
@@ -10233,6 +10560,124 @@ ${testLines.join("\n")}`;
10233
10560
  }
10234
10561
  });
10235
10562
  }
10563
+ const pendingCandidates = listCascadePendingNeighbors().filter((entry) => {
10564
+ const key = normalizeMapKey(entry.filePath);
10565
+ if (lastObserver.has(key) || editedKeys.has(key))
10566
+ return false;
10567
+ return isUnderDir(entry.filePath, cwd);
10568
+ });
10569
+ const { "cascade-pending-neighbors": pendingGate } = gateFindingsByPathFreshness({
10570
+ cwd,
10571
+ quiet: true,
10572
+ sources: {
10573
+ "cascade-pending-neighbors": {
10574
+ findings: pendingCandidates,
10575
+ citedPath: (entry) => entry.filePath,
10576
+ scannedAtOf: (entry) => entry.checkedAt
10577
+ }
10578
+ }
10579
+ });
10580
+ const unresolvedNeighbors = pendingGate.live;
10581
+ const changedNeighbors = pendingGate.stale;
10582
+ const gateKept = /* @__PURE__ */ new Set([...unresolvedNeighbors, ...changedNeighbors]);
10583
+ const deletedNeighbors = [];
10584
+ for (const entry of pendingCandidates) {
10585
+ if (gateKept.has(entry))
10586
+ continue;
10587
+ deletedNeighbors.push(entry);
10588
+ forgetCascadePendingNeighbor(entry.filePath);
10589
+ }
10590
+ const maxShown = RUNTIME_CONFIG.pipeline.cascadeMaxFiles;
10591
+ const pendingAge = (checkedAt) => formatCacheAgeLabel(checkedAt > 0 ? checkedAt : void 0);
10592
+ const pendingBlocks = [];
10593
+ let unresolvedShown = 0;
10594
+ if (unresolvedNeighbors.length > 0) {
10595
+ const lines = unresolvedNeighbors.slice(-maxShown).reverse().map((entry) => ` \u2022 ${toRunnerDisplayPath(cwd, entry.filePath)}: ${entry.errorCount} error(s) unresolved (${pendingAge(entry.checkedAt)})`);
10596
+ unresolvedShown = lines.length;
10597
+ const more = unresolvedNeighbors.length - lines.length;
10598
+ if (more > 0)
10599
+ lines.push(` ... and ${more} more neighbor file(s)`);
10600
+ pendingBlocks.push(`\u{1F4D0} Cascade errors from an earlier turn, not re-checked this turn \u2014 still unresolved until a cascade or an edit shows them gone:
10601
+ ${lines.join("\n")}`);
10602
+ }
10603
+ const changedShown = changedNeighbors.slice(-maxShown).reverse();
10604
+ if (changedShown.length > 0) {
10605
+ const lines = changedShown.map((entry) => ` \u2022 ${toRunnerDisplayPath(cwd, entry.filePath)}: ${entry.errorCount} error(s) changed since checked (${pendingAge(entry.checkedAt)}), not re-checked`);
10606
+ const more = changedNeighbors.length - changedShown.length;
10607
+ if (more > 0)
10608
+ lines.push(` ... and ${more} more neighbor file(s)`);
10609
+ pendingBlocks.push(`\u{1F4D0} Cascade errors from an earlier turn whose file changed outside pi since they were checked \u2014 not re-checked, named this once:
10610
+ ${lines.join("\n")}`);
10611
+ }
10612
+ if (pendingBlocks.length > 0) {
10613
+ const pendingSection = pendingBlocks.join("\n");
10614
+ blockerParts.push(pendingSection);
10615
+ if (changedShown.length > 0) {
10616
+ deliveryHolds.push({
10617
+ part: pendingSection,
10618
+ onDelivered: () => {
10619
+ for (const entry of changedShown) {
10620
+ forgetCascadePendingNeighbor(entry.filePath);
10621
+ }
10622
+ }
10623
+ });
10624
+ }
10625
+ logCascade({
10626
+ phase: "cascade_pending_rendered",
10627
+ filePath: cascadeLogFilePath,
10628
+ metadata: {
10629
+ neighbors: unresolvedNeighbors.length,
10630
+ shown: unresolvedShown,
10631
+ changed: changedShown.length
10632
+ }
10633
+ });
10634
+ }
10635
+ if (changedNeighbors.length + deletedNeighbors.length > 0) {
10636
+ logCascade({
10637
+ phase: "cascade_pending_gated",
10638
+ filePath: cascadeLogFilePath,
10639
+ metadata: {
10640
+ changed: changedNeighbors.length,
10641
+ deleted: deletedNeighbors.length,
10642
+ changedPaths: changedNeighbors.slice(0, 10).map((e) => e.filePath),
10643
+ deletedPaths: deletedNeighbors.slice(0, 10).map((e) => e.filePath)
10644
+ }
10645
+ });
10646
+ }
10647
+ const withCarryLabel = (advisory, runs) => {
10648
+ if (advisory === void 0)
10649
+ return void 0;
10650
+ const carried = runs.filter((r) => (r.carriedTurns ?? 0) > 0);
10651
+ if (carried.length === 0 || carried.length !== runs.length) {
10652
+ return advisory;
10653
+ }
10654
+ const stamps = carried.map((r) => r.observedAt);
10655
+ const observedAt = stamps.every((s) => s !== void 0) ? Math.min(...stamps) : void 0;
10656
+ const suffix = cascadeCarrySuffix(carried[0]?.carriedTurns, observedAt);
10657
+ if (!suffix)
10658
+ return advisory;
10659
+ labeledAdvisories += 1;
10660
+ return `${advisory}
10661
+ ${suffix}`;
10662
+ };
10663
+ const holdAdvisory = (advisory, runs, keep) => {
10664
+ if (advisory === void 0)
10665
+ return;
10666
+ deliveryHolds.push({
10667
+ part: advisory,
10668
+ onHeld: () => {
10669
+ for (const run of runs) {
10670
+ const restored = { ...run, result: void 0 };
10671
+ delete restored.observedNeighborPaths;
10672
+ if (keep !== "indeterminate")
10673
+ delete restored.indeterminate;
10674
+ if (keep !== "unconfirmed")
10675
+ delete restored.unconfirmed;
10676
+ runtime.appendCascadeRun(restored);
10677
+ }
10678
+ }
10679
+ });
10680
+ };
10236
10681
  const indeterminateRuns = cascadeRuns.filter((r) => r.indeterminate);
10237
10682
  if (indeterminateRuns.length > 0) {
10238
10683
  const buildAdvisory = (runs, frame) => {
@@ -10260,47 +10705,20 @@ ${lines.join("\n")}`;
10260
10705
  const graphRuns = indeterminateRuns.filter((r) => r.indeterminate?.reason !== "lsp_binding_rejected" && r.indeterminate?.reason !== "excluded_by_role" && r.indeterminate?.reason !== "budget_truncated" && r.indeterminate?.budget === void 0);
10261
10706
  const bindingRuns = indeterminateRuns.filter((r) => r.indeterminate?.reason === "lsp_binding_rejected" && r.indeterminate?.budget === void 0);
10262
10707
  const budgetRuns = indeterminateRuns.filter((r) => r.indeterminate?.reason === "budget_truncated" || r.indeterminate?.budget !== void 0);
10263
- const withCarryLabel = (advisory, runs) => {
10264
- if (advisory === void 0)
10265
- return void 0;
10266
- const carried = runs.filter((r) => (r.carriedTurns ?? 0) > 0);
10267
- if (carried.length === 0 || carried.length !== runs.length) {
10268
- return advisory;
10269
- }
10270
- const stamps = carried.map((r) => r.observedAt);
10271
- const observedAt = stamps.every((s) => s !== void 0) ? Math.min(...stamps) : void 0;
10272
- const suffix = cascadeCarrySuffix(carried[0]?.carriedTurns, observedAt);
10273
- if (!suffix)
10274
- return advisory;
10275
- labeledAdvisories += 1;
10276
- return `${advisory}
10277
- ${suffix}`;
10278
- };
10279
- const holdAdvisory = (advisory, runs) => {
10280
- if (advisory === void 0)
10281
- return;
10282
- deliveryHolds.push({
10283
- part: advisory,
10284
- onHeld: () => {
10285
- for (const run of runs)
10286
- runtime.appendCascadeRun({ ...run, result: void 0 });
10287
- }
10288
- });
10289
- };
10290
10708
  const graphAdvisory = withCarryLabel(buildAdvisory(graphRuns, {
10291
10709
  lead: (fileCount2, reasons) => `Cascade could not compute downstream impact for ${fileCount2} edited file(s) this turn \u2014 the review graph was unavailable (${reasons}), so their dependents were not cascade-checked and a clean cascade result does not cover them.`,
10292
10710
  fallbackDetail: (r) => r.indeterminate?.reason === "missing_node" ? "changed file not in the review graph" : "review graph unavailable"
10293
10711
  }), graphRuns);
10294
10712
  if (graphAdvisory)
10295
10713
  advisoryParts.push(graphAdvisory);
10296
- holdAdvisory(graphAdvisory, graphRuns);
10714
+ holdAdvisory(graphAdvisory, graphRuns, "indeterminate");
10297
10715
  const bindingAdvisory = withCarryLabel(buildAdvisory(bindingRuns, {
10298
10716
  lead: (fileCount2, reasons) => `Cascade identified dependents for ${fileCount2} edited file(s) this turn, but their diagnostics could not be freshly confirmed (${reasons}) and were withheld \u2014 a clean cascade result does not cover them.`,
10299
10717
  fallbackDetail: () => "cascade diagnostics withheld (binding rejected)"
10300
10718
  }), bindingRuns);
10301
10719
  if (bindingAdvisory)
10302
10720
  advisoryParts.push(bindingAdvisory);
10303
- holdAdvisory(bindingAdvisory, bindingRuns);
10721
+ holdAdvisory(bindingAdvisory, bindingRuns, "indeterminate");
10304
10722
  const budgetAdvisory = withCarryLabel(buildAdvisory(budgetRuns, {
10305
10723
  lead: (fileCount2, reasons) => `Cascade checked the selected neighbors for ${fileCount2} edited file(s) this turn, but some eligible dependents were not checked because the cascade budget was exhausted (${reasons}); a clean cascade result does not cover them.`,
10306
10724
  fallbackDetail: (r) => {
@@ -10313,7 +10731,7 @@ ${suffix}`;
10313
10731
  }), budgetRuns);
10314
10732
  if (budgetAdvisory)
10315
10733
  advisoryParts.push(budgetAdvisory);
10316
- holdAdvisory(budgetAdvisory, budgetRuns);
10734
+ holdAdvisory(budgetAdvisory, budgetRuns, "indeterminate");
10317
10735
  const fileCount = new Set(indeterminateRuns.map((r) => normalizeMapKey(r.filePath))).size;
10318
10736
  const byFile = indeterminateRuns.map((r) => ({
10319
10737
  file: toRunnerDisplayPath(cwd, r.filePath),
@@ -10337,6 +10755,57 @@ ${suffix}`;
10337
10755
  }
10338
10756
  });
10339
10757
  }
10758
+ const unconfirmedRuns = cascadeRuns.filter((r) => r.unconfirmed);
10759
+ if (unconfirmedRuns.length > 0) {
10760
+ const runOrder = new Map(cascadeRuns.map((r, i) => [r, i]));
10761
+ const byServer = /* @__PURE__ */ new Map();
10762
+ for (const run of unconfirmedRuns) {
10763
+ for (const group of run.unconfirmed?.servers ?? []) {
10764
+ for (const neighborPath of group.paths) {
10765
+ const nk = normalizeMapKey(neighborPath);
10766
+ if (editedKeys.has(nk))
10767
+ continue;
10768
+ const observer = lastObserver.get(nk)?.run;
10769
+ if (observer && (runOrder.get(observer) ?? -1) > (runOrder.get(run) ?? -1))
10770
+ continue;
10771
+ const bucket = byServer.get(group.serverId) ?? {
10772
+ waitMs: group.waitMs,
10773
+ primaries: /* @__PURE__ */ new Set(),
10774
+ paths: /* @__PURE__ */ new Map()
10775
+ };
10776
+ bucket.waitMs = Math.max(bucket.waitMs, group.waitMs);
10777
+ bucket.primaries.add(toRunnerDisplayPath(cwd, run.filePath));
10778
+ bucket.paths.set(nk, toRunnerDisplayPath(cwd, neighborPath));
10779
+ byServer.set(group.serverId, bucket);
10780
+ }
10781
+ }
10782
+ }
10783
+ const capped = (items, max) => items.length > max ? `${items.slice(0, max).join(", ")} (+${items.length - max} more)` : items.join(", ");
10784
+ const blocks = [];
10785
+ let pathCount = 0;
10786
+ for (const [serverId, bucket] of byServer) {
10787
+ const paths = [...bucket.paths.values()];
10788
+ pathCount += paths.length;
10789
+ const within = bucket.waitMs > 0 ? ` within ${bucket.waitMs} ms` : "";
10790
+ blocks.push(`Cascade sent ${paths.length} dependent(s) of ${capped([...bucket.primaries], 3)} to ${serverId}, but no answer arrived${within}; a clean cascade result does not cover them.
10791
+ \u2022 ${serverId}: ${capped(paths, 5)}`);
10792
+ }
10793
+ if (blocks.length > 0) {
10794
+ const pendingAdvisory = withCarryLabel(blocks.join("\n"), unconfirmedRuns);
10795
+ if (pendingAdvisory)
10796
+ advisoryParts.push(pendingAdvisory);
10797
+ holdAdvisory(pendingAdvisory, unconfirmedRuns, "unconfirmed");
10798
+ logCascade({
10799
+ phase: "cascade_unconfirmed_rendered",
10800
+ filePath: cascadeLogFilePath,
10801
+ metadata: {
10802
+ servers: byServer.size,
10803
+ paths: pathCount,
10804
+ runs: unconfirmedRuns.length
10805
+ }
10806
+ });
10807
+ }
10808
+ }
10340
10809
  if (carriedRunsRendered > 0 || labeledAdvisories > 0) {
10341
10810
  logCascade({
10342
10811
  phase: "cascade_carry_rendered",
@@ -10370,6 +10839,7 @@ ${suffix}`;
10370
10839
  skipped: cascadeSkipped
10371
10840
  }
10372
10841
  });
10842
+ const knipCarried = knipCarriedFilesOf(turnScope);
10373
10843
  const knipScan = resolveKnipScanRoots(cwd, files);
10374
10844
  for (const root of knipScan.overCap) {
10375
10845
  incrementDegradationCount({
@@ -10380,7 +10850,7 @@ ${suffix}`;
10380
10850
  }
10381
10851
  const KNOWN_SLOW_KNIP_GRACE_MS = 1;
10382
10852
  const KNIP_MAX_SHOWN = 5;
10383
- const applyKnipResult = (scanRoot, prevKnip, knipResult, rowNow) => {
10853
+ const applyKnipResult = (scanRoot, prevKnip, knipResult, rowNow, joined) => {
10384
10854
  const knipWouldPoison = wouldPoisonCache(rowNow, knipResult);
10385
10855
  if (knipWouldPoison) {
10386
10856
  dbg(`turn_end: keeping last good knip cache; this run failed: ${knipResult.summary}`);
@@ -10413,7 +10883,7 @@ ${suffix}`;
10413
10883
  if (knipResult.success && knipResult.issues.length > 0) {
10414
10884
  const issueKey = (i) => stableFindingKey(i.type, i.file, i.name, i.package);
10415
10885
  const prevKeys = new Set((prevKnip?.data?.issues ?? []).map(issueKey));
10416
- const modifiedSet = new Set(files.map((f) => resolveRunnerPath(cwd, f)));
10886
+ const modifiedSet = new Set([...files, ...knipCarried.get(scanRoot) ?? []].map((f) => resolveRunnerPath(cwd, f)));
10417
10887
  const newIssues = knipResult.issues.filter((issue) => {
10418
10888
  if (prevKeys.has(issueKey(issue)))
10419
10889
  return false;
@@ -10473,6 +10943,13 @@ ${suffix}`;
10473
10943
  deliveryHolds.push(cutAdvisoryHold("knip", knipAdvisoryLane, report, unusedExportDelta.slice(0, KNIP_MAX_SHOWN), (issue) => reOfferedKeys.has(issueKey(issue))));
10474
10944
  }
10475
10945
  }
10946
+ if (joined) {
10947
+ const carry = new Set(files);
10948
+ knipCarried.set(scanRoot, carry);
10949
+ knipMeta.joined = true;
10950
+ knipMeta.carried = carry.size;
10951
+ } else if (knipResult.success)
10952
+ knipCarried.delete(scanRoot);
10476
10953
  return knipMeta;
10477
10954
  };
10478
10955
  const logKnipRow = (root, startedAt, metadata) => logLatency({
@@ -10539,7 +11016,8 @@ ${suffix}`;
10539
11016
  });
10540
11017
  }
10541
11018
  } else {
10542
- metadata = applyKnipResult(scanRoot, prevKnip, scanned.result, scanned.row);
11019
+ const joined = scanned.result.success && scanned.result.execution !== "cache" && joinedEarlierScan(scanned.result, startedAt);
11020
+ metadata = applyKnipResult(scanRoot, prevKnip, scanned.result, scanned.row, joined);
10543
11021
  }
10544
11022
  }
10545
11023
  budgetSpent = metadata.execution === "deferred" && (metadata.scanFloorMs === void 0 || metadata.aborted === true);
@@ -10910,7 +11388,7 @@ ${suffix}`;
10910
11388
  metadata: madgeStats ?? { skipped: true }
10911
11389
  });
10912
11390
  if (!getFlag("no-tests") && files.length > 0) {
10913
- const seen = /* @__PURE__ */ new Set();
11391
+ const seen2 = /* @__PURE__ */ new Set();
10914
11392
  const targets = [];
10915
11393
  const testRootFor = createTurnEndTestRoots(cwd);
10916
11394
  const candidates = [];
@@ -10977,7 +11455,7 @@ ${suffix}`;
10977
11455
  foreignDeferred++;
10978
11456
  continue;
10979
11457
  }
10980
- if (seen.has(carriedKey)) {
11458
+ if (seen2.has(carriedKey)) {
10981
11459
  resolvedDeferralKeys.add(carriedKey);
10982
11460
  continue;
10983
11461
  }
@@ -11021,7 +11499,7 @@ ${suffix}`;
11021
11499
  continue;
11022
11500
  }
11023
11501
  resolvedDeferralKeys.add(carriedKey);
11024
- seen.add(carriedKey);
11502
+ seen2.add(carriedKey);
11025
11503
  redispatchedDeferred++;
11026
11504
  targets.push({
11027
11505
  testFile,
@@ -11072,8 +11550,8 @@ ${suffix}`;
11072
11550
  candidatesByRoot.set(testRoot, (candidatesByRoot.get(testRoot) ?? 0) + 1);
11073
11551
  const target = testRunnerClient.getTestRunTarget(abs, testRoot, runtime.turnIndex);
11074
11552
  const targetKey = target ? normalizeMapKey(target.testFile) : "";
11075
- if (target && !seen.has(targetKey)) {
11076
- seen.add(targetKey);
11553
+ if (target && !seen2.has(targetKey)) {
11554
+ seen2.add(targetKey);
11077
11555
  if (retiredKeys.has(targetKey)) {
11078
11556
  retiredSkips++;
11079
11557
  dbg(`turn_end: ${display} \u2192 test target retired earlier this session (outran the turn-end batch budget), skipping spawn (${path24.relative(cwd, target.testFile)})`);
@@ -11474,8 +11952,8 @@ ${suffix}`;
11474
11952
  } else {
11475
11953
  const callGraphStart = Date.now();
11476
11954
  try {
11477
- const { impact, formatImpact, parseSymbolKey } = await import("../chunk-BKE7E7TC.js");
11478
- const { callGraphImpactToProjectDiagnostics } = await import("../chunk-6GFRNVYR.js");
11955
+ const { impact, formatImpact, parseSymbolKey } = await import("../chunk-GMX5OS7B.js");
11956
+ const { callGraphImpactToProjectDiagnostics } = await import("../chunk-MZGQNL3Y.js");
11479
11957
  const impactLines = [];
11480
11958
  let callGraphDispositionSuppressed = 0;
11481
11959
  const impactFindings = [];
@@ -11598,6 +12076,8 @@ ${impactLines.join("\n")}`;
11598
12076
  modifiedRangesByFile,
11599
12077
  dispatchWarnings: runtime.peekActionableWarnings(sessionId),
11600
12078
  includeLspCodeActions: !!getFlag("lens-actionable-warning-actions"),
12079
+ // #4323: the cold LSP read leaves a switched-off Opengrep unstarted.
12080
+ getFlag,
11601
12081
  projectSeqStart: runtime.turnStartProjectSeq,
11602
12082
  projectSeqEnd: runtime.projectSeq,
11603
12083
  // #3676: the quick fix credits its writes with this epoch.
@@ -11716,31 +12196,7 @@ ${impactLines.join("\n")}`;
11716
12196
  }
11717
12197
  const t5 = Date.now();
11718
12198
  try {
11719
- const qualityWarningsByFile = /* @__PURE__ */ new Map();
11720
- for (const warning of runtime.peekCodeQualityWarnings(sessionId)) {
11721
- const group = qualityWarningsByFile.get(warning.filePath);
11722
- if (group)
11723
- group.push(warning);
11724
- else
11725
- qualityWarningsByFile.set(warning.filePath, [warning]);
11726
- }
11727
- const qualityWarnings = [];
11728
- let qualityDispositionSuppressed = 0;
11729
- for (const [filePath, group] of qualityWarningsByFile) {
11730
- let content;
11731
- try {
11732
- content = fs17.readFileSync(filePath, "utf-8");
11733
- } catch {
11734
- content = void 0;
11735
- }
11736
- const { kept, suppressed } = applyPushedFindingPolicy(group, {
11737
- cwd,
11738
- filePath,
11739
- content
11740
- });
11741
- qualityWarnings.push(...kept);
11742
- qualityDispositionSuppressed += suppressed;
11743
- }
12199
+ const { kept: qualityWarnings, suppressed: qualityDispositionSuppressed } = applyPushedFindingPolicyByFile(runtime.peekCodeQualityWarnings(sessionId), cwd);
11744
12200
  const qualityReport = buildCodeQualityWarningsReport({
11745
12201
  cwd,
11746
12202
  sessionId: runtime.telemetrySessionId,
@@ -12468,7 +12924,7 @@ var TurnEndQueue = class {
12468
12924
  }
12469
12925
  };
12470
12926
  var turnEndQueue = new TurnEndQueue();
12471
- var pendingTurnEndDeliveries = /* @__PURE__ */ new Map();
12927
+ var pendingTurnEndDeliveries = new BoundedFifoMap(TURN_END_DELIVERY_MAX);
12472
12928
  var inFlightIpcTurnEnds = /* @__PURE__ */ new Map();
12473
12929
  async function runTurnEndNowImpl(cwd, files = [], deferredDelivery = false) {
12474
12930
  const ctx = await getMcpSessionContext();
@@ -12546,12 +13002,6 @@ function prunePendingTurnEndDeliveries(now = Date.now()) {
12546
13002
  pendingTurnEndDeliveries.delete(deliveryId);
12547
13003
  }
12548
13004
  }
12549
- while (pendingTurnEndDeliveries.size > TURN_END_DELIVERY_MAX) {
12550
- const oldest = pendingTurnEndDeliveries.keys().next();
12551
- if (oldest.done)
12552
- break;
12553
- pendingTurnEndDeliveries.delete(oldest.value);
12554
- }
12555
13005
  }
12556
13006
  function pendingForCwd(cwd) {
12557
13007
  for (const [deliveryId, delivery] of pendingTurnEndDeliveries) {
@@ -13168,7 +13618,7 @@ function blastReadArgs(graph, normalizedFile) {
13168
13618
  return { path: normalizedFile, offset: 1, limit };
13169
13619
  }
13170
13620
  async function computeBlastRadius(graph, normalizedPath, projectRoot, maxDepth) {
13171
- const { computeTransitiveImpact } = await import("../chunk-4UNXBNE2.js");
13621
+ const { computeTransitiveImpact } = await import("../chunk-XTFDJI3T.js");
13172
13622
  const result = computeTransitiveImpact(graph, normalizedPath, { maxDepth });
13173
13623
  const byFile = /* @__PURE__ */ new Map();
13174
13624
  for (const hit of result.hits) {
@@ -13785,7 +14235,7 @@ async function moduleReport(file, cwd, options) {
13785
14235
  let callGraphIdentity;
13786
14236
  let revisionDriftNote;
13787
14237
  try {
13788
- const { getCachedReviewGraph, getReviewGraphCacheIdentity, getReviewGraphSizeSkipVerdict, getReviewGraphRevisionDrift, formatReviewGraphRevisionDriftNote: formatReviewGraphRevisionDriftNote2 } = await import("../chunk-2VUQMUMT.js");
14238
+ const { getCachedReviewGraph, getReviewGraphCacheIdentity, getReviewGraphSizeSkipVerdict, getReviewGraphRevisionDrift, formatReviewGraphRevisionDriftNote: formatReviewGraphRevisionDriftNote2 } = await import("../chunk-YCWYO6TQ.js");
13789
14239
  graph = getCachedReviewGraph(cwd);
13790
14240
  graphFileCap = getReviewGraphSizeSkipVerdict(cwd)?.maxFileCount;
13791
14241
  const drift = graph ? getReviewGraphRevisionDrift(cwd) : void 0;
@@ -14569,7 +15019,7 @@ function roleFor(graph, filePath) {
14569
15019
  return top.length > 0 ? top.map((s) => s.name).join(", ") : void 0;
14570
15020
  }
14571
15021
  async function computeHubs(graph, degrees, cwd, limit, focusTerms) {
14572
- const { computeTransitiveImpact } = await import("../chunk-4UNXBNE2.js");
15022
+ const { computeTransitiveImpact } = await import("../chunk-XTFDJI3T.js");
14573
15023
  const ranked = [...graph.fileNodes.entries()].map(([filePath, fileNodeId]) => ({
14574
15024
  filePath,
14575
15025
  fileNodeId,
@@ -14821,7 +15271,7 @@ async function projectReport(cwd, options) {
14821
15271
  const limit = clampLimit(options?.limit);
14822
15272
  const focusTerms = normalizeFocus2(options?.focus);
14823
15273
  const view = options?.view;
14824
- const { getCachedReviewGraph, getReviewGraphSizeSkipVerdict, getLastReviewGraphBuildAttempt, buildOrUpdateGraph, isGraphBuildInFlight, getReviewGraphRevisionDrift } = await import("../chunk-2VUQMUMT.js");
15274
+ const { getCachedReviewGraph, getReviewGraphSizeSkipVerdict, getLastReviewGraphBuildAttempt, buildOrUpdateGraph, isGraphBuildInFlight, getReviewGraphRevisionDrift } = await import("../chunk-YCWYO6TQ.js");
14825
15275
  let graph;
14826
15276
  try {
14827
15277
  graph = getCachedReviewGraph(cwd);
@@ -14845,7 +15295,7 @@ async function projectReport(cwd, options) {
14845
15295
  };
14846
15296
  }
14847
15297
  const previousAttempt = getLastReviewGraphBuildAttempt(cwd);
14848
- const { FactStore: FactStore2 } = await import("../chunk-QGQ5CCCG.js");
15298
+ const { FactStore: FactStore2 } = await import("../chunk-UMS2MFPZ.js");
14849
15299
  const trigger = triggerBackgroundGraphBuild(cwd, {
14850
15300
  buildOrUpdateGraph,
14851
15301
  isGraphBuildInFlight,
@@ -14946,7 +15396,7 @@ function renderCompactProjectReport(report) {
14946
15396
  }
14947
15397
 
14948
15398
  // dist/clients/effective-config.js
14949
- import * as os3 from "node:os";
15399
+ import * as os2 from "node:os";
14950
15400
  import * as path28 from "node:path";
14951
15401
  function isEffectiveFileViewError(file) {
14952
15402
  return "error" in file;
@@ -15012,7 +15462,7 @@ function decidedByOrNothing(entry) {
15012
15462
  }
15013
15463
  async function effectiveConfig(options = {}) {
15014
15464
  const cwd = options.cwd ?? process.cwd();
15015
- const homeDir = options.homeDir ?? os3.homedir();
15465
+ const homeDir = options.homeDir ?? os2.homedir();
15016
15466
  const resolveAt = (dir) => resolvePiLensConfig({
15017
15467
  cwd: dir,
15018
15468
  globalDir: getGlobalPiLensDir(),
@@ -15126,13 +15576,19 @@ function recentLatency(limit = 5, fileFilter) {
15126
15576
  }
15127
15577
  return reports.slice(-limit).reverse();
15128
15578
  }
15129
- function projectScan(cwd, maxFiles, includeGenerated) {
15130
- return scanProjectDiagnostics({
15579
+ async function projectScan(cwd, maxFiles, includeGenerated) {
15580
+ const scan = await scanProjectDiagnostics({
15131
15581
  cwd,
15132
15582
  tier: "cheap",
15133
15583
  maxFiles,
15134
15584
  includeGenerated
15135
15585
  });
15586
+ const policy = applyPushedFindingPolicyByFile(scan.diagnostics, cwd);
15587
+ return {
15588
+ ...scan,
15589
+ diagnostics: policy.kept,
15590
+ dispositionSuppressed: policy.suppressed
15591
+ };
15136
15592
  }
15137
15593
  function scanTruncationNotice(snapshot) {
15138
15594
  if (!snapshot.scanTruncated)
@@ -15264,7 +15720,7 @@ async function symbolSearch(query, cwd, limit = 20, options = {}) {
15264
15720
  warmLease.release();
15265
15721
  }
15266
15722
  const hits = results.map(toSymbolSearchHit);
15267
- const { getCachedReviewGraph } = await import("../chunk-2VUQMUMT.js");
15723
+ const { getCachedReviewGraph } = await import("../chunk-YCWYO6TQ.js");
15268
15724
  const graph = getCachedReviewGraph(cwd);
15269
15725
  if (graph)
15270
15726
  annotateSymbolSearchHitsWithGraph(hits, centrality, graph);
@@ -15461,10 +15917,12 @@ function canonicalLanguage(lang) {
15461
15917
  }
15462
15918
 
15463
15919
  // dist/tools/shared.js
15920
+ import * as path31 from "node:path";
15464
15921
  function resolveAstGrepPaths(paths, cwd) {
15465
15922
  const base = cwd || ".";
15466
- const requested = paths?.length ? paths : [base];
15467
- return requested.map((p) => resolveHostToolPath(p, base));
15923
+ if (!paths?.length)
15924
+ return [path31.resolve(base)];
15925
+ return paths.map((p) => resolveHostToolPath(p, base));
15468
15926
  }
15469
15927
  var LANGUAGES = [
15470
15928
  "bash",
@@ -16414,7 +16872,7 @@ ${hint}` : `${output}${paginationNote}`;
16414
16872
  // dist/tools/lens-diagnostics.js
16415
16873
  import { promises as fs23 } from "node:fs";
16416
16874
  import * as fsSync from "node:fs";
16417
- import * as path37 from "node:path";
16875
+ import * as path38 from "node:path";
16418
16876
 
16419
16877
  // dist/clients/project-diagnostics/extractors.js
16420
16878
  var WARM_TRIGGER = {
@@ -16473,13 +16931,13 @@ function reasonFromAvailabilityVerdict(tool, verdict) {
16473
16931
 
16474
16932
  // dist/clients/project-diagnostics/fresh-fetch.js
16475
16933
  import * as fs21 from "node:fs";
16476
- import * as os4 from "node:os";
16477
- import * as path35 from "node:path";
16934
+ import * as os3 from "node:os";
16935
+ import * as path36 from "node:path";
16478
16936
 
16479
16937
  // dist/clients/project-diagnostics/runner-adapters/jscpd.js
16480
- import * as path31 from "node:path";
16938
+ import * as path32 from "node:path";
16481
16939
  function resolveClonePath(cwd, file) {
16482
- return path31.isAbsolute(file) ? file : path31.resolve(cwd, file);
16940
+ return path32.isAbsolute(file) ? file : path32.resolve(cwd, file);
16483
16941
  }
16484
16942
  function jscpdCloneToProjectDiagnostics(cwd, clone) {
16485
16943
  const a = resolveClonePath(cwd, clone.fileA);
@@ -16492,7 +16950,7 @@ function jscpdCloneToProjectDiagnostics(cwd, clone) {
16492
16950
  tool: "jscpd",
16493
16951
  runner: "jscpd",
16494
16952
  rule: "jscpd:duplicate",
16495
- message: `Duplicate code (${clone.lines} lines) \u2014 also at ${path31.relative(cwd, otherFile)}:${otherLine}`,
16953
+ message: `Duplicate code (${clone.lines} lines) \u2014 also at ${path32.relative(cwd, otherFile)}:${otherLine}`,
16496
16954
  source: "project-scan"
16497
16955
  });
16498
16956
  return [
@@ -16507,16 +16965,16 @@ function jscpdResultToProjectDiagnostics(cwd, result) {
16507
16965
  }
16508
16966
 
16509
16967
  // dist/clients/project-diagnostics/runner-adapters/madge.js
16510
- import * as path32 from "node:path";
16968
+ import * as path33 from "node:path";
16511
16969
  function resolveCyclePath(cwd, file) {
16512
- return path32.isAbsolute(file) ? file : path32.resolve(cwd, file);
16970
+ return path33.isAbsolute(file) ? file : path33.resolve(cwd, file);
16513
16971
  }
16514
16972
  function cycleMembers(dep) {
16515
16973
  return dep.path.length > 0 ? dep.path : [dep.file];
16516
16974
  }
16517
16975
  function circularDepToProjectDiagnostics(cwd, dep) {
16518
16976
  const members = cycleMembers(dep);
16519
- const rendered = `${members.map((f) => path32.basename(f)).join(" \u2192 ")} \u2192 ${path32.basename(members[0] ?? "")}`;
16977
+ const rendered = `${members.map((f) => path33.basename(f)).join(" \u2192 ")} \u2192 ${path33.basename(members[0] ?? "")}`;
16520
16978
  return members.map((file) => ({
16521
16979
  filePath: resolveCyclePath(cwd, file),
16522
16980
  severity: "warning",
@@ -16542,7 +17000,7 @@ function circularDepsToProjectDiagnostics(cwd, circular) {
16542
17000
  }
16543
17001
 
16544
17002
  // dist/clients/project-diagnostics/runner-adapters/opengrep.js
16545
- import * as path33 from "node:path";
17003
+ import * as path34 from "node:path";
16546
17004
  function severityFor(raw) {
16547
17005
  switch (raw.toUpperCase()) {
16548
17006
  case "ERROR":
@@ -16557,7 +17015,7 @@ function opengrepFindingToProjectDiagnostic(cwd, finding) {
16557
17015
  const { severity, blocking } = severityFor(finding.severity);
16558
17016
  const cweSuffix = finding.cwe?.length ? ` (${finding.cwe[0]})` : "";
16559
17017
  return {
16560
- filePath: path33.isAbsolute(finding.path) ? finding.path : path33.resolve(cwd, finding.path),
17018
+ filePath: path34.isAbsolute(finding.path) ? finding.path : path34.resolve(cwd, finding.path),
16561
17019
  line: finding.startLine,
16562
17020
  column: finding.startCol,
16563
17021
  severity,
@@ -16577,7 +17035,7 @@ function opengrepResultToProjectDiagnostics(cwd, result) {
16577
17035
 
16578
17036
  // dist/clients/project-diagnostics/runner-adapters/runner-findings.js
16579
17037
  import * as fs20 from "node:fs";
16580
- import * as path34 from "node:path";
17038
+ import * as path35 from "node:path";
16581
17039
  function failureMessage(failure) {
16582
17040
  const firstLine2 = failure.message.split("\n")[0]?.slice(0, 300) ?? "";
16583
17041
  return firstLine2 ? `${failure.name}: ${firstLine2}` : failure.name;
@@ -16643,7 +17101,7 @@ function testRunnerFindingsToProjectDiagnostics(cache, cwd, runtime) {
16643
17101
  return [];
16644
17102
  const validation = cwd ? validateAdvisoryProvenance(cache, cwd, runtime) : { status: "unknown", reasons: ["validation-context-missing"] };
16645
17103
  const root = cwd ?? process.cwd();
16646
- const missingKeys = new Set((cache.provenance?.files ?? []).filter((file) => !fs20.existsSync(path34.resolve(root, file.path))).map((file) => advisoryPathKey(file.path, root)));
17104
+ const missingKeys = new Set((cache.provenance?.files ?? []).filter((file) => !fs20.existsSync(path35.resolve(root, file.path))).map((file) => advisoryPathKey(file.path, root)));
16647
17105
  const historical = cache.superseded === true || cache.stale === true || validation.status !== "current";
16648
17106
  return cache.results.filter((result) => !missingKeys.has(advisoryPathKey(result.file, root))).flatMap((result) => testResultToProjectDiagnostics(result, historical)).map((diagnostic) => historical ? {
16649
17107
  ...diagnostic,
@@ -16673,7 +17131,7 @@ function laneObservedAtMs(scannedAt, startedAtMs) {
16673
17131
  return parseScannedAtMs(scannedAt) ?? startedAtMs;
16674
17132
  }
16675
17133
  async function fetchFreshProjectDiagnostics(cacheManager2, cwd, clients, signal, options = {}) {
16676
- const requestedRoot = options.analysisRoot === void 0 ? cwd : path35.resolve(cwd, options.analysisRoot);
17134
+ const requestedRoot = options.analysisRoot === void 0 ? cwd : path36.resolve(cwd, options.analysisRoot);
16677
17135
  let analysisRoot;
16678
17136
  let analysisRootValidation;
16679
17137
  if (options.analysisRoot !== void 0) {
@@ -16712,7 +17170,7 @@ async function fetchFreshProjectDiagnostics(cacheManager2, cwd, clients, signal,
16712
17170
  analysisRoot = realpathOrResolve(cwd);
16713
17171
  }
16714
17172
  const unsafeRootReason = "the analysis root resolves at or above the home directory; heavyweight analyzers refuse to walk from there (#747)";
16715
- const homeRoot = realpathOrResolve(options.homeDir ?? os4.homedir());
17173
+ const homeRoot = realpathOrResolve(options.homeDir ?? os3.homedir());
16716
17174
  if (options.analysisRoot !== void 0) {
16717
17175
  const unsafeExplicitRoot = !isSameOrWithin(homeRoot, analysisRoot) || isSameOrWithin(analysisRoot, homeRoot);
16718
17176
  analysisRootValidation = unsafeExplicitRoot ? {
@@ -16840,7 +17298,7 @@ async function fetchFreshProjectDiagnostics(cacheManager2, cwd, clients, signal,
16840
17298
  markCold("jscpd", reasonFromAvailabilityVerdict("jscpd", clients.jscpdClient.getAvailabilityVerdict?.()));
16841
17299
  return;
16842
17300
  }
16843
- const isTsProject = fs21.existsSync(path35.join(analysisRoot, "tsconfig.json"));
17301
+ const isTsProject = fs21.existsSync(path36.join(analysisRoot, "tsconfig.json"));
16844
17302
  const scannerKey = isTsProject ? "jscpd-ts" : "jscpd";
16845
17303
  const startMs = Date.now();
16846
17304
  const result = await clients.jscpdClient.scan(analysisRoot, void 0, void 0, isTsProject);
@@ -17157,7 +17615,7 @@ function makeProgressReporter(onUpdate, label, throttleMs = 250) {
17157
17615
 
17158
17616
  // dist/tools/lsp-diagnostics.js
17159
17617
  import * as fs22 from "node:fs";
17160
- import * as path36 from "node:path";
17618
+ import * as path37 from "node:path";
17161
17619
  var MAX_FILES = 100;
17162
17620
  var MAX_BATCH_FILES = 100;
17163
17621
  var MAX_DIAGNOSTICS = 200;
@@ -17240,11 +17698,11 @@ async function collectFiles(dir, extensions, maxFiles, isIgnored = () => false)
17240
17698
  return;
17241
17699
  if (entry.isSymbolicLink())
17242
17700
  continue;
17243
- const full = path36.join(current, entry.name);
17701
+ const full = path37.join(current, entry.name);
17244
17702
  if (entry.isDirectory()) {
17245
17703
  if (!isExcludedDirName(entry.name) && !isIgnored(full, true))
17246
17704
  await walk(full);
17247
- } else if (entry.isFile() && extensions.includes(path36.extname(full))) {
17705
+ } else if (entry.isFile() && extensions.includes(path37.extname(full))) {
17248
17706
  if (isIgnored(full, false))
17249
17707
  continue;
17250
17708
  files.push(full);
@@ -17254,7 +17712,7 @@ async function collectFiles(dir, extensions, maxFiles, isIgnored = () => false)
17254
17712
  await walk(dir);
17255
17713
  return files;
17256
17714
  }
17257
- function createLspDiagnosticsTool(nextWriteIndex, onConfirmedNoBlockers, getService = getLSPService) {
17715
+ function createLspDiagnosticsTool(nextWriteIndex, onConfirmedNoBlockers, getService = getLSPService, getFlag) {
17258
17716
  return {
17259
17717
  async execute(_toolCallId, params, _signal, onUpdate, ctx) {
17260
17718
  const signal = combineAbortSignals(_signal, ctx.signal);
@@ -17265,6 +17723,7 @@ function createLspDiagnosticsTool(nextWriteIndex, onConfirmedNoBlockers, getServ
17265
17723
  const concurrency = boundedPositiveInt(typedParams.concurrency, DEFAULT_BATCH_CONCURRENCY, 1, MAX_BATCH_CONCURRENCY);
17266
17724
  const waitMs = typeof typedParams.waitMs === "number" && typedParams.waitMs >= 0 ? Math.floor(typedParams.waitMs) : void 0;
17267
17725
  const serverScope = typedParams.serverScope === "primary" ? "primary" : "all";
17726
+ const excludeServerIds = getFlag ? configDisabledAuxiliaryLspServerIds((name) => getFlag(name, cwd)) : void 0;
17268
17727
  const lspService = getService();
17269
17728
  if (!lspService) {
17270
17729
  return {
@@ -17322,7 +17781,8 @@ function createLspDiagnosticsTool(nextWriteIndex, onConfirmedNoBlockers, getServ
17322
17781
  nextWriteIndex,
17323
17782
  serverScope,
17324
17783
  cwd,
17325
- onConfirmedNoBlockers
17784
+ onConfirmedNoBlockers,
17785
+ excludeServerIds
17326
17786
  }, expanded);
17327
17787
  }
17328
17788
  const rawPath = typedParams.path;
@@ -17338,7 +17798,7 @@ function createLspDiagnosticsTool(nextWriteIndex, onConfirmedNoBlockers, getServ
17338
17798
  details: {}
17339
17799
  };
17340
17800
  }
17341
- const absPath = normalizeMapKey(path36.resolve(cwd, rawPath));
17801
+ const absPath = normalizeMapKey(typedParams.pathAlreadyResolved === true ? rawPath : resolveHostToolPath(rawPath, cwd));
17342
17802
  let stat;
17343
17803
  try {
17344
17804
  stat = fs22.statSync(absPath);
@@ -17360,14 +17820,15 @@ function createLspDiagnosticsTool(nextWriteIndex, onConfirmedNoBlockers, getServ
17360
17820
  nextWriteIndex,
17361
17821
  serverScope,
17362
17822
  cwd,
17363
- onConfirmedNoBlockers
17823
+ onConfirmedNoBlockers,
17824
+ excludeServerIds
17364
17825
  });
17365
17826
  }
17366
- return runFileDiagnostics(absPath, severity, lspService, waitMs, nextWriteIndex, serverScope, cwd, onConfirmedNoBlockers);
17827
+ return runFileDiagnostics(absPath, severity, lspService, waitMs, nextWriteIndex, serverScope, cwd, onConfirmedNoBlockers, excludeServerIds);
17367
17828
  }
17368
17829
  };
17369
17830
  }
17370
- async function collectDiagnosticsForFile(absPath, lspService, scanRoot, waitMs, serverScope = "all") {
17831
+ async function collectDiagnosticsForFile(absPath, lspService, scanRoot, waitMs, serverScope = "all", excludeServerIds) {
17371
17832
  let timedOut = false;
17372
17833
  let content;
17373
17834
  let touched;
@@ -17432,6 +17893,7 @@ async function collectDiagnosticsForFile(absPath, lspService, scanRoot, waitMs,
17432
17893
  maxClientWaitMs: waitMs,
17433
17894
  source: "lsp_diagnostics",
17434
17895
  clientScope: serverScope,
17896
+ excludeServerIds,
17435
17897
  // #3405: the caller asked whether THIS file is clean right now, and the
17436
17898
  // content above was just read from disk — so it is the file's saved
17437
17899
  // state. Without the save a save-triggered server answers zero
@@ -17439,10 +17901,11 @@ async function collectDiagnosticsForFile(absPath, lspService, scanRoot, waitMs,
17439
17901
  // as clean.
17440
17902
  saved: true
17441
17903
  });
17442
- timedOut = touched?.inconclusive === true;
17904
+ timedOut = touched?.inconclusive === true && touched.inconclusiveReason !== "primary-unavailable";
17443
17905
  } catch {
17444
17906
  }
17445
- const diagnostics = touched !== void 0 ? touched.diags : await lspService.getDiagnostics(absPath, waitMs !== void 0 ? "document" : "full");
17907
+ const diagnostics = touched !== void 0 ? touched.diags : await lspService.getDiagnostics(absPath, waitMs !== void 0 ? "document" : "full", excludeServerIds);
17908
+ const fallbackHealth = touched === void 0 ? lspService.getDiagnosticsHealth?.(absPath) : void 0;
17446
17909
  const filtered = content !== void 0 ? applyAuxiliarySuppressions(diagnostics, content, {
17447
17910
  fileRole: detectFileRole(absPath, content),
17448
17911
  filePath: absPath,
@@ -17450,11 +17913,18 @@ async function collectDiagnosticsForFile(absPath, lspService, scanRoot, waitMs,
17450
17913
  }) : diagnostics;
17451
17914
  const binding = touched?.binding;
17452
17915
  const confirmedByTouch = touchCompletedConfirmationPolicy(touched);
17916
+ const primaryUnavailable = describePrimaryUnavailable(touched, fallbackHealth);
17917
+ const noClientAnswered = fallbackHealth !== void 0 && String(fallbackHealth.health ?? "").startsWith("no_clients");
17453
17918
  return {
17454
17919
  diagnostics: filtered,
17455
17920
  timedOut,
17456
17921
  skipReason: touched?.skipReason,
17457
17922
  confirmedByTouch,
17923
+ ...noClientAnswered && { noClientAnswered: true },
17924
+ ...primaryUnavailable !== void 0 && {
17925
+ primaryUnavailable: primaryUnavailable.text,
17926
+ primaryUnavailableCause: primaryUnavailable.cause
17927
+ },
17458
17928
  // #1470: only a touch that resolved clients contributes a coverage gap;
17459
17929
  // the getDiagnostics fallback never reports one, which is honest — that
17460
17930
  // path claims no confirmation at all.
@@ -17464,6 +17934,27 @@ async function collectDiagnosticsForFile(absPath, lspService, scanRoot, waitMs,
17464
17934
  binding
17465
17935
  };
17466
17936
  }
17937
+ function describePrimaryUnavailable(touched, fallbackHealth) {
17938
+ let serverIds;
17939
+ let disabledServerIds;
17940
+ let carriedCause;
17941
+ if (touched?.inconclusiveReason === "primary-unavailable") {
17942
+ serverIds = touched.inconclusiveServerIds ?? [];
17943
+ disabledServerIds = touched.disabledPrimaryServerIds ?? [];
17944
+ carriedCause = touched.primaryUnavailableCause;
17945
+ } else if (fallbackHealth?.unavailablePrimaryServerIds?.length) {
17946
+ serverIds = fallbackHealth.unavailablePrimaryServerIds;
17947
+ disabledServerIds = fallbackHealth.disabledPrimaryServerIds ?? [];
17948
+ carriedCause = fallbackHealth.primaryUnavailableCause;
17949
+ } else {
17950
+ return void 0;
17951
+ }
17952
+ const cause = carriedCause ?? primaryUnavailableCause(serverIds, disabledServerIds);
17953
+ return {
17954
+ text: `Primary LSP (${serverIds.join(", ")}): not available \u2014 no language server answered for this file (${PRIMARY_UNAVAILABLE_CAUSE[cause]}). NOT the same as 0 diagnostics; auxiliary scanners do not check the language itself.`,
17955
+ cause
17956
+ };
17957
+ }
17467
17958
  function diagnosticsToFileDiags(file, diagnostics) {
17468
17959
  return diagnostics.map((d) => ({
17469
17960
  file,
@@ -17509,7 +18000,7 @@ async function resolveEmptyResult(file, lspService) {
17509
18000
  function canTrustTouchConfirmation(file, serverScope, confirmedByTouch) {
17510
18001
  return confirmedByTouch && (serverScope === "primary" || primaryServerId(file) !== "typescript");
17511
18002
  }
17512
- function coveredSourcesForCheck(file, serverScope, unconfirmedServerIds) {
18003
+ function coveredSourcesForCheck(file, serverScope, unconfirmedServerIds, excludeServerIds) {
17513
18004
  const unconfirmed = new Set(unconfirmedServerIds);
17514
18005
  const covered = /* @__PURE__ */ new Set();
17515
18006
  let servers = [];
@@ -17524,6 +18015,8 @@ function coveredSourcesForCheck(file, serverScope, unconfirmedServerIds) {
17524
18015
  continue;
17525
18016
  if (unconfirmed.has(server.id))
17526
18017
  continue;
18018
+ if (excludeServerIds?.has(server.id))
18019
+ continue;
17527
18020
  covered.add(auxiliary ? server.id : "lsp");
17528
18021
  }
17529
18022
  return [...covered];
@@ -17535,7 +18028,7 @@ function demoteInferredForFile(file, diagnostics, cwd, lspService) {
17535
18028
  service: lspService
17536
18029
  });
17537
18030
  }
17538
- function applyProbeFindingPolicy(file, diagnostics, cwd, content, severity) {
18031
+ function applyProbeFindingPolicy(file, diagnostics, cwd, content, severity, toolName = "lsp_diagnostics") {
17539
18032
  const started = Date.now();
17540
18033
  try {
17541
18034
  const result = applyLspFindingPolicy(diagnostics, {
@@ -17550,7 +18043,7 @@ function applyProbeFindingPolicy(file, diagnostics, cwd, content, severity) {
17550
18043
  if (suppressed > 0) {
17551
18044
  logLatency({
17552
18045
  type: "phase",
17553
- toolName: "lsp_diagnostics",
18046
+ toolName,
17554
18047
  filePath: file,
17555
18048
  phase: "lsp_probe_disposition_filter",
17556
18049
  durationMs: Date.now() - started,
@@ -17572,15 +18065,11 @@ function applyProbeFindingPolicy(file, diagnostics, cwd, content, severity) {
17572
18065
  }
17573
18066
  }
17574
18067
  function replayContentForStrictMarks(file, cwd) {
17575
- if (!hasStrictDispositionMarks(cwd))
17576
- return void 0;
17577
- try {
17578
- return fs22.readFileSync(file, "utf-8");
17579
- } catch {
17580
- return void 0;
17581
- }
18068
+ return hasStrictDispositionMarks(cwd) ? readFileContent(file) : void 0;
17582
18069
  }
17583
- function reconcileWidgetFromLspResult(file, rawDiags, confirmation, writeIndex, cwd, content, boundMismatch = false, observedAt) {
18070
+ function reconcileWidgetFromLspResult(file, rawDiags, confirmation, writeIndex, cwd, content, boundMismatch = false, observedAt, auxiliariesOnly = false) {
18071
+ if (auxiliariesOnly)
18072
+ return { confirmed: false, blocking: true };
17584
18073
  const confirmed = (rawDiags.length > 0 || confirmation !== "unconfirmed") && !boundMismatch;
17585
18074
  if (!confirmed)
17586
18075
  return { confirmed: false, blocking: true };
@@ -17597,7 +18086,7 @@ function reconcileWidgetFromLspResult(file, rawDiags, confirmation, writeIndex,
17597
18086
  return { confirmed: false, blocking: true };
17598
18087
  }
17599
18088
  }
17600
- async function collectFileDiagnosticResult(file, severity, lspService, waitMs, nextWriteIndex, serverScope = "all", cacheCtx, scopeKey, cwd = process.cwd(), onConfirmedNoBlockers) {
18089
+ async function collectFileDiagnosticResult(file, severity, lspService, waitMs, nextWriteIndex, serverScope = "all", cacheCtx, scopeKey, cwd = process.cwd(), onConfirmedNoBlockers, excludeServerIds) {
17601
18090
  const writeIndex = nextWriteIndex?.();
17602
18091
  const scannedAt = Date.now();
17603
18092
  let stat;
@@ -17641,7 +18130,7 @@ async function collectFileDiagnosticResult(file, severity, lspService, waitMs, n
17641
18130
  };
17642
18131
  }
17643
18132
  }
17644
- const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, diagnosticsUnsupportedServerIds, content: collectedContent, binding, tooLargeReason, skipReason } = await collectDiagnosticsForFile(file, lspService, cwd, waitMs, serverScope);
18133
+ const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, diagnosticsUnsupportedServerIds, content: collectedContent, binding, tooLargeReason, skipReason, primaryUnavailable, primaryUnavailableCause: primaryUnavailableCause2, noClientAnswered } = await collectDiagnosticsForFile(file, lspService, cwd, waitMs, serverScope, excludeServerIds);
17645
18134
  if (tooLargeReason !== void 0) {
17646
18135
  return { file, diagnostics: [], outcome: "too_large", tooLargeReason };
17647
18136
  }
@@ -17650,7 +18139,7 @@ async function collectFileDiagnosticResult(file, severity, lspService, waitMs, n
17650
18139
  let confirmation;
17651
18140
  if (diagnosticsUnsupportedServerIds.length > 0) {
17652
18141
  confirmation = void 0;
17653
- } else if (skipReason !== void 0) {
18142
+ } else if (skipReason !== void 0 || primaryUnavailable !== void 0) {
17654
18143
  confirmation = "unconfirmed";
17655
18144
  } else if (timedOut) {
17656
18145
  if (applySeverityFilter(rawDiags, severity).length === 0) {
@@ -17690,13 +18179,14 @@ async function collectFileDiagnosticResult(file, severity, lspService, waitMs, n
17690
18179
  boundMismatch,
17691
18180
  // #3505 r1 F1: the widget row is observed at the read, as the cache
17692
18181
  // entry recorded below is.
17693
- scannedAt
18182
+ scannedAt,
18183
+ primaryUnavailable !== void 0
17694
18184
  );
17695
18185
  if (verdict.confirmed && !verdict.blocking) {
17696
18186
  onConfirmedNoBlockers?.({
17697
18187
  filePath: file,
17698
18188
  writeIndex,
17699
- coveredSources: coveredSourcesForCheck(file, serverScope, unconfirmedServerIds),
18189
+ coveredSources: coveredSourcesForCheck(file, serverScope, unconfirmedServerIds, excludeServerIds),
17700
18190
  cwd
17701
18191
  });
17702
18192
  }
@@ -17711,6 +18201,11 @@ async function collectFileDiagnosticResult(file, severity, lspService, waitMs, n
17711
18201
  timedOut: confirmation === "unconfirmed" ? timedOut : void 0,
17712
18202
  ...rootFallback && { rootFallback },
17713
18203
  ...skipReason !== void 0 && { skipReason },
18204
+ ...primaryUnavailable !== void 0 && {
18205
+ primaryUnavailable,
18206
+ primaryUnavailableCause: primaryUnavailableCause2
18207
+ },
18208
+ ...noClientAnswered && { noClientAnswered },
17714
18209
  primaryServerId: primaryServerId(file),
17715
18210
  diagnosticsUnsupported: diagnosticsUnsupportedServerIds.length > 0,
17716
18211
  ...policy.suppressed > 0 && {
@@ -17718,10 +18213,14 @@ async function collectFileDiagnosticResult(file, severity, lspService, waitMs, n
17718
18213
  }
17719
18214
  };
17720
18215
  }
17721
- async function runFileDiagnostics(absPath, severity, lspService, waitMs, nextWriteIndex, serverScope = "all", cwd = process.cwd(), onConfirmedNoBlockers) {
18216
+ async function runFileDiagnostics(absPath, severity, lspService, waitMs, nextWriteIndex, serverScope = "all", cwd = process.cwd(), onConfirmedNoBlockers, excludeServerIds) {
17722
18217
  const writeIndex = nextWriteIndex?.();
18218
+ lspService.announceCreatedFiles([
18219
+ ...goPackageFilesToAnnounce([absPath]),
18220
+ ...await importTargetsToAnnounce(cwd, [absPath])
18221
+ ]);
17723
18222
  const observedAt = Date.now();
17724
- const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, diagnosticsUnsupportedServerIds, content: collectedContent, binding, tooLargeReason, skipReason } = await collectDiagnosticsForFile(absPath, lspService, cwd, waitMs, serverScope);
18223
+ const { diagnostics: rawDiags, timedOut, confirmedByTouch, unconfirmedServerIds, diagnosticsUnsupportedServerIds, content: collectedContent, binding, tooLargeReason, skipReason, primaryUnavailable } = await collectDiagnosticsForFile(absPath, lspService, cwd, waitMs, serverScope, excludeServerIds);
17725
18224
  if (tooLargeReason !== void 0) {
17726
18225
  return {
17727
18226
  content: [{ type: "text", text: tooLargeReason }],
@@ -17743,7 +18242,7 @@ async function runFileDiagnostics(absPath, severity, lspService, waitMs, nextWri
17743
18242
  let confirmation;
17744
18243
  if (diagnosticsUnsupportedServerIds.length > 0) {
17745
18244
  confirmation = void 0;
17746
- } else if (skipReason !== void 0) {
18245
+ } else if (skipReason !== void 0 || primaryUnavailable !== void 0) {
17747
18246
  confirmation = "unconfirmed";
17748
18247
  } else if (timedOut) {
17749
18248
  if (applySeverityFilter(rawDiags, severity).length === 0) {
@@ -17777,12 +18276,12 @@ async function runFileDiagnostics(absPath, severity, lspService, waitMs, nextWri
17777
18276
  const truncated = total > MAX_DIAGNOSTICS;
17778
18277
  const limited = truncated ? filtered.slice(0, MAX_DIAGNOSTICS) : filtered;
17779
18278
  const unconfirmed = confirmation === "unconfirmed";
17780
- const verdict = reconcileWidgetFromLspResult(absPath, effectiveRawDiags, confirmation, writeIndex, cwd, collectedContent, boundMismatch, observedAt);
18279
+ const verdict = reconcileWidgetFromLspResult(absPath, effectiveRawDiags, confirmation, writeIndex, cwd, collectedContent, boundMismatch, observedAt, primaryUnavailable !== void 0);
17781
18280
  if (verdict.confirmed && !verdict.blocking) {
17782
18281
  onConfirmedNoBlockers?.({
17783
18282
  filePath: absPath,
17784
18283
  writeIndex,
17785
- coveredSources: coveredSourcesForCheck(absPath, serverScope, unconfirmedServerIds),
18284
+ coveredSources: coveredSourcesForCheck(absPath, serverScope, unconfirmedServerIds, excludeServerIds),
17786
18285
  cwd
17787
18286
  });
17788
18287
  }
@@ -17790,6 +18289,8 @@ async function runFileDiagnostics(absPath, severity, lspService, waitMs, nextWri
17790
18289
  const primaryDiags = limited.filter((d) => d.serverId === primaryId);
17791
18290
  const auxiliaryDiags = limited.filter((d) => d.serverId !== primaryId);
17792
18291
  const primaryLine = (() => {
18292
+ if (primaryUnavailable !== void 0)
18293
+ return primaryUnavailable;
17793
18294
  if (diagnosticsUnsupportedServerIds.length > 0) {
17794
18295
  return `Primary LSP${primaryId ? ` (${primaryId})` : ""}: navigation-only \u2014 diagnostics unsupported (no pull provider or publish evidence).`;
17795
18296
  }
@@ -17809,12 +18310,14 @@ async function runFileDiagnostics(absPath, severity, lspService, waitMs, nextWri
17809
18310
  })();
17810
18311
  const coverageLine = unconfirmedServerIds.length > 0 ? `Auxiliary coverage INCOMPLETE \u2014 ${[...unconfirmedServerIds].join(", ")} did not answer within the wait budget, so ${unconfirmedServerIds.length === 1 ? "its findings are" : "their findings are"} NOT included here. This is not a clean bill of health for ${unconfirmedServerIds.length === 1 ? "that scanner" : "those scanners"}; re-check after the next edit. waitMs can extend an ordinary wait, but it cannot override a session-demoted scanner.` : void 0;
17811
18312
  const suppressedLine = dispositionSuppressedLine(policy.suppressed);
18313
+ const switchedOffServerIds = serverScope === "all" && excludeServerIds && excludeServerIds.size > 0 ? getServersForFileWithConfig(absPath).flatMap((server) => excludeServerIds.has(server.id) ? [server.id] : []) : [];
18314
+ const noAuxiliaryLine = switchedOffServerIds.length > 0 ? `No auxiliary findings. Switched off, not run: ${switchedOffServerIds.join(", ")}.` : "No auxiliary findings.";
17812
18315
  let text;
17813
18316
  if (total === 0) {
17814
18317
  text = [
17815
18318
  primaryLine,
17816
18319
  "",
17817
- unavailable ?? "No auxiliary findings.",
18320
+ unavailable ?? noAuxiliaryLine,
17818
18321
  ...coverageLine ? [coverageLine] : [],
17819
18322
  ...suppressedLine ? [suppressedLine] : []
17820
18323
  ].join("\n");
@@ -17866,6 +18369,7 @@ async function runFileDiagnostics(absPath, severity, lspService, waitMs, nextWri
17866
18369
  timedOut: unconfirmed ? timedOut : void 0,
17867
18370
  navigationOnlyFiles: diagnosticsUnsupportedServerIds.length > 0 ? 1 : void 0,
17868
18371
  ...skipReason !== void 0 && { skipReason },
18372
+ ...primaryUnavailable !== void 0 && { primaryUnavailable },
17869
18373
  ...rootFallback && {
17870
18374
  rootFallbackReason: describeRootFallback(rootFallback)
17871
18375
  },
@@ -17883,6 +18387,8 @@ function tallyConfirmation(results) {
17883
18387
  let clean = 0;
17884
18388
  let unconfirmed = 0;
17885
18389
  let timedOut = 0;
18390
+ let primaryUnavailable = 0;
18391
+ const primaryUnavailableCauses = [];
17886
18392
  const rootFallbackReasons = /* @__PURE__ */ new Set();
17887
18393
  let navigationOnly = 0;
17888
18394
  let tooLarge = 0;
@@ -17901,6 +18407,11 @@ function tallyConfirmation(results) {
17901
18407
  unconfirmed += 1;
17902
18408
  if (result.timedOut)
17903
18409
  timedOut += 1;
18410
+ if (result.primaryUnavailable !== void 0) {
18411
+ primaryUnavailable += 1;
18412
+ if (result.primaryUnavailableCause !== void 0)
18413
+ primaryUnavailableCauses.push(result.primaryUnavailableCause);
18414
+ }
17904
18415
  if (result.rootFallback) {
17905
18416
  rootFallbackReasons.add(describeRootFallback(result.rootFallback));
17906
18417
  }
@@ -17912,6 +18423,10 @@ function tallyConfirmation(results) {
17912
18423
  clean,
17913
18424
  unconfirmed,
17914
18425
  timedOut,
18426
+ primaryUnavailable: {
18427
+ count: primaryUnavailable,
18428
+ cause: combinePrimaryUnavailableCauses(primaryUnavailableCauses)
18429
+ },
17915
18430
  rootFallbackReasons: [...rootFallbackReasons],
17916
18431
  navigationOnly,
17917
18432
  tooLarge
@@ -17924,6 +18439,9 @@ function classifyBatchFileOutcome(result) {
17924
18439
  return "too_large";
17925
18440
  if (result.diagnosticsUnsupported)
17926
18441
  return "unsupported";
18442
+ if (result.noClientAnswered && result.primaryUnavailable !== void 0) {
18443
+ return "unavailable";
18444
+ }
17927
18445
  if (result.timedOut || result.confirmation === "unconfirmed") {
17928
18446
  return "inconclusive";
17929
18447
  }
@@ -17946,7 +18464,24 @@ function inconclusiveBatchResult(file, reason) {
17946
18464
  inconclusiveReason: reason
17947
18465
  };
17948
18466
  }
17949
- function unconfirmedReasonClause(unconfirmed, timedOut, rootFallbackReasons = []) {
18467
+ function batchOutcomeReason(result) {
18468
+ return result.tooLargeReason ?? // #4322: a file only auxiliaries answered for names its language server.
18469
+ result.primaryUnavailable ?? result.inconclusiveReason ?? result.error ?? result.unavailable;
18470
+ }
18471
+ function auxiliaryOnlyFindingsLine(results) {
18472
+ const auxiliaryOnly = results.filter((result) => result.diagnostics.length > 0 && result.primaryUnavailable !== void 0 && result.noClientAnswered === void 0);
18473
+ const count = auxiliaryOnly.length;
18474
+ if (count === 0)
18475
+ return void 0;
18476
+ const cause = combinePrimaryUnavailableCauses(auxiliaryOnly.flatMap((result) => result.primaryUnavailableCause ?? [])) ?? "unstarted";
18477
+ return `${count} file${count === 1 ? "" : "s"} with auxiliary findings only: no language server answered (${PRIMARY_UNAVAILABLE_CAUSE[cause]}), so the language itself was not checked.`;
18478
+ }
18479
+ function unconfirmedReasonClause(unconfirmed, timedOut, rootFallbackReasons = [], primaryUnavailable = { count: 0, cause: void 0 }) {
18480
+ if (primaryUnavailable.count > 0) {
18481
+ const head = `${primaryUnavailable.count} with no language server answer (${PRIMARY_UNAVAILABLE_CAUSE[primaryUnavailable.cause ?? "unstarted"]})`;
18482
+ const rest = unconfirmed - primaryUnavailable.count;
18483
+ return rest > 0 ? `${head}; the other ${rest}: ${unconfirmedReasonClause(rest, timedOut, rootFallbackReasons)}` : `${head}.`;
18484
+ }
17950
18485
  if (rootFallbackReasons.length > 0) {
17951
18486
  return `${rootFallbackReasons.join("; ")} (${unconfirmed} unconfirmed in all).`;
17952
18487
  }
@@ -17962,7 +18497,8 @@ function unconfirmedReasonClause(unconfirmed, timedOut, rootFallbackReasons = []
17962
18497
  async function collectBatchDiagnostics(files, severity, lspService, options) {
17963
18498
  const resolvedCwd = options.cwd ?? process.cwd();
17964
18499
  const cacheCtx = createWorkspaceDiagnosticsCacheContext(resolvedCwd);
17965
- const scopeKey = buildScopeKey(options.serverScope ?? "all");
18500
+ const serverScope = options.serverScope ?? "all";
18501
+ const scopeKey = buildProbeScopeKey(serverScope, options.excludeServerIds);
17966
18502
  const resultsByIndex = /* @__PURE__ */ new Map();
17967
18503
  let completed = 0;
17968
18504
  const pendingIndices = /* @__PURE__ */ new Map();
@@ -17973,6 +18509,11 @@ async function collectBatchDiagnostics(files, severity, lspService, options) {
17973
18509
  else
17974
18510
  pendingIndices.set(file, [index]);
17975
18511
  });
18512
+ lspService.announceCreatedFiles([
18513
+ ...files,
18514
+ ...goPackageFilesToAnnounce(files),
18515
+ ...await importTargetsToAnnounce(resolvedCwd, files.filter((file) => !cacheCtx.servesClean(file, scopeKey)), (file, scope) => cacheCtx.noteImportScope(file, scope))
18516
+ ]);
17976
18517
  const groups = groupFilesByPrimaryServer(files);
17977
18518
  await runPerServerGroups(groups, options.concurrency, async (group) => {
17978
18519
  if (options.signal?.aborted)
@@ -17987,7 +18528,7 @@ async function collectBatchDiagnostics(files, severity, lspService, options) {
17987
18528
  }
17988
18529
  await mapWithConcurrency(group.files, 1, async (file) => {
17989
18530
  const index = pendingIndices.get(file).shift();
17990
- const work = collectFileDiagnosticResult(file, severity, lspService, options.waitMs, options.nextWriteIndex, options.serverScope, cacheCtx, scopeKey, resolvedCwd, options.onConfirmedNoBlockers);
18531
+ const work = collectFileDiagnosticResult(file, severity, lspService, options.waitMs, options.nextWriteIndex, options.serverScope, cacheCtx, scopeKey, resolvedCwd, options.onConfirmedNoBlockers, options.excludeServerIds);
17991
18532
  const bounded2 = withDeadline(work, {
17992
18533
  ms: batchFileDeadlineMs(),
17993
18534
  onTimeout: "undefined",
@@ -18018,7 +18559,7 @@ async function collectBatchDiagnostics(files, severity, lspService, options) {
18018
18559
  const total = allDiags.length;
18019
18560
  const truncated = total > MAX_DIAGNOSTICS;
18020
18561
  const display = truncated ? allDiags.slice(0, MAX_DIAGNOSTICS) : allDiags;
18021
- const { clean, unconfirmed, timedOut, rootFallbackReasons } = tallyConfirmation(results);
18562
+ const { clean, unconfirmed, timedOut, primaryUnavailable, rootFallbackReasons } = tallyConfirmation(results);
18022
18563
  for (const result of results) {
18023
18564
  result.outcome = classifyBatchFileOutcome(result);
18024
18565
  }
@@ -18050,6 +18591,7 @@ async function collectBatchDiagnostics(files, severity, lspService, options) {
18050
18591
  unconfirmed,
18051
18592
  timedOut,
18052
18593
  rootFallbackReasons,
18594
+ primaryUnavailable,
18053
18595
  outcomeCounts,
18054
18596
  incompleteFiles: Math.max(0, files.length - results.length)
18055
18597
  };
@@ -18059,7 +18601,7 @@ async function expandPathsToFiles(cwd, rawPaths) {
18059
18601
  const directories = [];
18060
18602
  const seenFileKeys = /* @__PURE__ */ new Set();
18061
18603
  for (const entry of rawPaths) {
18062
- const abs = normalizeMapKey(path36.resolve(cwd, entry));
18604
+ const abs = normalizeMapKey(resolveHostToolPath(entry, cwd));
18063
18605
  let stat;
18064
18606
  try {
18065
18607
  stat = fs22.statSync(abs);
@@ -18104,7 +18646,7 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options,
18104
18646
  details: { mode: "batch", severity, filesChecked: 0 }
18105
18647
  };
18106
18648
  }
18107
- const { results, fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut, rootFallbackReasons, outcomeCounts, incompleteFiles } = await collectBatchDiagnostics(absPaths, severity, lspService, options);
18649
+ const { results, fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut, rootFallbackReasons, primaryUnavailable, outcomeCounts, incompleteFiles } = await collectBatchDiagnostics(absPaths, severity, lspService, options);
18108
18650
  const navigationOnly = results.filter((result) => result.diagnosticsUnsupported).length;
18109
18651
  const dispositionSuppressed = results.reduce((sum, result) => sum + (result.dispositionSuppressed ?? 0), 0);
18110
18652
  const lines = [
@@ -18130,7 +18672,7 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options,
18130
18672
  const outcomeDetails = results.map((result) => ({
18131
18673
  file: result.file,
18132
18674
  outcome: result.outcome,
18133
- reason: result.tooLargeReason ?? result.inconclusiveReason ?? result.error ?? result.unavailable
18675
+ reason: batchOutcomeReason(result)
18134
18676
  }));
18135
18677
  const tooLargeLines = renderBatchOutcomeLines(outcomeDetails);
18136
18678
  if (tooLargeLines.length > 0) {
@@ -18140,8 +18682,11 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options,
18140
18682
  lines.push("", "LSP health warnings:", ...lspHealthWarnings.slice(0, 10));
18141
18683
  }
18142
18684
  if (unconfirmed > 0) {
18143
- lines.push("", `${clean} file${clean === 1 ? "" : "s"} confirmed clean, ${unconfirmed} unconfirmed: ${unconfirmedReasonClause(unconfirmed, timedOut, rootFallbackReasons)} NOT the same as 0 diagnostics.`);
18685
+ lines.push("", `${clean} file${clean === 1 ? "" : "s"} confirmed clean, ${unconfirmed} unconfirmed: ${unconfirmedReasonClause(unconfirmed, timedOut, rootFallbackReasons, primaryUnavailable)} NOT the same as 0 diagnostics.`);
18144
18686
  }
18687
+ const auxiliaryOnlyLine = auxiliaryOnlyFindingsLine(results);
18688
+ if (auxiliaryOnlyLine)
18689
+ lines.push("", auxiliaryOnlyLine);
18145
18690
  if (display.length === 0) {
18146
18691
  if (unconfirmed === 0 && outcomeCounts.clean === results.length) {
18147
18692
  lines.push("", "No diagnostics found.");
@@ -18186,7 +18731,7 @@ async function runBatchFileDiagnostics(absPaths, severity, lspService, options,
18186
18731
  outcomes: results.map((result) => ({
18187
18732
  file: result.file,
18188
18733
  outcome: result.outcome,
18189
- reason: result.tooLargeReason ?? result.inconclusiveReason ?? result.error ?? result.unavailable,
18734
+ reason: batchOutcomeReason(result),
18190
18735
  primaryDiagnosticsCount: result.diagnostics.filter((diagnostic) => diagnostic.serverId === result.primaryServerId).length,
18191
18736
  auxiliaryDiagnosticsCount: result.diagnostics.filter((diagnostic) => diagnostic.serverId !== result.primaryServerId).length
18192
18737
  })),
@@ -18254,26 +18799,30 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
18254
18799
  }
18255
18800
  };
18256
18801
  }
18257
- const { results, fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut, rootFallbackReasons, outcomeCounts } = await collectBatchDiagnostics(filesToProcess, severity, lspService, options);
18802
+ const { results, fileErrors, lspHealthWarnings, total, truncated, display, primaryDisplay, auxiliaryDisplay, clean, unconfirmed, timedOut, rootFallbackReasons, primaryUnavailable, outcomeCounts } = await collectBatchDiagnostics(filesToProcess, severity, lspService, options);
18258
18803
  const navigationOnly = results.filter((result) => result.diagnosticsUnsupported).length;
18259
18804
  const dispositionSuppressed = results.reduce((sum, result) => sum + (result.dispositionSuppressed ?? 0), 0);
18260
18805
  const suppressedLine = dispositionSuppressedLine(dispositionSuppressed);
18261
18806
  const outcomeDetails = results.map((result) => ({
18262
18807
  file: result.file,
18263
18808
  outcome: result.outcome,
18264
- reason: result.tooLargeReason ?? result.inconclusiveReason ?? result.error ?? result.unavailable
18809
+ reason: batchOutcomeReason(result)
18265
18810
  }));
18266
- const tooLargeLines = renderBatchOutcomeLines(outcomeDetails, (file) => path36.relative(absPath, file));
18811
+ const auxiliaryOnlyLine = auxiliaryOnlyFindingsLine(results);
18812
+ const tooLargeLines = renderBatchOutcomeLines(outcomeDetails, (file) => path37.relative(absPath, file));
18267
18813
  let text;
18268
18814
  if (total === 0) {
18269
- const cleanLine = unconfirmed > 0 ? `${clean} clean \xB7 ${unconfirmed} unconfirmed: ${unconfirmedReasonClause(unconfirmed, timedOut, rootFallbackReasons)} NOT the same as 0 diagnostics.` : "No diagnostics found.";
18815
+ const cleanLine = unconfirmed > 0 ? `${clean} clean \xB7 ${unconfirmed} unconfirmed: ${unconfirmedReasonClause(unconfirmed, timedOut, rootFallbackReasons, primaryUnavailable)} NOT the same as 0 diagnostics.` : "No diagnostics found.";
18270
18816
  text = [
18271
18817
  `Directory: ${absPath}`,
18272
18818
  `Files scanned: ${filesToProcess.length}${wasCapped ? ` (capped at ${MAX_FILES})` : ""}`,
18273
18819
  ...lspHealthWarnings.length > 0 ? [
18274
18820
  "LSP unavailable for one or more files:",
18275
18821
  ...lspHealthWarnings.slice(0, 10)
18276
- ] : unconfirmed > 0 || outcomeCounts.clean === filesToProcess.length ? [cleanLine] : [],
18822
+ ] : [],
18823
+ // #4403: an unconfirmed file says why even beside a health warning,
18824
+ // which names what was missing but not that the file is unconfirmed.
18825
+ ...unconfirmed > 0 || lspHealthWarnings.length === 0 && outcomeCounts.clean === filesToProcess.length ? [cleanLine] : [],
18277
18826
  ...tooLargeLines.length > 0 ? ["", "Files too large for LSP diagnostics:", ...tooLargeLines] : [],
18278
18827
  ...suppressedLine ? ["", suppressedLine] : []
18279
18828
  ].join("\n");
@@ -18289,13 +18838,14 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
18289
18838
  // whole found diagnostics elsewhere.
18290
18839
  ...unconfirmed > 0 ? [
18291
18840
  "",
18292
- `${clean} other file${clean === 1 ? "" : "s"} confirmed clean, ${unconfirmed} unconfirmed: ` + unconfirmedReasonClause(unconfirmed, timedOut, rootFallbackReasons)
18841
+ `${clean} other file${clean === 1 ? "" : "s"} confirmed clean, ${unconfirmed} unconfirmed: ` + unconfirmedReasonClause(unconfirmed, timedOut, rootFallbackReasons, primaryUnavailable)
18293
18842
  ] : [],
18843
+ ...auxiliaryOnlyLine ? ["", auxiliaryOnlyLine] : [],
18294
18844
  ""
18295
18845
  ];
18296
18846
  const toRelative = (d) => ({
18297
18847
  ...d,
18298
- file: path36.relative(absPath, d.file)
18848
+ file: path37.relative(absPath, d.file)
18299
18849
  });
18300
18850
  if (primaryDisplay.length > 0) {
18301
18851
  lines.push(`Primary findings (${primaryDisplay.length}):`);
@@ -18326,7 +18876,7 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
18326
18876
  filesScanned: filesToProcess.length,
18327
18877
  capped: wasCapped,
18328
18878
  diagnostics: display.map((d) => ({
18329
- file: path36.relative(absPath, d.file),
18879
+ file: path37.relative(absPath, d.file),
18330
18880
  line: d.line,
18331
18881
  character: d.character,
18332
18882
  severity: d.severity,
@@ -18352,11 +18902,6 @@ async function runDirectoryDiagnostics(absPath, severity, lspService, options) {
18352
18902
  }
18353
18903
  };
18354
18904
  }
18355
- function dispositionSuppressedLine(suppressed) {
18356
- if (suppressed <= 0)
18357
- return void 0;
18358
- return `suppressed by disposition: ${suppressed} finding(s) dropped from this result because they're marked false-positive/won't-fix, disabled by \`.pi-lens.json\` rule policy, or suppressed by an inline \`pi-lens-ignore\` comment. Not a clean verdict for those locations \u2014 they were found, then intentionally hidden.`;
18359
- }
18360
18905
  function applySeverityFilter(diags, severity) {
18361
18906
  if (severity === "all")
18362
18907
  return diags;
@@ -18423,7 +18968,7 @@ async function findFullScanBindingMismatches(results) {
18423
18968
  return mismatched;
18424
18969
  }
18425
18970
  function createLensDiagnosticsTool(cacheManager2, getCwd, getLspService = getLSPService, flushPending = async () => {
18426
- }, nextWriteIndex, captureLspStatusRepaint, getRuntime, isLensGuardEnabled2 = () => true) {
18971
+ }, nextWriteIndex, captureLspStatusRepaint, getRuntime, isLensGuardEnabled2 = () => true, getFlag) {
18427
18972
  const lspProbe = createLspDiagnosticsTool(nextWriteIndex, (info) => {
18428
18973
  const runtime = getRuntime?.();
18429
18974
  if (!runtime)
@@ -18444,7 +18989,7 @@ function createLensDiagnosticsTool(cacheManager2, getCwd, getLspService = getLSP
18444
18989
  message: `inline_blocker: retired for ${info.filePath}`
18445
18990
  });
18446
18991
  }
18447
- }, getLspService);
18992
+ }, getLspService, getFlag);
18448
18993
  return {
18449
18994
  name: "lens_diagnostics",
18450
18995
  label: "Project Diagnostics",
@@ -18456,7 +19001,7 @@ function createLensDiagnosticsTool(cacheManager2, getCwd, getLspService = getLSP
18456
19001
  const files2 = details.filesChecked ?? details.filesScanned ?? 0;
18457
19002
  const noun = count === 1 ? "diagnostic" : "diagnostics";
18458
19003
  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;
18459
- const singleFile = (details?.mode === "file" || files2 === 1) && typeof filePath === "string" ? ` ${path37.basename(filePath)}` : "";
19004
+ const singleFile = (details?.mode === "file" || files2 === 1) && typeof filePath === "string" ? ` ${path38.basename(filePath)}` : "";
18460
19005
  const scope = files2 > 1 ? ` across ${files2} files` : singleFile;
18461
19006
  if (isError)
18462
19007
  return `lens_diagnostics lsp \u2014 ${text.split("\n")[0] ?? "error"}`;
@@ -18474,7 +19019,10 @@ function createLensDiagnosticsTool(cacheManager2, getCwd, getLspService = getLSP
18474
19019
  if ((details.incompleteFiles ?? 0) > 0)
18475
19020
  return `lens_diagnostics${scope} \u2014 incomplete (${details.incompleteFiles} files not confirmed)`;
18476
19021
  if (count === 0 && details.unconfirmed)
18477
- return details.timedOut ? `lens_diagnostics${scope} \u2014 timed out (result may be incomplete)` : `lens_diagnostics${scope} \u2014 unconfirmed (server cannot confirm clean)`;
19022
+ return details.timedOut ? `lens_diagnostics${scope} \u2014 timed out (result may be incomplete)` : (
19023
+ // #4322: no language server answered; it is not a silent server.
19024
+ details.primaryUnavailable !== void 0 ? `lens_diagnostics${scope} \u2014 unconfirmed (no language server answered)` : `lens_diagnostics${scope} \u2014 unconfirmed (server cannot confirm clean)`
19025
+ );
18478
19026
  return `lens_diagnostics${scope} \u2014 ${count} ${noun}`;
18479
19027
  }
18480
19028
  const scanning = scanningSummaryLine(details, text);
@@ -18584,6 +19132,7 @@ function createLensDiagnosticsTool(cacheManager2, getCwd, getLspService = getLSP
18584
19132
  const hasExplicitPaths = Array.isArray(lspParams.paths) && lspParams.paths.length > 0;
18585
19133
  if (scope === "workspace" && !hasExplicitPath && !hasExplicitPaths) {
18586
19134
  lspParams.path = cwd;
19135
+ lspParams.pathAlreadyResolved = true;
18587
19136
  }
18588
19137
  const lspStart = Date.now();
18589
19138
  const result = await lspProbe.execute(_toolCallId, lspParams, signal, onUpdate, ctx.signal ? { cwd, signal: ctx.signal } : { cwd });
@@ -18702,7 +19251,7 @@ function appendProjectDiagnosticsDeltaLines(groups, cwd, report, severity, inclu
18702
19251
  const diagnostics = [...gated.live, ...gated.stale];
18703
19252
  const byFile = /* @__PURE__ */ new Map();
18704
19253
  for (const diagnostic of diagnostics) {
18705
- const filePath = path37.resolve(diagnostic.filePath);
19254
+ const filePath = path38.resolve(diagnostic.filePath);
18706
19255
  const bucket = byFile.get(filePath) ?? [];
18707
19256
  bucket.push(diagnostic);
18708
19257
  byFile.set(filePath, bucket);
@@ -18829,7 +19378,7 @@ function applyDeltaFreshnessGate(files, cwd, generatedAt) {
18829
19378
  })).filter((file) => file.warnings.length > 0);
18830
19379
  }
18831
19380
  function getDeltaFileGroup(groups, cwd, filePath) {
18832
- const rel = path37.relative(cwd, filePath);
19381
+ const rel = path38.relative(cwd, filePath);
18833
19382
  let group = groups.get(rel);
18834
19383
  if (!group) {
18835
19384
  group = {
@@ -18953,7 +19502,7 @@ Summary (turn delta): ${aw} actionable warning${aw === 1 ? "" : "s"} \xB7 ${cq}
18953
19502
  function createCurrentIgnoreFilter(cwd) {
18954
19503
  try {
18955
19504
  const matcher = getProjectIgnoreMatcher(cwd);
18956
- return (filePath) => !matcher.isIgnored(path37.resolve(filePath), false);
19505
+ return (filePath) => !matcher.isIgnored(path38.resolve(filePath), false);
18957
19506
  } catch {
18958
19507
  return () => true;
18959
19508
  }
@@ -18964,7 +19513,7 @@ function resolvePathsScope(cwd, rawPaths) {
18964
19513
  if (rawPaths.length > MAX_PATHS_ENTRIES) {
18965
19514
  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.`);
18966
19515
  }
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)));
19516
+ const entries = rawPaths.filter((entry) => typeof entry === "string" && entry.trim().length > 0).map((entry) => resolveHostToolPath(entry, cwd));
18968
19517
  const missing = [];
18969
19518
  const fileEntries = [];
18970
19519
  const existingEntries = [];
@@ -18987,9 +19536,9 @@ function resolvePathsScope(cwd, rawPaths) {
18987
19536
  }
18988
19537
  const fileKeys = new Set(fileEntries.map((f) => normalizeFilePath(f)));
18989
19538
  const dirPrefixes = dirEntries.map((d) => normalizeFilePath(d));
18990
- const includeExplicitFile = (filePath) => fileKeys.has(normalizeFilePath(path37.resolve(filePath)));
19539
+ const includeExplicitFile = (filePath) => fileKeys.has(normalizeFilePath(path38.resolve(filePath)));
18991
19540
  const includeFile = (filePath) => {
18992
- const key = normalizeFilePath(path37.resolve(filePath));
19541
+ const key = normalizeFilePath(path38.resolve(filePath));
18993
19542
  if (fileKeys.has(key))
18994
19543
  return true;
18995
19544
  return dirPrefixes.some((prefix) => key === prefix || key.startsWith(`${prefix}/`));
@@ -19010,7 +19559,7 @@ function pathsScopeCacheOnlyNote(scope) {
19010
19559
  function pathsScopeMissingNote(scope) {
19011
19560
  if (!scope || scope.missing.length === 0)
19012
19561
  return "";
19013
- const shown2 = scope.missing.slice(0, 10).map((p) => path37.basename(p));
19562
+ const shown2 = scope.missing.slice(0, 10).map((p) => path38.basename(p));
19014
19563
  const more = scope.missing.length > shown2.length ? ` (+${scope.missing.length - shown2.length} more)` : "";
19015
19564
  return `
19016
19565
 
@@ -19141,7 +19690,7 @@ function projectDiagnosticToWidget(diagnostic, filePath) {
19141
19690
  }
19142
19691
  function diagnosticDedupKey(filePath, diagnostic) {
19143
19692
  const ruleId = normalizeRuleId(diagnostic.rule ?? diagnostic.tool ?? "");
19144
- return [path37.resolve(filePath), diagnostic.line ?? "?", ruleId].join(":");
19693
+ return [path38.resolve(filePath), diagnostic.line ?? "?", ruleId].join(":");
19145
19694
  }
19146
19695
  function runnerIdOf(diagnostic) {
19147
19696
  return diagnostic.tool ?? diagnostic.rule?.split(":", 1)[0] ?? "";
@@ -19155,8 +19704,8 @@ function runnerRetirementDecision(diagnostic, filePath, authoritativeRunnerIds,
19155
19704
  const realFilePath = realpathOrResolve(filePath);
19156
19705
  for (const entry of coverage) {
19157
19706
  const root = entry.root;
19158
- const relative9 = path37.relative(root, realFilePath);
19159
- const underRoot = relative9 === "" || !relative9.startsWith("..") && !path37.isAbsolute(relative9);
19707
+ const relative9 = path38.relative(root, realFilePath);
19708
+ const underRoot = relative9 === "" || !relative9.startsWith("..") && !path38.isAbsolute(relative9);
19160
19709
  if (!underRoot)
19161
19710
  continue;
19162
19711
  if (entry.files.has(realFilePath)) {
@@ -19206,7 +19755,11 @@ var UNCONFIRMED_REASON_SENTENCE = {
19206
19755
  binding_mismatch: "the file changed on disk since this result was computed",
19207
19756
  // #2052: no server was asked. Say WHY and say it is permanent for this
19208
19757
  // path, so the reader does not read an empty result as "clean" or retry it.
19209
- outside_project_root: "the file is outside every initialized session project root, so no language server was asked"
19758
+ outside_project_root: "the file is outside every initialized session project root, so no language server was asked",
19759
+ // #4403: the per-file note's wording, one sentence per cause.
19760
+ primary_unavailable: `no primary language server answered (${PRIMARY_UNAVAILABLE_CAUSE.unstarted})`,
19761
+ primary_disabled: `no primary language server answered (${PRIMARY_UNAVAILABLE_CAUSE.disabled})`,
19762
+ primary_disabled_fallback: `no primary language server answered (${PRIMARY_UNAVAILABLE_CAUSE.disabledFallback})`
19210
19763
  };
19211
19764
  var UNCONFIRMED_REASON_COUNT_LABEL = {
19212
19765
  budget: "timed out",
@@ -19215,7 +19768,10 @@ var UNCONFIRMED_REASON_COUNT_LABEL = {
19215
19768
  service_destroyed: "service reset mid-sweep",
19216
19769
  error: "errored",
19217
19770
  binding_mismatch: "stale binding",
19218
- outside_project_root: "outside project root"
19771
+ outside_project_root: "outside project root",
19772
+ primary_unavailable: "no primary started",
19773
+ primary_disabled: "primary disabled in config",
19774
+ primary_disabled_fallback: "primary disabled, fallback did not answer"
19219
19775
  };
19220
19776
  function classifyUnconfirmedReason(result, mismatched) {
19221
19777
  if (mismatched.has(result))
@@ -19280,7 +19836,7 @@ function mergeDiagnosticsWithWidgetSummaries(widgetSummaries, lspResults, projec
19280
19836
  const byFile = /* @__PURE__ */ new Map();
19281
19837
  const seen = /* @__PURE__ */ new Set();
19282
19838
  for (const summary of widgetSummaries) {
19283
- const filePath = path37.resolve(summary.filePath);
19839
+ const filePath = path38.resolve(summary.filePath);
19284
19840
  if (authoritativeLspFiles?.has(filePath))
19285
19841
  continue;
19286
19842
  const retained = summary.diagnostics ?? [];
@@ -19308,7 +19864,7 @@ function mergeDiagnosticsWithWidgetSummaries(widgetSummaries, lspResults, projec
19308
19864
  byFile.set(filePath, summarizeDiagnostics(filePath, diagnostics, existing?.hasFinalSnapshot ?? true));
19309
19865
  };
19310
19866
  for (const result of lspResults) {
19311
- const filePath = path37.resolve(result.filePath);
19867
+ const filePath = path38.resolve(result.filePath);
19312
19868
  for (const diagnostic of result.diagnostics ?? []) {
19313
19869
  addDiagnostic(filePath, {
19314
19870
  ...lspDiagnosticToWidget(diagnostic),
@@ -19328,19 +19884,19 @@ function mergeDiagnosticsWithWidgetSummaries(widgetSummaries, lspResults, projec
19328
19884
  });
19329
19885
  }
19330
19886
  for (const diagnostic of projectSnapshot?.diagnostics ?? []) {
19331
- addDiagnostic(path37.resolve(diagnostic.filePath), projectDiagnosticToWidget(diagnostic, diagnostic.filePath));
19887
+ addDiagnostic(path38.resolve(diagnostic.filePath), projectDiagnosticToWidget(diagnostic, diagnostic.filePath));
19332
19888
  }
19333
19889
  for (const diagnostic of deltaDiagnostics) {
19334
19890
  const widget = projectDiagnosticToWidget(diagnostic, diagnostic.filePath);
19335
19891
  const observedAt = widget.observedAt ?? deltaObservedAt;
19336
- addDiagnostic(path37.resolve(diagnostic.filePath), {
19892
+ addDiagnostic(path38.resolve(diagnostic.filePath), {
19337
19893
  ...widget,
19338
19894
  ...observedAt !== void 0 && { observedAt }
19339
19895
  });
19340
19896
  }
19341
19897
  return [...byFile.values()];
19342
19898
  }
19343
- async function applyInlineSuppressionsToSummaries(summaries, cwd, policyMap) {
19899
+ async function applyInlineSuppressionsToSummaries(summaries, cwd, policyMap, dispositionDropped) {
19344
19900
  return Promise.all(summaries.map(async (summary) => {
19345
19901
  if (!summary.diagnostics.length)
19346
19902
  return summary;
@@ -19354,12 +19910,19 @@ async function applyInlineSuppressionsToSummaries(summaries, cwd, policyMap) {
19354
19910
  });
19355
19911
  return policyKept2.length === summary.diagnostics.length ? summary : summarizeDiagnostics(summary.filePath, policyKept2, summary.hasFinalSnapshot);
19356
19912
  }
19357
- const { kept: policyKept } = applyFindingPolicy(summary.diagnostics, {
19913
+ const { kept: policyKept, inlineKept, disposed } = applyFindingPolicy(summary.diagnostics, {
19358
19914
  cwd,
19359
19915
  filePath: summary.filePath,
19360
19916
  content,
19361
19917
  policyMap
19362
19918
  });
19919
+ if (disposed.length < inlineKept.length) {
19920
+ const survivors = new Set(disposed);
19921
+ for (const d of inlineKept) {
19922
+ if (!survivors.has(d))
19923
+ dispositionDropped.push(d);
19924
+ }
19925
+ }
19363
19926
  for (const d of policyKept) {
19364
19927
  const { weak } = anchorsForDiagnostic(cwd, summary.filePath, d, content);
19365
19928
  if (getDisposition(cwd, weak)?.disposition === "flagged") {
@@ -19517,13 +20080,21 @@ async function formatFullMode(cwd, severity, lspService, cacheManager2, options
19517
20080
  const unconfirmedLspResults = lspResults.filter((result2) => result2.timedOut || result2.error || mismatchedLspResults.has(result2));
19518
20081
  const authoritativeLspFiles = /* @__PURE__ */ new Set();
19519
20082
  const unreconciledFiles = /* @__PURE__ */ new Set();
20083
+ const policyMap = loadProjectRulePolicyMap(cwd);
19520
20084
  for (const result2 of fullyCoveredLspResults) {
19521
20085
  try {
19522
20086
  const diagnostics = convertLspDiagnostics(result2.diagnostics, result2.filePath, { scanOrigin: "lens_diagnostics_full" });
19523
20087
  const retagged = retagAuxiliaryDiagnostics(diagnostics, result2.diagnostics, "", { cwd, fileRole: detectFileRole(result2.filePath) });
20088
+ const content = readFileContent(result2.filePath);
20089
+ const { kept: policyKept } = applyFindingPolicy(retagged, {
20090
+ cwd,
20091
+ filePath: result2.filePath,
20092
+ content: content ?? "",
20093
+ policyMap
20094
+ });
19524
20095
  const retired = reconcileScanDiagnostics(
19525
20096
  result2.filePath,
19526
- retagged,
20097
+ policyKept,
19527
20098
  true,
19528
20099
  // The service reserves this token when the file enters the scan. The
19529
20100
  // fallback preserves compatibility with older test doubles/services.
@@ -19536,32 +20107,31 @@ async function formatFullMode(cwd, severity, lspService, cacheManager2, options
19536
20107
  result2.observedAt
19537
20108
  );
19538
20109
  if (retired === true) {
19539
- authoritativeLspFiles.add(path37.resolve(result2.filePath));
20110
+ authoritativeLspFiles.add(path38.resolve(result2.filePath));
19540
20111
  } else {
19541
- unreconciledFiles.add(path37.resolve(result2.filePath));
20112
+ unreconciledFiles.add(path38.resolve(result2.filePath));
19542
20113
  recordDegradationOnce({
19543
20114
  kind: "diagnostic-retained-unreconciled",
19544
- subject: `${path37.resolve(result2.filePath)}:lsp`,
20115
+ subject: `${path38.resolve(result2.filePath)}:lsp`,
19545
20116
  reason: "confirmed diagnostic result could not replace retained widget state"
19546
20117
  });
19547
20118
  }
19548
20119
  } catch {
19549
- unreconciledFiles.add(path37.resolve(result2.filePath));
20120
+ unreconciledFiles.add(path38.resolve(result2.filePath));
19550
20121
  recordDegradationOnce({
19551
20122
  kind: "diagnostic-retained-unreconciled",
19552
- subject: `${path37.resolve(result2.filePath)}:lsp`,
20123
+ subject: `${path38.resolve(result2.filePath)}:lsp`,
19553
20124
  reason: "confirmed diagnostic result failed during widget reconciliation"
19554
20125
  });
19555
20126
  }
19556
20127
  }
19557
- const policyMap = loadProjectRulePolicyMap(cwd);
19558
20128
  const scannedSnapshot = filterProjectDiagnosticsSnapshot(rawProjectSnapshot, includeFile);
19559
20129
  const authoritativeRunnerIds = new Set(extracted.analyzed ?? []);
19560
20130
  const authoritativeRunnerCoverage = extracted.authoritativeCoverage ?? [];
19561
20131
  const foldedProjectSnapshot = foldExtraDiagnosticsIntoSnapshot(scannedSnapshot, extracted.diagnostics.filter((d) => includeFile(d.filePath)), extracted.runners, cwd);
19562
20132
  const projectSnapshot = applyProjectRulePolicy(foldedProjectSnapshot, policyMap, cwd);
19563
20133
  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;
20134
+ const authoritativeRetiredCount = getFileDiagnosticSummaries().filter((summary) => includeFile(summary.filePath) && authoritativeLspFiles.has(path38.resolve(summary.filePath)) && (summary.diagnostics?.length ?? 0) > 0).length;
19565
20135
  if (authoritativeRetiredCount > 0) {
19566
20136
  logLatency({
19567
20137
  type: "phase",
@@ -19614,9 +20184,10 @@ async function formatFullMode(cwd, severity, lspService, cacheManager2, options
19614
20184
  }
19615
20185
  });
19616
20186
  }
19617
- let summaries = await applyInlineSuppressionsToSummaries(mergeDiagnosticsWithWidgetSummaries(getFileDiagnosticSummaries().filter((summary) => includeFile(summary.filePath)), confirmedLspResults, projectSnapshot, projectDelta, authoritativeLspFiles, authoritativeRunnerIds, authoritativeRunnerCoverage), cwd, policyMap);
20187
+ const dispositionDropped = [];
20188
+ let summaries = await applyInlineSuppressionsToSummaries(mergeDiagnosticsWithWidgetSummaries(getFileDiagnosticSummaries().filter((summary) => includeFile(summary.filePath)), confirmedLspResults, projectSnapshot, projectDelta, authoritativeLspFiles, authoritativeRunnerIds, authoritativeRunnerCoverage), cwd, policyMap, dispositionDropped);
19618
20189
  if (unreconciledFiles.size > 0) {
19619
- summaries = summaries.map((summary) => unreconciledFiles.has(path37.resolve(summary.filePath)) ? summarizeDiagnostics(summary.filePath, markUnreconciledFindings(summary.diagnostics), summary.hasFinalSnapshot) : summary);
20190
+ summaries = summaries.map((summary) => unreconciledFiles.has(path38.resolve(summary.filePath)) ? summarizeDiagnostics(summary.filePath, markUnreconciledFindings(summary.diagnostics), summary.hasFinalSnapshot) : summary);
19620
20191
  }
19621
20192
  const parsedProjectScanObservedAt = projectSnapshot?.scannedAt ? Date.parse(projectSnapshot.scannedAt) : void 0;
19622
20193
  const projectScanObservedAt = parsedProjectScanObservedAt !== void 0 && Number.isFinite(parsedProjectScanObservedAt) ? parsedProjectScanObservedAt : void 0;
@@ -19638,14 +20209,26 @@ async function formatFullMode(cwd, severity, lspService, cacheManager2, options
19638
20209
  sources: projectDelta.sources,
19639
20210
  turnIndex: projectDelta.turnIndex
19640
20211
  }
19641
- }, 0, pathsScope);
20212
+ }, 0, pathsScope, 0, {
20213
+ dropped: dispositionDropped,
20214
+ // #1617: the #1616 suppressed-bucket rule applied to the analyzers —
20215
+ // a finding marked false-positive/won't-fix drops out of
20216
+ // `diagnostics` (fresh-fetch.ts's `record()`), and the drop stays
20217
+ // visible as a count. Review-round F4 (#1625): per-lane attribution
20218
+ // ("gitleaks 2, knip 1") says WHICH analyzer's marks are doing the
20219
+ // suppressing. A lane that's 100% suppressed still won't appear in
20220
+ // `runners`/`cold` as distinct from "ran clean" — #1623's territory.
20221
+ analyzers: extracted.dispositionSuppressed ?? 0,
20222
+ analyzersByLane: extracted.dispositionSuppressedByLane ?? {}
20223
+ });
19642
20224
  const missingNote = pathsScopeMissingNote(pathsScope);
19643
20225
  const unconfirmedByReason = tallyUnconfirmedReasons(unconfirmedLspResults, mismatchedLspResults);
19644
20226
  const lspServerBreakdown = tallyLspByPrimaryServer(confirmedLspResults, unconfirmedLspResults);
19645
20227
  const serverBreakdownClause = lspServerBreakdown.size > 1 ? ` \u2014 by server: ${formatLspServerBreakdown(lspServerBreakdown)}.` : "";
20228
+ const retryClause = [...unconfirmedByReason.keys()].every((reason) => reason === "primary_disabled") ? "" : "; re-run mode=full to retry them";
19646
20229
  const unconfirmedLspNote = unconfirmedLspResults.length > 0 ? `
19647
20230
 
19648
- \u26A0 LSP sweep: ${confirmedLspResults.length} file(s) confirmed via LSP, ${unconfirmedLspResults.length} unconfirmed (${formatUnconfirmedReasonClause(unconfirmedByReason)})${serverBreakdownClause} \u2014 NOT the same as 0 diagnostics for: ${unconfirmedLspResults.slice(0, 20).map((result2) => result2.filePath).join(", ")}${unconfirmedLspResults.length > 20 ? ", \u2026" : ""}. These files' LSP contribution is excluded from this result; re-run mode=full to retry them (they may still show findings above from cached/project-runner state).` : "";
20231
+ \u26A0 LSP sweep: ${confirmedLspResults.length} file(s) confirmed via LSP, ${unconfirmedLspResults.length} unconfirmed (${formatUnconfirmedReasonClause(unconfirmedByReason)})${serverBreakdownClause} \u2014 NOT the same as 0 diagnostics for: ${unconfirmedLspResults.slice(0, 20).map((result2) => result2.filePath).join(", ")}${unconfirmedLspResults.length > 20 ? ", \u2026" : ""}. These files' LSP contribution is excluded from this result${retryClause} (they may still show findings above from cached/project-runner state).` : "";
19649
20232
  const uncoveredScannerIds = [
19650
20233
  ...new Set(partiallyCoveredLspResults.flatMap((result2) => result2.unconfirmedServerIds ?? []))
19651
20234
  ].sort((a, b) => Number(a > b) - Number(a < b));
@@ -19693,10 +20276,6 @@ test-runner coverage is edit-scoped, NOT a full-project run: its findings only r
19693
20276
  const failedNote = failedAnalyzers.length > 0 ? `
19694
20277
 
19695
20278
  failed (ran, but no trustworthy result): ${failedAnalyzers.map(({ id, summary }) => `${id} \u2014 ${summary}`).join(", ")}. These analyzers were not cached and will be retried; absence of their findings is NOT a clean verdict.` : "";
19696
- const dispositionSuppressedByLaneText = Object.entries(extracted.dispositionSuppressedByLane ?? {}).map(([id, count]) => `${id} ${count}`).join(", ");
19697
- const dispositionSuppressedNote = (extracted.dispositionSuppressed ?? 0) > 0 ? `
19698
-
19699
- suppressed by disposition: ${extracted.dispositionSuppressed} finding(s) dropped from this result because they're marked false-positive or won't-fix (${dispositionSuppressedByLaneText}). Not a clean verdict for those locations \u2014 they were found, then intentionally hidden.` : "";
19700
20279
  const walkUnsafeRoot = explicitFiles === void 0 && isAtOrAboveHomeDir(cwd);
19701
20280
  const walkUnsafeRootNote = walkUnsafeRoot ? `
19702
20281
 
@@ -19744,7 +20323,9 @@ served from cache this call (not re-run): ${cachedAgeEntries.map(([id, ms]) => `
19744
20323
  coldReasons: extracted.coldReasons ?? {},
19745
20324
  failedAnalyzers,
19746
20325
  analyzerTimingsMs: extracted.timings,
19747
- dispositionSuppressed: extracted.dispositionSuppressed ?? 0,
20326
+ // #1634: every lane the call filtered — the sweep's and the
20327
+ // analyzers' — in one count, the one the text states.
20328
+ dispositionSuppressed: result.details.dispositionSuppressed ?? 0,
19748
20329
  dispositionSuppressedByLane: extracted.dispositionSuppressedByLane ?? {},
19749
20330
  // #1623: ms-old each cache-read-by-design lane's data was (today, only
19750
20331
  // test-runner) — lets a caller distinguish "just ran" from "served
@@ -19799,18 +20380,18 @@ served from cache this call (not re-run): ${cachedAgeEntries.map(([id, ms]) => `
19799
20380
  content: [
19800
20381
  {
19801
20382
  type: "text",
19802
- text: result.content[0].text + note + unconfirmedLspNote + auxiliaryCoverageNote + lspPrimaryVsAuxiliaryNote + partialNote + coldNote + testRunnerEditScopedNote + failedNote + dispositionSuppressedNote + walkUnsafeRootNote + scanTruncatedNote + generatedSkipNote + abortedNote + freshNote + cachedAgeNote + cheapScanStatusNote + missingNote
20383
+ text: result.content[0].text + note + unconfirmedLspNote + auxiliaryCoverageNote + lspPrimaryVsAuxiliaryNote + partialNote + coldNote + testRunnerEditScopedNote + failedNote + walkUnsafeRootNote + scanTruncatedNote + generatedSkipNote + abortedNote + freshNote + cachedAgeNote + cheapScanStatusNote + missingNote
19803
20384
  }
19804
20385
  ],
19805
20386
  details: { ...resultWithCold.details, timedOut }
19806
20387
  };
19807
20388
  }
19808
- if (missingNote || coldNote || partialNote || testRunnerEditScopedNote || failedNote || dispositionSuppressedNote || walkUnsafeRootNote || scanTruncatedNote || generatedSkipNote || abortedNote || freshNote || cachedAgeNote || cheapScanStatusNote || unconfirmedLspNote || auxiliaryCoverageNote || lspPrimaryVsAuxiliaryNote) {
20389
+ if (missingNote || coldNote || partialNote || testRunnerEditScopedNote || failedNote || walkUnsafeRootNote || scanTruncatedNote || generatedSkipNote || abortedNote || freshNote || cachedAgeNote || cheapScanStatusNote || unconfirmedLspNote || auxiliaryCoverageNote || lspPrimaryVsAuxiliaryNote) {
19809
20390
  return {
19810
20391
  content: [
19811
20392
  {
19812
20393
  type: "text",
19813
- text: result.content[0].text + unconfirmedLspNote + auxiliaryCoverageNote + lspPrimaryVsAuxiliaryNote + partialNote + coldNote + testRunnerEditScopedNote + failedNote + dispositionSuppressedNote + walkUnsafeRootNote + scanTruncatedNote + generatedSkipNote + abortedNote + freshNote + cachedAgeNote + cheapScanStatusNote + missingNote
20394
+ text: result.content[0].text + unconfirmedLspNote + auxiliaryCoverageNote + lspPrimaryVsAuxiliaryNote + partialNote + coldNote + testRunnerEditScopedNote + failedNote + walkUnsafeRootNote + scanTruncatedNote + generatedSkipNote + abortedNote + freshNote + cachedAgeNote + cheapScanStatusNote + missingNote
19814
20395
  }
19815
20396
  ],
19816
20397
  details: resultWithCold.details
@@ -19826,12 +20407,21 @@ async function projectResolvedCwd(summary, cwd) {
19826
20407
  diagnostics: summary.diagnostics ?? []
19827
20408
  };
19828
20409
  }
19829
- async function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummaries(), detailOverrides = { mode: "all" }, staleDropped = 0, pathsScope, dependencyDemoted = 0) {
20410
+ async function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummaries(), detailOverrides = { mode: "all" }, staleDropped = 0, pathsScope, dependencyDemoted = 0, fullModeDisposition = { dropped: [], analyzers: 0, analyzersByLane: {} }) {
19830
20411
  const footerCapped = summaries.reduce((n, s) => n + (s.diagnostics ?? []).filter((d) => d.footerRetired === true).length, 0);
19831
20412
  const staleNote = (staleDropped > 0 ? ` (${staleDropped} changed file${staleDropped === 1 ? "" : "s"} omitted as stale \u2014 use mode=full to rescan)` : "") + (dependencyDemoted > 0 ? ` (${dependencyDemoted} blocking finding${dependencyDemoted === 1 ? "" : "s"} demoted to [stale] \u2014 an imported file changed since; re-run to confirm)` : "") + (footerCapped > 0 ? ` (${footerCapped} demoted finding${footerCapped === 1 ? "" : "s"} capped after ${DEPENDENCY_DRIFT_MAX_DELIVERIES} unconfirmed deliveries \u2014 no longer shown in the pi-lens footer, still listed here; re-run to confirm)` : "");
19832
20413
  const isFullMode = detailOverrides.mode === "full";
19833
20414
  const includeFile = createScopedFileFilter(cwd, pathsScope);
19834
20415
  const policyMap = loadProjectRulePolicyMap(cwd);
20416
+ const inScope = (rows) => rows.reduce((n, d) => n + (matchesSeverity(d, severity) ? 1 : 0), 0);
20417
+ const sweepSuppressed = isFullMode ? inScope(fullModeDisposition.dropped) : 0;
20418
+ const lanes = [];
20419
+ for (const [id, count] of Object.entries(fullModeDisposition.analyzersByLane)) {
20420
+ if (count > 0)
20421
+ lanes.push(`${id} ${count}`);
20422
+ }
20423
+ const suppressedBreakdown = lanes.length > 0 ? `analyzer lanes: ${lanes.join(", ")}` : void 0;
20424
+ let dispositionSuppressed = sweepSuppressed + fullModeDisposition.analyzers;
19835
20425
  const dispositioned = isFullMode ? summaries : summaries.map((s) => {
19836
20426
  const diagnostics = s.diagnostics ?? [];
19837
20427
  const kept = applyCachedDispositions(diagnostics, cwd, s.filePath);
@@ -19839,6 +20429,9 @@ async function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummari
19839
20429
  root: cwd,
19840
20430
  filePath: s.filePath
19841
20431
  });
20432
+ if (includeFile(s.filePath)) {
20433
+ dispositionSuppressed += inScope(getSuppressedDiagnostics(s.filePath)) + inScope(diagnostics) - inScope(kept);
20434
+ }
19842
20435
  if (policyKept.length === kept.length && kept.length === diagnostics.length)
19843
20436
  return s;
19844
20437
  return summarizeDiagnostics(s.filePath, policyKept, s.hasFinalSnapshot);
@@ -19864,14 +20457,19 @@ async function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummari
19864
20457
  return s.blocking > 0 || s.errors > 0 || s.warnings > 0 || s.advisories > 0 || (s.diagnostics ?? []).some((d) => d.stale);
19865
20458
  });
19866
20459
  const pathsNote = isFullMode ? "" : pathsScopeCacheOnlyNote(pathsScope);
20460
+ const suppressedLine = dispositionSuppressedLine(dispositionSuppressed, suppressedBreakdown);
20461
+ const suppressedNote = suppressedLine ? `
20462
+ ${suppressedLine}` : "";
20463
+ const suppressedDetails = dispositionSuppressed > 0 ? { dispositionSuppressed } : {};
19867
20464
  if (withIssues.length === 0) {
19868
- const text = (visibleSummaries.length === 0 ? "No files diagnosed yet this session." : `No ${severity === "all" ? "" : severity + " "}issues across ${visibleSummaries.length} file${visibleSummaries.length === 1 ? "" : "s"} diagnosed this session. \u2713`) + staleNote + pathsNote;
20465
+ const text = (visibleSummaries.length === 0 ? "No files diagnosed yet this session." : `No ${severity === "all" ? "" : severity + " "}issues across ${visibleSummaries.length} file${visibleSummaries.length === 1 ? "" : "s"} diagnosed this session. \u2713`) + staleNote + suppressedNote + pathsNote;
19869
20466
  return {
19870
20467
  content: [{ type: "text", text }],
19871
20468
  details: {
19872
20469
  ...detailOverrides,
19873
20470
  filesChecked: visibleSummaries.length,
19874
- staleDropped
20471
+ staleDropped,
20472
+ ...suppressedDetails
19875
20473
  }
19876
20474
  };
19877
20475
  }
@@ -19883,7 +20481,7 @@ async function formatAllMode(cwd, severity, summaries = getFileDiagnosticSummari
19883
20481
  let totalWarnings = 0;
19884
20482
  let totalAdvisories = 0;
19885
20483
  for (const s of rendered) {
19886
- const rel = path37.relative(cwd, s.filePath);
20484
+ const rel = path38.relative(cwd, s.filePath);
19887
20485
  const parts = [];
19888
20486
  if (s.blocking > 0)
19889
20487
  parts.push(`\u{1F534} ${s.blocking} blocking`);
@@ -19935,7 +20533,10 @@ Summary (${visibleSummaries.length} files diagnosed this session):`,
19935
20533
  ].filter(Boolean).join("\n");
19936
20534
  return {
19937
20535
  content: [
19938
- { type: "text", text: lines.join("\n") + summary + staleNote }
20536
+ {
20537
+ type: "text",
20538
+ text: lines.join("\n") + summary + staleNote + suppressedNote
20539
+ }
19939
20540
  ],
19940
20541
  details: {
19941
20542
  ...detailOverrides,
@@ -19944,14 +20545,15 @@ Summary (${visibleSummaries.length} files diagnosed this session):`,
19944
20545
  totalErrors,
19945
20546
  totalWarnings,
19946
20547
  totalAdvisories,
19947
- staleDropped
20548
+ staleDropped,
20549
+ ...suppressedDetails
19948
20550
  }
19949
20551
  };
19950
20552
  }
19951
20553
 
19952
20554
  // dist/tools/lsp-navigation.js
19953
20555
  import * as nodeFs2 from "node:fs";
19954
- import * as path38 from "node:path";
20556
+ import * as path39 from "node:path";
19955
20557
  import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL2 } from "node:url";
19956
20558
 
19957
20559
  // dist/tools/lsp-structured-output.js
@@ -20311,6 +20913,12 @@ function operationSupportStatus(operation, support) {
20311
20913
  return support.callHierarchy;
20312
20914
  return null;
20313
20915
  }
20916
+ function noServerAnsweredText(filePath) {
20917
+ const { disabledServerIds, fallbackServerIds } = disabledPrimaries(filePath);
20918
+ const cause = primaryUnavailableCause([...fallbackServerIds, ...disabledServerIds], disabledServerIds, fallbackServerIds);
20919
+ const who = disabledServerIds.length > 0 ? `Primary LSP (${disabledServerIds.join(", ")}) did not answer` : "no language server answered";
20920
+ return `No LSP server available for ${path39.basename(filePath)}: ${who} (${PRIMARY_UNAVAILABLE_CAUSE[cause]}).`;
20921
+ }
20314
20922
  function emptyReasonForOperation(operation) {
20315
20923
  if (operation === "signatureHelp")
20316
20924
  return "position-sensitive-or-no-signature";
@@ -20661,7 +21269,7 @@ function collectSearchReadsForOperation(operation, result, callHierarchyItem) {
20661
21269
  }
20662
21270
  function formatCapabilities(snapshots, filePath) {
20663
21271
  if (snapshots.length === 0) {
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.";
21272
+ return filePath ? `No active LSP server for ${path39.basename(filePath)}. Open/touch the file first or run another LSP operation to start the server.` : "No active LSP servers in this session.";
20665
21273
  }
20666
21274
  const rows = [
20667
21275
  ["definition", (s) => !!s.operationSupport.definition],
@@ -20691,7 +21299,7 @@ function formatCapabilities(snapshots, filePath) {
20691
21299
  ];
20692
21300
  const lines = [];
20693
21301
  for (const snapshot of snapshots) {
20694
- const label = filePath ? `${snapshot.serverId} (${path38.basename(filePath)})` : `${snapshot.serverId} (${snapshot.root})`;
21302
+ const label = filePath ? `${snapshot.serverId} (${path39.basename(filePath)})` : `${snapshot.serverId} (${snapshot.root})`;
20695
21303
  lines.push(label);
20696
21304
  for (const [name, supported, note] of rows) {
20697
21305
  const suffix = note ? ` (${note})` : "";
@@ -20719,7 +21327,7 @@ function classifyCodeActions(actions) {
20719
21327
  }
20720
21328
  return { quickfix, refactor, other };
20721
21329
  }
20722
- async function openFileBestEffort(lspService, filePath, waitForDiagnostics = false) {
21330
+ async function openFileBestEffort(lspService, filePath, waitForDiagnostics = false, excludeServerIds) {
20723
21331
  let fileContent;
20724
21332
  try {
20725
21333
  fileContent = nodeFs2.readFileSync(filePath, "utf-8");
@@ -20732,7 +21340,8 @@ async function openFileBestEffort(lspService, filePath, waitForDiagnostics = fal
20732
21340
  await lspService.touchFile(filePath, fileContent, {
20733
21341
  diagnostics: waitForDiagnostics ? "document" : "none",
20734
21342
  source: "lsp_navigation",
20735
- clientScope: waitForDiagnostics ? "all" : "primary"
21343
+ clientScope: waitForDiagnostics ? "all" : "primary",
21344
+ ...excludeServerIds !== void 0 && { excludeServerIds }
20736
21345
  });
20737
21346
  } catch {
20738
21347
  }
@@ -20882,6 +21491,7 @@ function createLspNavigationTool(getFlag, mutationDeps) {
20882
21491
  const session = mutationDeps?.runtime?.captureSessionGeneration?.();
20883
21492
  const startedAt = Date.now();
20884
21493
  let supported = null;
21494
+ let noFileClient = false;
20885
21495
  let diagnosticsMode = "unknown";
20886
21496
  let columnResolution;
20887
21497
  let openedFileContent;
@@ -21031,7 +21641,7 @@ function createLspNavigationTool(getFlag, mutationDeps) {
21031
21641
  resultCount: 0
21032
21642
  });
21033
21643
  }
21034
- const filePath = rawPath ? path38.isAbsolute(rawPath) ? rawPath : path38.resolve(ctx.cwd || ".", rawPath) : "";
21644
+ const filePath = rawPath ? resolveHostToolPath(rawPath, ctx.cwd || ".") : "";
21035
21645
  let filePathIsDirectory = false;
21036
21646
  if (filePath) {
21037
21647
  try {
@@ -21058,16 +21668,17 @@ function createLspNavigationTool(getFlag, mutationDeps) {
21058
21668
  });
21059
21669
  }
21060
21670
  if (operation === "workspaceDiagnostics") {
21671
+ const policyCwd = ctx.cwd || process.cwd();
21061
21672
  const wsDiagSupport = rawPath ? await lspService.getWorkspaceDiagnosticsSupport(filePath) : await lspService.getWorkspaceDiagnosticsSupport(void 0, workspaceScopeAttribution);
21062
21673
  diagnosticsMode = wsDiagSupport?.mode ?? "unknown";
21063
21674
  if (rawPath && !filePathIsDirectory) {
21064
- const hasLSP2 = lspService.supportsLSP(filePath);
21675
+ const hasLSP2 = lspService.supportsLSP(filePath) || disabledPrimaries(filePath).disabledServerIds.length > 0;
21065
21676
  if (!hasLSP2) {
21066
21677
  return finalize({
21067
21678
  content: [
21068
21679
  {
21069
21680
  type: "text",
21070
- text: `No LSP server available for ${path38.basename(filePath)}. Check that the language server is installed.`
21681
+ text: `No LSP server available for ${path39.basename(filePath)}. Check that the language server is installed.`
21071
21682
  }
21072
21683
  ],
21073
21684
  isError: true
@@ -21078,8 +21689,12 @@ function createLspNavigationTool(getFlag, mutationDeps) {
21078
21689
  resultCount: 0
21079
21690
  });
21080
21691
  }
21081
- await openFileBestEffort(lspService, filePath, true);
21082
- const diagnostics = await lspService.getDiagnostics(filePath);
21692
+ const excludeServerIds = configDisabledAuxiliaryLspServerIds((name) => getFlag(name, ctx.cwd));
21693
+ const openedContent = await openFileBestEffort(lspService, filePath, true, excludeServerIds);
21694
+ const policy = applyProbeFindingPolicy(filePath, await lspService.getDiagnostics(filePath, void 0, excludeServerIds), policyCwd, openedContent, "all", "lsp_navigation");
21695
+ const diagnostics = policy.kept;
21696
+ const suppressedLine = dispositionSuppressedLine(policy.suppressed);
21697
+ const primaryUnavailable = describePrimaryUnavailable(void 0, lspService.getDiagnosticsHealth(filePath))?.text;
21083
21698
  const result3 = [
21084
21699
  {
21085
21700
  filePath,
@@ -21098,14 +21713,23 @@ function createLspNavigationTool(getFlag, mutationDeps) {
21098
21713
  {
21099
21714
  type: "text",
21100
21715
  text: `${note2}
21101
- ${JSON.stringify(result3)}`
21716
+ ${JSON.stringify(result3)}${primaryUnavailable ? `
21717
+ Note: ${primaryUnavailable}` : ""}${suppressedLine ? `
21718
+ Note: ${suppressedLine}` : ""}`
21102
21719
  }
21103
21720
  ],
21104
21721
  details: {
21105
21722
  operation,
21106
21723
  resultCount: resultCount2,
21107
21724
  diagnosticsMode,
21108
- coverage: "requested-file"
21725
+ coverage: "requested-file",
21726
+ ...policy.suppressed > 0 && {
21727
+ dispositionSuppressed: policy.suppressed
21728
+ },
21729
+ ...primaryUnavailable !== void 0 && {
21730
+ unconfirmed: true,
21731
+ primaryUnavailable
21732
+ }
21109
21733
  }
21110
21734
  }, {
21111
21735
  operation,
@@ -21114,12 +21738,23 @@ ${JSON.stringify(result3)}`
21114
21738
  resultCount: resultCount2
21115
21739
  });
21116
21740
  }
21117
- const allDiagnostics = await lspService.getAllDiagnostics();
21118
- const result2 = Array.from(allDiagnostics.entries()).map(([trackedFile, { diags }]) => ({
21119
- filePath: trackedFile,
21120
- diagnostics: diags,
21121
- count: diags.length
21122
- }));
21741
+ const allDiagnostics = await lspService.getAllDiagnostics(configDisabledAuxiliaryLspServerIds((name) => getFlag(name, ctx.cwd)));
21742
+ let snapshotSuppressed = 0;
21743
+ const result2 = Array.from(allDiagnostics.entries()).map(([trackedFile, { diags, ts }]) => {
21744
+ let kept = diags;
21745
+ if (diags.length > 0) {
21746
+ const policy = applyProbeFindingPolicy(trackedFile, diags, policyCwd, readFileContent(trackedFile), "all", "lsp_navigation");
21747
+ snapshotSuppressed += policy.suppressed;
21748
+ kept = policy.kept;
21749
+ }
21750
+ return {
21751
+ filePath: trackedFile,
21752
+ diagnostics: kept,
21753
+ count: kept.length,
21754
+ lag: formatCacheAgeLabel(ts)
21755
+ };
21756
+ });
21757
+ const snapshotSuppressedLine = dispositionSuppressedLine(snapshotSuppressed);
21123
21758
  const noteMap2 = {
21124
21759
  "push-only": "Note: push-only tracked diagnostics snapshot (not full workspace pull diagnostics).",
21125
21760
  pull: "Note: tracked diagnostics snapshot from active clients. Provide path to force file-level diagnostics collection"
@@ -21130,14 +21765,18 @@ ${JSON.stringify(result3)}`
21130
21765
  {
21131
21766
  type: "text",
21132
21767
  text: `${note}
21133
- ${JSON.stringify(result2)}`
21768
+ ${JSON.stringify(result2)}${snapshotSuppressedLine ? `
21769
+ Note: ${snapshotSuppressedLine}` : ""}`
21134
21770
  }
21135
21771
  ],
21136
21772
  details: {
21137
21773
  operation,
21138
21774
  resultCount: result2.length,
21139
21775
  diagnosticsMode,
21140
- coverage: "tracked-open-files"
21776
+ coverage: "tracked-open-files",
21777
+ ...snapshotSuppressed > 0 && {
21778
+ dispositionSuppressed: snapshotSuppressed
21779
+ }
21141
21780
  }
21142
21781
  }, {
21143
21782
  operation,
@@ -21168,7 +21807,7 @@ ${JSON.stringify(result2)}`
21168
21807
  content: [
21169
21808
  {
21170
21809
  type: "text",
21171
- text: `No LSP server available for ${path38.basename(filePath)}. Check that the language server is installed.`
21810
+ text: disabledPrimaries(filePath).disabledServerIds.length > 0 ? noServerAnsweredText(filePath) : `No LSP server available for ${path39.basename(filePath)}. Check that the language server is installed.`
21172
21811
  }
21173
21812
  ],
21174
21813
  isError: true
@@ -21181,13 +21820,14 @@ ${JSON.stringify(result2)}`
21181
21820
  }
21182
21821
  if (needsFilePath) {
21183
21822
  const support = await lspService.getOperationSupport(filePath);
21823
+ noFileClient = support === null;
21184
21824
  supported = operationSupportStatus(operation, support);
21185
21825
  if (supported === false) {
21186
21826
  return finalize({
21187
21827
  content: [
21188
21828
  {
21189
21829
  type: "text",
21190
- text: `LSP server for ${path38.basename(filePath)} does not advertise support for ${operation}`
21830
+ text: `LSP server for ${path39.basename(filePath)} does not advertise support for ${operation}`
21191
21831
  }
21192
21832
  ],
21193
21833
  isError: true,
@@ -21316,7 +21956,7 @@ ${JSON.stringify(result2)}`
21316
21956
  if (!newFilePath || newFilePath.trim().length === 0) {
21317
21957
  throw new Error("__BADINPUT__ newFilePath parameter required for rename_file");
21318
21958
  }
21319
- const resolvedNewFilePath = path38.isAbsolute(newFilePath) ? newFilePath : path38.resolve(ctx.cwd || ".", newFilePath);
21959
+ const resolvedNewFilePath = resolveHostToolPath(newFilePath, ctx.cwd || ".");
21320
21960
  const result2 = await lspService.renameFile(filePath, resolvedNewFilePath, {
21321
21961
  cwd: ctx.cwd || ".",
21322
21962
  apply: apply ?? false,
@@ -21460,7 +22100,15 @@ ${JSON.stringify(result2)}`
21460
22100
  }, { operation, filePath, failureKind: "lsp_error", resultCount: 0 });
21461
22101
  }
21462
22102
  const isEmpty = !result || Array.isArray(result) && result.length === 0;
21463
- const fileCtx = filePath ? " at " + path38.basename(filePath) : "";
22103
+ if (isEmpty && noFileClient) {
22104
+ return finalize({
22105
+ content: [
22106
+ { type: "text", text: noServerAnsweredText(filePath) }
22107
+ ],
22108
+ isError: true
22109
+ }, { operation, filePath, failureKind: "no_server", resultCount: 0 });
22110
+ }
22111
+ const fileCtx = filePath ? " at " + path39.basename(filePath) : "";
21464
22112
  const lineCtx = line ? ":" + line + ":" + character : "";
21465
22113
  let output = isEmpty ? "No results for " + operation + fileCtx + lineCtx : JSON.stringify(result);
21466
22114
  if (isEmpty && operation === "workspaceSymbol" && !rawPath) {
@@ -21702,11 +22350,11 @@ function resolveDefaultCwd() {
21702
22350
  const fromArg = process.argv.find((arg) => arg.startsWith("--cwd="))?.slice("--cwd=".length);
21703
22351
  return fromArg ?? process.env.PI_LENS_MCP_CWD ?? process.cwd();
21704
22352
  }
21705
- var DEFAULT_CWD = path39.resolve(resolveDefaultCwd());
22353
+ var DEFAULT_CWD = path40.resolve(resolveDefaultCwd());
21706
22354
  var lspReadyCwds = /* @__PURE__ */ new Set();
21707
22355
  var SERVER_FILE = fileURLToPath3(import.meta.url);
21708
- var SERVER_DIR = path39.dirname(SERVER_FILE);
21709
- var WORKER_PATH = path39.join(SERVER_DIR, "worker.js");
22356
+ var SERVER_DIR = path40.dirname(SERVER_FILE);
22357
+ var WORKER_PATH = path40.join(SERVER_DIR, "worker.js");
21710
22358
  var REBUILD_SCRIPT = resolveRebuildScript(SERVER_FILE);
21711
22359
  var STALENESS_STAT_PATH = process.env.PI_LENS_MCP_STALENESS_STAT_PATH ?? SERVER_FILE;
21712
22360
  var BUILD_STAMP = computeBuildStamp(STALENESS_STAT_PATH);
@@ -21728,7 +22376,7 @@ function isWarmBuildStale() {
21728
22376
  function findRepoRoot(start) {
21729
22377
  let dir = start;
21730
22378
  for (let depth = 0; depth < 6; depth++) {
21731
- const pkgPath = path39.join(dir, "package.json");
22379
+ const pkgPath = path40.join(dir, "package.json");
21732
22380
  if (fs25.existsSync(pkgPath)) {
21733
22381
  try {
21734
22382
  const pkg = JSON.parse(fs25.readFileSync(pkgPath, "utf8"));
@@ -21737,16 +22385,16 @@ function findRepoRoot(start) {
21737
22385
  } catch {
21738
22386
  }
21739
22387
  }
21740
- const parent = path39.dirname(dir);
22388
+ const parent = path40.dirname(dir);
21741
22389
  if (parent === dir)
21742
22390
  break;
21743
22391
  dir = parent;
21744
22392
  }
21745
- return path39.resolve(start, "..", "..");
22393
+ return path40.resolve(start, "..", "..");
21746
22394
  }
21747
- var REPO_ROOT = process.env.PI_LENS_MCP_REPO_ROOT ? path39.resolve(process.env.PI_LENS_MCP_REPO_ROOT) : findRepoRoot(SERVER_DIR);
22395
+ var REPO_ROOT = process.env.PI_LENS_MCP_REPO_ROOT ? path40.resolve(process.env.PI_LENS_MCP_REPO_ROOT) : findRepoRoot(SERVER_DIR);
21748
22396
  async function ensureReady(cwd) {
21749
- const normalized = path39.resolve(cwd);
22397
+ const normalized = path40.resolve(cwd);
21750
22398
  if (!shouldInitializeSessionRoot(normalized, lspReadyCwds)) {
21751
22399
  return;
21752
22400
  }
@@ -21792,22 +22440,17 @@ function getAutoSessionStatus() {
21792
22440
  }
21793
22441
  var IPC_PATH = ipcPathForCwd(DEFAULT_CWD);
21794
22442
  function rejectForeignTurnCwd(route, cwd) {
21795
- const message = `${route} cwd outside this server's workspace: ${path39.resolve(cwd)} not within ${DEFAULT_CWD}`;
22443
+ const message = `${route} cwd outside this server's workspace: ${path40.resolve(cwd)} not within ${DEFAULT_CWD}`;
21796
22444
  console.error(`[pi-lens-mcp] rejected ${message}`);
21797
22445
  return message;
21798
22446
  }
21799
22447
  function isWithinServerWorkspace(cwd) {
21800
- const target = path39.resolve(cwd);
21801
- const rel = path39.relative(DEFAULT_CWD, target);
21802
- return !rel.startsWith("..") && !path39.isAbsolute(rel);
22448
+ const target = path40.resolve(cwd);
22449
+ const rel = path40.relative(DEFAULT_CWD, target);
22450
+ return !rel.startsWith("..") && !path40.isAbsolute(rel);
21803
22451
  }
21804
22452
  function startIpcServer() {
21805
- if (process.platform !== "win32") {
21806
- try {
21807
- fs25.unlinkSync(IPC_PATH);
21808
- } catch {
21809
- }
21810
- }
22453
+ unlinkStaleUnixSocket(IPC_PATH);
21811
22454
  const requestQueue = createWarmIpcRequestQueue();
21812
22455
  const ipc = net.createServer((socket) => {
21813
22456
  socket.setEncoding("utf8");
@@ -21909,12 +22552,6 @@ function startIpcServer() {
21909
22552
  ipc.close();
21910
22553
  } catch {
21911
22554
  }
21912
- if (process.platform !== "win32") {
21913
- try {
21914
- fs25.unlinkSync(IPC_PATH);
21915
- } catch {
21916
- }
21917
- }
21918
22555
  };
21919
22556
  process.on("exit", cleanup);
21920
22557
  }
@@ -21975,7 +22612,11 @@ var lensDiagnosticsTool = createLensDiagnosticsTool(
21975
22612
  // `lens-guard` off — a config bypass on a durable cross-surface store.
21976
22613
  // Matches index.ts:1656 and the two sibling readers
21977
22614
  // (clients/runtime-tool-call.ts, clients/runtime-tool-result.ts).
21978
- () => isLensGuardEnabled()
22615
+ () => isLensGuardEnabled(),
22616
+ // #4323: `opengrep.enabled=false` keeps the explicit `source=lsp` probe from
22617
+ // starting Opengrep, as it does for `pilens_analyze`. Resolved per call
22618
+ // against the call's own project, like `lspNavigationTool` below (#792).
22619
+ (name, cwd) => createMcpHost(void 0, cwd).getFlag(name)
21979
22620
  );
21980
22621
  var astGrepClient = new AstGrepClient();
21981
22622
  var astGrepSearchTool = createAstGrepSearchTool(astGrepClient);
@@ -22294,7 +22935,7 @@ function enabledToolsForCwd(cwd) {
22294
22935
  }
22295
22936
  function formatAnalyze(result, cwd, mode, servedBy) {
22296
22937
  const lspNote = result.lsp ? ` \xB7 lsp ${result.lsp.diagnosticCount} (${result.lsp.status}${result.lsp.failureKind ? `: ${result.lsp.failureKind}` : ""}, ${result.lsp.durationMs}ms)` : "";
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 ? `
22938
+ const summary = `${path40.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 ? `
22298
22939
 
22299
22940
  servedBy: ${servedBy}` : "");
22300
22941
  return renderToolText(summary, servedBy ? { ...result, servedBy } : result);
@@ -22428,7 +23069,7 @@ ${outcome.output}`, {
22428
23069
  }
22429
23070
  const { deduped, byRule, byFile } = summarizeScan(snapshot.diagnostics);
22430
23071
  const topRules = Object.entries(byRule).sort((a, b) => b[1] - a[1]);
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 }));
23072
+ const topFiles = Object.entries(byFile).sort((a, b) => b[1] - a[1]).slice(0, 15).map(([file, count]) => ({ file: path40.relative(cwd, file), count }));
22432
23073
  const summaryLines = [
22433
23074
  `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)` : ""),
22434
23075
  ...topRules.slice(0, 12).map(([rule, count]) => ` ${count}\xD7 ${rule}`)
@@ -22439,8 +23080,13 @@ ${outcome.output}`, {
22439
23080
  const skipNotice = generatedSkipNotice(snapshot, "mcp");
22440
23081
  if (skipNotice)
22441
23082
  summaryLines.push(skipNotice);
23083
+ const { dispositionSuppressed } = snapshot;
23084
+ const suppressedLine = dispositionSuppressedLine(dispositionSuppressed);
23085
+ if (suppressedLine)
23086
+ summaryLines.push(suppressedLine);
22442
23087
  return renderToolText(summaryLines.join("\n"), {
22443
23088
  filesScanned: snapshot.filesScanned,
23089
+ ...dispositionSuppressed > 0 ? { dispositionSuppressed } : {},
22444
23090
  runners: snapshot.runners,
22445
23091
  uniqueDiagnostics: deduped.length,
22446
23092
  rawDiagnostics: snapshot.diagnostics.length,
@@ -22481,7 +23127,7 @@ ${stalenessNote}` : ""), {
22481
23127
  }
22482
23128
  const lines = [
22483
23129
  `Top ${results.length} file(s) for "${query}":`,
22484
- ...results.map((result, i) => ` ${i + 1}. ${path39.relative(cwd, result.file)} (score ${result.score.toFixed(2)}, ${result.hits} hit(s), line ${result.startLine})`),
23130
+ ...results.map((result, i) => ` ${i + 1}. ${path40.relative(cwd, result.file)} (score ${result.score.toFixed(2)}, ${result.hits} hit(s), line ${result.startLine})`),
22485
23131
  ...stalenessNote ? ["", stalenessNote] : [],
22486
23132
  ...coverage ? [
22487
23133
  `Index covers ${coverage.files} files${coverage.truncated ? " (capped \u2014 results may be incomplete)" : ""}.`
@@ -22491,7 +23137,7 @@ ${stalenessNote}` : ""), {
22491
23137
  query,
22492
23138
  coverage,
22493
23139
  results: results.map((result) => {
22494
- const relFile = path39.relative(cwd, result.file);
23140
+ const relFile = path40.relative(cwd, result.file);
22495
23141
  return {
22496
23142
  file: relFile,
22497
23143
  score: result.score,
@@ -22530,12 +23176,12 @@ ${stalenessNote}` : ""), {
22530
23176
  });
22531
23177
  if (!report.available) {
22532
23178
  return {
22533
- ...renderToolText(`No module report for ${path39.relative(cwd, path39.resolve(cwd, file))} \u2014 not a symbol-bearing file, or unreadable.`, report, true),
23179
+ ...renderToolText(`No module report for ${path40.relative(cwd, path40.resolve(cwd, file))} \u2014 not a symbol-bearing file, or unreadable.`, report, true),
22534
23180
  isError: true
22535
23181
  };
22536
23182
  }
22537
23183
  const graphStaleness = graphStalenessNote(report.graphBuiltAt, "Review graph");
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 ? `
23184
+ const summary = `${path40.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 ? `
22539
23185
 
22540
23186
  ${graphStaleness}` : "");
22541
23187
  if (view === "compact") {
@@ -22596,7 +23242,7 @@ ${graphStaleness}` : "");
22596
23242
  if (!result.found) {
22597
23243
  const suggestionSuffix = result.suggestions?.length ? ` Did you mean: ${result.suggestions.join(", ")}?` : " Use pilens_module_report to list symbols.";
22598
23244
  return {
22599
- ...renderToolText(`Symbol "${symbol}" not found in ${path39.basename(file)}.${suggestionSuffix}`, {
23245
+ ...renderToolText(`Symbol "${symbol}" not found in ${path40.basename(file)}.${suggestionSuffix}`, {
22600
23246
  found: false,
22601
23247
  ...result.suggestions ? { suggestions: result.suggestions } : {}
22602
23248
  }),
@@ -22605,7 +23251,7 @@ ${graphStaleness}` : "");
22605
23251
  }
22606
23252
  const sigSuffix = result.signature ? ` ${result.signature}` : "";
22607
23253
  const ambiguityNote = result.ambiguous ? ` (${result.ambiguous.count} matches \u2014 returned the ${result.kind}; pass \`kind\` to disambiguate: ${result.ambiguous.kinds.join(", ")})` : "";
22608
- const header = `${result.kind} ${result.name}${ambiguityNote}${sigSuffix} ${path39.relative(cwd, result.path)}:${result.startLine}-${result.endLine}`;
23254
+ const header = `${result.kind} ${result.name}${ambiguityNote}${sigSuffix} ${path40.relative(cwd, result.path)}:${result.startLine}-${result.endLine}`;
22609
23255
  return {
22610
23256
  content: [
22611
23257
  {
@@ -22643,14 +23289,14 @@ ${result.source ?? ""}`
22643
23289
 
22644
23290
  Nested outline:
22645
23291
  ${JSON.stringify(result.outline)}` : "";
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}`;
23292
+ const text = result.error ? `Could not read enclosing range in ${path40.basename(file)}:${result.line}: ${result.error}${warningSuffix}${outlineSuffix}` : `No enclosing symbol/callback found in ${path40.basename(file)}:${result.line}.${warningSuffix}`;
22647
23293
  return {
22648
23294
  ...renderToolText(text, { found: false, line: result.line }),
22649
23295
  isError: true
22650
23296
  };
22651
23297
  }
22652
23298
  const range = result.partial ? `${result.startLine}-${result.endLine} (partial of ${result.enclosingStartLine}-${result.enclosingEndLine})` : `${result.startLine}-${result.endLine}`;
22653
- const header = `${result.kind} ${result.name} ${path39.relative(cwd, result.path)}:${range}`;
23299
+ const header = `${result.kind} ${result.name} ${path40.relative(cwd, result.path)}:${range}`;
22654
23300
  return {
22655
23301
  content: [
22656
23302
  {
@@ -22685,7 +23331,7 @@ ${result.source ?? ""}`
22685
23331
  ]),
22686
23332
  ...renderLspBrokenStatusLines(brokenServers),
22687
23333
  ...renderDegradationLines(degradations),
22688
- last ? `Last dispatch: ${path39.basename(last.filePath)} \u2014 ${last.totalDurationMs}ms, ${last.totalDiagnostics} diagnostic(s)` : "Last dispatch: none yet",
23334
+ last ? `Last dispatch: ${path40.basename(last.filePath)} \u2014 ${last.totalDurationMs}ms, ${last.totalDiagnostics} diagnostic(s)` : "Last dispatch: none yet",
22689
23335
  `Diagnostics this session: ${stats.totalShown} shown \xB7 ${stats.totalAutoFixed} auto-fixed \xB7 ${stats.totalUnresolved} unresolved`,
22690
23336
  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)",
22691
23337
  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)",
@@ -22751,7 +23397,7 @@ ${result.source ?? ""}`
22751
23397
  const limit = typeof args.limit === "number" && Number.isFinite(args.limit) ? Math.max(1, Math.floor(args.limit)) : 5;
22752
23398
  const fileFilter = typeof args.file === "string" ? args.file : void 0;
22753
23399
  const recent = recentLatency(limit, fileFilter);
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");
23400
+ const summary = recent.length === 0 ? "No dispatch latency reports yet." : recent.map((report) => `${path40.basename(report.filePath)}: ${report.totalDurationMs}ms (${report.totalDiagnostics} diag${report.stoppedEarly ? ", stopped early" : ""})`).join("\n");
22755
23401
  return renderToolText(summary, recent);
22756
23402
  }
22757
23403
  if (name === "pilens_session_start") {