@herbertgao/pi-extensions 2026.8.6 → 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 +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +1 -1
- 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/renderer/compact-mode.ts +94 -77
- 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 +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 +1 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +26 -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 +2 -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 +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-lens/CHANGELOG.md +909 -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 +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 +285 -8
- 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 +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/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 +62 -13
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +25 -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/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 +60 -53
- 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 +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 +72 -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 +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/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 +294 -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 +82 -6
- 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 +2 -0
- 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 +102 -7
- package/node_modules/pi-lens/dist/clients/lsp/client.js +1516 -159
- 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 +2274 -147
- 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 +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 +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 +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 +52 -4
- 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 +308 -45
- package/node_modules/pi-lens/dist/clients/runtime-session.js +332 -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 +575 -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/sg-runner.js +116 -22
- package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
- package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
- 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-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 +67738 -55739
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +466 -54
- 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 +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 +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/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 +23 -0
- package/node_modules/pi-web-access/README.md +27 -9
- 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/curator-page.ts +6 -3
- package/node_modules/pi-web-access/curator-server.ts +4 -1
- package/node_modules/pi-web-access/exa.ts +18 -7
- package/node_modules/pi-web-access/extract.ts +28 -3
- package/node_modules/pi-web-access/gemini-search.ts +16 -4
- package/node_modules/pi-web-access/index.ts +41 -57
- 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/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 +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 +13 -13
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Past-EOF validity gate for the turn-end inline-blocker store (#1641
|
|
3
|
+
* remainder).
|
|
4
|
+
*
|
|
5
|
+
* #1664 wired `demotePastEofDiagnostics` into every surface that renders a
|
|
6
|
+
* `WidgetDiagnostic` — widget-state, `lens_diagnostics` (`mode=all`/`full`),
|
|
7
|
+
* and the TUI blocking-render loop. It deliberately deferred one surface:
|
|
8
|
+
* `RuntimeCoordinator`'s `_pendingInlineBlockers` map (the "Unresolved from
|
|
9
|
+
* this turn" blocker re-served at `turn_end`, `clients/runtime-turn.ts`).
|
|
10
|
+
* That store's records (`InlineBlockerRecord`) never carried a `WidgetDiagnostic`
|
|
11
|
+
* — just a rendered `summary` string — so the shared gate had nothing to run
|
|
12
|
+
* against. #1633 was meant to add the structured line field and never did;
|
|
13
|
+
* it merged with only the dependency-drift gate (`blocker-freshness.ts`)
|
|
14
|
+
* wired in. This module is that missing half.
|
|
15
|
+
*
|
|
16
|
+
* Design choice (#1641 remainder, decided here): carry a STRUCTURED `lines`
|
|
17
|
+
* field on `InlineBlockerRecord`, captured at write time from the same
|
|
18
|
+
* `Diagnostic[]` the summary text was rendered from
|
|
19
|
+
* (`dispatchResult.blockers` in `pipeline.ts`), rather than regex-parsing the
|
|
20
|
+
* cited line back out of the rendered summary prose. Re-deriving a number
|
|
21
|
+
* that was already in hand one call earlier — by parsing a string built for
|
|
22
|
+
* human display, not machine reading — is exactly the re-derivation-vs-
|
|
23
|
+
* correlation screen's failure shape: any future change to the summary's
|
|
24
|
+
* wording (a different prefix, a renamed tool label) would silently break
|
|
25
|
+
* the parse with no type error. Threading the number through is one field on
|
|
26
|
+
* an existing interface and a few lines at each hop.
|
|
27
|
+
*
|
|
28
|
+
* Composition with #1631's dependency-drift gate (`blocker-freshness.ts`,
|
|
29
|
+
* also swept at `turn_end`): each gate demotes with its own
|
|
30
|
+
* `InlineBlockerRecord.staleReason` and only re-derives (heals) entries
|
|
31
|
+
* carrying its OWN reason — `setInlineBlockerPastEofStale` refuses to touch a
|
|
32
|
+
* `"dependency-drift"` demotion, and this gate skips a record already
|
|
33
|
+
* demoted for that reason before it even computes a verdict. The two gates
|
|
34
|
+
* therefore layer rather than race: whichever ran first "wins" the record for
|
|
35
|
+
* this turn, and the other's condition is picked up again once the first
|
|
36
|
+
* heals or a fresh dispatch/retire clears the record outright.
|
|
37
|
+
*
|
|
38
|
+
* RE-ARMS, never latches (mirrors `demotePastEofDiagnostics`): the verdict is
|
|
39
|
+
* recomputed from the file's CURRENT line count on every turn end, so a
|
|
40
|
+
* transient shrink-then-restore un-demotes on its own next turn without any
|
|
41
|
+
* session-lifetime flag to reset.
|
|
42
|
+
*/
|
|
43
|
+
import { demotePastEofDiagnostics, resyncDocumentOnPastEof, } from "./diagnostic-line-freshness.js";
|
|
44
|
+
/**
|
|
45
|
+
* Past-EOF sweep over the cached inline blockers. Called at turn end
|
|
46
|
+
* alongside (and, per the module doc, ordered before) the dependency-drift
|
|
47
|
+
* sweep — a cheap `statSync` per file is worth paying first so the pricier
|
|
48
|
+
* import-parsing sweep can skip anything this gate already took out of the
|
|
49
|
+
* authoritative channel.
|
|
50
|
+
*
|
|
51
|
+
* Never throws: a per-entry failure leaves that entry untouched rather than
|
|
52
|
+
* failing the turn end.
|
|
53
|
+
*/
|
|
54
|
+
export function sweepInlineBlockerPastEof(runtime, cwd, options) {
|
|
55
|
+
const counts = {
|
|
56
|
+
total: 0,
|
|
57
|
+
checked: 0,
|
|
58
|
+
demoted: 0,
|
|
59
|
+
healed: 0,
|
|
60
|
+
};
|
|
61
|
+
const resync = options?.resync ?? resyncDocumentOnPastEof;
|
|
62
|
+
let entries;
|
|
63
|
+
try {
|
|
64
|
+
entries = runtime.getInlineBlockersSnapshot();
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return counts;
|
|
68
|
+
}
|
|
69
|
+
counts.total = entries.length;
|
|
70
|
+
for (const entry of entries) {
|
|
71
|
+
try {
|
|
72
|
+
if (!entry.lines || entry.lines.length === 0)
|
|
73
|
+
continue;
|
|
74
|
+
// Not this gate's demotion to heal or re-assert (#1641 composition
|
|
75
|
+
// rule — see module doc).
|
|
76
|
+
if (entry.stale && entry.staleReason !== "past-eof")
|
|
77
|
+
continue;
|
|
78
|
+
counts.checked += 1;
|
|
79
|
+
// Reuse the shared gate's per-line verdict machinery: feed it one
|
|
80
|
+
// synthetic "diagnostic" per cited line, sharing this record's
|
|
81
|
+
// current stale/staleReason so it can detect the rising/falling
|
|
82
|
+
// edge itself instead of this module re-implementing that logic.
|
|
83
|
+
const probes = entry.lines.map((line) => ({
|
|
84
|
+
line,
|
|
85
|
+
stale: entry.stale,
|
|
86
|
+
staleReason: entry.staleReason,
|
|
87
|
+
}));
|
|
88
|
+
// `demotePastEofDiagnostics` already logs (`diagnostic_past_eof`) and
|
|
89
|
+
// fires `resync` exactly once per call on a rising edge — reusing it
|
|
90
|
+
// here (rather than re-implementing that bookkeeping) keeps this
|
|
91
|
+
// gate's telemetry and resync-on-demotion behavior identical to the
|
|
92
|
+
// widget/lens surfaces #1664 already wired.
|
|
93
|
+
const { diagnostics } = demotePastEofDiagnostics({
|
|
94
|
+
store: "inline-blocker",
|
|
95
|
+
cwd,
|
|
96
|
+
filePath: entry.filePath,
|
|
97
|
+
diagnostics: probes,
|
|
98
|
+
lineCountCache: options?.lineCountCache,
|
|
99
|
+
resync,
|
|
100
|
+
});
|
|
101
|
+
// The record cites any of its lines being past EOF as a whole-record
|
|
102
|
+
// demotion — one summary string, not one flag per line.
|
|
103
|
+
const isPastEof = diagnostics.some((d) => d.stale);
|
|
104
|
+
const transitioned = runtime.setInlineBlockerPastEofStale(entry.filePath, isPastEof);
|
|
105
|
+
if (!transitioned)
|
|
106
|
+
continue;
|
|
107
|
+
if (isPastEof) {
|
|
108
|
+
counts.demoted += 1;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
counts.healed += 1;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// Per-entry failure: leave the entry as-is.
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return counts;
|
|
119
|
+
}
|
|
@@ -47,6 +47,14 @@ async function logBootstrapFailures(failures) {
|
|
|
47
47
|
// the per-analyzer lines above already named the failures
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Every per-client load below is individually fail-soft (`load`/`loadList`
|
|
52
|
+
* degrade to a stub instead of throwing), so this promise is not expected to
|
|
53
|
+
* reject in practice. It is still memoized with eviction-on-rejection —
|
|
54
|
+
* consistent with the other lazy-import memos (#1570) — so a genuinely
|
|
55
|
+
* unexpected throw (e.g. `logBootstrapFailures`) cannot latch a permanently
|
|
56
|
+
* rejected bootstrap for the rest of the process.
|
|
57
|
+
*/
|
|
50
58
|
export function loadBootstrapClients() {
|
|
51
59
|
bootstrapPromise ??= (async () => {
|
|
52
60
|
const failures = [];
|
|
@@ -112,6 +120,9 @@ export function loadBootstrapClients() {
|
|
|
112
120
|
agentBehaviorClient,
|
|
113
121
|
deadCodeClients,
|
|
114
122
|
};
|
|
115
|
-
})()
|
|
123
|
+
})().catch((err) => {
|
|
124
|
+
bootstrapPromise = null;
|
|
125
|
+
throw err;
|
|
126
|
+
});
|
|
116
127
|
return bootstrapPromise;
|
|
117
128
|
}
|
|
@@ -57,9 +57,18 @@ async function releaseQuarantineLock(lockPath, token) {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
function quarantineOwnerIsStale(owner, staleMs) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
// #1816: the two staleness signals are INDEPENDENT, and the original
|
|
61
|
+
// conjunction made the dead-PID one unreachable. An `owner.json` with a
|
|
62
|
+
// valid PID but a missing or non-numeric `createdAt` (an older writer, a
|
|
63
|
+
// half-written file, a hand-edited one) short-circuited on the
|
|
64
|
+
// `Number.isFinite` guard, so a dead owner never reclaimed and the lock
|
|
65
|
+
// stayed poisoned for the life of the directory. This is
|
|
66
|
+
// `installer/index.ts:173`'s predicate: a dead PID reclaims regardless of
|
|
67
|
+
// `createdAt`, and an aged lock reclaims regardless of what the PID says.
|
|
68
|
+
const pidUsable = Number.isInteger(owner.pid) && owner.pid > 0;
|
|
69
|
+
if (pidUsable && !ownerPidIsLive(owner.pid))
|
|
70
|
+
return true;
|
|
71
|
+
return (Number.isFinite(owner.createdAt) && Date.now() - owner.createdAt > staleMs);
|
|
63
72
|
}
|
|
64
73
|
async function reclaimQuarantineLock(lockPath, staleMs) {
|
|
65
74
|
const quarantined = quarantinePath(lockPath, `reclaim-${Date.now()}-${randomUUID()}`);
|
|
@@ -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
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -3,8 +3,18 @@ import { toRunnerDisplayPath } from "./dispatch/runner-context.js";
|
|
|
3
3
|
export function formatCascadeNeighborDiagnostics(cwd, neighbors, options = {}) {
|
|
4
4
|
const withErrors = neighbors.filter((n) => n.diagnostics.length > 0);
|
|
5
5
|
const inconclusive = neighbors.filter((n) => n.inconclusive === true && n.diagnostics.length === 0);
|
|
6
|
-
|
|
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) {
|
|
7
16
|
return "";
|
|
17
|
+
}
|
|
8
18
|
const noun = options.noun ?? "neighbor";
|
|
9
19
|
let out = withErrors.length > 0
|
|
10
20
|
? `📐 Cascade errors in ${withErrors.length} ${noun} file(s) — fix before finishing turn:`
|
|
@@ -29,6 +39,15 @@ export function formatCascadeNeighborDiagnostics(cwd, neighbors, options = {}) {
|
|
|
29
39
|
out += `\n ${toRunnerDisplayPath(cwd, neighbor.filePath)}`;
|
|
30
40
|
}
|
|
31
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
|
+
}
|
|
32
51
|
return out;
|
|
33
52
|
}
|
|
34
53
|
/**
|
|
@@ -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;
|