@herbertgao/pi-extensions 2026.8.6 → 2026.8.8
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/README.md +8 -6
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +320 -22
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +79 -17
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +27 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +66 -17
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
- package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
- package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
- package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
- package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
- package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +17 -13
- package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
- package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
- package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
- package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
- package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
- package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
- package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
- package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +21 -6
- package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
- package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +20 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +5 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +174 -100
- package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +5 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +10 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +10 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/tool-schema.md +11 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +5 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +140 -97
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +63 -28
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/selectors/projections.ts +22 -15
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/state-reducer.ts +11 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/tool/response-envelope.ts +11 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/tool/types.ts +11 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/multi-select-view.ts +31 -17
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/preview/preview-block-renderer.ts +15 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/wrapping-select.ts +15 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +86 -38
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +76 -9
- package/node_modules/@narumitw/pi-btw/README.md +120 -111
- package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
- package/node_modules/@narumitw/pi-btw/package.json +10 -6
- package/node_modules/@narumitw/pi-btw/src/btw.ts +36 -40
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
- package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
- package/node_modules/@narumitw/pi-btw/src/menu.ts +45 -2
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +44 -1856
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
- package/node_modules/@pi-plugins/fast-mode/package.json +4 -6
- package/node_modules/pi-lens/CHANGELOG.md +999 -0
- package/node_modules/pi-lens/README.md +117 -3
- package/node_modules/pi-lens/config/ruff/core.toml +42 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +126 -13
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +67 -11
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
- package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +381 -13
- package/node_modules/pi-lens/dist/clients/biome-client.js +61 -30
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
- package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +129 -0
- package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +206 -0
- package/node_modules/pi-lens/dist/clients/cache-observability.js +353 -76
- package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
- package/node_modules/pi-lens/dist/clients/cascade-format.js +20 -1
- package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
- package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
- package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +123 -31
- package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
- package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +84 -14
- package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +155 -0
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +25 -3
- package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +8 -2
- package/node_modules/pi-lens/dist/clients/deps/typebox.js +8 -3
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +147 -33
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +52 -2
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +256 -88
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +9 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +9 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +8 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +60 -53
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +25 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +20 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +44 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +17 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +32 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +47 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +14 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +127 -28
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +12 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +275 -13
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +21 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +734 -64
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +188 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +9 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +21 -10
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
- package/node_modules/pi-lens/dist/clients/extension-log.js +22 -1
- package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +316 -0
- package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
- package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
- package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
- package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
- package/node_modules/pi-lens/dist/clients/git-guard.js +31 -0
- package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +109 -9
- package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
- package/node_modules/pi-lens/dist/clients/host-ports.js +8 -0
- package/node_modules/pi-lens/dist/clients/installer/index.js +1114 -100
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
- package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +87 -8
- package/node_modules/pi-lens/dist/clients/knip-client.js +310 -10
- package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +9 -2
- package/node_modules/pi-lens/dist/clients/latency-logger.js +440 -11
- package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
- package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +128 -8
- package/node_modules/pi-lens/dist/clients/lsp/client.js +1660 -164
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +83 -1
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
- package/node_modules/pi-lens/dist/clients/lsp/index.js +2423 -151
- package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
- package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
- package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
- package/node_modules/pi-lens/dist/clients/lsp/server.js +277 -16
- package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
- package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +79 -3
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +43 -0
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
- package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
- package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
- package/node_modules/pi-lens/dist/clients/mcp/session.js +10 -2
- package/node_modules/pi-lens/dist/clients/module-report.js +22 -1
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +114 -23
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
- package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
- package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
- package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
- package/node_modules/pi-lens/dist/clients/pipeline.js +154 -33
- package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +84 -14
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +23 -9
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
- package/node_modules/pi-lens/dist/clients/project-report.js +62 -27
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
- package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
- package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +8 -3
- package/node_modules/pi-lens/dist/clients/read-guard.js +253 -10
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
- package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +194 -31
- package/node_modules/pi-lens/dist/clients/review-graph/query.js +52 -4
- package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +21 -0
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +6 -3
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +96 -7
- package/node_modules/pi-lens/dist/clients/runtime-context.js +8 -1
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +352 -45
- package/node_modules/pi-lens/dist/clients/runtime-session.js +373 -9
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +212 -79
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +620 -67
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
- package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
- package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +157 -24
- package/node_modules/pi-lens/dist/clients/session-event-guard.js +184 -0
- package/node_modules/pi-lens/dist/clients/sg-runner.js +116 -22
- package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
- package/node_modules/pi-lens/dist/clients/source-filter.js +6 -2
- package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
- package/node_modules/pi-lens/dist/clients/startup-scan.js +5 -2
- package/node_modules/pi-lens/dist/clients/startup-timing.js +19 -1
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +151 -16
- package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
- package/node_modules/pi-lens/dist/clients/tool-policy.js +125 -20
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +1 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
- package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
- package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
- package/node_modules/pi-lens/dist/index.js +61407 -48577
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +476 -57
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +132 -43
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
- package/node_modules/pi-lens/docs/agent-guide.md +6 -1
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +515 -506
- package/node_modules/pi-lens/docs/dispositions.md +21 -0
- package/node_modules/pi-lens/docs/features.md +19 -5
- package/node_modules/pi-lens/docs/globalconfig.md +3 -0
- package/node_modules/pi-lens/docs/language-coverage.md +71 -5
- package/node_modules/pi-lens/docs/mcp.md +2 -2
- package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
- package/node_modules/pi-lens/docs/settings.md +19 -1
- package/node_modules/pi-lens/docs/usage.md +1 -0
- package/node_modules/pi-lens/package.json +27 -10
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +15 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +2 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +2 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +57 -12
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +22 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +23 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +33 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +34 -8
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +6 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +53 -1
- package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
- package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
- package/node_modules/pi-lens/scripts/install-selftest.mjs +99 -11
- package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +75 -0
- package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +285 -0
- package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +233 -0
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +2 -0
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +80 -3
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +41 -0
- package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +36 -0
- package/node_modules/pi-mcp-adapter/README.md +47 -5
- package/node_modules/pi-mcp-adapter/cli.js +163 -1
- package/node_modules/pi-mcp-adapter/commands.ts +128 -22
- package/node_modules/pi-mcp-adapter/config.ts +25 -7
- package/node_modules/pi-mcp-adapter/direct-tools.ts +5 -4
- package/node_modules/pi-mcp-adapter/index.ts +219 -32
- package/node_modules/pi-mcp-adapter/init.ts +36 -4
- package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
- package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
- package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
- package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
- package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +32 -21
- package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
- package/node_modules/pi-mcp-adapter/package.json +3 -1
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +31 -37
- package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
- package/node_modules/pi-mcp-adapter/server-manager.ts +140 -2
- package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
- package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
- package/node_modules/pi-mcp-adapter/types.ts +2 -0
- package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
- package/node_modules/pi-mcp-adapter/utils.ts +70 -0
- package/node_modules/pi-web-access/CHANGELOG.md +37 -0
- package/node_modules/pi-web-access/README.md +32 -13
- package/node_modules/pi-web-access/anysearch.ts +4 -2
- package/node_modules/pi-web-access/brave.ts +16 -4
- package/node_modules/pi-web-access/chrome-cookies.ts +86 -15
- package/node_modules/pi-web-access/curator-page.ts +6 -3
- package/node_modules/pi-web-access/curator-server.ts +4 -1
- package/node_modules/pi-web-access/data-uri-sanitize.ts +406 -0
- package/node_modules/pi-web-access/exa.ts +18 -7
- package/node_modules/pi-web-access/extract.ts +40 -5
- package/node_modules/pi-web-access/firecrawl.ts +18 -2
- package/node_modules/pi-web-access/gemini-search.ts +16 -4
- package/node_modules/pi-web-access/github-extract.ts +65 -36
- package/node_modules/pi-web-access/index.ts +60 -61
- package/node_modules/pi-web-access/openai-search.ts +17 -6
- package/node_modules/pi-web-access/package.json +1 -1
- package/node_modules/pi-web-access/page-query.ts +4 -2
- package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
- package/node_modules/pi-web-access/parallel.ts +2 -2
- package/node_modules/pi-web-access/pdf-extract.ts +18 -5
- package/node_modules/pi-web-access/query-rewrite.ts +51 -0
- package/node_modules/pi-web-access/serper.ts +211 -0
- package/node_modules/pi-web-access/ssrf-protection.ts +12 -2
- package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
- package/node_modules/pi-web-access/summary-review.ts +44 -22
- package/node_modules/pi-web-access/tavily.ts +16 -4
- package/node_modules/pi-web-access/utils.ts +87 -0
- package/node_modules/pi-web-access/valyu.ts +199 -0
- package/package.json +14 -14
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Provides utilities for integrating the declarative dispatch system
|
|
5
5
|
* with the existing index.ts tool_result handler.
|
|
6
6
|
*/
|
|
7
|
+
import { CASCADE_DIAGNOSTICS_TTL_MS } from "../cascade-types.js";
|
|
7
8
|
import { getDiagnosticLogger } from "../diagnostic-logger.js";
|
|
8
9
|
import { detectFileKind } from "../file-kinds.js";
|
|
9
10
|
import { getLspCapableKinds, getPrimaryDispatchGroup, } from "../language-policy.js";
|
|
@@ -16,6 +17,7 @@ export { clearLatencyReports, formatLatencyReport, getLatencyReports };
|
|
|
16
17
|
import * as nodeFs from "node:fs";
|
|
17
18
|
import * as nodePath from "node:path";
|
|
18
19
|
import { fileURLToPath } from "node:url";
|
|
20
|
+
import { CASCADE_NEIGHBOUR_BUDGET, deriveCascadeNeighbourBudget, } from "../cascade-budget.js";
|
|
19
21
|
import { formatCascadeNeighborDiagnostics } from "../cascade-format.js";
|
|
20
22
|
import { logCascade } from "../cascade-logger.js";
|
|
21
23
|
import { getDiagnosticTracker } from "../diagnostic-tracker.js";
|
|
@@ -25,7 +27,8 @@ import { getServersForFileWithConfig } from "../lsp/config.js";
|
|
|
25
27
|
import { getLSPService } from "../lsp/index.js";
|
|
26
28
|
import { isExternalOrVendorFile, normalizeMapKey } from "../path-utils.js";
|
|
27
29
|
import { getProjectIgnoreMatcher } from "../file-utils.js";
|
|
28
|
-
import { resetAstGrepUnsupportedLanguageLog } from "./runners/ast-grep-napi.js";
|
|
30
|
+
import { resetAstGrepNapiLoadState, resetAstGrepUnsupportedLanguageLog, } from "./runners/ast-grep-napi.js";
|
|
31
|
+
import { resetTreeSitterClientLoadState } from "../tree-sitter-shared.js";
|
|
29
32
|
import { isTestRoleCollateral } from "../collateral-test-role.js";
|
|
30
33
|
import { clearReviewGraphWorkspaceCache, getGraphBuildInfoForGraph, getGraphImportChanges, graphBuildInfoIsTrustworthy, } from "../review-graph/builder.js";
|
|
31
34
|
import { buildReverseDependencyIndexFromGraph, getAffectedFilesFromIndex, patchReverseDependencyIndex, writeReverseDependencyIndexToSnapshot, } from "../reverse-deps.js";
|
|
@@ -298,13 +301,18 @@ export function resetDispatchBaselines(cwd) {
|
|
|
298
301
|
if (cwd)
|
|
299
302
|
applyProjectLensConfig(cwd);
|
|
300
303
|
resetAstGrepUnsupportedLanguageLog();
|
|
304
|
+
// #1567: a genuine-failure verdict or a live transient cooldown on the
|
|
305
|
+
// napi load latch is session-scoped state, not process-lifetime state.
|
|
306
|
+
resetAstGrepNapiLoadState();
|
|
307
|
+
// #1592: same shape for the web-tree-sitter load latch — an evaluation-
|
|
308
|
+
// shaped rejection is session-scoped state, not process-lifetime state.
|
|
309
|
+
resetTreeSitterClientLoadState();
|
|
301
310
|
sessionFacts.clearAll();
|
|
302
311
|
resetSessionSlopScore();
|
|
303
312
|
clearCoverageNoticeState();
|
|
304
313
|
clearReviewGraphWorkspaceCache();
|
|
305
314
|
clearReverseDepsIndexCache();
|
|
306
315
|
clearModuleGraphCache();
|
|
307
|
-
neighborTouchCache.clear();
|
|
308
316
|
recentlyCleanNeighborCache.clear();
|
|
309
317
|
primaryFilesThisTurn.clear();
|
|
310
318
|
cascadeDiagnosticBaselines.clear();
|
|
@@ -325,13 +333,11 @@ let cascadeSessionStats = {
|
|
|
325
333
|
export function getCascadeSessionStats() {
|
|
326
334
|
return { ...cascadeSessionStats };
|
|
327
335
|
}
|
|
328
|
-
const neighborTouchCache = new Map();
|
|
329
336
|
const recentlyCleanNeighborCache = new Map();
|
|
330
|
-
/** O(1) entry
|
|
331
|
-
* memory attribution) —
|
|
337
|
+
/** O(1) entry count of this module's turn-bounded cache (#1123 item 2
|
|
338
|
+
* memory attribution) — a `Map.size` read, never iterated. */
|
|
332
339
|
export function getDispatchCascadeCacheStats() {
|
|
333
340
|
return {
|
|
334
|
-
neighborTouchCacheSize: neighborTouchCache.size,
|
|
335
341
|
recentlyCleanNeighborCacheSize: recentlyCleanNeighborCache.size,
|
|
336
342
|
};
|
|
337
343
|
}
|
|
@@ -346,14 +352,12 @@ function ensureCascadeTurnScope(turnSeq) {
|
|
|
346
352
|
return;
|
|
347
353
|
cascadeTurnScope = turnSeq;
|
|
348
354
|
primaryFilesThisTurn.clear();
|
|
349
|
-
neighborTouchCache.clear();
|
|
350
355
|
for (const [key, entry] of recentlyCleanNeighborCache) {
|
|
351
356
|
if (turnSeq - entry.turnSeq > RECENTLY_CLEAN_TTL_TURNS) {
|
|
352
357
|
recentlyCleanNeighborCache.delete(key);
|
|
353
358
|
}
|
|
354
359
|
}
|
|
355
360
|
}
|
|
356
|
-
const CASCADE_TTL_MS = 240_000;
|
|
357
361
|
const MAX_PER_FILE = RUNTIME_CONFIG.pipeline.cascadeMaxDiagnosticsPerFile;
|
|
358
362
|
const MAX_FILES = RUNTIME_CONFIG.pipeline.cascadeMaxFiles;
|
|
359
363
|
/**
|
|
@@ -411,11 +415,17 @@ function readBoundToCurrentDisk(rawDiags) {
|
|
|
411
415
|
* a future flag cannot be silently missed at just one of the several gate sites:
|
|
412
416
|
* - `inconclusive` (#1093/#571): the notify/diagnostics wait lapsed its deadline,
|
|
413
417
|
* so a resolved `[]` is NOT a confirmed clean (the #533 false-clean trap).
|
|
414
|
-
*
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
* is
|
|
418
|
-
*
|
|
418
|
+
* #1549: this now means a PRIMARY's deadline lapsed. A slow auxiliary lands in
|
|
419
|
+
* the coverage-gap bullet below instead of collapsing the whole touch, so this
|
|
420
|
+
* predicate still fails closed for it while the primary's findings survive —
|
|
421
|
+
* which is the point: the two disqualifiers were doing one job for two causes.
|
|
422
|
+
* - a COVERAGE GAP (#1470/#1493): an auxiliary never reported — its push wait was
|
|
423
|
+
* cut off by the aux grace timer (#1470), or it stayed silent with no stored
|
|
424
|
+
* publication for this content (#1493) — so the merged result is missing whatever
|
|
425
|
+
* that scanner would have said. Such a touch is deliberately NOT `inconclusive`
|
|
426
|
+
* (the primary answered), which is exactly why it needs naming here: reading
|
|
427
|
+
* `!inconclusive` alone would let a hung or silent opengrep wipe a live footer
|
|
428
|
+
* finding and seed the recently-clean cache.
|
|
419
429
|
* - `binding.boundToCurrentDisk === false` (#1095): the diagnostics were computed
|
|
420
430
|
* against a DIFFERENT disk state than what is on disk now (the server's view
|
|
421
431
|
* diverged / a pre-fix buffer) — not an observation of current disk. `true` and
|
|
@@ -429,6 +439,25 @@ function readBoundToCurrentDisk(rawDiags) {
|
|
|
429
439
|
function readInconclusive(rawDiags) {
|
|
430
440
|
return rawDiags?.inconclusive === true;
|
|
431
441
|
}
|
|
442
|
+
/**
|
|
443
|
+
* #1549: an inconclusive `neighbor_touch` row used to carry NO attribution, so a
|
|
444
|
+
* forensic sweep could only infer the cause from duration histograms. `touchFile`
|
|
445
|
+
* now names the PRIMARY server(s) whose deadline produced the verdict and which
|
|
446
|
+
* deadline it was (`notify-write` vs `diagnostics-wait`, or `mixed`) — carry both
|
|
447
|
+
* into cascade.log so the record stands on its own. Read off the wrapper, like
|
|
448
|
+
* every other flag here (#1179).
|
|
449
|
+
*/
|
|
450
|
+
function readInconclusiveAttribution(rawDiags) {
|
|
451
|
+
const wrapper = rawDiags;
|
|
452
|
+
return {
|
|
453
|
+
...(wrapper?.inconclusiveServerIds?.length && {
|
|
454
|
+
inconclusiveServerIds: [...wrapper.inconclusiveServerIds],
|
|
455
|
+
}),
|
|
456
|
+
...(wrapper?.inconclusiveReason && {
|
|
457
|
+
inconclusiveReason: wrapper.inconclusiveReason,
|
|
458
|
+
}),
|
|
459
|
+
};
|
|
460
|
+
}
|
|
432
461
|
function isConfirmedTouch(rawDiags) {
|
|
433
462
|
return (!readInconclusive(rawDiags) &&
|
|
434
463
|
touchCoverageGap(rawDiags).length === 0 &&
|
|
@@ -439,7 +468,9 @@ const REVERSE_DEPS_MAX_WARM_ROOTS = 8;
|
|
|
439
468
|
const REVERSE_DEPS_IDLE_EVICT_MS_DEFAULT = 20 * 60_000;
|
|
440
469
|
function reverseDepsIdleEvictMs() {
|
|
441
470
|
const value = Number.parseInt(process.env.PI_LENS_REVERSE_DEPS_IDLE_EVICT_MS ?? "", 10);
|
|
442
|
-
return Number.isSafeInteger(value) && value > 0
|
|
471
|
+
return Number.isSafeInteger(value) && value > 0
|
|
472
|
+
? value
|
|
473
|
+
: REVERSE_DEPS_IDLE_EVICT_MS_DEFAULT;
|
|
443
474
|
}
|
|
444
475
|
function deleteReverseDepsEntry(key) {
|
|
445
476
|
const entry = reverseDepsIndexCache.get(key);
|
|
@@ -500,8 +531,9 @@ export function _seedReverseDepsIndexCacheForTests(key, index, generation) {
|
|
|
500
531
|
// one-hop set is always the floor (it sorts first, before depth-2). Both
|
|
501
532
|
// env-tunable; set depth to 1 to restore the old one-hop-only behaviour.
|
|
502
533
|
const CASCADE_TRANSITIVE_DEPTH = Math.max(1, Number.parseInt(process.env.PI_LENS_CASCADE_TRANSITIVE_DEPTH ?? "2", 10) || 2);
|
|
503
|
-
|
|
504
|
-
|
|
534
|
+
// #1462: the cap itself, and the derivation that narrows it when this run has
|
|
535
|
+
// already spent the settle window, live in clients/cascade-budget.ts alongside
|
|
536
|
+
// `cascadeSettleWaitMs` — the two knobs only make sense read together.
|
|
505
537
|
// Exported (not just module-local) so the MCP warm-analyze seam
|
|
506
538
|
// (clients/mcp/analyze.ts, #536) can gate its own buildOrUpdateGraph call on
|
|
507
539
|
// the SAME file-kind eligibility this cascade path uses — one source of truth
|
|
@@ -605,8 +637,12 @@ function updateWordIndexForCascade(args) {
|
|
|
605
637
|
export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
606
638
|
const reverseDepsTimersToRelease = new Set();
|
|
607
639
|
const reverseDepsEntriesAtStart = new Set(reverseDepsIndexCache.values());
|
|
640
|
+
// #1462: legacy callers use this run's age when sizing the neighbour walk.
|
|
641
|
+
// The pipeline supplies a live turn_end clock so deferred work does not charge
|
|
642
|
+
// time that elapsed before turn_end began waiting.
|
|
643
|
+
const cascadeStart = Date.now();
|
|
608
644
|
try {
|
|
609
|
-
const { hasBlockers = false, dbg, turnSeq = 0, writeSeq, seqState, fileContent, wordIndex, onWordIndexUpdated, } = options;
|
|
645
|
+
const { hasBlockers = false, dbg, turnSeq = 0, writeSeq, seqState, turnEndCascadeSettleStart, fileContent, wordIndex, onWordIndexUpdated, } = options;
|
|
610
646
|
ensureCascadeTurnScope(turnSeq);
|
|
611
647
|
if (hasBlockers) {
|
|
612
648
|
logCascade({
|
|
@@ -672,12 +708,24 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
672
708
|
let importerSet = new Set();
|
|
673
709
|
let callerSet = new Set();
|
|
674
710
|
let referenceCount = 0;
|
|
711
|
+
let cascadeCandidateCount = 0;
|
|
712
|
+
let cascadeEligibleCount = 0;
|
|
713
|
+
let cascadeFilterDroppedCount = 0;
|
|
675
714
|
// #1446 item 4: how many eligible neighbors the flat CASCADE_NEIGHBOUR_BUDGET
|
|
676
715
|
// cut off, distinct from candidates dropped by the filters above it
|
|
677
716
|
// (missing on disk, vendor, ignored, already-primary-this-turn) — those are
|
|
678
717
|
// never actionable regardless of budget, so counting them as "truncated"
|
|
679
718
|
// would overstate what a larger budget could actually recover.
|
|
680
719
|
let cascadeBudgetTruncated = 0;
|
|
720
|
+
let transitiveTruncated = false;
|
|
721
|
+
// #1462: the budget actually in force for THIS run and the settle time left
|
|
722
|
+
// when it was sized. Both are overwritten inside the graph branch below,
|
|
723
|
+
// which is the only path that reaches `cascade_result` — the other branch
|
|
724
|
+
// returns a `non_code` skip before any of this is logged.
|
|
725
|
+
let cascadeNeighbourBudget = CASCADE_NEIGHBOUR_BUDGET;
|
|
726
|
+
let cascadeBudgetRemainingMs = 0;
|
|
727
|
+
let cascadeBudgetZone = "fits";
|
|
728
|
+
let cascadeDeliveryWindowMs = 0;
|
|
681
729
|
if (CASCADE_GRAPH_KINDS.has(fileKind)) {
|
|
682
730
|
const graphStart = Date.now();
|
|
683
731
|
const graph = await buildOrUpdateGraph(cwd, [normalizedFile], sessionFacts, seqState);
|
|
@@ -959,6 +1007,29 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
959
1007
|
});
|
|
960
1008
|
}
|
|
961
1009
|
}
|
|
1010
|
+
// #1462: size the rest of this run against the on-time window it has NOT
|
|
1011
|
+
// already spent. Derived HERE — after the graph build, the reverse-deps
|
|
1012
|
+
// refresh and the LSP-reference expansion, which is where the measured
|
|
1013
|
+
// overruns were bought — and used for both the transitive BFS cap and the
|
|
1014
|
+
// final slice, so a rescued run expands less as well as walking less.
|
|
1015
|
+
// Narrowing happens ONLY inside the rescue band (see cascade-budget.ts):
|
|
1016
|
+
// a run that is merely late keeps the whole set and is delivered complete
|
|
1017
|
+
// by #1443's carry-over, because a dropped neighbour is lost for good
|
|
1018
|
+
// while a late one is not. A wired callback that cannot observe an active
|
|
1019
|
+
// turn_end wait uses zero elapsed time rather than charging pre-turn age.
|
|
1020
|
+
{
|
|
1021
|
+
const settleStart = turnEndCascadeSettleStart?.();
|
|
1022
|
+
const elapsedMs = turnEndCascadeSettleStart
|
|
1023
|
+
? settleStart !== undefined && Number.isFinite(settleStart)
|
|
1024
|
+
? Math.max(0, Date.now() - settleStart)
|
|
1025
|
+
: 0
|
|
1026
|
+
: Date.now() - cascadeStart;
|
|
1027
|
+
const decision = deriveCascadeNeighbourBudget({ elapsedMs });
|
|
1028
|
+
cascadeNeighbourBudget = decision.budget;
|
|
1029
|
+
cascadeBudgetRemainingMs = decision.remainingMs;
|
|
1030
|
+
cascadeBudgetZone = decision.zone;
|
|
1031
|
+
cascadeDeliveryWindowMs = decision.deliveryWindowMs;
|
|
1032
|
+
}
|
|
962
1033
|
// Bounded transitive expansion: add depth>1 dependents (indirect
|
|
963
1034
|
// importers/callers/referencers) so the blast radius isn't limited to one
|
|
964
1035
|
// hop. The one-hop sets above remain the floor (they sort first); these
|
|
@@ -966,8 +1037,9 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
966
1037
|
if (CASCADE_TRANSITIVE_DEPTH > 1) {
|
|
967
1038
|
const transitive = computeTransitiveImpact(graph, normalizedFile, {
|
|
968
1039
|
maxDepth: CASCADE_TRANSITIVE_DEPTH,
|
|
969
|
-
maxHits:
|
|
1040
|
+
maxHits: cascadeNeighbourBudget,
|
|
970
1041
|
});
|
|
1042
|
+
transitiveTruncated = transitive.truncated;
|
|
971
1043
|
const added = [
|
|
972
1044
|
...new Set(transitive.hits
|
|
973
1045
|
.map((hit) => hit.file)
|
|
@@ -1003,6 +1075,7 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
1003
1075
|
// touched solely for cascade diagnostics. Composes the shared `detectFileRole`
|
|
1004
1076
|
// seam; a classifier failure RETAINS the candidate (honest — never a false
|
|
1005
1077
|
// clean). The project ignore filter below is separate and unchanged (#297).
|
|
1078
|
+
cascadeCandidateCount = impact.neighborFiles.length;
|
|
1006
1079
|
impact.directImporters = impact.directImporters.filter((f) => !isTestRoleCollateral(f));
|
|
1007
1080
|
impact.directCallers = impact.directCallers.filter((f) => !isTestRoleCollateral(f));
|
|
1008
1081
|
impact.neighborFiles = impact.neighborFiles.filter((f) => !isTestRoleCollateral(f));
|
|
@@ -1029,8 +1102,42 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
1029
1102
|
const rank = (p) => importerSet.has(p) ? 0 : callerSet.has(p) ? 1 : 2;
|
|
1030
1103
|
return rank(a) - rank(b);
|
|
1031
1104
|
});
|
|
1032
|
-
|
|
1033
|
-
|
|
1105
|
+
cascadeEligibleCount = eligibleNeighbors.length;
|
|
1106
|
+
cascadeFilterDroppedCount = Math.max(0, cascadeCandidateCount - cascadeEligibleCount);
|
|
1107
|
+
cascadeBudgetTruncated = Math.max(0, eligibleNeighbors.length - cascadeNeighbourBudget);
|
|
1108
|
+
sortedNeighbors = eligibleNeighbors.slice(0, cascadeNeighbourBudget);
|
|
1109
|
+
if (cascadeBudgetTruncated > 0 || transitiveTruncated) {
|
|
1110
|
+
const budget = {
|
|
1111
|
+
candidateCount: cascadeCandidateCount,
|
|
1112
|
+
eligibleCount: cascadeEligibleCount,
|
|
1113
|
+
selectedCount: sortedNeighbors.length,
|
|
1114
|
+
truncatedCount: cascadeBudgetTruncated,
|
|
1115
|
+
...(transitiveTruncated && { transitiveTruncated: true }),
|
|
1116
|
+
};
|
|
1117
|
+
const detailParts = [
|
|
1118
|
+
...(cascadeBudgetTruncated > 0
|
|
1119
|
+
? [
|
|
1120
|
+
`cascade neighbor budget checked ${budget.selectedCount} of ${budget.eligibleCount} eligible dependents ` +
|
|
1121
|
+
`(${budget.truncatedCount} omitted)`,
|
|
1122
|
+
]
|
|
1123
|
+
: []),
|
|
1124
|
+
...(transitiveTruncated
|
|
1125
|
+
? [
|
|
1126
|
+
"transitive expansion was capped before all eligible dependents were enumerated",
|
|
1127
|
+
]
|
|
1128
|
+
: []),
|
|
1129
|
+
];
|
|
1130
|
+
const detail = detailParts.join("; ");
|
|
1131
|
+
impact.indeterminate = impact.indeterminate
|
|
1132
|
+
? {
|
|
1133
|
+
...impact.indeterminate,
|
|
1134
|
+
detail: impact.indeterminate.detail
|
|
1135
|
+
? `${impact.indeterminate.detail}; ${detail}`
|
|
1136
|
+
: detail,
|
|
1137
|
+
budget,
|
|
1138
|
+
}
|
|
1139
|
+
: { reason: "budget_truncated", detail, budget };
|
|
1140
|
+
}
|
|
1034
1141
|
}
|
|
1035
1142
|
else {
|
|
1036
1143
|
logCascade({
|
|
@@ -1056,7 +1163,13 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
1056
1163
|
callerCount: impact.directCallers.length,
|
|
1057
1164
|
referenceCount: Math.max(0, referenceCount),
|
|
1058
1165
|
riskFlags: impact.riskFlags,
|
|
1059
|
-
metadata: {
|
|
1166
|
+
metadata: {
|
|
1167
|
+
neighbors: sortedNeighbors.slice(0, 10),
|
|
1168
|
+
candidateNeighborCount: cascadeCandidateCount,
|
|
1169
|
+
eligibleNeighborCount: cascadeEligibleCount,
|
|
1170
|
+
filterDroppedCount: cascadeFilterDroppedCount,
|
|
1171
|
+
budgetTrimmedCount: cascadeBudgetTruncated,
|
|
1172
|
+
},
|
|
1060
1173
|
});
|
|
1061
1174
|
const lspService = getLSPService();
|
|
1062
1175
|
// Hoist passive snapshot once — used for auto-propagating LSPs and fallback path.
|
|
@@ -1079,28 +1192,33 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
1079
1192
|
// the latter must not collapse into a clean-looking result (#1104 honesty
|
|
1080
1193
|
// rule, same doctrine as #1023's graph-degraded indeterminate marker).
|
|
1081
1194
|
let fallbackBindingRejected = false;
|
|
1195
|
+
const noLspCandidatePaths = new Set();
|
|
1082
1196
|
// #1444: neighbours whose in-lane wait was skipped for the quiet-window
|
|
1083
1197
|
// reconcile to answer later. Logged on `cascade_result` so a cascade that
|
|
1084
1198
|
// deferred EVERY neighbour is distinguishable from a genuine leaf (both are
|
|
1085
1199
|
// `neighborCount: 0` with no output otherwise).
|
|
1086
1200
|
let collectLaterSkipped = 0;
|
|
1087
|
-
// #1446 item 5: `recentlyCleanNeighborCache`
|
|
1088
|
-
//
|
|
1089
|
-
//
|
|
1090
|
-
//
|
|
1201
|
+
// #1446 item 5: `recentlyCleanNeighborCache` hits are the whole point of the
|
|
1202
|
+
// cache, but they were never counted — the only visible signal was 267s/day
|
|
1203
|
+
// of touch wall time with no way to tell whether the cache was absorbing
|
|
1204
|
+
// repeat work or every touch was cold. (#1899 removed the sibling
|
|
1205
|
+
// `cacheHits` counter along with the dead `neighborTouchCache`.)
|
|
1091
1206
|
let recentlyCleanHits = 0;
|
|
1092
|
-
let cacheHits = 0;
|
|
1093
1207
|
// F1 (#1446 follow-up): `coldTouches` must be counted at the point each
|
|
1094
1208
|
// neighbour's OUTCOME is actually known, not derived from `coldSnapshotPaths`
|
|
1095
1209
|
// (finalized earlier, before the cache-hit checks below run against it). Using
|
|
1096
1210
|
// the pre-outcome list let a neighbour double-count (cold-snapshot AND cache/
|
|
1097
1211
|
// recently-clean hit) or vanish from every bucket (an `activePaths` neighbour —
|
|
1098
|
-
// e.g. Python/Go — that misses both caches). These
|
|
1099
|
-
//
|
|
1100
|
-
//
|
|
1101
|
-
//
|
|
1212
|
+
// e.g. Python/Go — that misses both caches). These counters partition the
|
|
1213
|
+
// selected-neighbour outcomes; passive snapshots and no-server selections are
|
|
1214
|
+
// tracked separately instead of silently disappearing from the denominator.
|
|
1215
|
+
// `coldTouches` includes every genuine active touch attempt, while
|
|
1216
|
+
// `touchFailures` is its separate bounded failure subtype.
|
|
1102
1217
|
let deferredTouches = 0;
|
|
1103
1218
|
let coldTouches = 0;
|
|
1219
|
+
let passiveSnapshotHits = 0;
|
|
1220
|
+
let noLspConfigured = 0;
|
|
1221
|
+
let touchFailures = 0;
|
|
1104
1222
|
if (sortedNeighbors.length > 0) {
|
|
1105
1223
|
const snapshotPaths = sortedNeighbors.filter(shouldReadCascadeFromSnapshot);
|
|
1106
1224
|
const activePaths = sortedNeighbors.filter((n) => !shouldReadCascadeFromSnapshot(n));
|
|
@@ -1115,7 +1233,7 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
1115
1233
|
const snapshotAgeSec = entry
|
|
1116
1234
|
? Math.round((Date.now() - entry.ts) / 1000)
|
|
1117
1235
|
: undefined;
|
|
1118
|
-
const ttlFresh = entry != null && Date.now() - entry.ts <
|
|
1236
|
+
const ttlFresh = entry != null && Date.now() - entry.ts < CASCADE_DIAGNOSTICS_TTL_MS;
|
|
1119
1237
|
// #1095: content binding is the INNER gate; TTL stays the outer bound.
|
|
1120
1238
|
// false → the server's diagnostics were computed against a DIFFERENT
|
|
1121
1239
|
// disk state (e.g. the PRE-fix content) — don't trust or
|
|
@@ -1163,6 +1281,7 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
1163
1281
|
// had no remaining purpose and is simply dropped rather than migrated.
|
|
1164
1282
|
const diags = convertLspDiagnostics(entry.diags.filter((d) => d.severity === 1).slice(0, MAX_PER_FILE), neighborPath);
|
|
1165
1283
|
producedLspData = true;
|
|
1284
|
+
passiveSnapshotHits++;
|
|
1166
1285
|
const durationMs = Date.now() - neighborStart;
|
|
1167
1286
|
logCascade({
|
|
1168
1287
|
phase: "neighbor_snapshot",
|
|
@@ -1184,7 +1303,7 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
1184
1303
|
// LSP warning on the neighbor is preserved. Keyed by the primary edit's
|
|
1185
1304
|
// `writeSeq` so a genuinely newer per-edit write still wins the
|
|
1186
1305
|
// WriteOrderingGuard. `observedAt = entry.ts` (the snapshot's own publish
|
|
1187
|
-
// time, up to
|
|
1306
|
+
// time, up to CASCADE_DIAGNOSTICS_TTL_MS old) — NOT now() — so replaying an aging
|
|
1188
1307
|
// snapshot never re-arms the mtime-staleness gate (the same #1092
|
|
1189
1308
|
// re-arming defect this PR fixes for cache hits).
|
|
1190
1309
|
//
|
|
@@ -1214,7 +1333,7 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
1214
1333
|
const cacheKey = normalizeMapKey(neighborPath);
|
|
1215
1334
|
const passiveEntry = allDiags.get(cacheKey);
|
|
1216
1335
|
const hasFreshPassiveErrors = passiveEntry != null &&
|
|
1217
|
-
Date.now() - passiveEntry.ts <
|
|
1336
|
+
Date.now() - passiveEntry.ts < CASCADE_DIAGNOSTICS_TTL_MS &&
|
|
1218
1337
|
passiveEntry.diags.some((d) => d.severity === 1);
|
|
1219
1338
|
const recentlyClean = recentlyCleanNeighborCache.get(cacheKey);
|
|
1220
1339
|
if (recentlyClean &&
|
|
@@ -1244,33 +1363,13 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
1244
1363
|
durationMs,
|
|
1245
1364
|
};
|
|
1246
1365
|
}
|
|
1247
|
-
//
|
|
1248
|
-
//
|
|
1249
|
-
|
|
1250
|
-
if (cached?.turnSeq === turnSeq && cached?.writeSeq === writeSeq) {
|
|
1251
|
-
producedLspData = true;
|
|
1252
|
-
cacheHits++;
|
|
1253
|
-
const durationMs = Date.now() - neighborStart;
|
|
1254
|
-
logCascade({
|
|
1255
|
-
phase: "neighbor_snapshot",
|
|
1256
|
-
filePath,
|
|
1257
|
-
neighborFile: neighborPath,
|
|
1258
|
-
diagnosticCount: cached.diagnostics.length,
|
|
1259
|
-
durationMs,
|
|
1260
|
-
autoPropagate: false,
|
|
1261
|
-
snapshotMissing: false,
|
|
1262
|
-
metadata: { cachedWriteSeq: writeSeq },
|
|
1263
|
-
});
|
|
1264
|
-
return {
|
|
1265
|
-
filePath: neighborPath,
|
|
1266
|
-
reason: neighborReason(importerSet, callerSet, neighborPath),
|
|
1267
|
-
diagnostics: cached.diagnostics,
|
|
1268
|
-
lspTouched: false,
|
|
1269
|
-
durationMs,
|
|
1270
|
-
};
|
|
1271
|
-
}
|
|
1366
|
+
// #1899: the A5 same-write cache read stood here. See the removal note
|
|
1367
|
+
// at the module's cache declarations — the gate could only pass inside
|
|
1368
|
+
// a single write, which never runs two cascades.
|
|
1272
1369
|
const configuredServerCount = getServersForFileWithConfig(neighborPath).length;
|
|
1273
1370
|
if (configuredServerCount === 0) {
|
|
1371
|
+
noLspConfigured++;
|
|
1372
|
+
noLspCandidatePaths.add(cacheKey);
|
|
1274
1373
|
logCascade({
|
|
1275
1374
|
phase: "neighbor_fallback",
|
|
1276
1375
|
filePath,
|
|
@@ -1341,8 +1440,8 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
1341
1440
|
},
|
|
1342
1441
|
});
|
|
1343
1442
|
// Deliberately NOT cached as clean/diagnosed — the wait was
|
|
1344
|
-
// skipped, not resolved, so
|
|
1345
|
-
//
|
|
1443
|
+
// skipped, not resolved, so recentlyCleanNeighborCache must
|
|
1444
|
+
// not treat this as a real answer
|
|
1346
1445
|
// (#240 doctrine). Return undefined: the degraded-fallback
|
|
1347
1446
|
// path below still has a chance to surface a passive/stale
|
|
1348
1447
|
// snapshot, same as any other "no fresh data this touch" case.
|
|
@@ -1367,13 +1466,27 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
1367
1466
|
// which is finalized before any of the above checks run and includes
|
|
1368
1467
|
// neighbours that resolve via cache/recently-clean instead.
|
|
1369
1468
|
coldTouches++;
|
|
1469
|
+
// #1720: language-server scope only, matching the tier-aware touch
|
|
1470
|
+
// above (`clientScope: "primary"`, integration.ts:1826). A neighbor's
|
|
1471
|
+
// content did not change — only its import target did — so an
|
|
1472
|
+
// auxiliary scanner's (ast-grep/opengrep/typos) file-local verdict for
|
|
1473
|
+
// it cannot have changed; only cross-file type semantics (the
|
|
1474
|
+
// language server) can. `reconcileCascadeNeighborLspErrors` already
|
|
1475
|
+
// merges errors-only and preserves the neighbor's existing aux
|
|
1476
|
+
// findings by construction (see its doc comment and
|
|
1477
|
+
// `cascadeReconcilableLspErrors` above), so an `"all"`-scope touch
|
|
1478
|
+
// paid full aux notify traffic and aux confirmation-wait latency for
|
|
1479
|
+
// a re-derivation nothing downstream ever reads. `"primary"` resolves
|
|
1480
|
+
// per `neighborPath` (`getClientForFile` → `getServersForFileWithConfig`),
|
|
1481
|
+
// so a multi-language neighbor still gets its OWN language server,
|
|
1482
|
+
// not the primary edit's.
|
|
1370
1483
|
const rawDiags = await lspService.touchFile(neighborPath, content, {
|
|
1371
1484
|
diagnostics: "document",
|
|
1372
1485
|
collectDiagnostics: true,
|
|
1373
1486
|
maxClientWaitMs: isColdSnapshot ? 1000 : 2000,
|
|
1374
1487
|
silent: true,
|
|
1375
1488
|
source: "cascade",
|
|
1376
|
-
clientScope: "
|
|
1489
|
+
clientScope: "primary",
|
|
1377
1490
|
});
|
|
1378
1491
|
if (!rawDiags)
|
|
1379
1492
|
return undefined;
|
|
@@ -1392,9 +1505,10 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
1392
1505
|
const confirmed = isConfirmedTouch(rawDiags);
|
|
1393
1506
|
const bindingRejected = readBoundToCurrentDisk(rawDiags) === false;
|
|
1394
1507
|
const inconclusive = readInconclusive(rawDiags);
|
|
1395
|
-
// #1470: the third, independent reason a touch is unconfirmed — an
|
|
1396
|
-
// auxiliary
|
|
1397
|
-
//
|
|
1508
|
+
// #1470/#1493: the third, independent reason a touch is unconfirmed — an
|
|
1509
|
+
// auxiliary that never reported, cut off by our grace timer or silent
|
|
1510
|
+
// with nothing published for this content. Logged alongside the other two
|
|
1511
|
+
// so cascade.log alone still tells the three apart.
|
|
1398
1512
|
const unconfirmedServerIds = touchCoverageGap(rawDiags);
|
|
1399
1513
|
// #692: `source: "cascade"` no longer overrides `rule` (see the
|
|
1400
1514
|
// doc comment on the sibling call above) — dropped rather than
|
|
@@ -1405,15 +1519,7 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
1405
1519
|
// wrapper and are unaffected by this copy (the shape-5 fix).
|
|
1406
1520
|
const diags = convertLspDiagnostics(rawDiags.diags.filter((d) => d.severity === 1).slice(0, MAX_PER_FILE), neighborPath);
|
|
1407
1521
|
const durationMs = Date.now() - neighborStart;
|
|
1408
|
-
//
|
|
1409
|
-
// result must not become a confirmed cache hit on the next cascade.
|
|
1410
|
-
if (writeSeq != null && confirmed) {
|
|
1411
|
-
neighborTouchCache.set(cacheKey, {
|
|
1412
|
-
turnSeq,
|
|
1413
|
-
writeSeq,
|
|
1414
|
-
diagnostics: diags,
|
|
1415
|
-
});
|
|
1416
|
-
}
|
|
1522
|
+
// #1899: the A5 same-write cache write stood here.
|
|
1417
1523
|
if (diags.length === 0) {
|
|
1418
1524
|
// Only a CONFIRMED clean touch may seed the recently-clean cache
|
|
1419
1525
|
// (#1095: a bound-false touch is unconfirmed, exactly like inconclusive).
|
|
@@ -1446,7 +1552,12 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
1446
1552
|
// stays conditional since "bound" carries no extra signal.
|
|
1447
1553
|
metadata: {
|
|
1448
1554
|
inconclusive,
|
|
1449
|
-
|
|
1555
|
+
// #1549: WHICH primary and WHICH deadline, present only when the
|
|
1556
|
+
// touch actually reported itself inconclusive.
|
|
1557
|
+
...(inconclusive && readInconclusiveAttribution(rawDiags)),
|
|
1558
|
+
...(bindingRejected && {
|
|
1559
|
+
bindingState: bindingStateLabel(false),
|
|
1560
|
+
}),
|
|
1450
1561
|
...(unconfirmedServerIds.length > 0 && {
|
|
1451
1562
|
unconfirmedServerIds: [...unconfirmedServerIds],
|
|
1452
1563
|
}),
|
|
@@ -1476,6 +1587,15 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
1476
1587
|
diagnostics: diags,
|
|
1477
1588
|
lspTouched: true,
|
|
1478
1589
|
...(inconclusive && { inconclusive: true }),
|
|
1590
|
+
// #1459: carry the coverage gap to the agent-facing surface. A
|
|
1591
|
+
// scanner whose breaker was open, whose resync the fan-out gate
|
|
1592
|
+
// deferred, or whom the aux grace timer cut off never looked at this
|
|
1593
|
+
// file — without this the neighbour reaches the agent as
|
|
1594
|
+
// `{ diagnostics: [] }` with no marker, which is the false-clean the
|
|
1595
|
+
// touch already refuses to claim for itself.
|
|
1596
|
+
...(unconfirmedServerIds.length > 0 && {
|
|
1597
|
+
unconfirmedServerIds: [...unconfirmedServerIds],
|
|
1598
|
+
}),
|
|
1479
1599
|
durationMs,
|
|
1480
1600
|
};
|
|
1481
1601
|
}));
|
|
@@ -1488,10 +1608,11 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
1488
1608
|
neighbors.push(result.value);
|
|
1489
1609
|
}
|
|
1490
1610
|
else {
|
|
1611
|
+
touchFailures++;
|
|
1491
1612
|
// A3: one failed LSP doesn't kill the rest — fall back to passive snapshot
|
|
1492
1613
|
dbg?.(`cascade neighbor touch error for ${neighborPath}: ${result.reason}`);
|
|
1493
1614
|
const entry = allDiags.get(normalizeMapKey(neighborPath));
|
|
1494
|
-
const ttlFresh = entry != null && Date.now() - entry.ts <
|
|
1615
|
+
const ttlFresh = entry != null && Date.now() - entry.ts < CASCADE_DIAGNOSTICS_TTL_MS;
|
|
1495
1616
|
// #1104: consult binding before trusting a TTL-fresh fallback snapshot —
|
|
1496
1617
|
// MATCH #1100/#1095 semantics (false → skip, "unknown" → keep the
|
|
1497
1618
|
// pre-#1104 TTL-only behavior, true → use). Without this, a failed
|
|
@@ -1538,8 +1659,15 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
1538
1659
|
}
|
|
1539
1660
|
}
|
|
1540
1661
|
// CR-3/A2: degraded fallback when no neighbor produced trustworthy LSP data —
|
|
1541
|
-
// not merely when the graph returned zero neighbors.
|
|
1542
|
-
|
|
1662
|
+
// not merely when the graph returned zero neighbors. In a mixed run, only
|
|
1663
|
+
// candidates that had no configured LSP may use passive fallback; broadening
|
|
1664
|
+
// to unrelated cache entries would make the partial active result misleading.
|
|
1665
|
+
if (producedLspData && noLspCandidatePaths.size > 0) {
|
|
1666
|
+
const bindingRejected = appendFallbackNeighbors(neighbors, allDiags, normalizedFileKey, cwd, filePath, noLspCandidatePaths);
|
|
1667
|
+
if (bindingRejected)
|
|
1668
|
+
fallbackBindingRejected = true;
|
|
1669
|
+
}
|
|
1670
|
+
else if (!producedLspData) {
|
|
1543
1671
|
const bindingRejected = appendFallbackNeighbors(neighbors, allDiags, normalizedFileKey, cwd, filePath);
|
|
1544
1672
|
if (bindingRejected)
|
|
1545
1673
|
fallbackBindingRejected = true;
|
|
@@ -1570,6 +1698,12 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
1570
1698
|
};
|
|
1571
1699
|
}
|
|
1572
1700
|
const filesWithErrors = visibleNeighbors.filter((n) => n.diagnostics.length > 0).length;
|
|
1701
|
+
const selectedOutcomeCount = passiveSnapshotHits +
|
|
1702
|
+
recentlyCleanHits +
|
|
1703
|
+
deferredTouches +
|
|
1704
|
+
coldTouches +
|
|
1705
|
+
noLspConfigured;
|
|
1706
|
+
const selectedOutcomeGap = Math.max(0, sortedNeighbors.length - selectedOutcomeCount);
|
|
1573
1707
|
logCascade({
|
|
1574
1708
|
phase: "cascade_result",
|
|
1575
1709
|
filePath,
|
|
@@ -1578,6 +1712,16 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
1578
1712
|
metadata: {
|
|
1579
1713
|
filesWithErrors,
|
|
1580
1714
|
hasOutput: formatted.length > 0,
|
|
1715
|
+
indeterminateReason: impact.indeterminate?.reason,
|
|
1716
|
+
candidateNeighborCount: cascadeCandidateCount,
|
|
1717
|
+
eligibleNeighborCount: cascadeEligibleCount,
|
|
1718
|
+
filterDroppedCount: cascadeFilterDroppedCount,
|
|
1719
|
+
selectedNeighborCount: sortedNeighbors.length,
|
|
1720
|
+
selectedOutcomeCount,
|
|
1721
|
+
selectedOutcomeGap,
|
|
1722
|
+
passiveSnapshotHits,
|
|
1723
|
+
noLspConfigured,
|
|
1724
|
+
touchFailures,
|
|
1581
1725
|
// #1444: >0 means "answers are still outstanding", not "nothing found".
|
|
1582
1726
|
collectLaterSkipped,
|
|
1583
1727
|
// Log when cascade ran but found nothing — distinguishes "clean" from "no signal"
|
|
@@ -1586,21 +1730,42 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
1586
1730
|
// #1446 item 5: cache effectiveness as a number instead of an inference
|
|
1587
1731
|
// from `coldSnapshot`/`snapshotMissing` flags scattered across
|
|
1588
1732
|
// per-neighbor `neighbor_touch`/`neighbor_snapshot` rows.
|
|
1589
|
-
// F1:
|
|
1590
|
-
//
|
|
1591
|
-
//
|
|
1592
|
-
//
|
|
1593
|
-
// before the cache-hit checks run). A neighbour with no LSP server
|
|
1594
|
-
// configured is the one deliberately uncounted outcome (never touched).
|
|
1595
|
-
cacheHits,
|
|
1733
|
+
// F1: recentlyCleanHits + deferredTouches + coldTouches
|
|
1734
|
+
// retain their outcome semantics. Passive snapshots and no-server selections
|
|
1735
|
+
// complete the selected-neighbor denominator; touchFailures is a bounded
|
|
1736
|
+
// subtype of coldTouches, not an additional outcome.
|
|
1596
1737
|
recentlyCleanHits,
|
|
1597
1738
|
deferredTouches,
|
|
1598
1739
|
coldTouches,
|
|
1599
1740
|
// #1446 item 4: the budget in force and how many eligible candidates
|
|
1600
1741
|
// it cut off this run — the correctness half (a truncated run being
|
|
1601
|
-
// silently discarded) is #1443
|
|
1602
|
-
|
|
1742
|
+
// silently discarded) is #1443.
|
|
1743
|
+
// #1462: `neighborBudget` is now the DERIVED budget (still "the one in
|
|
1744
|
+
// force"); `neighborBudgetCeiling` is the flat cap, `budgetRemainingMs`
|
|
1745
|
+
// the on-time window left when it was sized, and `budgetZone` the rule
|
|
1746
|
+
// that decided it. Only `budgetZone: "narrowed"` shortens a walk — the
|
|
1747
|
+
// other three keep the flat cap, so a row where budget < ceiling is a
|
|
1748
|
+
// deliberate rescue, never a cold-start stub. `budgetDeliveryWindowMs`
|
|
1749
|
+
// records how long the pipeline (turn_end settle + quiet-window drain)
|
|
1750
|
+
// keeps a slow run alive, which is why the other zones can afford to
|
|
1751
|
+
// stay wide.
|
|
1752
|
+
//
|
|
1753
|
+
// NOTE for whoever reads these rows: `budgetTruncated` INVERTS under a
|
|
1754
|
+
// narrowed budget — it is `eligible - budget`, so a smaller budget
|
|
1755
|
+
// mechanically raises it. Compare truncation WITHIN a zone, never
|
|
1756
|
+
// across one.
|
|
1757
|
+
neighborBudget: cascadeNeighbourBudget,
|
|
1758
|
+
neighborBudgetCeiling: CASCADE_NEIGHBOUR_BUDGET,
|
|
1759
|
+
budgetRemainingMs: cascadeBudgetRemainingMs,
|
|
1760
|
+
budgetZone: cascadeBudgetZone,
|
|
1761
|
+
budgetDeliveryWindowMs: cascadeDeliveryWindowMs,
|
|
1603
1762
|
budgetTruncated: cascadeBudgetTruncated,
|
|
1763
|
+
transitiveTruncated,
|
|
1764
|
+
budgetTrimmedCount: cascadeBudgetTruncated,
|
|
1765
|
+
budgetDerivationDisabled: cascadeBudgetZone === "no-rescue-window",
|
|
1766
|
+
budgetDerivationDisabledReason: cascadeBudgetZone === "no-rescue-window"
|
|
1767
|
+
? "no-rescue-window"
|
|
1768
|
+
: undefined,
|
|
1604
1769
|
neighbors: visibleNeighbors.slice(0, 10).map((n) => ({
|
|
1605
1770
|
file: n.filePath.replace(/\\/g, "/").split("/").slice(-2).join("/"),
|
|
1606
1771
|
diagnostics: n.diagnostics.length,
|
|
@@ -1624,6 +1789,7 @@ export async function computeCascadeForFile(filePath, cwd, options = {}) {
|
|
|
1624
1789
|
diagnosticCount: diagCount,
|
|
1625
1790
|
skipReason: "indeterminate",
|
|
1626
1791
|
indeterminate: impact.indeterminate,
|
|
1792
|
+
selectedNeighborPaths: sortedNeighbors.slice(),
|
|
1627
1793
|
};
|
|
1628
1794
|
}
|
|
1629
1795
|
const skipReason = visibleNeighbors.length === 0 ? "no_neighbors" : "clean";
|
|
@@ -1701,7 +1867,7 @@ function applyCascadeDeltaBaselines(neighbors) {
|
|
|
1701
1867
|
* because its content binding was rejected (#1104) — the caller folds this
|
|
1702
1868
|
* into the run-level `fallbackBindingRejected` flag for the honesty check.
|
|
1703
1869
|
*/
|
|
1704
|
-
function appendFallbackNeighbors(neighbors, allDiags, normalizedFileKey, cwd, filePath) {
|
|
1870
|
+
function appendFallbackNeighbors(neighbors, allDiags, normalizedFileKey, cwd, filePath, allowedPaths) {
|
|
1705
1871
|
const now = Date.now();
|
|
1706
1872
|
const seen = new Set(neighbors.map((n) => normalizeMapKey(n.filePath)));
|
|
1707
1873
|
let bindingRejected = false;
|
|
@@ -1710,6 +1876,8 @@ function appendFallbackNeighbors(neighbors, allDiags, normalizedFileKey, cwd, fi
|
|
|
1710
1876
|
const diagKey = normalizeMapKey(diagPath);
|
|
1711
1877
|
if (diagKey === normalizedFileKey || seen.has(diagKey))
|
|
1712
1878
|
continue;
|
|
1879
|
+
if (allowedPaths && !allowedPaths.has(diagKey))
|
|
1880
|
+
continue;
|
|
1713
1881
|
if (primaryFilesThisTurn.has(diagKey))
|
|
1714
1882
|
continue;
|
|
1715
1883
|
if (isExternalOrVendorFile(diagPath, cwd))
|
|
@@ -1723,7 +1891,7 @@ function appendFallbackNeighbors(neighbors, allDiags, normalizedFileKey, cwd, fi
|
|
|
1723
1891
|
continue;
|
|
1724
1892
|
if (!nodeFs.existsSync(diagPath))
|
|
1725
1893
|
continue;
|
|
1726
|
-
if (now - ts >
|
|
1894
|
+
if (now - ts > CASCADE_DIAGNOSTICS_TTL_MS)
|
|
1727
1895
|
continue;
|
|
1728
1896
|
// #1104: a TTL-fresh entry is not automatically trustworthy — consult
|
|
1729
1897
|
// binding the same way the reconcile path (#1100) and the touch-error
|