pi-lens 4.1.2 → 4.1.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.
- package/CHANGELOG.md +1308 -0
- package/README.md +3 -0
- package/config/biome/core.jsonc +11 -2
- package/config/dependency-cruiser-eager-allowlist.json +91 -0
- package/dist/clients/actionable-warnings-logger.js +18 -3
- package/dist/clients/actionable-warnings.js +927 -80
- package/dist/clients/agent-behavior-client.js +13 -4
- package/dist/clients/ast-grep-client.js +136 -9
- package/dist/clients/ast-grep-rule-manager.js +60 -5
- package/dist/clients/ast-grep-tool-logger.js +2 -2
- package/dist/clients/availability-probe-flight.js +12 -0
- package/dist/clients/bash-file-access.js +1 -3
- package/dist/clients/biome-client.js +16 -2
- package/dist/clients/blocker-freshness.js +34 -0
- package/dist/clients/bootstrap.js +509 -73
- package/dist/clients/bounded-cache.js +152 -12
- package/dist/clients/bounded-telemetry.js +115 -23
- package/dist/clients/build-identity.js +95 -0
- package/dist/clients/bundled-resource-health.js +113 -0
- package/dist/clients/bus-events-logger.js +2 -2
- package/dist/clients/bus-publish.js +3 -0
- package/dist/clients/cache/rule-cache.js +33 -5
- package/dist/clients/cache-manager.js +105 -6
- package/dist/clients/cache-observability.js +46 -19
- package/dist/clients/cargo-manifest.js +422 -0
- package/dist/clients/cascade-logger.js +17 -3
- package/dist/clients/code-quality-warnings.js +13 -3
- package/dist/clients/complexity-client.js +16 -5
- package/dist/clients/config-core/deny.js +221 -0
- package/dist/clients/config-core/index.js +47 -0
- package/dist/clients/config-core/merge.js +357 -0
- package/dist/clients/config-core/normalize.js +340 -0
- package/dist/clients/config-core/process-spec.js +248 -0
- package/dist/clients/config-core/provenance.js +171 -0
- package/dist/clients/config-core/records.js +218 -0
- package/dist/clients/config-core/resolve.js +125 -0
- package/dist/clients/config-core/safe-object.js +78 -0
- package/dist/clients/config-core/schema.js +167 -0
- package/dist/clients/config-diagnostic-codes.js +281 -0
- package/dist/clients/config-locations.js +160 -0
- package/dist/clients/config-resolve.js +789 -0
- package/dist/clients/config-schema.js +197 -0
- package/dist/clients/config-warn.js +407 -0
- package/dist/clients/dead-code-client.js +33 -25
- package/dist/clients/dead-code-logger.js +2 -2
- package/dist/clients/deadline-utils.js +178 -0
- package/dist/clients/debug-handles.js +2 -2
- package/dist/clients/debug-heap.js +3 -3
- package/dist/clients/deferred-lsp-work.js +106 -0
- package/dist/clients/degradation-ledger.js +166 -8
- package/dist/clients/demoted-finding-render.js +14 -0
- package/dist/clients/dependency-checker.js +28 -22
- package/dist/clients/deps/minimatch.js +1 -1
- package/dist/clients/diagnostic-line-freshness.js +12 -9
- package/dist/clients/diagnostic-logger.js +14 -4
- package/dist/clients/dispatch/collect-later-tier.js +39 -0
- package/dist/clients/dispatch/dispatcher.js +304 -19
- package/dist/clients/dispatch/fact-store.js +328 -9
- package/dist/clients/dispatch/integration.js +214 -107
- package/dist/clients/dispatch/pending-runner-findings.js +87 -0
- package/dist/clients/dispatch/plan.js +21 -5
- package/dist/clients/dispatch/runners/actionlint.js +0 -1
- package/dist/clients/dispatch/runners/ast-grep-napi.js +550 -77
- package/dist/clients/dispatch/runners/biome-check.js +0 -1
- package/dist/clients/dispatch/runners/cpp-check.js +0 -1
- package/dist/clients/dispatch/runners/credo.js +0 -1
- package/dist/clients/dispatch/runners/cue-vet.js +0 -1
- package/dist/clients/dispatch/runners/dart-analyze.js +0 -1
- package/dist/clients/dispatch/runners/detekt.js +0 -1
- package/dist/clients/dispatch/runners/dotnet-build.js +0 -1
- package/dist/clients/dispatch/runners/elixir-check.js +0 -1
- package/dist/clients/dispatch/runners/eslint.js +5 -2
- package/dist/clients/dispatch/runners/fact-rules.js +0 -1
- package/dist/clients/dispatch/runners/fish-indent.js +0 -1
- package/dist/clients/dispatch/runners/gleam-check.js +0 -1
- package/dist/clients/dispatch/runners/go-vet.js +1 -3
- package/dist/clients/dispatch/runners/golangci-lint.js +0 -1
- package/dist/clients/dispatch/runners/hadolint.js +0 -1
- package/dist/clients/dispatch/runners/helm-lint.js +16 -4
- package/dist/clients/dispatch/runners/helm-render.js +54 -10
- package/dist/clients/dispatch/runners/htmlhint.js +0 -1
- package/dist/clients/dispatch/runners/index.js +1 -3
- package/dist/clients/dispatch/runners/javac.js +0 -1
- package/dist/clients/dispatch/runners/ktlint.js +0 -1
- package/dist/clients/dispatch/runners/lsp.js +37 -2
- package/dist/clients/dispatch/runners/markdownlint.js +4 -2
- package/dist/clients/dispatch/runners/mypy.js +0 -1
- package/dist/clients/dispatch/runners/oxlint.js +24 -22
- package/dist/clients/dispatch/runners/php-lint.js +0 -1
- package/dist/clients/dispatch/runners/phpstan.js +0 -1
- package/dist/clients/dispatch/runners/prisma-validate.js +0 -1
- package/dist/clients/dispatch/runners/psscriptanalyzer.js +4 -1
- package/dist/clients/dispatch/runners/pyright.js +7 -4
- package/dist/clients/dispatch/runners/rubocop.js +0 -1
- package/dist/clients/dispatch/runners/ruff.js +0 -1
- package/dist/clients/dispatch/runners/rust-clippy.js +9 -6
- package/dist/clients/dispatch/runners/shellcheck.js +0 -1
- package/dist/clients/dispatch/runners/shfmt.js +0 -1
- package/dist/clients/dispatch/runners/spellcheck.js +0 -1
- package/dist/clients/dispatch/runners/spotbugs.js +0 -1
- package/dist/clients/dispatch/runners/sqlfluff.js +0 -1
- package/dist/clients/dispatch/runners/stylelint.js +0 -1
- package/dist/clients/dispatch/runners/swiftlint.js +0 -1
- package/dist/clients/dispatch/runners/taplo.js +0 -1
- package/dist/clients/dispatch/runners/terragrunt.js +0 -1
- package/dist/clients/dispatch/runners/tflint.js +0 -1
- package/dist/clients/dispatch/runners/tree-sitter.js +0 -1
- package/dist/clients/dispatch/runners/trivy-config.js +0 -1
- package/dist/clients/dispatch/runners/utils/availability-policy.js +3 -0
- package/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +3 -4
- package/dist/clients/dispatch/runners/utils/runner-helpers.js +270 -105
- package/dist/clients/dispatch/runners/utils/spawn-outcome.js +4 -0
- package/dist/clients/dispatch/runners/utils/tool-failure.js +19 -7
- package/dist/clients/dispatch/runners/utils/toolchain-availability.js +89 -26
- package/dist/clients/dispatch/runners/vale.js +0 -1
- package/dist/clients/dispatch/runners/yaml-rule-parser.js +51 -17
- package/dist/clients/dispatch/runners/yamllint.js +0 -1
- package/dist/clients/dispatch/runners/zig-check.js +0 -1
- package/dist/clients/disposition-logger.js +3 -3
- package/dist/clients/effective-config.js +403 -0
- package/dist/clients/error-class.js +23 -0
- package/dist/clients/event-loop-hold.js +274 -0
- package/dist/clients/event-loop-monitor.js +66 -1
- package/dist/clients/extension-log.js +2 -2
- package/dist/clients/feature-hints.js +2 -1
- package/dist/clients/file-time.js +6 -1
- package/dist/clients/file-utils.js +266 -13
- package/dist/clients/finding-delivery-gate.js +55 -13
- package/dist/clients/format-service.js +6 -1
- package/dist/clients/formatters.js +303 -104
- package/dist/clients/freshness-cadence.js +17 -0
- package/dist/clients/generated-artifacts.js +114 -19
- package/dist/clients/generation-guard.js +4 -9
- package/dist/clients/git-guard.js +198 -48
- package/dist/clients/git-tracked-ignore.js +32 -1
- package/dist/clients/go-client.js +39 -0
- package/dist/clients/govulncheck-client.js +5 -1
- package/dist/clients/gradle-ktfmt-style.js +252 -0
- package/dist/clients/gzip-stage-write.js +7 -0
- package/dist/clients/hashline-anchor.js +424 -0
- package/dist/clients/hook-budgets.js +76 -0
- package/dist/clients/host-edit-normalize.js +5 -2
- package/dist/clients/host-ports.js +1 -1
- package/dist/clients/installer/index.js +200 -84
- package/dist/clients/installer/managed-tool-refresh.js +3 -1
- package/dist/clients/instance-reaper.js +108 -243
- package/dist/clients/instance-registry-lock.js +145 -0
- package/dist/clients/instance-registry.js +435 -122
- package/dist/clients/jscpd-client.js +6 -1
- package/dist/clients/language-profile.js +4 -2
- package/dist/clients/language-registry.js +595 -0
- package/dist/clients/latency-logger.js +179 -3
- package/dist/clients/ledger-bounds.js +13 -0
- package/dist/clients/lens-config.js +141 -30
- package/dist/clients/lens-engine.js +30 -37
- package/dist/clients/lens-flag-registry.js +67 -6
- package/dist/clients/lens-map.js +3 -1
- package/dist/clients/log-cleanup.js +2 -2
- package/dist/clients/lsp/aggregation.js +3 -1
- package/dist/clients/lsp/cascade-tier.js +4 -0
- package/dist/clients/lsp/client.js +672 -49
- package/dist/clients/lsp/config.js +504 -156
- package/dist/clients/lsp/diagnostic-binding.js +10 -5
- package/dist/clients/lsp/edits.js +15 -2
- package/dist/clients/lsp/index.js +1179 -247
- package/dist/clients/lsp/jvm-runtime.js +5 -0
- package/dist/clients/lsp/language.js +18 -169
- package/dist/clients/lsp/launch.js +38 -9
- package/dist/clients/lsp/pending-aux-coverage.js +113 -12
- package/dist/clients/lsp/server.js +426 -160
- package/dist/clients/lsp/session-roots.js +192 -0
- package/dist/clients/lsp/spawn-history.js +2 -7
- package/dist/clients/lsp/tsserver-sync.js +9 -2
- package/dist/clients/lsp/workspace-diagnostics-cache.js +17 -7
- package/dist/clients/lsp/workspace-sweep-hold.js +32 -23
- package/dist/clients/lsp-mutation.js +179 -23
- package/dist/clients/map-with-concurrency.js +36 -0
- package/dist/clients/mcp/analyze.js +45 -10
- package/dist/clients/mcp/session.js +19 -17
- package/dist/clients/memory-sampler.js +28 -1
- package/dist/clients/message-end-attribution.js +30 -0
- package/dist/clients/metrics-history.js +21 -4
- package/dist/clients/middle-man-analysis.js +2 -4
- package/dist/clients/module-report.js +20 -45
- package/dist/clients/mutating-tool.js +651 -0
- package/dist/clients/mutation-attribution.js +368 -0
- package/dist/clients/mutation-bridge.js +240 -0
- package/dist/clients/ndjson-logger.js +247 -20
- package/dist/clients/observed-mutation-sources.js +101 -0
- package/dist/clients/observed-mutation.js +1215 -0
- package/dist/clients/opaque-mutation-scan.js +300 -40
- package/dist/clients/opengrep-config.js +4 -0
- package/dist/clients/package-manager.js +201 -44
- package/dist/clients/partial-edit-apply.js +359 -80
- package/dist/clients/path-attribution-telemetry.js +23 -7
- package/dist/clients/path-keyed-map.js +35 -3
- package/dist/clients/path-utils.js +460 -13
- package/dist/clients/persist-debounce.js +8 -1
- package/dist/clients/php-cs-fixer-config.js +114 -0
- package/dist/clients/pipeline.js +77 -24
- package/dist/clients/probe-home-state.js +228 -0
- package/dist/clients/process-bridge.js +66 -0
- package/dist/clients/process-singletons.js +210 -0
- package/dist/clients/process-snapshot.js +68 -0
- package/dist/clients/project-diagnostics/fresh-fetch.js +4 -3
- package/dist/clients/project-diagnostics/runner-adapters/madge.js +2 -1
- package/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +27 -3
- package/dist/clients/project-diagnostics/scanner.js +61 -24
- package/dist/clients/project-lens-config.js +393 -122
- package/dist/clients/project-snapshot.js +85 -35
- package/dist/clients/python-environment.js +217 -0
- package/dist/clients/python-provenance.js +639 -0
- package/dist/clients/quiet-window.js +6 -1
- package/dist/clients/read-bridge.js +13 -21
- package/dist/clients/read-expansion.js +9 -46
- package/dist/clients/read-guard-logger.js +39 -6
- package/dist/clients/read-guard-tool-lines.js +364 -212
- package/dist/clients/read-guard.js +95 -7
- package/dist/clients/recent-touches.js +17 -5
- package/dist/clients/resource-sampler.js +195 -96
- package/dist/clients/review-graph/builder.js +752 -132
- package/dist/clients/review-graph/service.js +55 -3
- package/dist/clients/review-graph/shared-extraction-ir.js +7 -15
- package/dist/clients/review-graph/tsconfig-paths.js +5 -1
- package/dist/clients/review-graph/workspace-modules.js +10 -74
- package/dist/clients/review-graph-logger.js +30 -4
- package/dist/clients/runtime-agent-end.js +18 -2
- package/dist/clients/runtime-context.js +49 -16
- package/dist/clients/runtime-coordinator.js +93 -6
- package/dist/clients/runtime-session.js +169 -34
- package/dist/clients/runtime-tool-call.js +183 -35
- package/dist/clients/runtime-tool-result.js +894 -241
- package/dist/clients/runtime-turn.js +1264 -74
- package/dist/clients/rust-client.js +31 -0
- package/dist/clients/safe-spawn.js +191 -28
- package/dist/clients/sanitize.js +7 -1
- package/dist/clients/scratch-tree-policy.js +1 -3
- package/dist/clients/security-scan-client.js +51 -17
- package/dist/clients/session-lifecycle.js +238 -42
- package/dist/clients/session-start-observability.js +79 -0
- package/dist/clients/sessionstart-logger.js +12 -3
- package/dist/clients/sg-runner.js +27 -15
- package/dist/clients/sgconfig.js +6 -1
- package/dist/clients/shared-checkout-guard.js +338 -0
- package/dist/clients/skills-resolver.js +105 -0
- package/dist/clients/smells-rollup.js +2 -2
- package/dist/clients/spawn-output-cap.js +37 -0
- package/dist/clients/spawn-timeout-cooldown.js +15 -1
- package/dist/clients/startup-scan.js +6 -7
- package/dist/clients/startup-timing.js +69 -4
- package/dist/clients/string-utils.js +33 -0
- package/dist/clients/subagent-mode.js +17 -4
- package/dist/clients/test-runner-client.js +586 -118
- package/dist/clients/test-runner-delivery.js +239 -0
- package/dist/clients/tool-definition.js +41 -1
- package/dist/clients/tool-policy.js +92 -22
- package/dist/clients/tree-sitter-cache.js +40 -40
- package/dist/clients/tree-sitter-client.js +97 -53
- package/dist/clients/tree-sitter-logger.js +19 -3
- package/dist/clients/tree-sitter-query-loader.js +81 -1
- package/dist/clients/tree-sitter-shared.js +51 -46
- package/dist/clients/tree-sitter-symbol-extractor.js +28 -0
- package/dist/clients/typos-config.js +5 -0
- package/dist/clients/user-notify.js +6 -2
- package/dist/clients/warm-attach.js +8 -10
- package/dist/clients/widget-state.js +191 -26
- package/dist/clients/word-index-logger.js +15 -3
- package/dist/clients/word-index-store.js +463 -0
- package/dist/clients/word-index.js +815 -125
- package/dist/clients/workspace-topology.js +25 -1
- package/dist/clients/zizmor-config.js +29 -6
- package/dist/index.js +68577 -53950
- package/dist/mcp/analyze-cli.js +3 -1
- package/dist/mcp/build-staleness.js +8 -7
- package/dist/mcp/cli.js +3 -1
- package/dist/mcp/server.js +133 -6
- package/dist/scripts/lib/process-scan.mjs +583 -0
- package/dist/scripts/lib/skills-predicate.mjs +129 -0
- package/dist/tools/effective-config.js +89 -0
- package/dist/tools/lens-diagnostics.js +99 -24
- package/dist/tools/lsp-diagnostics.js +81 -85
- package/dist/tools/lsp-navigation.js +40 -17
- package/dist/tools/shared.js +0 -1
- package/docs/agent-guide.md +68 -3
- package/docs/ast-grep_rules_catalog.md +41 -10
- package/docs/astplayground.md +26 -17
- package/docs/configuration.md +222 -0
- package/docs/dependencies.md +3 -3
- package/docs/features.md +44 -5
- package/docs/language-coverage.md +2 -2
- package/docs/pi-lens-fixer.md +25 -0
- package/docs/pi-lens-investigator.md +25 -0
- package/docs/pi-lens-reviewer.md +27 -0
- package/docs/pi-lens-subagent.md +38 -0
- package/docs/pi-lens-warden.md +55 -0
- package/docs/public-api-stability.md +359 -0
- package/docs/release-qa-baseline.md +182 -0
- package/docs/subagent-compat.md +110 -30
- package/docs/tree-sitter_rules_catalog.md +2 -2
- package/package.json +17 -21
- package/rules/ast-grep-rules/rule-tests/c-no-malloc-free-test.yml +20 -0
- package/rules/ast-grep-rules/rule-tests/cpp-no-malloc-free-test.yml +25 -0
- package/rules/ast-grep-rules/rule-tests/cpp-no-printf-test.yml +19 -0
- package/rules/ast-grep-rules/rule-tests/go-no-fmt-println-test.yml +19 -0
- package/rules/ast-grep-rules/rule-tests/go-no-panic-in-lib-test.yml +17 -0
- package/rules/ast-grep-rules/rule-tests/go-no-underscore-func-name-test.yml +18 -0
- package/rules/ast-grep-rules/rule-tests/go-prefer-errors-is-test.yml +15 -0
- package/rules/ast-grep-rules/rule-tests/go-prefer-string-builder-test.yml +21 -0
- package/rules/ast-grep-rules/rule-tests/hyphenated-svg-attribute-test.yml +11 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-no-lateinit-test.yml +17 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-no-nullable-boolean-test.yml +15 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-no-println-test.yml +19 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-no-unnecessary-let-test.yml +7 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-no-unsafe-cast-test.yml +7 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-no-var-test.yml +7 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-prefer-data-class-test.yml +25 -0
- package/rules/ast-grep-rules/rule-tests/kotlin-prefer-is-empty-test.yml +11 -0
- package/rules/ast-grep-rules/rule-tests/no-important-test.yml +21 -0
- package/rules/ast-grep-rules/rule-tests/no-raw-types-test.yml +30 -0
- package/rules/ast-grep-rules/rule-tests/no-string-concat-in-loop-test.yml +100 -0
- package/rules/ast-grep-rules/rule-tests/no-system-out-println-test.yml +7 -0
- package/rules/ast-grep-rules/rule-tests/prefer-string-is-empty-test.yml +7 -0
- package/rules/ast-grep-rules/rule-tests/python-optional-type-test.yml +9 -0
- package/rules/ast-grep-rules/rule-tests/reducible-list-comprehension-test.yml +11 -0
- package/rules/ast-grep-rules/rule-tests/ruby-symbol-to-proc-candidate-test.yml +10 -0
- package/rules/ast-grep-rules/rule-tests/rust-no-chars-enumerate-test.yml +7 -0
- package/rules/ast-grep-rules/rules/c-no-malloc-free.yml +39 -0
- package/rules/ast-grep-rules/rules/cpp-no-malloc-free.yml +45 -0
- package/rules/ast-grep-rules/rules/cpp-no-printf.yml +41 -0
- package/rules/ast-grep-rules/rules/go-no-fmt-println.yml +43 -0
- package/rules/ast-grep-rules/rules/go-no-panic-in-lib.yml +41 -0
- package/rules/ast-grep-rules/rules/go-no-underscore-func-name.yml +35 -0
- package/rules/ast-grep-rules/rules/go-prefer-errors-is.yml +40 -0
- package/rules/ast-grep-rules/rules/go-prefer-string-builder.yml +43 -0
- package/rules/ast-grep-rules/rules/hyphenated-svg-attribute.yml +32 -0
- package/rules/ast-grep-rules/rules/kotlin-no-lateinit.yml +37 -0
- package/rules/ast-grep-rules/rules/kotlin-no-nullable-boolean.yml +33 -0
- package/rules/ast-grep-rules/rules/kotlin-no-println.yml +42 -0
- package/rules/ast-grep-rules/rules/kotlin-no-unnecessary-let.yml +28 -0
- package/rules/ast-grep-rules/rules/kotlin-no-unsafe-cast.yml +28 -0
- package/rules/ast-grep-rules/rules/kotlin-no-var.yml +35 -0
- package/rules/ast-grep-rules/rules/kotlin-prefer-data-class.yml +66 -0
- package/rules/ast-grep-rules/rules/kotlin-prefer-is-empty.yml +35 -0
- package/rules/ast-grep-rules/rules/no-important.yml +21 -0
- package/rules/ast-grep-rules/rules/no-raw-types.yml +33 -0
- package/rules/ast-grep-rules/rules/no-string-concat-in-loop.yml +90 -0
- package/rules/ast-grep-rules/rules/no-system-out-println.yml +16 -0
- package/rules/ast-grep-rules/rules/prefer-string-is-empty.yml +11 -0
- package/rules/ast-grep-rules/rules/python-optional-type.yml +22 -0
- package/rules/ast-grep-rules/rules/reducible-list-comprehension.yml +29 -0
- package/rules/ast-grep-rules/rules/ruby-symbol-to-proc-candidate.yml +25 -0
- package/rules/ast-grep-rules/rules/rust-no-chars-enumerate.yml +19 -0
- package/rules/tree-sitter-queries/python/python-cross-language-method.yml +3 -3
- package/rules/tree-sitter-queries/python/python-hallucinated-import.yml +2 -2
- package/rules/tree-sitter-queries/python/python-sql-injection.yml +12 -2
- package/scripts/analyze-pi-lens-logs.mjs +192 -1
- package/scripts/install-selftest.mjs +58 -2
- package/scripts/lib/skills-predicate.mjs +129 -0
- package/scripts/rpc-load-check.mjs +3 -1
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The session-root registry: which project roots does this process serve?
|
|
3
|
+
*
|
|
4
|
+
* #2052. A file is IN-SESSION when it lies within any initialized session cwd.
|
|
5
|
+
* `initLSPConfig` (`clients/lsp/config.ts`) is the single writer, and every
|
|
6
|
+
* entry point funnels through it — `ensureLSPConfigInitialized` (`index.ts`),
|
|
7
|
+
* `ensureReady` (`mcp/server.ts`), `clients/runtime-session.ts`, and
|
|
8
|
+
* `clients/lens-engine.ts` — so registration needs no second call site.
|
|
9
|
+
*
|
|
10
|
+
* WHY THIS IS ITS OWN MODULE, not a helper inside `config.ts`: `clients/lsp/
|
|
11
|
+
* index.ts`'s import surface from `config.js` is mirrored by an explicit
|
|
12
|
+
* `vi.mock` factory in ~58 test files. Importing a new symbol from `config.js`
|
|
13
|
+
* breaks every one of them with "No export is defined on the mock", which is a
|
|
14
|
+
* standing tax on any future export.
|
|
15
|
+
*
|
|
16
|
+
* WHY THE CONFIG PAYLOAD LIVES HERE TOO (#2518). Until this fix the payload
|
|
17
|
+
* lived in `config.ts`'s own 32-entry `workspaceConfigs` LRU while this
|
|
18
|
+
* registry held 128, and the two answered halves of ONE question: the registry
|
|
19
|
+
* decides whether a root still needs initializing (`ensureReady`,
|
|
20
|
+
* `mcp/server.ts`), the payload decides whether the operator's
|
|
21
|
+
* `lsp.disabledServers` denial applies (`isServerDisabled`). Thirty-three
|
|
22
|
+
* `ensureReady` calls on other cwds therefore dropped a LIVE root's denial
|
|
23
|
+
* while the registry still reported the root ready, so nothing re-initialized
|
|
24
|
+
* it and a server the operator had turned off came back — #2415 AC 3's exact
|
|
25
|
+
* prohibition, silently.
|
|
26
|
+
*
|
|
27
|
+
* Tying the two CAPS would not have fixed it. `initLSPConfig` registers the
|
|
28
|
+
* root BEFORE its `await` and writes the payload AFTER, so two interleaved
|
|
29
|
+
* initializations enter the two containers in different orders; with separate
|
|
30
|
+
* containers the same cap still evicts different keys from each. One entry per
|
|
31
|
+
* root — the key IS the registration, the value IS the payload — makes a
|
|
32
|
+
* payload miss for a served root impossible by construction instead of by an
|
|
33
|
+
* ordering agreement between two modules.
|
|
34
|
+
*
|
|
35
|
+
* WHY IT IS NOT A LATCH. The first attempt at #2052 used a single
|
|
36
|
+
* last-writer-wins session cwd that fell back to `process.cwd()`. Two bugs
|
|
37
|
+
* followed directly from that shape, and this module's contract excludes both:
|
|
38
|
+
*
|
|
39
|
+
* - MULTI-ROOT. A process that initializes projA and then projB serves BOTH.
|
|
40
|
+
* A latch silently made projA's files foreign the moment projB registered.
|
|
41
|
+
* - FAIL-OPEN ON EMPTY. An empty registry declines NOTHING. Declining is a
|
|
42
|
+
* hard refusal to answer, so it must rest on positive evidence that a
|
|
43
|
+
* session exists AND that the file sits outside it — never on the absence
|
|
44
|
+
* of evidence. Callers that never declared a session (isolated tests, API
|
|
45
|
+
* consumers, a request racing initialization) therefore keep the pre-#2052
|
|
46
|
+
* clamp behavior instead of having `process.cwd()` gate a refusal.
|
|
47
|
+
*
|
|
48
|
+
* This is also the seam #2053 needs: an explicit `analysisRoot` registers here.
|
|
49
|
+
*/
|
|
50
|
+
import { BoundedFifoMap } from "../bounded-cache.js";
|
|
51
|
+
import { incrementDegradationCount } from "../degradation-ledger.js";
|
|
52
|
+
import { isSameOrWithin } from "./server.js";
|
|
53
|
+
import path from "node:path";
|
|
54
|
+
/**
|
|
55
|
+
* Insertion-ordered, so eviction drops the OLDEST root. `BoundedFifoMap`
|
|
56
|
+
* (#2442) owns the eviction; this module no longer hand-rolls it. FIFO and not
|
|
57
|
+
* LRU on purpose: eviction order is REGISTRATION order, so a config write for
|
|
58
|
+
* an already-registered root cannot reorder the registry underneath the
|
|
59
|
+
* `ensureReady` memo that reads it.
|
|
60
|
+
*
|
|
61
|
+
* The value is `undefined` between `registerSessionRoot` and the config write
|
|
62
|
+
* that follows the loader's `await` — the same window in which, before #2518,
|
|
63
|
+
* no payload existed for the root at all. {@link sessionRootConfigEntries}
|
|
64
|
+
* skips those entries, so an in-flight root resolves exactly as it did before:
|
|
65
|
+
* to a shorter ancestor root's config, or to none.
|
|
66
|
+
*/
|
|
67
|
+
const SESSION_ROOT_CAP = 128;
|
|
68
|
+
const sessionRoots = new BoundedFifoMap(SESSION_ROOT_CAP);
|
|
69
|
+
/**
|
|
70
|
+
* The cap dropped a root this process was serving. Its files fall back to
|
|
71
|
+
* pre-#2052 clamp behavior and its `lsp.disabledServers` denial stops applying
|
|
72
|
+
* until something initializes the root again — which `shouldInitializeSessionRoot`
|
|
73
|
+
* now guarantees the next SESSION START OR TOOL CALL naming that root does,
|
|
74
|
+
* because the registration went with the payload. Nothing else re-initializes:
|
|
75
|
+
* the readers of the denial (`isServerDisabled`, `getServersForFileWithConfig`,
|
|
76
|
+
* `getServerInitOverride`) ask the store, they never load it.
|
|
77
|
+
*
|
|
78
|
+
* A CONSTANT subject, counted rather than latched (#2518 review F3). The
|
|
79
|
+
* subject cannot be the evicted root: the ledger's `recordDegradationOnce` /
|
|
80
|
+
* `incrementDegradationCount` dedupe keys are unbounded, so a per-root subject
|
|
81
|
+
* would grow one key per root a long-lived process ever cycles through — a
|
|
82
|
+
* container bounded on one axis and unbounded on another, which is the shape
|
|
83
|
+
* this whole fix is about. That argument bounds the SUBJECT; it is not an
|
|
84
|
+
* argument for throwing the tally away, and the number of roots this process
|
|
85
|
+
* has had to drop is exactly what an operator tunes the cap against. The
|
|
86
|
+
* ledger keeps one entry per kind/subject and emits durable rows only on
|
|
87
|
+
* power-of-two counts, so counting is as bounded here as latching.
|
|
88
|
+
*
|
|
89
|
+
* Returns the evicted roots so the caller can drop the per-root session
|
|
90
|
+
* bookkeeping that described them — `initLSPConfig` releases each one's
|
|
91
|
+
* `config_resolved` claim (review F1).
|
|
92
|
+
*/
|
|
93
|
+
function noteEvictedRoots(evicted) {
|
|
94
|
+
const roots = evicted.map(([root]) => root);
|
|
95
|
+
if (roots.length > 0) {
|
|
96
|
+
incrementDegradationCount({
|
|
97
|
+
kind: "lsp-session-root-evicted",
|
|
98
|
+
subject: `cap=${SESSION_ROOT_CAP}`,
|
|
99
|
+
reason: `session root registry at capacity; dropped ${roots[0]} and will load it again on the next session start or tool call naming that root`,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return roots;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Record a session cwd as served. Idempotent; re-registering refreshes nothing.
|
|
106
|
+
* Returns the roots this insertion dropped at the cap, oldest first.
|
|
107
|
+
*/
|
|
108
|
+
export function registerSessionRoot(cwd) {
|
|
109
|
+
const root = path.resolve(cwd);
|
|
110
|
+
// Present already: leave the entry alone. Writing the `undefined`
|
|
111
|
+
// placeholder over a loaded config would blank a served root's denial for
|
|
112
|
+
// the length of the re-initializing load — the #2518 window in miniature.
|
|
113
|
+
if (sessionRoots.has(root))
|
|
114
|
+
return [];
|
|
115
|
+
return noteEvictedRoots(sessionRoots.set(root, undefined));
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Store the config `initLSPConfig` just loaded for a root it registered.
|
|
119
|
+
*
|
|
120
|
+
* Re-inserts the root when the cap dropped it while the load was in flight:
|
|
121
|
+
* the caller is a live session declaring this root right now, so the pair
|
|
122
|
+
* belongs in the registry — and it is a PAIR, which is what keeps the payload
|
|
123
|
+
* from outliving the registration or the registration from outliving the
|
|
124
|
+
* payload.
|
|
125
|
+
*
|
|
126
|
+
* Returns the roots this write dropped at the cap, oldest first.
|
|
127
|
+
*/
|
|
128
|
+
export function setSessionRootConfig(cwd, config) {
|
|
129
|
+
return noteEvictedRoots(sessionRoots.set(path.resolve(cwd), config));
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Every served root that has a loaded config, in registration order —
|
|
133
|
+
* `getConfigForFile`'s (`config.ts`) longest-prefix walk. Entries still
|
|
134
|
+
* awaiting their first load are skipped; see the store's doc comment.
|
|
135
|
+
*/
|
|
136
|
+
export function sessionRootConfigEntries() {
|
|
137
|
+
return sessionRoots;
|
|
138
|
+
}
|
|
139
|
+
/** Return whether this exact root is still served after cap eviction. */
|
|
140
|
+
export function isSessionRootRegistered(cwd) {
|
|
141
|
+
return sessionRoots.has(path.resolve(cwd));
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* True when a readiness memo must run initialization for this root.
|
|
145
|
+
*
|
|
146
|
+
* The memo alone is never enough (#2052 R1, #2518): a readiness set caps
|
|
147
|
+
* itself, and `initLSPConfig` has callers (`clients/runtime-session.ts`,
|
|
148
|
+
* `clients/lens-engine.ts`) that no memo sees, so a memo hit can outlive the
|
|
149
|
+
* registration — and with it the config that carries the operator's denial.
|
|
150
|
+
* Every readiness memo asks this function, never its own `has` alone.
|
|
151
|
+
*/
|
|
152
|
+
export function shouldInitializeSessionRoot(cwd, readyRoots) {
|
|
153
|
+
const normalized = path.resolve(cwd);
|
|
154
|
+
return !readyRoots.has(normalized) || !isSessionRootRegistered(normalized);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Is `filePath` outside EVERY registered session root?
|
|
158
|
+
*
|
|
159
|
+
* Returns false (serve it) when the registry is empty — see the fail-open rule
|
|
160
|
+
* in this module's header. Containment uses `isSameOrWithin`, the same
|
|
161
|
+
* comparator the LSP root ceiling uses, which selects `path.win32` by path
|
|
162
|
+
* SHAPE rather than by `process.platform` (shape 2 / #1150).
|
|
163
|
+
*/
|
|
164
|
+
export function isOutsideAllSessionRoots(filePath) {
|
|
165
|
+
if (sessionRoots.size === 0)
|
|
166
|
+
return false;
|
|
167
|
+
const resolved = path.resolve(filePath);
|
|
168
|
+
for (const root of sessionRoots.keys()) {
|
|
169
|
+
if (isSameOrWithin(root, resolved))
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* The registered roots, for the decline telemetry record. Sorted with a
|
|
176
|
+
* code-unit comparator so the emitted string is deterministic across locales
|
|
177
|
+
* (#1883), and capped because the record is BOUNDED — a process with many
|
|
178
|
+
* roots must not emit an unbounded path list.
|
|
179
|
+
*/
|
|
180
|
+
export function getSessionRootsForTelemetry(limit = 4) {
|
|
181
|
+
return [...sessionRoots.keys()]
|
|
182
|
+
.sort((a, b) => Number(a > b) - Number(a < b))
|
|
183
|
+
.slice(0, limit);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Clear the registry — the roots AND the configs they carry, which are one
|
|
187
|
+
* store since #2518, so a reset cannot leave the two disagreeing about which
|
|
188
|
+
* roots exist. Called by `resetLSPConfigStateForTests`.
|
|
189
|
+
*/
|
|
190
|
+
export function resetSessionRootsForTests() {
|
|
191
|
+
sessionRoots.clear();
|
|
192
|
+
}
|
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
/** Process-lifetime history of successful LSP spawn plus initialize durations. */
|
|
2
|
+
import { BoundedFifoMap } from "../bounded-cache.js";
|
|
2
3
|
const MAX_SERVER_HISTORIES = 64;
|
|
3
|
-
const successfulSpawnDurationMs = new
|
|
4
|
+
const successfulSpawnDurationMs = new BoundedFifoMap(MAX_SERVER_HISTORIES);
|
|
4
5
|
export function recordSuccessfulLspSpawn(serverId, durationMs) {
|
|
5
6
|
if (!Number.isFinite(durationMs) || durationMs < 0)
|
|
6
7
|
return;
|
|
7
8
|
successfulSpawnDurationMs.delete(serverId);
|
|
8
9
|
successfulSpawnDurationMs.set(serverId, durationMs);
|
|
9
|
-
while (successfulSpawnDurationMs.size > MAX_SERVER_HISTORIES) {
|
|
10
|
-
const oldest = successfulSpawnDurationMs.keys().next().value;
|
|
11
|
-
if (oldest === undefined)
|
|
12
|
-
break;
|
|
13
|
-
successfulSpawnDurationMs.delete(oldest);
|
|
14
|
-
}
|
|
15
10
|
}
|
|
16
11
|
export function getSuccessfulLspSpawnDurationMs(serverId) {
|
|
17
12
|
return successfulSpawnDurationMs.get(serverId);
|
|
@@ -303,11 +303,18 @@ export async function runTsserverSyncCommand(svc, file, command) {
|
|
|
303
303
|
* server had computed but never published (silentOnClean) — these must be
|
|
304
304
|
* surfaced to the caller, not discarded. `confirmed: false` = sync path
|
|
305
305
|
* unavailable, fall through to existing behavior.
|
|
306
|
+
*
|
|
307
|
+
* #2598: `getAdvertisedCommands` is REQUIRED here rather than probed for. The
|
|
308
|
+
* real `LSPService` defines it unconditionally (clients/lsp/index.ts), and both
|
|
309
|
+
* production callers hand this function that service, so the former
|
|
310
|
+
* `typeof … !== "function"` bail was reachable only from a partial test double
|
|
311
|
+
* (AGENTS.md shape 7). It was also unobservable even there: an absent method
|
|
312
|
+
* throws a `TypeError` that this function's own catch turns into the same
|
|
313
|
+
* `undefined` the bail returned. `executeCommand` stays optional on
|
|
314
|
+
* {@link TsserverSyncCapableService} — see {@link runTsserverSyncCommand}.
|
|
306
315
|
*/
|
|
307
316
|
export async function attemptTsserverSyncDiagnostics(file, svc) {
|
|
308
317
|
try {
|
|
309
|
-
if (typeof svc.getAdvertisedCommands !== "function")
|
|
310
|
-
return undefined;
|
|
311
318
|
const advertised = await svc.getAdvertisedCommands(file);
|
|
312
319
|
if (!advertised.includes(TSSERVER_REQUEST_COMMAND))
|
|
313
320
|
return undefined;
|
|
@@ -8,6 +8,7 @@ import { normalizeMapKey } from "../path-utils.js";
|
|
|
8
8
|
import { loadReverseDependencyIndexFromSnapshot } from "../reverse-deps.js";
|
|
9
9
|
import { freshnessFromMtime } from "../freshness.js";
|
|
10
10
|
import { logLatency } from "../latency-logger.js";
|
|
11
|
+
import { compareOrdinal } from "../string-utils.js";
|
|
11
12
|
import { workspaceDiagnosticsCacheSessionStart } from "./workspace-diagnostics-session.js";
|
|
12
13
|
import { createDiskBindingCache, } from "./diagnostic-binding.js";
|
|
13
14
|
/**
|
|
@@ -281,7 +282,11 @@ export function classifyWorkspaceDiagnosticsCacheExpiry(entry, now, sessionStart
|
|
|
281
282
|
* Two invalidation layers:
|
|
282
283
|
* 1. The file's OWN mtime must be unchanged since it was scanned (exact
|
|
283
284
|
* match against `entry.mtimeMs`) — any drift, or a stat failure
|
|
284
|
-
* (deleted/unreadable), invalidates.
|
|
285
|
+
* (deleted/unreadable), invalidates. #2300: when the entry also carries a
|
|
286
|
+
* `sizeBytes`, the current size must match too — an external rewrite that
|
|
287
|
+
* lands in the same mtime bucket with a different length is otherwise
|
|
288
|
+
* invisible to mtime alone. A pre-#2300 entry without `sizeBytes` fails
|
|
289
|
+
* OPEN to the mtime-only check (see the field's doc comment).
|
|
285
290
|
* 2. When a reverse-dependency index is available (`getImports` returns an
|
|
286
291
|
* array rather than `undefined`), every file THIS file imports must not
|
|
287
292
|
* have changed after `entry.scannedAt` either — this is the fix for the
|
|
@@ -326,15 +331,19 @@ export function classifyWorkspaceDiagnosticsCacheExpiry(entry, now, sessionStart
|
|
|
326
331
|
* (#1491/#1498) rather than just a redundant re-touch.
|
|
327
332
|
*/
|
|
328
333
|
export function isEntryFresh(filePath, entry, getImports) {
|
|
329
|
-
let
|
|
334
|
+
let ownStat;
|
|
330
335
|
try {
|
|
331
|
-
|
|
336
|
+
ownStat = fs.statSync(filePath);
|
|
332
337
|
}
|
|
333
338
|
catch {
|
|
334
339
|
return false; // deleted / unreadable
|
|
335
340
|
}
|
|
336
|
-
if (
|
|
341
|
+
if (ownStat.mtimeMs !== entry.mtimeMs)
|
|
337
342
|
return false;
|
|
343
|
+
// #2300: same axis reused from the stat above — no extra syscall.
|
|
344
|
+
if (entry.sizeBytes !== undefined && ownStat.size !== entry.sizeBytes) {
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
338
347
|
const imports = getImports(filePath);
|
|
339
348
|
if (imports === undefined) {
|
|
340
349
|
// #1793: an entry recorded WITH dependency knowledge must not fall
|
|
@@ -376,7 +385,7 @@ export function cacheKeyFor(filePath) {
|
|
|
376
385
|
*/
|
|
377
386
|
export function buildScopeKey(clientScope, excludeServerIds) {
|
|
378
387
|
const excluded = excludeServerIds
|
|
379
|
-
? [...excludeServerIds].sort(
|
|
388
|
+
? [...excludeServerIds].sort(compareOrdinal)
|
|
380
389
|
: [];
|
|
381
390
|
return `${clientScope}|${excluded.join(",")}`;
|
|
382
391
|
}
|
|
@@ -389,7 +398,7 @@ export function createWorkspaceDiagnosticsCacheContext(cwd) {
|
|
|
389
398
|
const epoch = _cacheEpochs.capture(root);
|
|
390
399
|
const existing = loadWorkspaceDiagnosticsCache(root);
|
|
391
400
|
const entries = {
|
|
392
|
-
...
|
|
401
|
+
...existing?.entries,
|
|
393
402
|
};
|
|
394
403
|
const reverseDepsIndex = loadReverseDependencyIndexFromSnapshot({
|
|
395
404
|
cwd: root,
|
|
@@ -542,7 +551,7 @@ export function createWorkspaceDiagnosticsCacheContext(cwd) {
|
|
|
542
551
|
scannedAt: entry.scannedAt,
|
|
543
552
|
};
|
|
544
553
|
},
|
|
545
|
-
record(filePath, scopeKey, diagnostics, mtimeMs, contentHash) {
|
|
554
|
+
record(filePath, scopeKey, diagnostics, mtimeMs, contentHash, sizeBytes) {
|
|
546
555
|
entries[cacheKeyFor(filePath)] = {
|
|
547
556
|
diagnostics,
|
|
548
557
|
count: diagnostics.length,
|
|
@@ -556,6 +565,7 @@ export function createWorkspaceDiagnosticsCacheContext(cwd) {
|
|
|
556
565
|
// weaker mtime-only path only when that refusal is warranted.
|
|
557
566
|
depIndexAtScan: hasDepKnowledge(filePath),
|
|
558
567
|
...(contentHash !== undefined && { contentHash }),
|
|
568
|
+
...(sizeBytes !== undefined && { sizeBytes }),
|
|
559
569
|
};
|
|
560
570
|
dirty = true;
|
|
561
571
|
},
|
|
@@ -40,22 +40,26 @@
|
|
|
40
40
|
* apart again (#1618 acceptance criterion 6).
|
|
41
41
|
*/
|
|
42
42
|
import { logLatency } from "../latency-logger.js";
|
|
43
|
+
import { getProcessSingleton } from "../process-singletons.js";
|
|
43
44
|
/** Extra headroom beyond the sweep's own wall-clock ceiling before an
|
|
44
45
|
* idle-reset delay (or the max-hold-age failsafe) trusts that a real sweep
|
|
45
46
|
* must be done. Single source for both derivations below AND for every
|
|
46
47
|
* idle-reset path `clients/runtime-turn.ts` derives (base, subagent, and
|
|
47
48
|
* budget-shortened) — never a second independently-tunable literal. */
|
|
48
49
|
export const SWEEP_IDLE_SAFETY_MARGIN_MS = 60_000;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
const WORKSPACE_SWEEP_HOLD_FAMILY = "lsp.workspace-sweep-hold";
|
|
51
|
+
const WORKSPACE_SWEEP_HOLD_VERSION = 1;
|
|
52
|
+
function state() {
|
|
53
|
+
return getProcessSingleton(WORKSPACE_SWEEP_HOLD_FAMILY, WORKSPACE_SWEEP_HOLD_VERSION, () => ({ holds: new Map(), nextHoldId: 1, idleWaiters: [] }));
|
|
54
|
+
}
|
|
52
55
|
function flushIdleWaitersIfEmpty() {
|
|
53
|
-
|
|
56
|
+
const current = state();
|
|
57
|
+
if (current.holds.size > 0)
|
|
54
58
|
return;
|
|
55
|
-
if (idleWaiters.length === 0)
|
|
59
|
+
if (current.idleWaiters.length === 0)
|
|
56
60
|
return;
|
|
57
|
-
const waiters = idleWaiters;
|
|
58
|
-
idleWaiters = [];
|
|
61
|
+
const waiters = current.idleWaiters;
|
|
62
|
+
current.idleWaiters = [];
|
|
59
63
|
for (const waiter of waiters) {
|
|
60
64
|
try {
|
|
61
65
|
waiter();
|
|
@@ -77,15 +81,16 @@ export function getWorkspaceSweepMaxHoldAgeMs() {
|
|
|
77
81
|
return getFullScanWallClockMs() + SWEEP_IDLE_SAFETY_MARGIN_MS;
|
|
78
82
|
}
|
|
79
83
|
function reapStaleHolds() {
|
|
80
|
-
|
|
84
|
+
const current = state();
|
|
85
|
+
if (current.holds.size === 0)
|
|
81
86
|
return;
|
|
82
87
|
const maxAgeMs = getWorkspaceSweepMaxHoldAgeMs();
|
|
83
88
|
const now = Date.now();
|
|
84
|
-
for (const [holdId, acquiredAt] of holds) {
|
|
89
|
+
for (const [holdId, acquiredAt] of current.holds) {
|
|
85
90
|
const ageMs = now - acquiredAt;
|
|
86
91
|
if (ageMs <= maxAgeMs)
|
|
87
92
|
continue;
|
|
88
|
-
holds.delete(holdId);
|
|
93
|
+
current.holds.delete(holdId);
|
|
89
94
|
logLatency({
|
|
90
95
|
type: "phase",
|
|
91
96
|
phase: "lsp_workspace_sweep_hold_force_released",
|
|
@@ -104,10 +109,11 @@ function reapStaleHolds() {
|
|
|
104
109
|
* force-released (session reset / max-age reap), is a no-op.
|
|
105
110
|
*/
|
|
106
111
|
export function acquireWorkspaceSweepHold() {
|
|
107
|
-
const
|
|
108
|
-
|
|
112
|
+
const current = state();
|
|
113
|
+
const holdId = current.nextHoldId++;
|
|
114
|
+
current.holds.set(holdId, Date.now());
|
|
109
115
|
return () => {
|
|
110
|
-
if (!holds.delete(holdId))
|
|
116
|
+
if (!state().holds.delete(holdId))
|
|
111
117
|
return;
|
|
112
118
|
flushIdleWaitersIfEmpty();
|
|
113
119
|
};
|
|
@@ -117,7 +123,7 @@ export function acquireWorkspaceSweepHold() {
|
|
|
117
123
|
* "active" past its own max age. */
|
|
118
124
|
export function isWorkspaceSweepActive() {
|
|
119
125
|
reapStaleHolds();
|
|
120
|
-
return holds.size > 0;
|
|
126
|
+
return state().holds.size > 0;
|
|
121
127
|
}
|
|
122
128
|
/**
|
|
123
129
|
* Run `cb` once the hold count next returns to zero. Runs synchronously and
|
|
@@ -126,11 +132,12 @@ export function isWorkspaceSweepActive() {
|
|
|
126
132
|
*/
|
|
127
133
|
export function runWhenWorkspaceSweepIdle(cb) {
|
|
128
134
|
reapStaleHolds();
|
|
129
|
-
|
|
135
|
+
const current = state();
|
|
136
|
+
if (current.holds.size === 0) {
|
|
130
137
|
cb();
|
|
131
138
|
return;
|
|
132
139
|
}
|
|
133
|
-
idleWaiters.push(cb);
|
|
140
|
+
current.idleWaiters.push(cb);
|
|
134
141
|
}
|
|
135
142
|
/**
|
|
136
143
|
* `resetLSPService({reason: "session_start"})` calls this: a session
|
|
@@ -142,23 +149,25 @@ export function runWhenWorkspaceSweepIdle(cb) {
|
|
|
142
149
|
* silent.
|
|
143
150
|
*/
|
|
144
151
|
export function clearWorkspaceSweepHoldForSessionStart() {
|
|
145
|
-
|
|
152
|
+
const current = state();
|
|
153
|
+
if (current.holds.size === 0)
|
|
146
154
|
return;
|
|
147
155
|
logLatency({
|
|
148
156
|
type: "phase",
|
|
149
157
|
phase: "lsp_workspace_sweep_hold_session_reset",
|
|
150
158
|
filePath: "",
|
|
151
159
|
durationMs: 0,
|
|
152
|
-
metadata: { clearedHolds: holds.size },
|
|
160
|
+
metadata: { clearedHolds: current.holds.size },
|
|
153
161
|
});
|
|
154
|
-
holds.clear();
|
|
162
|
+
current.holds.clear();
|
|
155
163
|
flushIdleWaitersIfEmpty();
|
|
156
164
|
}
|
|
157
|
-
/** Test-only: reset the
|
|
165
|
+
/** Test-only: reset the process-singleton hold state between tests. */
|
|
158
166
|
export function _resetWorkspaceSweepHoldForTests() {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
167
|
+
const current = state();
|
|
168
|
+
current.holds.clear();
|
|
169
|
+
current.idleWaiters = [];
|
|
170
|
+
current.nextHoldId = 1;
|
|
162
171
|
}
|
|
163
172
|
/**
|
|
164
173
|
* Wall-clock ceiling for one `lens_diagnostics mode=full` scan
|