@herbertgao/pi-extensions 2026.8.5 → 2026.8.7
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 +5 -5
- 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 +272 -22
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +48 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +14 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -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 +16 -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 +22 -15
- 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 +34 -10
- package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
- package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +29 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +3 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +46 -10
- 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 +8 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +3 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +2 -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 +3 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +30 -7
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +30 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +26 -5
- package/node_modules/@narumitw/pi-btw/README.md +119 -103
- 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 +13 -9
- package/node_modules/@narumitw/pi-btw/src/btw.ts +40 -42
- 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 +78 -15
- package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
- package/node_modules/pi-lens/CHANGELOG.md +1004 -0
- package/node_modules/pi-lens/README.md +11 -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 +427 -0
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +79 -16
- 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 +285 -8
- package/node_modules/pi-lens/dist/clients/biome-client.js +176 -37
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
- package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +119 -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 +192 -0
- package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
- package/node_modules/pi-lens/dist/clients/bus-publish.js +11 -3
- package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
- package/node_modules/pi-lens/dist/clients/cascade-format.js +76 -2
- 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/console-guard-install.js +16 -4
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +249 -52
- 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 +62 -13
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +43 -9
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +153 -37
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
- package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +165 -11
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +325 -72
- 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/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/helm-render.js +895 -0
- 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 +81 -41
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +23 -0
- 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 +14 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +20 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +22 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +10 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +18 -6
- 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 +488 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +84 -0
- 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 +943 -93
- 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 +72 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +106 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +9 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
- package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
- package/node_modules/pi-lens/dist/clients/extension-log.js +317 -3
- package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +294 -0
- package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
- package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
- package/node_modules/pi-lens/dist/clients/format-events-publish.js +39 -8
- 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 +49 -21
- package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
- package/node_modules/pi-lens/dist/clients/go-client.js +21 -39
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +219 -10
- package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
- package/node_modules/pi-lens/dist/clients/host-ports.js +9 -1
- package/node_modules/pi-lens/dist/clients/installer/index.js +1119 -101
- 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 +86 -15
- package/node_modules/pi-lens/dist/clients/knip-client.js +361 -24
- package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +2 -0
- package/node_modules/pi-lens/dist/clients/latency-logger.js +487 -9
- 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-events.js +56 -25
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +45 -2
- package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +160 -20
- package/node_modules/pi-lens/dist/clients/lsp/client.js +1666 -148
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +111 -0
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
- package/node_modules/pi-lens/dist/clients/lsp/index.js +2496 -171
- 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 +517 -69
- 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 +172 -0
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +61 -3
- 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/analyze.js +4 -0
- package/node_modules/pi-lens/dist/clients/mcp/session.js +39 -18
- package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
- package/node_modules/pi-lens/dist/clients/module-report.js +8 -0
- 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 +178 -30
- 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 +85 -15
- 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/runner-findings.js +23 -2
- 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 +5 -0
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
- 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 +1 -1
- package/node_modules/pi-lens/dist/clients/read-guard.js +184 -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/query.js +76 -4
- package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +248 -13
- package/node_modules/pi-lens/dist/clients/runtime-context.js +112 -12
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +472 -61
- package/node_modules/pi-lens/dist/clients/runtime-session.js +359 -13
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +269 -22
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +948 -87
- package/node_modules/pi-lens/dist/clients/rust-client.js +21 -37
- 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 +224 -8
- package/node_modules/pi-lens/dist/clients/sg-runner.js +249 -37
- package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +18 -11
- package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
- package/node_modules/pi-lens/dist/clients/startup-timing.js +26 -2
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +574 -32
- package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
- package/node_modules/pi-lens/dist/clients/tool-policy.js +127 -20
- package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
- 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-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/warm-attach.js +17 -0
- package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
- package/node_modules/pi-lens/dist/clients/word-index.js +305 -33
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
- package/node_modules/pi-lens/dist/index.js +64677 -49484
- package/node_modules/pi-lens/dist/mcp/server.js +8 -2
- package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
- package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
- package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +469 -56
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +190 -46
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
- package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
- package/node_modules/pi-lens/docs/agent-guide.md +44 -14
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +518 -501
- package/node_modules/pi-lens/docs/dispositions.md +21 -0
- package/node_modules/pi-lens/docs/features.md +33 -6
- package/node_modules/pi-lens/docs/globalconfig.md +14 -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 +25 -1
- package/node_modules/pi-lens/docs/usage.md +24 -5
- package/node_modules/pi-lens/docs/word-index.md +35 -0
- package/node_modules/pi-lens/package.json +10 -6
- 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 +22 -0
- 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-conditional-empty-object-spread-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
- 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 +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +9 -0
- 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-laundering-test.yml +11 -0
- 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 +66 -0
- 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-conditional-empty-object-spread-js.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
- 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 +29 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +30 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +41 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +42 -0
- 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 +32 -0
- 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 +132 -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/skills/pi-lens-ast-grep/SKILL.md +12 -11
- package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +87 -116
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +170 -0
- package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
- package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +49 -0
- package/node_modules/pi-mcp-adapter/README.md +51 -6
- package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
- package/node_modules/pi-mcp-adapter/cli.js +188 -5
- package/node_modules/pi-mcp-adapter/commands.ts +128 -22
- package/node_modules/pi-mcp-adapter/config.ts +28 -10
- package/node_modules/pi-mcp-adapter/direct-tools.ts +23 -19
- 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/mcp-setup-panel.ts +2 -1
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +46 -25
- package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
- package/node_modules/pi-mcp-adapter/package.json +4 -1
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +330 -0
- package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
- package/node_modules/pi-mcp-adapter/server-manager.ts +145 -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-metadata.ts +38 -18
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
- package/node_modules/pi-mcp-adapter/types.ts +95 -10
- 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 +38 -14
- package/node_modules/pi-web-access/anysearch.ts +4 -2
- package/node_modules/pi-web-access/auth-fetch.ts +148 -0
- package/node_modules/pi-web-access/brave.ts +16 -4
- package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
- package/node_modules/pi-web-access/curator-page.ts +8 -3
- package/node_modules/pi-web-access/curator-server.ts +5 -1
- package/node_modules/pi-web-access/exa.ts +18 -7
- package/node_modules/pi-web-access/extract.ts +134 -37
- package/node_modules/pi-web-access/fetch-params.ts +17 -3
- package/node_modules/pi-web-access/firecrawl.ts +172 -12
- package/node_modules/pi-web-access/gemini-search.ts +33 -7
- package/node_modules/pi-web-access/index.ts +160 -104
- package/node_modules/pi-web-access/package.json +2 -2
- 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/query-rewrite.ts +51 -0
- package/node_modules/pi-web-access/serper.ts +211 -0
- package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
- package/node_modules/pi-web-access/summary-review.ts +51 -23
- 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/node_modules/pi-web-access/youtube-extract.ts +2 -2
- package/package.json +13 -13
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed bounded telemetry emission (#1743).
|
|
3
|
+
*
|
|
4
|
+
* Four PRs in two days hand-built the same machinery: a failure path that
|
|
5
|
+
* needs a detailed `latency.log` record, an exact per-identity tally, and a
|
|
6
|
+
* bound so a storming subsystem cannot flood the log. #1705 (orphan
|
|
7
|
+
* forensics), #1717 (pull timeouts and late answers), #1726 (navRequest
|
|
8
|
+
* timeouts, rising edge per method and file), and #1733 (loop-block floor
|
|
9
|
+
* with a per-turn bound) each rebuilt it, and each needed a review round to
|
|
10
|
+
* get the bounding right. This module is the intersection of those four, not
|
|
11
|
+
* a speculative framework. Three of its four options come straight from those
|
|
12
|
+
* sites. The fourth, `capPerTurn`, expresses #1733's per-turn bound
|
|
13
|
+
* structurally but has no caller yet: that site's bound is its call cadence,
|
|
14
|
+
* and pinning a cap there would red its own wiring tests. It ships tested and
|
|
15
|
+
* mutation-proofed, awaiting its first site.
|
|
16
|
+
*
|
|
17
|
+
* Three rules the helper makes structural instead of prose:
|
|
18
|
+
*
|
|
19
|
+
* 1. **The rising edge comes from the ledger's own tally.** #1705 established
|
|
20
|
+
* that `incrementDegradationCount` already knows whether a
|
|
21
|
+
* (kind, subject) pair is being seen for the first time, and returns it.
|
|
22
|
+
* A caller that wants "log the detail once, count the rest" reads that
|
|
23
|
+
* return value. It never keeps a second `Set` — a parallel latch is state
|
|
24
|
+
* that must re-arm at `session_start` and will eventually be forgotten
|
|
25
|
+
* (the #1266–#1625 defect class).
|
|
26
|
+
* 2. **A per-turn cap names its turn.** `capPerTurn` carries the caller's
|
|
27
|
+
* `turnIndex` in the same object as the limit, so there is no separate
|
|
28
|
+
* reset to wire and forget: the counters clear when the observed turn
|
|
29
|
+
* changes. This is the same lazy clear-on-transition shape as
|
|
30
|
+
* `noteImportResolutionMemoTurn` (`clients/blocker-freshness.ts`).
|
|
31
|
+
* 3. **Identity always survives.** Every emitted record carries the
|
|
32
|
+
* discriminating identity in `filePath` and in `metadata.identity`, so
|
|
33
|
+
* aggregation can still answer "which file, which method, which record is
|
|
34
|
+
* stuck" (the rule AGENTS.md states, now enforced by the signature).
|
|
35
|
+
*
|
|
36
|
+
* `BOUNDED_TELEMETRY_PHASES` is the registry.
|
|
37
|
+
* `tests/clients/bounded-telemetry-sweep.test.ts` sweeps it registered-or-
|
|
38
|
+
* fail: a failure-path phase must either route through this helper or carry a
|
|
39
|
+
* stated reason for staying raw.
|
|
40
|
+
*
|
|
41
|
+
* Nothing here ever throws. Telemetry must not perturb the path it observes —
|
|
42
|
+
* every one of the four migrated sites had already decided its outcome before
|
|
43
|
+
* calling in.
|
|
44
|
+
*/
|
|
45
|
+
import { incrementDegradationCount } from "./degradation-ledger.js";
|
|
46
|
+
import { logLatency } from "./latency-logger.js";
|
|
47
|
+
/**
|
|
48
|
+
* Phases that emit through this helper. The `phase` parameter is typed to
|
|
49
|
+
* this list, so a new bounded phase cannot be emitted without joining the
|
|
50
|
+
* registry, and the sweep proves the reverse direction: a registered phase
|
|
51
|
+
* must not also be written by a raw `logLatency` call somewhere else.
|
|
52
|
+
*/
|
|
53
|
+
export const BOUNDED_TELEMETRY_PHASES = [
|
|
54
|
+
/** #1705: a deferred-format record whose origin worktree never claimed it. */
|
|
55
|
+
"agent_end_deferred_format_orphan_origin_mismatch",
|
|
56
|
+
/** #1713: a `textDocument`/`workspace` diagnostic pull abandoned at budget. */
|
|
57
|
+
"lsp_pull_diagnostic_timeout",
|
|
58
|
+
/** #1713: that abandoned pull answered anyway, too late to serve anyone. */
|
|
59
|
+
"lsp_pull_late_answer_discarded",
|
|
60
|
+
/** #1773: a pull skipped outright because the caller's budget was already
|
|
61
|
+
* exhausted — never dispatched, so it must not be confused with a genuine
|
|
62
|
+
* timeout. */
|
|
63
|
+
"lsp_pull_skipped_budget_exhausted",
|
|
64
|
+
/** #1774: an abandoned pull's request eventually REJECTED (e.g. a server
|
|
65
|
+
* `ContentModified`) rather than answering or staying silent forever —
|
|
66
|
+
* the third outcome late-answer telemetry previously could not see. */
|
|
67
|
+
"lsp_pull_late_rejection",
|
|
68
|
+
/** #1716: a hover/definition/references/etc. request abandoned at budget. */
|
|
69
|
+
"lsp_nav_request_timeout",
|
|
70
|
+
/** #1716: that abandoned nav request answered anyway, after the caller left. */
|
|
71
|
+
"lsp_nav_late_answer_discarded",
|
|
72
|
+
/**
|
|
73
|
+
* #1743 review: a per-file touch skipped a server in breaker cooldown or
|
|
74
|
+
* latched permanently broken. Fires once per file per touch during an
|
|
75
|
+
* outage, so it is rising-edge per (server, file).
|
|
76
|
+
*/
|
|
77
|
+
"lsp_client_skipped_broken",
|
|
78
|
+
/** #1743 review: the same skip, for a temporarily unavailable command. */
|
|
79
|
+
"lsp_client_skipped_unavailable_command",
|
|
80
|
+
/**
|
|
81
|
+
* #1723: an event-loop block at or above the floor. Not a degradation, so
|
|
82
|
+
* no ledger kind; bounded by call cadence (one `turn_end` runs it once per
|
|
83
|
+
* turn) rather than by an option here.
|
|
84
|
+
*/
|
|
85
|
+
"loop_block",
|
|
86
|
+
];
|
|
87
|
+
const REGISTERED_PHASES = new Set(BOUNDED_TELEMETRY_PHASES);
|
|
88
|
+
/** Report whether a phase name is in the registry (used by the sweep). */
|
|
89
|
+
export function isBoundedTelemetryPhase(phase) {
|
|
90
|
+
return REGISTERED_PHASES.has(phase);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Per-turn admission counters, keyed by phase. Cleared lazily when a call
|
|
94
|
+
* observes a turn index different from the one the counters were built for,
|
|
95
|
+
* and eagerly by `resetBoundedTelemetry` at `session_start` (a new session
|
|
96
|
+
* restarts turn numbering at 0, so the lazy check alone could carry a stale
|
|
97
|
+
* count across a session boundary that happened to land on the same index).
|
|
98
|
+
*/
|
|
99
|
+
let turnCounts = new Map();
|
|
100
|
+
let countedTurnIndex;
|
|
101
|
+
/**
|
|
102
|
+
* Decide whether this occurrence earns a detailed record, and count it in the
|
|
103
|
+
* ledger either way. Returns `true` when the caller should write the record.
|
|
104
|
+
*
|
|
105
|
+
* Split out from `emitBounded` for the #1705 shape: that site loops over N
|
|
106
|
+
* abandoned records, admits each one, and then writes ONE batched
|
|
107
|
+
* `logLatency` naming only the admitted ones. A helper that could only emit
|
|
108
|
+
* per-call would have turned one record into N.
|
|
109
|
+
*/
|
|
110
|
+
export function admitBounded(phase, identity, options = {}) {
|
|
111
|
+
try {
|
|
112
|
+
let isRisingEdge = true;
|
|
113
|
+
if (options.ledgerKind !== undefined) {
|
|
114
|
+
// `subject` is the identity, unmodified: the ledger's per-subject
|
|
115
|
+
// entry is what tells a reader WHICH file or method is stuck after
|
|
116
|
+
// the detailed records stop.
|
|
117
|
+
isRisingEdge = incrementDegradationCount({
|
|
118
|
+
kind: options.ledgerKind,
|
|
119
|
+
subject: identity,
|
|
120
|
+
reason: options.reason ?? phase,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
if (options.risingEdgePer !== undefined && !isRisingEdge)
|
|
124
|
+
return false;
|
|
125
|
+
return admitAgainstTurnCap(phase, options.capPerTurn);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// Telemetry must never break the observed path. Emitting is the safer
|
|
129
|
+
// failure: an extra record is noise, a swallowed one is a blind spot.
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Count every occurrence in the ledger, then write ONE bounded `latency.log`
|
|
135
|
+
* record if this occurrence is admitted. Returns whether the record was
|
|
136
|
+
* written, so a caller can branch on it (all four migrated sites either
|
|
137
|
+
* ignore it or, in #1705's case, use `admitBounded` directly).
|
|
138
|
+
*/
|
|
139
|
+
export function emitBounded(phase, identity, payload, options = {}) {
|
|
140
|
+
try {
|
|
141
|
+
if (!admitBounded(phase, identity, options))
|
|
142
|
+
return false;
|
|
143
|
+
logLatency({
|
|
144
|
+
...payload,
|
|
145
|
+
type: payload.type ?? "phase",
|
|
146
|
+
phase,
|
|
147
|
+
// `filePath` is what existing latency.log queries filter on, so a site
|
|
148
|
+
// may keep a coarser one; `metadata.identity` below always carries the
|
|
149
|
+
// full discriminating identity. Aggregation that loses WHICH subject
|
|
150
|
+
// is stuck is the failure AGENTS.md names.
|
|
151
|
+
filePath: payload.filePath ?? identity,
|
|
152
|
+
metadata: { ...payload.metadata, identity },
|
|
153
|
+
});
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// Telemetry must never break the observed path.
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Apply the per-turn ceiling. Separated so the turn-rollover clear happens in
|
|
163
|
+
* exactly one place: a cap and its reset drifting apart is how #1733's
|
|
164
|
+
* compensating-pair guards got past review.
|
|
165
|
+
*/
|
|
166
|
+
function admitAgainstTurnCap(phase, cap) {
|
|
167
|
+
if (cap === undefined)
|
|
168
|
+
return true;
|
|
169
|
+
if (countedTurnIndex !== cap.turnIndex) {
|
|
170
|
+
turnCounts = new Map();
|
|
171
|
+
countedTurnIndex = cap.turnIndex;
|
|
172
|
+
}
|
|
173
|
+
const used = turnCounts.get(phase) ?? 0;
|
|
174
|
+
if (used >= cap.limit)
|
|
175
|
+
return false;
|
|
176
|
+
turnCounts.set(phase, used + 1);
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Session-boundary/test reset. The rising-edge state is NOT here — it lives
|
|
181
|
+
* in the degradation ledger, which `handleSessionStart` already re-arms via
|
|
182
|
+
* `resetDegradationLedger`. Only the per-turn counters are this module's own,
|
|
183
|
+
* and they need this because turn numbering restarts at 0 each session.
|
|
184
|
+
*/
|
|
185
|
+
export function resetBoundedTelemetry() {
|
|
186
|
+
turnCounts = new Map();
|
|
187
|
+
countedTurnIndex = undefined;
|
|
188
|
+
}
|
|
189
|
+
/** Test-only: the live per-turn admission count for a phase. */
|
|
190
|
+
export function _boundedTurnCountForTest(phase) {
|
|
191
|
+
return turnCounts.get(phase) ?? 0;
|
|
192
|
+
}
|
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Persistent NDJSON trace of `pi.events` bus publish attempts
|
|
3
3
|
* (`pilens:files:touched` #482 / `pilens:diagnostics` #502 /
|
|
4
|
-
* `pilens:
|
|
4
|
+
* `pilens:diagnostic:disposition` / `pilens:format:queued` +
|
|
5
|
+
* `pilens:format:start` + `pilens:autofix:start` #673/#684 /
|
|
6
|
+
* `pi-lens/analysis-complete` + `pi-lens/findings` + `pi-lens/turn-findings`
|
|
7
|
+
* #1415) — nine event names across five producers.
|
|
5
8
|
*
|
|
6
|
-
* All
|
|
7
|
-
* clients/format-events-publish.ts
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* All five producers (clients/bus-publish.ts, clients/diagnostics-publish.ts,
|
|
10
|
+
* clients/disposition-publish.ts, clients/format-events-publish.ts,
|
|
11
|
+
* clients/lens-events.ts) are fire-and-forget: on failure or on a structural
|
|
12
|
+
* no-op (never wired, kill switch off) the four #482/#502/#673/#684
|
|
13
|
+
* producers only invoke an optional `dbg` callback, which varies by host and
|
|
14
|
+
* is a documented no-op in the MCP host (clients/mcp/session.ts's
|
|
15
|
+
* `dbg: noop`); `lens-events.ts` (#1415) has no `dbg` param at all — its
|
|
16
|
+
* events are purely observational inter-extension telemetry, so this NDJSON
|
|
17
|
+
* trace is its ONLY failure-visible surface. Either way, that leaves
|
|
11
18
|
* bus-publish outcomes invisible in exactly the context where they matter
|
|
12
19
|
* most — same failure shape as the #544 MCP session_start incident this repo
|
|
13
20
|
* already fixed once.
|
|
@@ -26,6 +33,9 @@
|
|
|
26
33
|
* spam one identical line per write batch for an entire session with zero
|
|
27
34
|
* new information after the first. Both are gated log-once-per-process, the
|
|
28
35
|
* same `hasLoggedFailure` shape the producers already use for emit_failed.
|
|
36
|
+
* `skipped_stale_session` is an info-level per-occurrence outcome: the emit
|
|
37
|
+
* seam intentionally declined a target whose associated ctx was confirmed
|
|
38
|
+
* stale, so it is neither a bus failure nor input for the smells rollup.
|
|
29
39
|
* The empty-batch branch (`paths.length === 0` / `files.length === 0`) is
|
|
30
40
|
* NOT logged at all: every call site already guards against invoking these
|
|
31
41
|
* functions with nothing to report (see clients/pipeline.ts,
|
|
@@ -37,17 +47,63 @@ import { isTestMode } from "./env-utils.js";
|
|
|
37
47
|
import { getGlobalPiLensDir } from "./file-utils.js";
|
|
38
48
|
import { createNdjsonLogger } from "./ndjson-logger.js";
|
|
39
49
|
import { getMaxLogSizeMB } from "./log-cleanup.js";
|
|
50
|
+
import { logLatency } from "./latency-logger.js";
|
|
40
51
|
const BUS_EVENTS_LOG_FILE = path.join(getGlobalPiLensDir(), "bus-events.log");
|
|
41
52
|
const writer = createNdjsonLogger({
|
|
42
53
|
filePath: BUS_EVENTS_LOG_FILE,
|
|
43
54
|
maxBytes: getMaxLogSizeMB() * 1024 * 1024,
|
|
44
55
|
});
|
|
56
|
+
const eventRollupCounts = new Map();
|
|
57
|
+
function bumpRollupCount(event, outcome) {
|
|
58
|
+
const existing = eventRollupCounts.get(event) ?? {
|
|
59
|
+
emitted: 0,
|
|
60
|
+
skipped_stale_session: 0,
|
|
61
|
+
emit_failed: 0,
|
|
62
|
+
};
|
|
63
|
+
existing[outcome] += 1;
|
|
64
|
+
eventRollupCounts.set(event, existing);
|
|
65
|
+
}
|
|
45
66
|
export function logBusEvent(entry) {
|
|
67
|
+
if (entry.outcome === "emitted" ||
|
|
68
|
+
entry.outcome === "skipped_stale_session" ||
|
|
69
|
+
entry.outcome === "emit_failed") {
|
|
70
|
+
bumpRollupCount(entry.event, entry.outcome);
|
|
71
|
+
}
|
|
46
72
|
if (isTestMode()) {
|
|
47
73
|
return;
|
|
48
74
|
}
|
|
49
75
|
writer.log({ ts: new Date().toISOString(), ...entry });
|
|
50
76
|
}
|
|
77
|
+
/** Snapshot the current session's rollup, keyed by event name. Non-mutating —
|
|
78
|
+
* pair with {@link resetBusEventRollupCounts} at session end. */
|
|
79
|
+
export function getBusEventRollupCounts() {
|
|
80
|
+
return Object.fromEntries(eventRollupCounts);
|
|
81
|
+
}
|
|
82
|
+
/** Clear the rollup — call once the session-end snapshot has been logged
|
|
83
|
+
* (or from tests) so a new session starts from zero. */
|
|
84
|
+
export function resetBusEventRollupCounts() {
|
|
85
|
+
eventRollupCounts.clear();
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Log one `session_end_bus_rollup` latency row per event NAME that had any
|
|
89
|
+
* activity this session, then clear the rollup. Called from index.ts's
|
|
90
|
+
* `session_shutdown` handler (the session-end hook this repo's other
|
|
91
|
+
* teardown work — LSP fleet shutdown, cache-prefix eviction — already runs
|
|
92
|
+
* from). A no-op (logs nothing) when nothing was ever published, matching
|
|
93
|
+
* `formatSmellsSessionStartLine`'s "no noise on an ordinary session" shape.
|
|
94
|
+
*/
|
|
95
|
+
export function emitBusEventRollupAtSessionEnd(cwd) {
|
|
96
|
+
for (const [event, counts] of eventRollupCounts) {
|
|
97
|
+
logLatency({
|
|
98
|
+
type: "phase",
|
|
99
|
+
phase: "session_end_bus_rollup",
|
|
100
|
+
filePath: cwd,
|
|
101
|
+
durationMs: 0,
|
|
102
|
+
metadata: { event, ...counts },
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
resetBusEventRollupCounts();
|
|
106
|
+
}
|
|
51
107
|
export function getBusEventsLogPath() {
|
|
52
108
|
return BUS_EVENTS_LOG_FILE;
|
|
53
109
|
}
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
import { logBusEvent } from "./bus-events-logger.js";
|
|
21
21
|
import { normalizeFilePath } from "./path-utils.js";
|
|
22
22
|
import { appendRecentTouches } from "./recent-touches.js";
|
|
23
|
-
import { createLiveBusEmitter, recordStaleBusFailure, } from "./live-bus-emitter.js";
|
|
23
|
+
import { createLiveBusEmitter, recordStaleBusFailure, resolveLiveBusEmitter, } from "./live-bus-emitter.js";
|
|
24
24
|
export const BUS_FILES_TOUCHED_EVENT = "pilens:files:touched";
|
|
25
25
|
export const BUS_FILES_TOUCHED_VERSION = 1;
|
|
26
26
|
const liveEmitter = createLiveBusEmitter();
|
|
@@ -106,8 +106,14 @@ export function publishFilesTouched(args) {
|
|
|
106
106
|
}).catch((err) => {
|
|
107
107
|
args.dbg?.(`bus-publish: recent-touches append failed: ${err}`);
|
|
108
108
|
});
|
|
109
|
-
const
|
|
110
|
-
|
|
109
|
+
const resolution = resolveLiveBusEmitter(liveEmitter, () => ({
|
|
110
|
+
event: BUS_FILES_TOUCHED_EVENT,
|
|
111
|
+
cwd: normalizeFilePath(args.cwd),
|
|
112
|
+
reason: args.reason,
|
|
113
|
+
}));
|
|
114
|
+
if (resolution.outcome === "stale-session")
|
|
115
|
+
return;
|
|
116
|
+
if (resolution.outcome === "unwired") {
|
|
111
117
|
if (!hasLoggedUnwired) {
|
|
112
118
|
hasLoggedUnwired = true;
|
|
113
119
|
logBusEvent({
|
|
@@ -119,6 +125,7 @@ export function publishFilesTouched(args) {
|
|
|
119
125
|
}
|
|
120
126
|
return;
|
|
121
127
|
}
|
|
128
|
+
const busEmit = resolution.emit;
|
|
122
129
|
try {
|
|
123
130
|
const payload = {
|
|
124
131
|
v: BUS_FILES_TOUCHED_VERSION,
|
|
@@ -150,6 +157,7 @@ export function publishFilesTouched(args) {
|
|
|
150
157
|
cwd: normalizeFilePath(args.cwd),
|
|
151
158
|
reason: args.reason,
|
|
152
159
|
error: String(err),
|
|
160
|
+
ctxSource: resolution.ctxSource,
|
|
153
161
|
});
|
|
154
162
|
if (!hasLoggedFailure) {
|
|
155
163
|
hasLoggedFailure = true;
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cascade neighbour-budget sizing (#1462).
|
|
3
|
+
*
|
|
4
|
+
* The flat neighbour cap (40) and the turn-end settle wait (5000 ms) were set in
|
|
5
|
+
* two different files with nothing relating them. Dogfood 2026-08-15 measured
|
|
6
|
+
* what that costs: n=50 cascades, median 30 ms, max 3.88 s at nbr=40 — and with
|
|
7
|
+
* a `reverse_deps_cache` refresh ahead of the graph build (2046 ms for
|
|
8
|
+
* `logger.ts`) the run overran the turn_end cap, so `cascade_settle_wait`
|
|
9
|
+
* returned `settled: 0` and the finding arrived a turn late.
|
|
10
|
+
*
|
|
11
|
+
* ## What a slow run actually costs
|
|
12
|
+
*
|
|
13
|
+
* It is NOT lost. `RuntimeCoordinator.settleCascadeRuns` re-parks a promise
|
|
14
|
+
* still in flight at the cap, `agent_settled` gives it a second and more
|
|
15
|
+
* generous drain (`cascade_carry_over_settle`, `quietWindowWaitMs`, 15 000 ms),
|
|
16
|
+
* and `beginTurn` deliberately keeps `_pendingCascadeRuns` so anything still
|
|
17
|
+
* running surfaces at a later turn_end — that is #1443, and pre-#450 these
|
|
18
|
+
* findings were always awaited. So the real trade is:
|
|
19
|
+
*
|
|
20
|
+
* full neighbour set, one turn late vs. a narrowed set, on time,
|
|
21
|
+
* with the remainder lost for good
|
|
22
|
+
*
|
|
23
|
+
* Narrowing is therefore only worth paying for when it genuinely converts a
|
|
24
|
+
* late run into an on-time one. That gives three zones, and the derivation
|
|
25
|
+
* below is exactly those three:
|
|
26
|
+
*
|
|
27
|
+
* fits the full walk still fits the on-time window — nothing to do
|
|
28
|
+
* narrowed the RESCUE BAND: the full walk no longer fits but a shorter
|
|
29
|
+
* one does, so trading the tail for on-time delivery buys
|
|
30
|
+
* something real
|
|
31
|
+
* past-rescue not even a floor-sized walk fits any more, so narrowing would
|
|
32
|
+
* drop neighbours permanently AND still miss the window. Keep
|
|
33
|
+
* the full budget and let the carry-over deliver it complete.
|
|
34
|
+
* This is the cold-session case: a fresh graph build measures up
|
|
35
|
+
* to ~19 s (see `runtime-coordinator.ts`'s carry-over note), and
|
|
36
|
+
* a floor-sized stub there is strictly worse than pre-#1462.
|
|
37
|
+
*
|
|
38
|
+
* One more guard, for the same reason. If the on-time window could not fit a
|
|
39
|
+
* full walk even at zero prelude, there is no LATE run to rescue — every
|
|
40
|
+
* cascade would be permanently downsized because a *wait* was configured small.
|
|
41
|
+
* That is a budget change wearing a timeout's clothes, so the derivation stands
|
|
42
|
+
* down entirely (`no-rescue-window`) and the flat cap applies. This is what
|
|
43
|
+
* keeps `PI_LENS_CASCADE_SETTLE_WAIT_MS` from doubling as a neighbour-count
|
|
44
|
+
* knob: at the 5000/100/40 defaults the band is a prelude of 1.0–4.5 s, and
|
|
45
|
+
* shrinking the wait to 1000 ms disables narrowing rather than capping every
|
|
46
|
+
* cascade at ~10 neighbours.
|
|
47
|
+
*/
|
|
48
|
+
import { recordDegradationOnce } from "./degradation-ledger.js";
|
|
49
|
+
import { toPositiveFinite } from "./env-utils.js";
|
|
50
|
+
// The knobs only, never the scheduler — this module is arithmetic and has no
|
|
51
|
+
// business depending on a task registry to read two numbers (#1462 review N4).
|
|
52
|
+
// Note it buys nothing at load time: `runtime-config.js` below already reaches
|
|
53
|
+
// `pidusage` via `lens-config.js`.
|
|
54
|
+
import { isQuietWindowEnabled, quietWindowWaitMs, } from "./quiet-window-config.js";
|
|
55
|
+
import { RUNTIME_CONFIG } from "./runtime-config.js";
|
|
56
|
+
/**
|
|
57
|
+
* Bounded wait for the turn's deferred cascade computes (#450) to settle before
|
|
58
|
+
* they are merged at turn_end. A late compute is carried over, never dropped.
|
|
59
|
+
*
|
|
60
|
+
* `0` is a meaningful setting (never block turn_end), so this deliberately does
|
|
61
|
+
* NOT use `lazyEnvNumber` — that helper treats 0 as "unset" and would silently
|
|
62
|
+
* restore the 5000 ms default.
|
|
63
|
+
*/
|
|
64
|
+
export function cascadeSettleWaitMs() {
|
|
65
|
+
const raw = Number(process.env.PI_LENS_CASCADE_SETTLE_WAIT_MS);
|
|
66
|
+
return Number.isFinite(raw) && raw >= 0 ? raw : 5000;
|
|
67
|
+
}
|
|
68
|
+
/** The budget floor, and the minimum any env override of the cap can produce. */
|
|
69
|
+
const MIN_NEIGHBOUR_BUDGET = RUNTIME_CONFIG.pipeline.cascadeMaxFiles;
|
|
70
|
+
/**
|
|
71
|
+
* The flat per-run cap: the most neighbours a cascade walks. Still the ceiling
|
|
72
|
+
* — the derivation below only ever narrows it, and only inside the rescue band.
|
|
73
|
+
*/
|
|
74
|
+
const DEFAULT_NEIGHBOUR_BUDGET = 40;
|
|
75
|
+
export const CASCADE_NEIGHBOUR_BUDGET = Math.max(MIN_NEIGHBOUR_BUDGET, Number.parseInt(process.env.PI_LENS_CASCADE_NEIGHBOUR_BUDGET ?? "40", 10) ||
|
|
76
|
+
DEFAULT_NEIGHBOUR_BUDGET);
|
|
77
|
+
/**
|
|
78
|
+
* True when `PI_LENS_CASCADE_NEIGHBOUR_BUDGET` moved the ceiling away from the
|
|
79
|
+
* default (#1462 review F-E). Raising it far enough can push
|
|
80
|
+
* `ceiling * perNeighbourMs` past the settle window, so EVERY cascade reads
|
|
81
|
+
* `no-rescue-window` — the whole derivation disarmed by a cap change nobody
|
|
82
|
+
* meant as a timeout change. Read once at load, same lifetime as the constant
|
|
83
|
+
* it describes.
|
|
84
|
+
*/
|
|
85
|
+
const NEIGHBOUR_BUDGET_OVERRIDDEN = process.env.PI_LENS_CASCADE_NEIGHBOUR_BUDGET !== undefined &&
|
|
86
|
+
CASCADE_NEIGHBOUR_BUDGET !== DEFAULT_NEIGHBOUR_BUDGET;
|
|
87
|
+
/**
|
|
88
|
+
* Marginal wall-clock cost of one more neighbour in the walk, in ms. Calibrated
|
|
89
|
+
* on the 2026-08-15 dogfood tail: 3.88 s at nbr=40 and 3.71 s at nbr=38, both
|
|
90
|
+
* ~97 ms per neighbour. Touches fan out in parallel, so this is the observed
|
|
91
|
+
* marginal cost under LSP contention, not one touch's own latency.
|
|
92
|
+
*
|
|
93
|
+
* It is an ESTIMATE, and the derivation is sensitive to it: if the true cost is
|
|
94
|
+
* materially above this, a walk sized against it still overruns (the rescue
|
|
95
|
+
* fails and the tail was dropped for nothing). `scripts/bench-cascade-budget.mjs
|
|
96
|
+
* --true-cost-ms=<n>` measures that sensitivity directly. Rounded UP from the
|
|
97
|
+
* measurement so the estimate errs toward narrowing less.
|
|
98
|
+
*/
|
|
99
|
+
const DEFAULT_NEIGHBOUR_COST_MS = 100;
|
|
100
|
+
/** Read per call — sized once per cascade run, never on a hot path. */
|
|
101
|
+
function neighbourCostMs() {
|
|
102
|
+
return (toPositiveFinite(process.env.PI_LENS_CASCADE_NEIGHBOUR_COST_MS) ||
|
|
103
|
+
DEFAULT_NEIGHBOUR_COST_MS);
|
|
104
|
+
}
|
|
105
|
+
function neighbourFloor() {
|
|
106
|
+
return (toPositiveFinite(process.env.PI_LENS_CASCADE_NEIGHBOUR_FLOOR) ||
|
|
107
|
+
MIN_NEIGHBOUR_BUDGET);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Size one run's neighbour walk against the on-time window supplied by its
|
|
111
|
+
* caller.
|
|
112
|
+
*
|
|
113
|
+
* `elapsedMs` is arithmetic input, not an observation made by this function.
|
|
114
|
+
* The production caller measures it against the active turn_end settle window
|
|
115
|
+
* and passes that elapsed time here. When that boundary is unavailable, the
|
|
116
|
+
* caller passes zero to preserve the flat cap. Legacy callers may still pass
|
|
117
|
+
* deferred run age for compatibility.
|
|
118
|
+
*
|
|
119
|
+
* Every override exists so the derivation can be tested and benchmarked as a
|
|
120
|
+
* pure function; the function itself does not observe a deferred run's age.
|
|
121
|
+
*/
|
|
122
|
+
export function deriveCascadeNeighbourBudget(options) {
|
|
123
|
+
const ceiling = options.ceiling ?? CASCADE_NEIGHBOUR_BUDGET;
|
|
124
|
+
// The floor can never exceed the ceiling — a floor above the cap would make
|
|
125
|
+
// the derived budget LARGER than the flat one it exists to bound.
|
|
126
|
+
const floor = Math.min(ceiling, options.floor ?? neighbourFloor());
|
|
127
|
+
const onTimeMs = options.settleWaitMs ?? cascadeSettleWaitMs();
|
|
128
|
+
const perNeighbourMs = options.perNeighbourMs ?? neighbourCostMs();
|
|
129
|
+
const elapsedMs = toPositiveFinite(options.elapsedMs);
|
|
130
|
+
const remainingMs = onTimeMs - elapsedMs;
|
|
131
|
+
const deliveryWindowMs = onTimeMs +
|
|
132
|
+
(options.quietDrainMs ??
|
|
133
|
+
(isQuietWindowEnabled() ? quietWindowWaitMs() : 0));
|
|
134
|
+
const full = { budget: ceiling, ceiling, remainingMs, deliveryWindowMs };
|
|
135
|
+
// No on-time window at all, or one too small to ever fit a full walk. There
|
|
136
|
+
// is no late run to rescue here, only every run to shrink — stand down.
|
|
137
|
+
//
|
|
138
|
+
// Boundary, acknowledged rather than special-cased: at exactly
|
|
139
|
+
// `onTimeMs === ceiling * perNeighbourMs` this does not fire and the `fits`
|
|
140
|
+
// zone has zero width, so ANY prelude narrows. Not slightly, either — at
|
|
141
|
+
// 4000/100/40 a 2 s prelude gives 20 and a 3.5 s prelude gives the floor.
|
|
142
|
+
// Left alone because the narrowing is honest at that setting: with the
|
|
143
|
+
// window sized to exactly one full walk, a run that has spent any of it
|
|
144
|
+
// really would miss, so every one of those cuts is a genuine rescue rather
|
|
145
|
+
// than the `no-rescue-window` case this guard exists to catch. What it does
|
|
146
|
+
// cost is headroom — see the divisor bands in the bench's `--sweep` text.
|
|
147
|
+
if (onTimeMs <= 0 || onTimeMs < ceiling * perNeighbourMs) {
|
|
148
|
+
// #1462 review F-E: name the override, once per session, when it is the
|
|
149
|
+
// REASON the derivation stood down — not when a caller under test asked
|
|
150
|
+
// for a specific ceiling, and not on every one of the cascades this
|
|
151
|
+
// disarms for the rest of the session.
|
|
152
|
+
if (options.ceiling === undefined && NEIGHBOUR_BUDGET_OVERRIDDEN) {
|
|
153
|
+
recordDegradationOnce({
|
|
154
|
+
kind: "cascade-budget-override-disarmed",
|
|
155
|
+
subject: "cascade-neighbour-budget",
|
|
156
|
+
reason: `PI_LENS_CASCADE_NEIGHBOUR_BUDGET=${CASCADE_NEIGHBOUR_BUDGET} needs ` +
|
|
157
|
+
`${ceiling * perNeighbourMs}ms to walk the full cap, which exceeds ` +
|
|
158
|
+
`the ${onTimeMs}ms settle window — the rescue band is disarmed and ` +
|
|
159
|
+
`every cascade keeps the flat (overridden) cap`,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
return { ...full, zone: "no-rescue-window" };
|
|
163
|
+
}
|
|
164
|
+
// The full walk still fits. Nothing to buy.
|
|
165
|
+
if (remainingMs >= ceiling * perNeighbourMs) {
|
|
166
|
+
return { ...full, zone: "fits" };
|
|
167
|
+
}
|
|
168
|
+
// Past rescue: not even a floor-sized walk fits, so narrowing would drop the
|
|
169
|
+
// tail permanently and still miss the window. The carry-over delivers the
|
|
170
|
+
// whole set one turn later instead.
|
|
171
|
+
//
|
|
172
|
+
// #1462 review F-B, kept rather than shrunk: one dogfood log measured this
|
|
173
|
+
// zone at 0/1090 cascades, and asked whether a 3-zone module earns its keep
|
|
174
|
+
// at that frequency. It was measured against the WRITE-relative clock this
|
|
175
|
+
// same round fixed, which charges elapsed time no cascade actually spent
|
|
176
|
+
// waiting — so it under-counts how often a genuinely slow compute (a cold
|
|
177
|
+
// graph build, ~19 s) overlaps an already-running settle window, which is
|
|
178
|
+
// exactly what this zone exists to catch (F3, a real regression the first
|
|
179
|
+
// review round found: a floor-sized stub on that cold-start case is
|
|
180
|
+
// strictly worse than pre-#1462). Reachability under the corrected clock
|
|
181
|
+
// has not been re-measured; shrinking on a stale sample would trade a
|
|
182
|
+
// cheap, already-tested guard against a proven regression for a paper
|
|
183
|
+
// simplification. Revisit if a post-fix dogfood window confirms it still
|
|
184
|
+
// never fires.
|
|
185
|
+
if (remainingMs < floor * perNeighbourMs) {
|
|
186
|
+
return { ...full, zone: "past-rescue" };
|
|
187
|
+
}
|
|
188
|
+
// The rescue band: a shorter walk lands on time.
|
|
189
|
+
return {
|
|
190
|
+
budget: Math.min(ceiling, Math.max(floor, Math.floor(remainingMs / perNeighbourMs))),
|
|
191
|
+
ceiling,
|
|
192
|
+
remainingMs,
|
|
193
|
+
deliveryWindowMs,
|
|
194
|
+
zone: "narrowed",
|
|
195
|
+
};
|
|
196
|
+
}
|
|
@@ -1,10 +1,24 @@
|
|
|
1
|
+
import { convertLspDiagnostics } from "./dispatch/utils/lsp-diagnostics.js";
|
|
1
2
|
import { toRunnerDisplayPath } from "./dispatch/runner-context.js";
|
|
2
3
|
export function formatCascadeNeighborDiagnostics(cwd, neighbors, options = {}) {
|
|
3
4
|
const withErrors = neighbors.filter((n) => n.diagnostics.length > 0);
|
|
4
|
-
|
|
5
|
+
const inconclusive = neighbors.filter((n) => n.inconclusive === true && n.diagnostics.length === 0);
|
|
6
|
+
// #1459: a neighbour whose scanner never looked at it is not a clean leaf. It
|
|
7
|
+
// is also not `inconclusive` — the language server answered — so it gets its
|
|
8
|
+
// own honest line instead of being folded into either bucket. Only the
|
|
9
|
+
// zero-diagnostic case needs saying: a neighbour with findings already renders.
|
|
10
|
+
const uncovered = neighbors.filter((n) => n.diagnostics.length === 0 &&
|
|
11
|
+
n.inconclusive !== true &&
|
|
12
|
+
(n.unconfirmedServerIds?.length ?? 0) > 0);
|
|
13
|
+
if (withErrors.length === 0 &&
|
|
14
|
+
inconclusive.length === 0 &&
|
|
15
|
+
uncovered.length === 0) {
|
|
5
16
|
return "";
|
|
17
|
+
}
|
|
6
18
|
const noun = options.noun ?? "neighbor";
|
|
7
|
-
let out =
|
|
19
|
+
let out = withErrors.length > 0
|
|
20
|
+
? `📐 Cascade errors in ${withErrors.length} ${noun} file(s) — fix before finishing turn:`
|
|
21
|
+
: "";
|
|
8
22
|
for (const neighbor of withErrors) {
|
|
9
23
|
const display = toRunnerDisplayPath(cwd, neighbor.filePath);
|
|
10
24
|
const reason = options.includeReason ? ` reason="${neighbor.reason}"` : "";
|
|
@@ -17,5 +31,65 @@ export function formatCascadeNeighborDiagnostics(cwd, neighbors, options = {}) {
|
|
|
17
31
|
}
|
|
18
32
|
out += "\n</diagnostics>";
|
|
19
33
|
}
|
|
34
|
+
if (inconclusive.length > 0) {
|
|
35
|
+
if (out)
|
|
36
|
+
out += "\n";
|
|
37
|
+
out += `⚠️ Cascade diagnostics inconclusive for ${inconclusive.length} ${noun} file(s) — no clean result was confirmed:`;
|
|
38
|
+
for (const neighbor of inconclusive) {
|
|
39
|
+
out += `\n ${toRunnerDisplayPath(cwd, neighbor.filePath)}`;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (uncovered.length > 0) {
|
|
43
|
+
if (out)
|
|
44
|
+
out += "\n";
|
|
45
|
+
out += `⚠️ Cascade scanners did not cover ${uncovered.length} ${noun} file(s) — no findings does NOT mean clean here:`;
|
|
46
|
+
for (const neighbor of uncovered) {
|
|
47
|
+
const servers = (neighbor.unconfirmedServerIds ?? []).join(", ");
|
|
48
|
+
out += `\n ${toRunnerDisplayPath(cwd, neighbor.filePath)} (not scanned by ${servers})`;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
20
51
|
return out;
|
|
21
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Build the turn-end `CascadeRun` for a neighbour whose diagnostics landed only
|
|
55
|
+
* AFTER its cascade touch skipped the in-lane wait (#1023's `resolved-found`
|
|
56
|
+
* quiet-window outcome; #1444 made native TS7 take that same path). Returns
|
|
57
|
+
* `undefined` when there is nothing agent-facing to say — no ERROR-severity
|
|
58
|
+
* diagnostics, or nothing the formatter renders.
|
|
59
|
+
*
|
|
60
|
+
* Lives here rather than inline in the quiet-window callback so the delivery
|
|
61
|
+
* path (reconcile → run → turn_end) is testable end to end; index.ts only wires
|
|
62
|
+
* it to `runtime.appendCascadeRun`.
|
|
63
|
+
*/
|
|
64
|
+
export function buildResolvedFoundCascadeRun(cwd, neighbor) {
|
|
65
|
+
const { filePath } = neighbor;
|
|
66
|
+
const diagnostics = convertLspDiagnostics(neighbor.diagnostics.filter((d) => d.severity === 1), filePath, { tool: "lsp" });
|
|
67
|
+
if (diagnostics.length === 0)
|
|
68
|
+
return undefined;
|
|
69
|
+
const neighbors = [
|
|
70
|
+
{ filePath, reason: "references", diagnostics, lspTouched: true },
|
|
71
|
+
];
|
|
72
|
+
const formatted = formatCascadeNeighborDiagnostics(cwd, neighbors, {
|
|
73
|
+
noun: "cold neighbor",
|
|
74
|
+
});
|
|
75
|
+
if (!formatted)
|
|
76
|
+
return undefined;
|
|
77
|
+
return {
|
|
78
|
+
filePath,
|
|
79
|
+
result: {
|
|
80
|
+
filePath,
|
|
81
|
+
impact: {
|
|
82
|
+
filePath,
|
|
83
|
+
changedSymbols: [],
|
|
84
|
+
directImporters: [],
|
|
85
|
+
directCallers: [],
|
|
86
|
+
neighborFiles: [filePath],
|
|
87
|
+
riskFlags: [],
|
|
88
|
+
},
|
|
89
|
+
neighbors,
|
|
90
|
+
formatted,
|
|
91
|
+
},
|
|
92
|
+
neighborCount: 1,
|
|
93
|
+
diagnosticCount: diagnostics.length,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -1 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* How long a cascade neighbour's diagnostics stay usable (#1816).
|
|
3
|
+
*
|
|
4
|
+
* One constant for two consumers that each declared their own `240_000`:
|
|
5
|
+
* `clients/lsp/index.ts` (the neighbour-diagnostics cache) and
|
|
6
|
+
* `clients/dispatch/integration.ts` (the cascade turn scope). They must move
|
|
7
|
+
* together — a split pair reads as two independent policies and drifts.
|
|
8
|
+
*/
|
|
9
|
+
export const CASCADE_DIAGNOSTICS_TTL_MS = 240_000;
|