@herbertgao/pi-extensions 2026.9.0 → 2026.9.1
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/node_modules/@czottmann/pi-automode/CHANGELOG.md +13 -1
- package/node_modules/@czottmann/pi-automode/README.md +20 -0
- package/node_modules/@czottmann/pi-automode/docs/GLOSSARY.md +1 -1
- package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +2 -1
- package/node_modules/@czottmann/pi-automode/docs/defaults.md +3 -2
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +38 -2
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +5 -3
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +11 -3
- package/node_modules/@czottmann/pi-automode/package.json +1 -1
- package/node_modules/@pi-plugins/fast-mode/README.md +5 -3
- package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts.map +1 -1
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +20 -55
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
- package/node_modules/@pi-plugins/fast-mode/package.json +1 -1
- package/node_modules/pi-lens/CHANGELOG.md +988 -0
- package/node_modules/pi-lens/README.md +3 -0
- package/node_modules/pi-lens/config/biome/core.jsonc +11 -2
- package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +13 -1
- package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +927 -80
- package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +13 -4
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +123 -3
- package/node_modules/pi-lens/dist/clients/ast-grep-rule-manager.js +60 -5
- package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +1 -3
- package/node_modules/pi-lens/dist/clients/biome-client.js +9 -2
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +14 -0
- package/node_modules/pi-lens/dist/clients/bootstrap.js +509 -73
- package/node_modules/pi-lens/dist/clients/bounded-cache.js +152 -12
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +59 -4
- package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +113 -0
- package/node_modules/pi-lens/dist/clients/bus-events-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +31 -4
- package/node_modules/pi-lens/dist/clients/cache-manager.js +105 -6
- package/node_modules/pi-lens/dist/clients/cache-observability.js +44 -18
- package/node_modules/pi-lens/dist/clients/cargo-manifest.js +422 -0
- package/node_modules/pi-lens/dist/clients/cascade-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +13 -3
- package/node_modules/pi-lens/dist/clients/complexity-client.js +16 -5
- package/node_modules/pi-lens/dist/clients/config-core/deny.js +221 -0
- package/node_modules/pi-lens/dist/clients/config-core/index.js +47 -0
- package/node_modules/pi-lens/dist/clients/config-core/merge.js +357 -0
- package/node_modules/pi-lens/dist/clients/config-core/normalize.js +340 -0
- package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +248 -0
- package/node_modules/pi-lens/dist/clients/config-core/provenance.js +171 -0
- package/node_modules/pi-lens/dist/clients/config-core/records.js +218 -0
- package/node_modules/pi-lens/dist/clients/config-core/resolve.js +125 -0
- package/node_modules/pi-lens/dist/clients/config-core/safe-object.js +78 -0
- package/node_modules/pi-lens/dist/clients/config-core/schema.js +167 -0
- package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +281 -0
- package/node_modules/pi-lens/dist/clients/config-locations.js +160 -0
- package/node_modules/pi-lens/dist/clients/config-resolve.js +789 -0
- package/node_modules/pi-lens/dist/clients/config-schema.js +197 -0
- package/node_modules/pi-lens/dist/clients/config-warn.js +407 -0
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +19 -14
- package/node_modules/pi-lens/dist/clients/dead-code-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/deadline-utils.js +178 -0
- package/node_modules/pi-lens/dist/clients/debug-handles.js +2 -2
- package/node_modules/pi-lens/dist/clients/debug-heap.js +3 -3
- package/node_modules/pi-lens/dist/clients/deferred-lsp-work.js +106 -0
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +113 -7
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +8 -19
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +12 -9
- package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +14 -4
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +162 -18
- package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +97 -0
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +9 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +380 -26
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/fact-rules.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +1 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +13 -18
- package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +7 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +1 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +3 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +3 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +194 -80
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +84 -15
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +0 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -1
- package/node_modules/pi-lens/dist/clients/disposition-logger.js +3 -3
- package/node_modules/pi-lens/dist/clients/effective-config.js +403 -0
- package/node_modules/pi-lens/dist/clients/error-class.js +23 -0
- package/node_modules/pi-lens/dist/clients/event-loop-hold.js +274 -0
- package/node_modules/pi-lens/dist/clients/extension-log.js +2 -2
- package/node_modules/pi-lens/dist/clients/feature-hints.js +2 -1
- package/node_modules/pi-lens/dist/clients/file-time.js +6 -1
- package/node_modules/pi-lens/dist/clients/file-utils.js +112 -9
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +50 -13
- package/node_modules/pi-lens/dist/clients/format-service.js +6 -1
- package/node_modules/pi-lens/dist/clients/formatters.js +276 -102
- package/node_modules/pi-lens/dist/clients/freshness-cadence.js +17 -0
- package/node_modules/pi-lens/dist/clients/generated-artifacts.js +114 -19
- package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -9
- package/node_modules/pi-lens/dist/clients/git-guard.js +1 -1
- package/node_modules/pi-lens/dist/clients/go-client.js +39 -0
- package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +252 -0
- package/node_modules/pi-lens/dist/clients/hashline-anchor.js +424 -0
- package/node_modules/pi-lens/dist/clients/hook-budgets.js +76 -0
- package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +5 -2
- package/node_modules/pi-lens/dist/clients/host-ports.js +1 -1
- package/node_modules/pi-lens/dist/clients/installer/index.js +71 -52
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +104 -230
- package/node_modules/pi-lens/dist/clients/instance-registry.js +6 -3
- package/node_modules/pi-lens/dist/clients/language-registry.js +595 -0
- package/node_modules/pi-lens/dist/clients/latency-logger.js +160 -2
- package/node_modules/pi-lens/dist/clients/ledger-bounds.js +13 -0
- package/node_modules/pi-lens/dist/clients/lens-config.js +141 -30
- package/node_modules/pi-lens/dist/clients/lens-engine.js +30 -37
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +59 -6
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -2
- package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +3 -1
- package/node_modules/pi-lens/dist/clients/lsp/client.js +168 -30
- package/node_modules/pi-lens/dist/clients/lsp/config.js +483 -158
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +15 -2
- package/node_modules/pi-lens/dist/clients/lsp/index.js +676 -71
- package/node_modules/pi-lens/dist/clients/lsp/language.js +18 -169
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +5 -16
- package/node_modules/pi-lens/dist/clients/lsp/server.js +284 -147
- package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +116 -23
- package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +2 -7
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +9 -2
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp-mutation.js +163 -21
- package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +36 -0
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +33 -4
- package/node_modules/pi-lens/dist/clients/mcp/session.js +5 -16
- package/node_modules/pi-lens/dist/clients/memory-sampler.js +8 -3
- package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +2 -4
- package/node_modules/pi-lens/dist/clients/module-report.js +20 -45
- package/node_modules/pi-lens/dist/clients/mutating-tool.js +651 -0
- package/node_modules/pi-lens/dist/clients/mutation-attribution.js +368 -0
- package/node_modules/pi-lens/dist/clients/mutation-bridge.js +240 -0
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +247 -20
- package/node_modules/pi-lens/dist/clients/observed-mutation-sources.js +101 -0
- package/node_modules/pi-lens/dist/clients/observed-mutation.js +1215 -0
- package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +68 -22
- package/node_modules/pi-lens/dist/clients/opengrep-config.js +4 -0
- package/node_modules/pi-lens/dist/clients/package-manager.js +195 -26
- package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +359 -80
- package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +23 -7
- package/node_modules/pi-lens/dist/clients/path-keyed-map.js +21 -2
- package/node_modules/pi-lens/dist/clients/path-utils.js +428 -13
- package/node_modules/pi-lens/dist/clients/persist-debounce.js +8 -1
- package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +114 -0
- package/node_modules/pi-lens/dist/clients/pipeline.js +77 -16
- package/node_modules/pi-lens/dist/clients/probe-home-state.js +228 -0
- package/node_modules/pi-lens/dist/clients/process-bridge.js +66 -0
- package/node_modules/pi-lens/dist/clients/process-snapshot.js +68 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +4 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +27 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +51 -20
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +393 -122
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +52 -24
- package/node_modules/pi-lens/dist/clients/python-environment.js +217 -0
- package/node_modules/pi-lens/dist/clients/python-provenance.js +639 -0
- package/node_modules/pi-lens/dist/clients/quiet-window.js +6 -1
- package/node_modules/pi-lens/dist/clients/read-bridge.js +13 -21
- package/node_modules/pi-lens/dist/clients/read-expansion.js +9 -46
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +15 -4
- package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +364 -212
- package/node_modules/pi-lens/dist/clients/read-guard.js +21 -0
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +195 -96
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +152 -109
- package/node_modules/pi-lens/dist/clients/review-graph/service.js +55 -3
- package/node_modules/pi-lens/dist/clients/review-graph/shared-extraction-ir.js +7 -15
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +10 -74
- package/node_modules/pi-lens/dist/clients/review-graph-logger.js +9 -3
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +18 -2
- package/node_modules/pi-lens/dist/clients/runtime-context.js +49 -16
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +25 -6
- package/node_modules/pi-lens/dist/clients/runtime-session.js +139 -22
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +168 -35
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +797 -243
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +1028 -53
- package/node_modules/pi-lens/dist/clients/rust-client.js +31 -0
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +33 -10
- package/node_modules/pi-lens/dist/clients/sanitize.js +7 -1
- package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +1 -3
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +3 -0
- package/node_modules/pi-lens/dist/clients/session-lifecycle.js +1 -1
- package/node_modules/pi-lens/dist/clients/session-start-observability.js +79 -0
- package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +12 -3
- package/node_modules/pi-lens/dist/clients/sgconfig.js +6 -1
- package/node_modules/pi-lens/dist/clients/skills-resolver.js +105 -0
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +2 -2
- package/node_modules/pi-lens/dist/clients/string-utils.js +13 -0
- package/node_modules/pi-lens/dist/clients/subagent-mode.js +17 -4
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +176 -39
- package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +239 -0
- package/node_modules/pi-lens/dist/clients/tool-definition.js +41 -1
- package/node_modules/pi-lens/dist/clients/tool-policy.js +91 -21
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +40 -40
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +86 -48
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +81 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +51 -46
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +28 -0
- package/node_modules/pi-lens/dist/clients/typos-config.js +5 -0
- package/node_modules/pi-lens/dist/clients/user-notify.js +6 -2
- package/node_modules/pi-lens/dist/clients/widget-state.js +191 -26
- package/node_modules/pi-lens/dist/clients/word-index-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/word-index-store.js +20 -5
- package/node_modules/pi-lens/dist/clients/word-index.js +243 -31
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +8 -1
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +3 -0
- package/node_modules/pi-lens/dist/index.js +64335 -54300
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -1
- package/node_modules/pi-lens/dist/mcp/server.js +127 -5
- package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +583 -0
- package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +129 -0
- package/node_modules/pi-lens/dist/tools/effective-config.js +89 -0
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +95 -24
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +67 -80
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +37 -16
- package/node_modules/pi-lens/dist/tools/shared.js +0 -1
- package/node_modules/pi-lens/docs/agent-guide.md +64 -1
- package/node_modules/pi-lens/docs/configuration.md +222 -0
- package/node_modules/pi-lens/docs/dependencies.md +3 -3
- package/node_modules/pi-lens/docs/features.md +44 -5
- package/node_modules/pi-lens/docs/language-coverage.md +2 -2
- package/node_modules/pi-lens/docs/pi-lens-fixer.md +25 -0
- package/node_modules/pi-lens/docs/pi-lens-investigator.md +25 -0
- package/node_modules/pi-lens/docs/pi-lens-reviewer.md +27 -0
- package/node_modules/pi-lens/docs/pi-lens-subagent.md +38 -0
- package/node_modules/pi-lens/docs/pi-lens-warden.md +55 -0
- package/node_modules/pi-lens/docs/public-api-stability.md +359 -0
- package/node_modules/pi-lens/docs/release-qa-baseline.md +182 -0
- package/node_modules/pi-lens/docs/subagent-compat.md +110 -30
- package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +2 -2
- package/node_modules/pi-lens/package.json +15 -23
- package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-cross-language-method.yml +3 -3
- package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-hallucinated-import.yml +2 -2
- package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-sql-injection.yml +12 -2
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +192 -1
- package/node_modules/pi-lens/scripts/install-selftest.mjs +58 -2
- package/node_modules/pi-lens/scripts/lib/skills-predicate.mjs +129 -0
- package/node_modules/pi-lens/scripts/rpc-load-check.mjs +3 -1
- package/node_modules/pi-web-access/CHANGELOG.md +28 -0
- package/node_modules/pi-web-access/README.md +62 -15
- package/node_modules/pi-web-access/curator-page.ts +3 -1
- package/node_modules/pi-web-access/curator-server.ts +5 -1
- package/node_modules/pi-web-access/gemini-search.ts +8 -4
- package/node_modules/pi-web-access/github-extract.ts +242 -1
- package/node_modules/pi-web-access/index.ts +118 -64
- package/node_modules/pi-web-access/mistral-search.ts +281 -0
- package/node_modules/pi-web-access/package.json +2 -2
- package/node_modules/pi-web-access/perplexity.ts +14 -1
- package/node_modules/pi-web-access/utils.ts +23 -6
- package/node_modules/pi-web-access/xai-search.ts +96 -33
- package/package.json +5 -5
|
@@ -17,15 +17,16 @@ import { logExtension } from "../extension-log.js";
|
|
|
17
17
|
import * as fs from "node:fs";
|
|
18
18
|
import * as path from "node:path";
|
|
19
19
|
import { recordRunner } from "../widget-state.js";
|
|
20
|
-
import { incrementDegradationCount } from "../degradation-ledger.js";
|
|
20
|
+
import { incrementDegradationCount, recordDegradationOnce, } from "../degradation-ledger.js";
|
|
21
21
|
import { detectFileKind } from "../file-kinds.js";
|
|
22
22
|
import { detectFileRole } from "../file-role.js";
|
|
23
|
+
import { classifyGeneratedOrArtifactDetailed, } from "../generated-artifacts.js";
|
|
23
24
|
import { isTestFile } from "../file-utils.js";
|
|
24
25
|
import { getPrimaryDispatchGroup } from "../language-policy.js";
|
|
25
26
|
import { resolveLanguageRootForFile } from "../language-profile.js";
|
|
26
27
|
import { logLatency, phaseFinished, phaseStarted } from "../latency-logger.js";
|
|
27
28
|
import { isSpawnableCommand } from "../installer/index.js";
|
|
28
|
-
import { normalizeEphemeralMapKey, normalizeMapKey } from "../path-utils.js";
|
|
29
|
+
import { isAbsoluteFilePath, normalizeEphemeralMapKey, normalizeMapKey, } from "../path-utils.js";
|
|
29
30
|
import { loadPiLensProjectConfig } from "../project-lens-config.js";
|
|
30
31
|
import { RUNTIME_CONFIG, getRunnerTimeoutFloorMs } from "../runtime-config.js";
|
|
31
32
|
import { safeSpawnAsync } from "../safe-spawn.js";
|
|
@@ -61,7 +62,7 @@ export class RunnerRegistry {
|
|
|
61
62
|
for (const runner of this.runners.values()) {
|
|
62
63
|
if (isTest && runner.skipTestFiles)
|
|
63
64
|
continue;
|
|
64
|
-
if (
|
|
65
|
+
if (runnerAppliesToKind(runner, kind)) {
|
|
65
66
|
matching.push(runner);
|
|
66
67
|
}
|
|
67
68
|
}
|
|
@@ -74,6 +75,10 @@ export class RunnerRegistry {
|
|
|
74
75
|
this.runners.clear();
|
|
75
76
|
}
|
|
76
77
|
}
|
|
78
|
+
function runnerAppliesToKind(runner, kind) {
|
|
79
|
+
return (runner.appliesTo.length === 0 ||
|
|
80
|
+
(kind !== undefined && runner.appliesTo.includes(kind)));
|
|
81
|
+
}
|
|
77
82
|
// --- Tool Availability Cache ---
|
|
78
83
|
/**
|
|
79
84
|
* Normalize a command name to a FactStore session key.
|
|
@@ -245,7 +250,19 @@ telemetryModel, telemetryProvider) {
|
|
|
245
250
|
const normalizedCwd = normalizeMapKey(resolveLanguageRootForFile(absoluteFilePath, cwd));
|
|
246
251
|
const normalizedFilePath = normalizeMapKey(absoluteFilePath);
|
|
247
252
|
const kind = detectFileKind(normalizedFilePath);
|
|
248
|
-
const
|
|
253
|
+
const contentPrefix = readFilePrefix(normalizedFilePath);
|
|
254
|
+
const fileRole = detectFileRole(normalizedFilePath, contentPrefix);
|
|
255
|
+
// Captured once here so the generated short-circuit below can emit a
|
|
256
|
+
// `dispatch_skipped_generated` record carrying the deciding evidence tier
|
|
257
|
+
// and the measured line-shape statistic — without re-reading the file
|
|
258
|
+
// (refs #2346). The content passed is the same 4096-byte prefix already
|
|
259
|
+
// read for role detection, so this classification costs no extra I/O.
|
|
260
|
+
const generatedDetail = fileRole === "generated"
|
|
261
|
+
? classifyGeneratedOrArtifactDetailed(normalizedFilePath, {
|
|
262
|
+
content: contentPrefix,
|
|
263
|
+
includeDeclarations: false,
|
|
264
|
+
})
|
|
265
|
+
: undefined;
|
|
249
266
|
const projectConfig = loadPiLensProjectConfig(normalizedCwd);
|
|
250
267
|
return {
|
|
251
268
|
filePath: normalizedFilePath,
|
|
@@ -253,6 +270,8 @@ telemetryModel, telemetryProvider) {
|
|
|
253
270
|
cwd: normalizedCwd,
|
|
254
271
|
kind,
|
|
255
272
|
fileRole,
|
|
273
|
+
generatedEvidence: generatedDetail?.evidence,
|
|
274
|
+
generatedLineShapeMean: generatedDetail?.lineShapeMean,
|
|
256
275
|
pi,
|
|
257
276
|
autofix: false,
|
|
258
277
|
deltaMode: !pi.getFlag("no-delta"),
|
|
@@ -462,7 +481,9 @@ function buildCoverageNotice(ctx, runnerLatencies) {
|
|
|
462
481
|
const primaryHasCoverage = relevant.some((r) => r.status === "succeeded" || r.status === "failed");
|
|
463
482
|
if (primaryHasCoverage)
|
|
464
483
|
return undefined;
|
|
465
|
-
const allPrimarySkipped = relevant.every((r) => r.status === "skipped" ||
|
|
484
|
+
const allPrimarySkipped = relevant.every((r) => r.status === "skipped" ||
|
|
485
|
+
r.status === "when_skipped" ||
|
|
486
|
+
r.status === "test_file_skipped");
|
|
466
487
|
if (!allPrimarySkipped)
|
|
467
488
|
return undefined;
|
|
468
489
|
const plan = getToolPlan(ctx.kind);
|
|
@@ -499,6 +520,11 @@ function buildCoverageNotice(ctx, runnerLatencies) {
|
|
|
499
520
|
}
|
|
500
521
|
const latencyReports = [];
|
|
501
522
|
const coverageNoticeSeen = new Set();
|
|
523
|
+
// One `dispatch_skipped_generated` phase record per file per process (refs
|
|
524
|
+
// #2346): a generated file dispatched repeatedly must not spam latency.log,
|
|
525
|
+
// so only the first skip of each file emits the row; the degradation ledger
|
|
526
|
+
// below still tallies every repetition with a bounded per-subject count.
|
|
527
|
+
const generatedSkipRecorded = new Set();
|
|
502
528
|
export function getLatencyReports() {
|
|
503
529
|
return [...latencyReports];
|
|
504
530
|
}
|
|
@@ -507,6 +533,7 @@ export function clearLatencyReports() {
|
|
|
507
533
|
}
|
|
508
534
|
export function clearCoverageNoticeState() {
|
|
509
535
|
coverageNoticeSeen.clear();
|
|
536
|
+
generatedSkipRecorded.clear();
|
|
510
537
|
}
|
|
511
538
|
export function formatLatencyReport(report) {
|
|
512
539
|
const lines = [];
|
|
@@ -588,6 +615,59 @@ async function runGroup(ctx, group, registry, onRunnerResult) {
|
|
|
588
615
|
});
|
|
589
616
|
continue;
|
|
590
617
|
}
|
|
618
|
+
// Same skipTestFiles gate RunnerRegistry.getForKind applies for the
|
|
619
|
+
// per-edit path (#2337): the plan/group path resolves runners by id via
|
|
620
|
+
// registry.get() instead, which bypasses getForKind entirely, so a
|
|
621
|
+
// runner declaring skipTestFiles never had it enforced here.
|
|
622
|
+
if (runner.skipTestFiles && isTestFile(ctx.filePath)) {
|
|
623
|
+
latencies.push({
|
|
624
|
+
runnerId,
|
|
625
|
+
startTime: runnerStart,
|
|
626
|
+
endTime: Date.now(),
|
|
627
|
+
durationMs: Date.now() - runnerStart,
|
|
628
|
+
status: "test_file_skipped",
|
|
629
|
+
diagnosticCount: 0,
|
|
630
|
+
semantic: "none",
|
|
631
|
+
});
|
|
632
|
+
logLatency({
|
|
633
|
+
type: "runner",
|
|
634
|
+
filePath: ctx.filePath,
|
|
635
|
+
runnerId,
|
|
636
|
+
durationMs: 0,
|
|
637
|
+
status: "test_file_skipped",
|
|
638
|
+
diagnosticCount: 0,
|
|
639
|
+
semantic: "none",
|
|
640
|
+
});
|
|
641
|
+
continue;
|
|
642
|
+
}
|
|
643
|
+
// Keep explicit groups aligned with RunnerRegistry.getForKind(): an empty
|
|
644
|
+
// appliesTo list means every kind, while a populated list must contain the
|
|
645
|
+
// current kind. A filtered runner remains visible as skipped telemetry so
|
|
646
|
+
// language mismatches cannot be mistaken for runner failures.
|
|
647
|
+
const appliesToCurrentKind = runnerAppliesToKind(runner, ctx.kind);
|
|
648
|
+
if (!appliesToCurrentKind) {
|
|
649
|
+
const runnerEnd = Date.now();
|
|
650
|
+
latencies.push({
|
|
651
|
+
runnerId,
|
|
652
|
+
startTime: runnerStart,
|
|
653
|
+
endTime: runnerEnd,
|
|
654
|
+
durationMs: 0,
|
|
655
|
+
status: "skipped",
|
|
656
|
+
diagnosticCount: 0,
|
|
657
|
+
semantic: "unknown",
|
|
658
|
+
});
|
|
659
|
+
logLatency({
|
|
660
|
+
type: "runner",
|
|
661
|
+
filePath: ctx.filePath,
|
|
662
|
+
runnerId,
|
|
663
|
+
durationMs: 0,
|
|
664
|
+
status: "skipped",
|
|
665
|
+
diagnosticCount: 0,
|
|
666
|
+
semantic: "unknown",
|
|
667
|
+
metadata: { reason: "applies_to", kind: ctx.kind },
|
|
668
|
+
});
|
|
669
|
+
continue;
|
|
670
|
+
}
|
|
591
671
|
// Check preconditions
|
|
592
672
|
let shouldRun = true;
|
|
593
673
|
if (runner.when) {
|
|
@@ -802,6 +882,31 @@ async function runGroup(ctx, group, registry, onRunnerResult) {
|
|
|
802
882
|
export async function dispatchForFile(ctx, groups, registry, onRunnerResult) {
|
|
803
883
|
const _overallStart = Date.now();
|
|
804
884
|
if (ctx.fileRole === "generated") {
|
|
885
|
+
// The generated short-circuit (refs #2346): never ran before this fix
|
|
886
|
+
// for name-less machine-emitted files (a scraped/minified page has no
|
|
887
|
+
// `.min.js` pattern to catch it), so the classification now also has a
|
|
888
|
+
// content-shape tier. The skip is observable — one `dispatch_skipped_generated`
|
|
889
|
+
// phase record per file per process carrying the deciding evidence tier
|
|
890
|
+
// and the measured line-shape statistic. Deliberately NOT a degradation
|
|
891
|
+
// ledger entry: skipping a generated file is healthy behavior working
|
|
892
|
+
// as designed, and the ledger's bounded kind slots are reserved for
|
|
893
|
+
// genuine degradations (#2348 review F3 — the ledger precedent at the
|
|
894
|
+
// collect-later tier flip below records an actual capability loss).
|
|
895
|
+
const evidence = ctx.generatedEvidence;
|
|
896
|
+
const lineShapeMean = ctx.generatedLineShapeMean;
|
|
897
|
+
if (!generatedSkipRecorded.has(ctx.filePath)) {
|
|
898
|
+
generatedSkipRecorded.add(ctx.filePath);
|
|
899
|
+
logLatency({
|
|
900
|
+
type: "phase",
|
|
901
|
+
filePath: ctx.filePath,
|
|
902
|
+
phase: "dispatch_skipped_generated",
|
|
903
|
+
durationMs: 0,
|
|
904
|
+
metadata: {
|
|
905
|
+
evidence: evidence ?? "unknown",
|
|
906
|
+
...(lineShapeMean !== undefined && { lineShapeMean }),
|
|
907
|
+
},
|
|
908
|
+
});
|
|
909
|
+
}
|
|
805
910
|
return {
|
|
806
911
|
diagnostics: [],
|
|
807
912
|
blockers: [],
|
|
@@ -836,17 +941,46 @@ export async function dispatchForFile(ctx, groups, registry, onRunnerResult) {
|
|
|
836
941
|
// group order so output is deterministic.
|
|
837
942
|
const groupResults = await Promise.all(groups.map((group) => runGroup(ctx, group, registry, onRunnerResult)));
|
|
838
943
|
// Count baseline warnings before filtering (for delta count display)
|
|
839
|
-
|
|
944
|
+
//
|
|
945
|
+
// #2489: `ctx.filePath` is `createDispatchContext`'s `normalizedFilePath`
|
|
946
|
+
// (`resolveRunnerPath` -> `path.resolve` -> `normalizeMapKey`), which is
|
|
947
|
+
// ALWAYS absolute — the #2016 invariant this seam already relies on. A
|
|
948
|
+
// prior relative-path fallback key (`session.baseline.<path relative to
|
|
949
|
+
// ctx.cwd>`) existed here for self-consistency across the abs/rel read
|
|
950
|
+
// and write, but on `ctx.facts` singletons that outlive one project root
|
|
951
|
+
// (`dispatch/integration.ts`'s module-scope `sessionFacts`, reached by
|
|
952
|
+
// the warm `pilens_analyze` MCP route across any number of cwds) two
|
|
953
|
+
// different projects dispatching files that share a relative path (e.g.
|
|
954
|
+
// both have `src/index.ts`) collided on that fallback key, so project
|
|
955
|
+
// B's first-ever delta baseline read could silently return project A's
|
|
956
|
+
// stored diagnostics. Since the absolute key can never collide across
|
|
957
|
+
// projects (an absolute path is unique on the filesystem) and both
|
|
958
|
+
// writes always land together, the relative fallback is both unneeded
|
|
959
|
+
// and the sole source of the collision — removed rather than cwd-scoped
|
|
960
|
+
// (`cwd` is not a stable identity across this seam's callers, #2494).
|
|
961
|
+
//
|
|
962
|
+
// #2489 round 2: the #2016 invariant above is enforced by
|
|
963
|
+
// `createDispatchContext`, the sole real constructor, but nothing enforced
|
|
964
|
+
// it AT THIS SEAM — a hand-built `DispatchContext` (test scaffolding, a
|
|
965
|
+
// future caller) carrying a relative `filePath` would silently key the
|
|
966
|
+
// baseline under a value the constructor could never produce, and every
|
|
967
|
+
// subsequent same-session dispatch of the real (absolute-keyed) file would
|
|
968
|
+
// read back `undefined`, never the collision itself. Mirrors the sibling
|
|
969
|
+
// guard `recordEntitySnapshotDiff` already enforces for the entity-snapshot
|
|
970
|
+
// seam (`clients/review-graph/service.ts`, #2477): reject visibly and skip
|
|
971
|
+
// the baseline read/write for this dispatch rather than compute one under
|
|
972
|
+
// an unreachable key.
|
|
973
|
+
const baselinePathSafe = isAbsoluteFilePath(ctx.filePath);
|
|
974
|
+
if (ctx.deltaMode && !baselinePathSafe) {
|
|
975
|
+
recordDegradationOnce({
|
|
976
|
+
kind: "dispatch-non-absolute-baseline-path",
|
|
977
|
+
subject: ctx.filePath,
|
|
978
|
+
reason: "dispatchForFile requires an absolute ctx.filePath to key the delta baseline (refs #2489)",
|
|
979
|
+
});
|
|
980
|
+
}
|
|
840
981
|
const baselineAbsKey = `session.baseline.${ctx.filePath}`;
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
// became an absolute path anchored on the wrong root while POSIX left it
|
|
844
|
-
// relative. Both the read here and the write below use this const, so the
|
|
845
|
-
// key stays self-consistent within a session.
|
|
846
|
-
const baselineRelKey = `session.baseline.${normalizeEphemeralMapKey(relativeKey)}`;
|
|
847
|
-
const previousBaseline = ctx.deltaMode
|
|
848
|
-
? (ctx.facts.getSessionFact(baselineAbsKey) ??
|
|
849
|
-
ctx.facts.getSessionFact(baselineRelKey))
|
|
982
|
+
const previousBaseline = ctx.deltaMode && baselinePathSafe
|
|
983
|
+
? ctx.facts.getBoundedSessionFact(baselineAbsKey)
|
|
850
984
|
: undefined;
|
|
851
985
|
const baselineWarnings = previousBaseline?.filter((d) => d.semantic === "warning" || d.semantic === "none");
|
|
852
986
|
const baselineWarningCount = baselineWarnings?.length ?? 0;
|
|
@@ -913,9 +1047,12 @@ export async function dispatchForFile(ctx, groups, registry, onRunnerResult) {
|
|
|
913
1047
|
resolvedCount = filtered.fixed.length;
|
|
914
1048
|
}
|
|
915
1049
|
// Persist full current snapshot for next run (not delta-filtered subset).
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
1050
|
+
// Gated on `baselinePathSafe` too: a non-absolute `ctx.filePath` already
|
|
1051
|
+
// skipped the read above (the degradation record fired there), and writing
|
|
1052
|
+
// under that same unreachable key here would just seed a baseline no real
|
|
1053
|
+
// reader (always absolute) could ever read back.
|
|
1054
|
+
if (ctx.deltaMode && baselinePathSafe) {
|
|
1055
|
+
ctx.facts.setBoundedSessionFact(baselineAbsKey, [...dedupedDiagnostics]);
|
|
919
1056
|
}
|
|
920
1057
|
// Categorize results
|
|
921
1058
|
const blockers = visibleDiagnostics.filter((d) => d.semantic === "blocking");
|
|
@@ -1003,6 +1140,13 @@ export async function dispatchForFile(ctx, groups, registry, onRunnerResult) {
|
|
|
1003
1140
|
})),
|
|
1004
1141
|
totalDiagnostics: visibleDiagnostics.length,
|
|
1005
1142
|
blockers: blockers.length,
|
|
1143
|
+
// #2489 round 2: distinguishes a delta baseline HIT (a prior snapshot
|
|
1144
|
+
// was read for this file this session) from a MISS (first dispatch, or
|
|
1145
|
+
// the non-absolute-path guard above skipped the read) — `dispatch_start`
|
|
1146
|
+
// (above, `metadata: { groupCount, kind, runners }`) fires before the
|
|
1147
|
+
// baseline read ever runs and cannot carry either value.
|
|
1148
|
+
baselineHit: previousBaseline !== undefined,
|
|
1149
|
+
baselineWarningCount,
|
|
1006
1150
|
},
|
|
1007
1151
|
});
|
|
1008
1152
|
return {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BoundedLruCache } from "../bounded-cache.js";
|
|
1
2
|
import { normalizeMapKey } from "../path-utils.js";
|
|
2
3
|
let capacityEvictionReporter;
|
|
3
4
|
export function setFactStoreEvictionReporter(reporter) {
|
|
@@ -17,11 +18,60 @@ const MAX_FILE_FACT_RECORDS = 1024;
|
|
|
17
18
|
// preventing a small number of generated or vendored files from retaining
|
|
18
19
|
// hundreds of MiB for the process lifetime (#2247).
|
|
19
20
|
const MAX_FILE_FACT_CONTENT_BYTES = 64 * 1024 * 1024;
|
|
21
|
+
// Pinned records are exempt from capacity eviction. A dispatch reads its
|
|
22
|
+
// file's facts back after the runner groups settle (`file.content` misses read as
|
|
23
|
+
// empty content, not as "re-derive"), and the fire-and-forget blast-radius
|
|
24
|
+
// build runs a whole-project walk against the SAME store meanwhile — plain LRU
|
|
25
|
+
// recency would let that walk evict the file being dispatched. Every other holder
|
|
26
|
+
// re-derives an evicted fact from disk.
|
|
27
|
+
//
|
|
28
|
+
// #2243 item 2: a dispatch pins its file at start (`clearFileFactsFor`) and
|
|
29
|
+
// releases it at completion (`endDispatchFor`, in the dispatch's finally). The
|
|
30
|
+
// pin set therefore tracks dispatches actually IN FLIGHT, not the last N files
|
|
31
|
+
// touched.
|
|
32
|
+
// #2282: `sessionFacts` below is a flat `Map<factId, value>` shared by two
|
|
33
|
+
// unrelated fact shapes — a small FIXED vocabulary (tool availability keyed by
|
|
34
|
+
// command, the singleton "session.reviewGraph") and PER-FILE families that
|
|
35
|
+
// mint one key per file touched (session baseline, cascade baseline, review-
|
|
36
|
+
// graph entity snapshots) and never shrink. Only the second shape grows with
|
|
37
|
+
// batch size, so only it gets a bound; the fixed vocabulary stays on the plain
|
|
38
|
+
// map, unbounded, because its cardinality is small and stable. Per-file
|
|
39
|
+
// session facts opt into `boundedSessionFacts` instead, reusing fileFacts'
|
|
40
|
+
// LRU-count-cap-and-report discipline rather than adding a third mechanism.
|
|
41
|
+
// No pin exemption here (unlike fileFacts): every current per-file session
|
|
42
|
+
// fact is read once near dispatch start and written once at dispatch end, so
|
|
43
|
+
// nothing re-reads its OWN key later in the same dispatch the way a runner
|
|
44
|
+
// re-reads `file.content` — an eviction mid-batch only means the next dispatch
|
|
45
|
+
// of that path sees no previous baseline, the same safe state as its first
|
|
46
|
+
// ever dispatch this session.
|
|
47
|
+
//
|
|
48
|
+
// That equivalence holds only where an absent baseline means "report
|
|
49
|
+
// everything", which is true for the diagnostic baselines and false for the
|
|
50
|
+
// review-graph entity snapshot: there, an absent snapshot read as an empty one
|
|
51
|
+
// puts every symbol in `added` and drives a blast-radius run for a file that
|
|
52
|
+
// did not change. So an eviction leaves a tombstone, and a reader that needs
|
|
53
|
+
// the distinction asks `wasBoundedSessionFactEvicted`. Tombstones are keys
|
|
54
|
+
// only, and carry the same cap, so the coldest of them fall off in turn and
|
|
55
|
+
// that key reverts to first-dispatch semantics.
|
|
56
|
+
//
|
|
57
|
+
// Four per-file keys are minted per dispatched file today (diagnostic
|
|
58
|
+
// baseline, cascade baseline, entity snapshot, changed symbols), so 4096
|
|
59
|
+
// records covers roughly 1024 distinct files before the coldest file's
|
|
60
|
+
// baseline starts degrading.
|
|
61
|
+
const MAX_SESSION_FACT_RECORDS = 4096;
|
|
20
62
|
export class FactStore {
|
|
21
63
|
subject;
|
|
22
64
|
// Insertion order is eviction order: the oldest-used record is evicted first.
|
|
23
65
|
fileFacts = new Map();
|
|
24
66
|
sessionFacts = new Map();
|
|
67
|
+
// Bounded sibling of `sessionFacts` for per-file-keyed session facts (#2282).
|
|
68
|
+
// True LRU: every fetch (get or create, see getBoundedSessionFact) refreshes
|
|
69
|
+
// recency, so the oldest-UNUSED record is evicted first, not merely the
|
|
70
|
+
// oldest-inserted.
|
|
71
|
+
boundedSessionFacts = new BoundedLruCache(MAX_SESSION_FACT_RECORDS);
|
|
72
|
+
// Keys evicted from `boundedSessionFacts` and not written since, so a reader
|
|
73
|
+
// can tell "evicted" from "never seen" (#2282). Same cap, same FIFO order.
|
|
74
|
+
evictedSessionFactKeys = new Set();
|
|
25
75
|
// Pin refcount per file. A file is exempt from capacity eviction while its
|
|
26
76
|
// count is > 0. Refcounted, not a Set, so two overlapping dispatches of the
|
|
27
77
|
// same file both hold the pin until BOTH settle (#2243 item 2).
|
|
@@ -269,10 +319,57 @@ export class FactStore {
|
|
|
269
319
|
hasSessionFact(factId) {
|
|
270
320
|
return this.sessionFacts.has(factId);
|
|
271
321
|
}
|
|
322
|
+
/** Bounded counterpart to {@link getSessionFact} for per-file-keyed session
|
|
323
|
+
* facts (#2282) — session baselines, cascade baselines, review-graph entity
|
|
324
|
+
* snapshots. LRU touch: a read moves the record off the eviction end. */
|
|
325
|
+
getBoundedSessionFact(factId) {
|
|
326
|
+
if (!this.boundedSessionFacts.has(factId))
|
|
327
|
+
return undefined;
|
|
328
|
+
return this.boundedSessionFacts.get(factId);
|
|
329
|
+
}
|
|
330
|
+
/** Bounded counterpart to {@link setSessionFact}. Callers use this instead
|
|
331
|
+
* of `setSessionFact` for any family that mints one key per file/entity
|
|
332
|
+
* touched, so the store's count stays bounded across a large batch instead
|
|
333
|
+
* of retaining one entry per distinct path for the process lifetime. */
|
|
334
|
+
setBoundedSessionFact(factId, value) {
|
|
335
|
+
this.evictedSessionFactKeys.delete(factId);
|
|
336
|
+
const evicted = this.boundedSessionFacts.set(factId, value);
|
|
337
|
+
for (const [oldestKey] of evicted) {
|
|
338
|
+
this.evictedSessionFactKeys.add(oldestKey);
|
|
339
|
+
while (this.evictedSessionFactKeys.size > MAX_SESSION_FACT_RECORDS) {
|
|
340
|
+
const oldestTombstone = this.evictedSessionFactKeys
|
|
341
|
+
.values()
|
|
342
|
+
.next().value;
|
|
343
|
+
if (oldestTombstone === undefined)
|
|
344
|
+
break;
|
|
345
|
+
this.evictedSessionFactKeys.delete(oldestTombstone);
|
|
346
|
+
}
|
|
347
|
+
capacityEvictionReporter?.(this.subject, "session-count", `session-fact store axis=count exceeded ${MAX_SESSION_FACT_RECORDS} records; evicted least-recently-used fact for ${oldestKey}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
hasBoundedSessionFact(factId) {
|
|
351
|
+
return this.boundedSessionFacts.has(factId);
|
|
352
|
+
}
|
|
353
|
+
/** True when this key held a bounded session fact that capacity eviction
|
|
354
|
+
* dropped and nothing has rewritten since (#2282). Readers whose "absent"
|
|
355
|
+
* branch is not a safe default — the review-graph entity snapshot, where
|
|
356
|
+
* absent would read as "every symbol is new" — use this to tell an evicted
|
|
357
|
+
* key from one this session has never seen. */
|
|
358
|
+
wasBoundedSessionFactEvicted(factId) {
|
|
359
|
+
return this.evictedSessionFactKeys.has(factId);
|
|
360
|
+
}
|
|
361
|
+
/** O(1) entry count for memory-attribution sampling (#2282 Observability):
|
|
362
|
+
* the fixed-vocabulary map plus the bounded per-file map, so a caller gets
|
|
363
|
+
* this store's whole `sessionFacts` footprint in one number. */
|
|
364
|
+
getSessionFactEntryCount() {
|
|
365
|
+
return this.sessionFacts.size + this.boundedSessionFacts.size;
|
|
366
|
+
}
|
|
272
367
|
/** Call on session reset only. Clears everything including tool cache and baselines. */
|
|
273
368
|
clearAll() {
|
|
274
369
|
this.fileFacts.clear();
|
|
275
370
|
this.sessionFacts.clear();
|
|
371
|
+
this.boundedSessionFacts.clear();
|
|
372
|
+
this.evictedSessionFactKeys.clear();
|
|
276
373
|
this.pinnedFiles.clear();
|
|
277
374
|
this.retainedContentBytes = 0;
|
|
278
375
|
this.pinnedContentBytesTotal = 0;
|
|
@@ -139,7 +139,6 @@ setFactStoreEvictionReporter((subject, axis, reason) => {
|
|
|
139
139
|
reason,
|
|
140
140
|
});
|
|
141
141
|
});
|
|
142
|
-
const cascadeDiagnosticBaselines = new Map();
|
|
143
142
|
const sessionRunnerRegistry = new RunnerRegistry();
|
|
144
143
|
registerDefaultRunners(sessionRunnerRegistry);
|
|
145
144
|
const LSP_CAPABLE_KINDS = new Set(getLspCapableKinds());
|
|
@@ -385,7 +384,6 @@ export function resetDispatchBaselines(cwd) {
|
|
|
385
384
|
clearModuleGraphCache();
|
|
386
385
|
recentlyCleanNeighborCache.clear();
|
|
387
386
|
primaryFilesThisTurn.clear();
|
|
388
|
-
cascadeDiagnosticBaselines.clear();
|
|
389
387
|
cascadeSessionStats = {
|
|
390
388
|
runs: 0,
|
|
391
389
|
diagnosticsSurfaced: 0,
|
|
@@ -405,10 +403,16 @@ export function getCascadeSessionStats() {
|
|
|
405
403
|
}
|
|
406
404
|
const recentlyCleanNeighborCache = new Map();
|
|
407
405
|
/** O(1) entry count of this module's turn-bounded cache (#1123 item 2
|
|
408
|
-
* memory attribution) — a `Map.size` read, never iterated.
|
|
406
|
+
* memory attribution) — a `Map.size` read, never iterated.
|
|
407
|
+
* `sessionFactEntries` (#2282) is the dispatch `FactStore`'s own
|
|
408
|
+
* `sessionFacts` footprint: the fixed-vocabulary map (small, unbounded by
|
|
409
|
+
* design) plus the bounded per-file map (capped at `MAX_SESSION_FACT_RECORDS`
|
|
410
|
+
* — see `fact-store.ts`), so this number is now itself bounded rather than
|
|
411
|
+
* invisible growth. */
|
|
409
412
|
export function getDispatchCascadeCacheStats() {
|
|
410
413
|
return {
|
|
411
414
|
recentlyCleanNeighborCacheSize: recentlyCleanNeighborCache.size,
|
|
415
|
+
sessionFactEntries: sessionFacts.getSessionFactEntryCount(),
|
|
412
416
|
};
|
|
413
417
|
}
|
|
414
418
|
const RECENTLY_CLEAN_TTL_TURNS = 5;
|
|
@@ -1940,10 +1944,8 @@ function diagnosticDeltaKey(diagnostic) {
|
|
|
1940
1944
|
function applyCascadeDeltaBaselines(neighbors) {
|
|
1941
1945
|
return neighbors.map((neighbor) => {
|
|
1942
1946
|
const baselineKey = `session.baseline.cascade.${normalizeMapKey(neighbor.filePath)}`;
|
|
1943
|
-
const previous =
|
|
1944
|
-
|
|
1945
|
-
cascadeDiagnosticBaselines.set(baselineKey, [...neighbor.diagnostics]);
|
|
1946
|
-
sessionFacts.setSessionFact(baselineKey, [...neighbor.diagnostics]);
|
|
1947
|
+
const previous = sessionFacts.getBoundedSessionFact(baselineKey);
|
|
1948
|
+
sessionFacts.setBoundedSessionFact(baselineKey, [...neighbor.diagnostics]);
|
|
1947
1949
|
if (!previous)
|
|
1948
1950
|
return neighbor;
|
|
1949
1951
|
const before = new Set(previous.map(diagnosticDeltaKey));
|