@herbertgao/pi-extensions 2026.8.6 → 2026.8.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -6
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +320 -22
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +79 -17
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +27 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +66 -17
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
- package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
- package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
- package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
- package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
- package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +17 -13
- package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
- package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
- package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
- package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
- package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
- package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
- package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
- package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +21 -6
- package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
- package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +20 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +5 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +174 -100
- package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +5 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +10 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +10 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/tool-schema.md +11 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +5 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +140 -97
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +63 -28
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/selectors/projections.ts +22 -15
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/state-reducer.ts +11 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/tool/response-envelope.ts +11 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/tool/types.ts +11 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/multi-select-view.ts +31 -17
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/preview/preview-block-renderer.ts +15 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/wrapping-select.ts +15 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +86 -38
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +76 -9
- package/node_modules/@narumitw/pi-btw/README.md +120 -111
- package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
- package/node_modules/@narumitw/pi-btw/package.json +10 -6
- package/node_modules/@narumitw/pi-btw/src/btw.ts +36 -40
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
- package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
- package/node_modules/@narumitw/pi-btw/src/menu.ts +45 -2
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +44 -1856
- package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
- package/node_modules/@pi-plugins/fast-mode/package.json +4 -6
- package/node_modules/pi-lens/CHANGELOG.md +999 -0
- package/node_modules/pi-lens/README.md +117 -3
- package/node_modules/pi-lens/config/ruff/core.toml +42 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +126 -13
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +67 -11
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
- package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +381 -13
- package/node_modules/pi-lens/dist/clients/biome-client.js +61 -30
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
- package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +129 -0
- package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +206 -0
- package/node_modules/pi-lens/dist/clients/cache-observability.js +353 -76
- package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
- package/node_modules/pi-lens/dist/clients/cascade-format.js +20 -1
- package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
- package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
- package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +123 -31
- package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
- package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +84 -14
- package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +155 -0
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +25 -3
- package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +8 -2
- package/node_modules/pi-lens/dist/clients/deps/typebox.js +8 -3
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +147 -33
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +52 -2
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +256 -88
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +9 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +9 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +8 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +60 -53
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +25 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +20 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +44 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +17 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +32 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +47 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +14 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +127 -28
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +12 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +275 -13
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +21 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +734 -64
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +188 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +9 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +21 -10
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
- package/node_modules/pi-lens/dist/clients/extension-log.js +22 -1
- package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +316 -0
- package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
- package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
- package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
- package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
- package/node_modules/pi-lens/dist/clients/git-guard.js +31 -0
- package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +109 -9
- package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
- package/node_modules/pi-lens/dist/clients/host-ports.js +8 -0
- package/node_modules/pi-lens/dist/clients/installer/index.js +1114 -100
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
- package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +87 -8
- package/node_modules/pi-lens/dist/clients/knip-client.js +310 -10
- package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +9 -2
- package/node_modules/pi-lens/dist/clients/latency-logger.js +440 -11
- package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
- package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +128 -8
- package/node_modules/pi-lens/dist/clients/lsp/client.js +1660 -164
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +83 -1
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
- package/node_modules/pi-lens/dist/clients/lsp/index.js +2423 -151
- package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
- package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
- package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
- package/node_modules/pi-lens/dist/clients/lsp/server.js +277 -16
- package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
- package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +79 -3
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +43 -0
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
- package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
- package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
- package/node_modules/pi-lens/dist/clients/mcp/session.js +10 -2
- package/node_modules/pi-lens/dist/clients/module-report.js +22 -1
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +114 -23
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
- package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
- package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
- package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
- package/node_modules/pi-lens/dist/clients/pipeline.js +154 -33
- package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +84 -14
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +23 -9
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
- package/node_modules/pi-lens/dist/clients/project-report.js +62 -27
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
- package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
- package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +8 -3
- package/node_modules/pi-lens/dist/clients/read-guard.js +253 -10
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
- package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +194 -31
- package/node_modules/pi-lens/dist/clients/review-graph/query.js +52 -4
- package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +21 -0
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +6 -3
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +96 -7
- package/node_modules/pi-lens/dist/clients/runtime-context.js +8 -1
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +352 -45
- package/node_modules/pi-lens/dist/clients/runtime-session.js +373 -9
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +212 -79
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +620 -67
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
- package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
- package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +157 -24
- package/node_modules/pi-lens/dist/clients/session-event-guard.js +184 -0
- package/node_modules/pi-lens/dist/clients/sg-runner.js +116 -22
- package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
- package/node_modules/pi-lens/dist/clients/source-filter.js +6 -2
- package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
- package/node_modules/pi-lens/dist/clients/startup-scan.js +5 -2
- package/node_modules/pi-lens/dist/clients/startup-timing.js +19 -1
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +151 -16
- package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
- package/node_modules/pi-lens/dist/clients/tool-policy.js +125 -20
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +1 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
- package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
- package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
- package/node_modules/pi-lens/dist/index.js +61407 -48577
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +476 -57
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +132 -43
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
- package/node_modules/pi-lens/docs/agent-guide.md +6 -1
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +515 -506
- package/node_modules/pi-lens/docs/dispositions.md +21 -0
- package/node_modules/pi-lens/docs/features.md +19 -5
- package/node_modules/pi-lens/docs/globalconfig.md +3 -0
- package/node_modules/pi-lens/docs/language-coverage.md +71 -5
- package/node_modules/pi-lens/docs/mcp.md +2 -2
- package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
- package/node_modules/pi-lens/docs/settings.md +19 -1
- package/node_modules/pi-lens/docs/usage.md +1 -0
- package/node_modules/pi-lens/package.json +27 -10
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +15 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +2 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +2 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +57 -12
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +22 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +23 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +33 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +34 -8
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +6 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +53 -1
- package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
- package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
- package/node_modules/pi-lens/scripts/install-selftest.mjs +99 -11
- package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +75 -0
- package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +285 -0
- package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +233 -0
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +2 -0
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +80 -3
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +41 -0
- package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +36 -0
- package/node_modules/pi-mcp-adapter/README.md +47 -5
- package/node_modules/pi-mcp-adapter/cli.js +163 -1
- package/node_modules/pi-mcp-adapter/commands.ts +128 -22
- package/node_modules/pi-mcp-adapter/config.ts +25 -7
- package/node_modules/pi-mcp-adapter/direct-tools.ts +5 -4
- package/node_modules/pi-mcp-adapter/index.ts +219 -32
- package/node_modules/pi-mcp-adapter/init.ts +36 -4
- package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
- package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
- package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
- package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
- package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +32 -21
- package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
- package/node_modules/pi-mcp-adapter/package.json +3 -1
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +31 -37
- package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
- package/node_modules/pi-mcp-adapter/server-manager.ts +140 -2
- package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
- package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
- package/node_modules/pi-mcp-adapter/types.ts +2 -0
- package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
- package/node_modules/pi-mcp-adapter/utils.ts +70 -0
- package/node_modules/pi-web-access/CHANGELOG.md +37 -0
- package/node_modules/pi-web-access/README.md +32 -13
- package/node_modules/pi-web-access/anysearch.ts +4 -2
- package/node_modules/pi-web-access/brave.ts +16 -4
- package/node_modules/pi-web-access/chrome-cookies.ts +86 -15
- package/node_modules/pi-web-access/curator-page.ts +6 -3
- package/node_modules/pi-web-access/curator-server.ts +4 -1
- package/node_modules/pi-web-access/data-uri-sanitize.ts +406 -0
- package/node_modules/pi-web-access/exa.ts +18 -7
- package/node_modules/pi-web-access/extract.ts +40 -5
- package/node_modules/pi-web-access/firecrawl.ts +18 -2
- package/node_modules/pi-web-access/gemini-search.ts +16 -4
- package/node_modules/pi-web-access/github-extract.ts +65 -36
- package/node_modules/pi-web-access/index.ts +60 -61
- package/node_modules/pi-web-access/openai-search.ts +17 -6
- package/node_modules/pi-web-access/package.json +1 -1
- package/node_modules/pi-web-access/page-query.ts +4 -2
- package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
- package/node_modules/pi-web-access/parallel.ts +2 -2
- package/node_modules/pi-web-access/pdf-extract.ts +18 -5
- package/node_modules/pi-web-access/query-rewrite.ts +51 -0
- package/node_modules/pi-web-access/serper.ts +211 -0
- package/node_modules/pi-web-access/ssrf-protection.ts +12 -2
- package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
- package/node_modules/pi-web-access/summary-review.ts +44 -22
- package/node_modules/pi-web-access/tavily.ts +16 -4
- package/node_modules/pi-web-access/utils.ts +87 -0
- package/node_modules/pi-web-access/valyu.ts +199 -0
- package/package.json +14 -14
|
@@ -1,11 +1,26 @@
|
|
|
1
|
+
import { SdkError, SdkErrorCode } from "@modelcontextprotocol/client";
|
|
1
2
|
import { isServerDisabled, type ServerDefinition } from "./types.ts";
|
|
2
|
-
import type { McpServerManager } from "./server-manager.ts";
|
|
3
|
+
import type { McpServerManager, ServerConnection } from "./server-manager.ts";
|
|
3
4
|
import { hasPendingAuth } from "./mcp-auth-flow.ts";
|
|
4
5
|
import { logger } from "./logger.ts";
|
|
5
|
-
import { formatTerminalError, sanitizeTerminalText } from "./utils.ts";
|
|
6
|
+
import { formatTerminalError, parallelLimit, sanitizeTerminalText } from "./utils.ts";
|
|
7
|
+
import { isTerminatedSession } from "./session-recovery.ts";
|
|
6
8
|
|
|
7
|
-
export type ReconnectCallback = (serverName: string) => void
|
|
9
|
+
export type ReconnectCallback = (serverName: string) => void | Promise<void>;
|
|
8
10
|
export type ReconnectFailureCallback = (serverName: string, error: unknown) => void;
|
|
11
|
+
export type HealthRestoredCallback = (serverName: string) => void | Promise<void>;
|
|
12
|
+
export type AuthRequiredCallback = (serverName: string) => void | Promise<void>;
|
|
13
|
+
|
|
14
|
+
const KEEP_ALIVE_RETRY_BASE_MS = 30_000;
|
|
15
|
+
const KEEP_ALIVE_RETRY_MAX_MS = 5 * 60_000;
|
|
16
|
+
const KEEP_ALIVE_CHECK_CONCURRENCY = 10;
|
|
17
|
+
|
|
18
|
+
interface RetryState {
|
|
19
|
+
attempts: number;
|
|
20
|
+
nextAttemptAt: number;
|
|
21
|
+
connection: ServerConnection | undefined;
|
|
22
|
+
status: ServerConnection["status"] | undefined;
|
|
23
|
+
}
|
|
9
24
|
|
|
10
25
|
export class McpLifecycleManager {
|
|
11
26
|
private keepAliveServers = new Map<string, ServerDefinition>();
|
|
@@ -15,9 +30,14 @@ export class McpLifecycleManager {
|
|
|
15
30
|
private healthCheckInterval: NodeJS.Timeout | undefined;
|
|
16
31
|
private onReconnect: ReconnectCallback | undefined;
|
|
17
32
|
private onReconnectFailure: ReconnectFailureCallback | undefined;
|
|
33
|
+
private onHealthRestored: HealthRestoredCallback | undefined;
|
|
34
|
+
private onAuthRequired: AuthRequiredCallback | undefined;
|
|
18
35
|
private onIdleShutdown: ((serverName: string) => void) | undefined;
|
|
19
36
|
private activeHealthCheck: Promise<void> | undefined;
|
|
37
|
+
private activeConvergence: Promise<void> | undefined;
|
|
20
38
|
private shutdownPromise: Promise<void> | undefined;
|
|
39
|
+
private retryStates = new Map<string, RetryState>();
|
|
40
|
+
private pendingMetadataPublications = new Set<string>();
|
|
21
41
|
private stopped = false;
|
|
22
42
|
private removeHealthAbortListener: (() => void) | undefined;
|
|
23
43
|
|
|
@@ -34,6 +54,14 @@ export class McpLifecycleManager {
|
|
|
34
54
|
this.onReconnectFailure = callback;
|
|
35
55
|
}
|
|
36
56
|
|
|
57
|
+
setHealthRestoredCallback(callback: HealthRestoredCallback): void {
|
|
58
|
+
this.onHealthRestored = callback;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
setAuthRequiredCallback(callback: AuthRequiredCallback): void {
|
|
62
|
+
this.onAuthRequired = callback;
|
|
63
|
+
}
|
|
64
|
+
|
|
37
65
|
markKeepAlive(name: string, definition: ServerDefinition): void {
|
|
38
66
|
if (isServerDisabled(definition)) return;
|
|
39
67
|
this.keepAliveServers.set(name, definition);
|
|
@@ -45,6 +73,14 @@ export class McpLifecycleManager {
|
|
|
45
73
|
if (settings?.idleTimeout !== undefined) this.serverSettings.set(name, settings);
|
|
46
74
|
}
|
|
47
75
|
|
|
76
|
+
unregisterServer(name: string): void {
|
|
77
|
+
this.allServers.delete(name);
|
|
78
|
+
this.keepAliveServers.delete(name);
|
|
79
|
+
this.serverSettings.delete(name);
|
|
80
|
+
this.retryStates.delete(name);
|
|
81
|
+
this.pendingMetadataPublications.delete(name);
|
|
82
|
+
}
|
|
83
|
+
|
|
48
84
|
setGlobalIdleTimeout(minutes: number): void {
|
|
49
85
|
this.globalIdleTimeout = minutes * 60 * 1000;
|
|
50
86
|
}
|
|
@@ -82,29 +118,23 @@ export class McpLifecycleManager {
|
|
|
82
118
|
this.healthCheckInterval.unref();
|
|
83
119
|
}
|
|
84
120
|
|
|
85
|
-
|
|
121
|
+
async ensureConverged(signal?: AbortSignal): Promise<void> {
|
|
86
122
|
if (this.stopped || signal?.aborted) return;
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
try {
|
|
96
|
-
await this.manager.connect(name, definition, signal);
|
|
97
|
-
if (this.stopped || signal?.aborted) return;
|
|
98
|
-
logger.debug(`Reconnected to ${name}`);
|
|
99
|
-
this.onReconnect?.(name);
|
|
100
|
-
} catch (error) {
|
|
101
|
-
if (this.stopped || signal?.aborted) return;
|
|
102
|
-
this.onReconnectFailure?.(name, error);
|
|
103
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
104
|
-
console.error(`MCP: Failed to reconnect to ${name}: ${sanitizeTerminalText(message)}`);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
123
|
+
if (this.activeConvergence) return this.activeConvergence;
|
|
124
|
+
|
|
125
|
+
const check = this.checkKeepAliveConnections(signal);
|
|
126
|
+
this.activeConvergence = check;
|
|
127
|
+
try {
|
|
128
|
+
await check;
|
|
129
|
+
} finally {
|
|
130
|
+
if (this.activeConvergence === check) this.activeConvergence = undefined;
|
|
107
131
|
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private async checkConnections(signal?: AbortSignal): Promise<void> {
|
|
135
|
+
if (this.stopped || signal?.aborted) return;
|
|
136
|
+
await this.ensureConverged(signal);
|
|
137
|
+
if (this.stopped || signal?.aborted) return;
|
|
108
138
|
|
|
109
139
|
for (const [name] of this.allServers) {
|
|
110
140
|
if (this.keepAliveServers.has(name)) continue;
|
|
@@ -117,6 +147,255 @@ export class McpLifecycleManager {
|
|
|
117
147
|
}
|
|
118
148
|
}
|
|
119
149
|
|
|
150
|
+
private async checkKeepAliveConnections(signal?: AbortSignal): Promise<void> {
|
|
151
|
+
if (this.stopped || signal?.aborted) return;
|
|
152
|
+
await parallelLimit(
|
|
153
|
+
[...this.keepAliveServers],
|
|
154
|
+
KEEP_ALIVE_CHECK_CONCURRENCY,
|
|
155
|
+
([name, definition]) => this.checkKeepAliveConnection(name, definition, signal),
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private async checkKeepAliveConnection(
|
|
160
|
+
name: string,
|
|
161
|
+
definition: ServerDefinition,
|
|
162
|
+
signal?: AbortSignal,
|
|
163
|
+
retrySuperseded = true,
|
|
164
|
+
): Promise<void> {
|
|
165
|
+
if (isServerDisabled(definition) || this.stopped || signal?.aborted) return;
|
|
166
|
+
// Fence against unregisterServer racing an in-flight convergence pass.
|
|
167
|
+
// Identity comparison also rejects stale passes after a same-name
|
|
168
|
+
// replacement registration, which a name check would wrongly accept.
|
|
169
|
+
if (this.keepAliveServers.get(name) !== definition) return;
|
|
170
|
+
const connection = this.manager.getConnection(name);
|
|
171
|
+
if (connection?.status === "needs-auth") {
|
|
172
|
+
this.pendingMetadataPublications.delete(name);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (!this.shouldAttemptConnection(name, connection)) return;
|
|
176
|
+
if (!connection || connection.status !== "connected") {
|
|
177
|
+
if (this.hasPendingAuthForServer(name)) {
|
|
178
|
+
logger.debug(`Skipping reconnect for ${name} while OAuth authorization is pending`);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
let freshConnection: ServerConnection;
|
|
182
|
+
try {
|
|
183
|
+
freshConnection = await this.manager.connect(name, definition, signal);
|
|
184
|
+
} catch (error) {
|
|
185
|
+
if (this.stopped || signal?.aborted) return;
|
|
186
|
+
this.reportConnectionFailure(name, definition, error, "reconnect", this.manager.getConnection(name));
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (this.stopped || signal?.aborted) return;
|
|
190
|
+
if (freshConnection.status === "needs-auth") {
|
|
191
|
+
await this.notifyAuthRequired(name, definition, freshConnection);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (freshConnection.status !== "connected") {
|
|
195
|
+
this.reportConnectionFailure(
|
|
196
|
+
name,
|
|
197
|
+
definition,
|
|
198
|
+
new Error(`MCP server ${name} did not return a connected session`),
|
|
199
|
+
"reconnect",
|
|
200
|
+
freshConnection,
|
|
201
|
+
);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
logger.debug(`Reconnected to ${name}`);
|
|
205
|
+
await this.publishConnectedMetadata(name, definition, freshConnection);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (this.pendingMetadataPublications.has(name)) {
|
|
210
|
+
await this.publishConnectedMetadata(name, definition, connection);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (!definition.url) return;
|
|
214
|
+
const hadSessionId = (connection.transport as { sessionId?: string } | undefined)?.sessionId != null;
|
|
215
|
+
let refreshResult: Awaited<ReturnType<McpServerManager["refreshTools"]>>;
|
|
216
|
+
try {
|
|
217
|
+
refreshResult = await this.manager.refreshTools(name, connection, signal);
|
|
218
|
+
} catch (error) {
|
|
219
|
+
if (this.stopped || signal?.aborted) return;
|
|
220
|
+
const current = this.manager.getConnection(name);
|
|
221
|
+
if (current !== connection || connection.status !== "connected") {
|
|
222
|
+
await this.handleSupersededConnection(name, definition, connection, signal, retrySuperseded);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (!shouldReconnectAfterRefresh(error, hadSessionId)) {
|
|
226
|
+
this.reportConnectionFailure(name, definition, error, "refresh", connection);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (this.hasPendingAuthForServer(name)) {
|
|
230
|
+
logger.debug(`Skipping reconnect for ${name} while OAuth authorization is pending`);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
let freshConnection: ServerConnection;
|
|
234
|
+
try {
|
|
235
|
+
freshConnection = await this.manager.reconnect(name, definition, connection, signal);
|
|
236
|
+
} catch (reconnectError) {
|
|
237
|
+
if (this.stopped || signal?.aborted) return;
|
|
238
|
+
this.reportConnectionFailure(name, definition, reconnectError, "reconnect", this.manager.getConnection(name));
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
if (this.stopped || signal?.aborted) return;
|
|
242
|
+
if (freshConnection.status === "needs-auth") {
|
|
243
|
+
await this.notifyAuthRequired(name, definition, freshConnection);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (freshConnection.status !== "connected") {
|
|
247
|
+
this.reportConnectionFailure(
|
|
248
|
+
name,
|
|
249
|
+
definition,
|
|
250
|
+
new Error(`MCP server ${name} did not return a connected session`),
|
|
251
|
+
"reconnect",
|
|
252
|
+
freshConnection,
|
|
253
|
+
);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
logger.debug(`Reconnected stale MCP session for ${name}`);
|
|
257
|
+
await this.publishConnectedMetadata(name, definition, freshConnection);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (refreshResult === "superseded") {
|
|
262
|
+
await this.handleSupersededConnection(name, definition, connection, signal, retrySuperseded);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (refreshResult === "refresh-timeout") {
|
|
266
|
+
this.deferRefreshTimeout(name, definition, connection);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
if (this.keepAliveServers.get(name) !== definition) return;
|
|
270
|
+
if (this.retryStates.delete(name)) {
|
|
271
|
+
await this.onHealthRestored?.(name);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
private async handleSupersededConnection(
|
|
276
|
+
name: string,
|
|
277
|
+
definition: ServerDefinition,
|
|
278
|
+
staleConnection: ServerConnection,
|
|
279
|
+
signal: AbortSignal | undefined,
|
|
280
|
+
retrySuperseded: boolean,
|
|
281
|
+
): Promise<void> {
|
|
282
|
+
const current = this.manager.getConnection(name);
|
|
283
|
+
if (this.keepAliveServers.get(name) !== definition) return;
|
|
284
|
+
if (current === staleConnection && current.status === "connected") {
|
|
285
|
+
if (this.retryStates.delete(name)) await this.onHealthRestored?.(name);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
if (current?.status === "connected") {
|
|
289
|
+
await this.publishConnectedMetadata(name, definition, current);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (current?.status === "needs-auth") {
|
|
293
|
+
await this.notifyAuthRequired(name, definition, current);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
if (retrySuperseded) {
|
|
297
|
+
await this.checkKeepAliveConnection(name, definition, signal, false);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private async publishConnectedMetadata(name: string, definition: ServerDefinition, connection: ServerConnection): Promise<void> {
|
|
302
|
+
// Fence stale convergence passes by definition identity so disposal, and
|
|
303
|
+
// disposal followed by a same-name replacement, never re-track this
|
|
304
|
+
// server. Close the pass's own connection only when it is still current,
|
|
305
|
+
// so a replacement's connection is never touched.
|
|
306
|
+
if (this.keepAliveServers.get(name) !== definition) {
|
|
307
|
+
if (this.manager.getConnection(name) === connection) await this.manager.close(name);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
this.pendingMetadataPublications.add(name);
|
|
311
|
+
try {
|
|
312
|
+
await this.onReconnect?.(name);
|
|
313
|
+
this.pendingMetadataPublications.delete(name);
|
|
314
|
+
this.retryStates.delete(name);
|
|
315
|
+
} catch (error) {
|
|
316
|
+
if (this.stopped) return;
|
|
317
|
+
this.reportConnectionFailure(name, definition, error, "publish", connection);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
private async notifyAuthRequired(name: string, definition: ServerDefinition, connection: ServerConnection): Promise<void> {
|
|
322
|
+
// Fence stale convergence passes by definition identity. Close the
|
|
323
|
+
// leftover needs-auth connection only while it is still the manager's
|
|
324
|
+
// current entry, so a replacement's connection is never touched and the
|
|
325
|
+
// stale client/transport cannot leak when a replacement connects later.
|
|
326
|
+
if (this.keepAliveServers.get(name) !== definition) {
|
|
327
|
+
if (this.manager.getConnection(name) === connection) await this.manager.close(name);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
this.pendingMetadataPublications.delete(name);
|
|
331
|
+
this.retryStates.delete(name);
|
|
332
|
+
try {
|
|
333
|
+
await this.onAuthRequired?.(name);
|
|
334
|
+
} catch (error) {
|
|
335
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
336
|
+
logger.debug(`MCP: auth-required callback failed for ${name}: ${sanitizeTerminalText(message)}`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
private shouldAttemptConnection(name: string, connection: ServerConnection | undefined): boolean {
|
|
341
|
+
const retry = this.retryStates.get(name);
|
|
342
|
+
if (!retry) return true;
|
|
343
|
+
if (retry.connection !== connection || retry.status !== connection?.status) {
|
|
344
|
+
this.retryStates.delete(name);
|
|
345
|
+
return true;
|
|
346
|
+
}
|
|
347
|
+
return Date.now() >= retry.nextAttemptAt;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
private reportConnectionFailure(
|
|
351
|
+
name: string,
|
|
352
|
+
definition: ServerDefinition,
|
|
353
|
+
error: unknown,
|
|
354
|
+
action: "refresh" | "reconnect" | "publish",
|
|
355
|
+
connection: ServerConnection | undefined,
|
|
356
|
+
): void {
|
|
357
|
+
if (!this.recordRetry(name, definition, connection)) return;
|
|
358
|
+
this.onReconnectFailure?.(name, error);
|
|
359
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
360
|
+
const target = action === "reconnect"
|
|
361
|
+
? `reconnect to ${name}`
|
|
362
|
+
: action === "publish"
|
|
363
|
+
? `publish metadata for ${name}`
|
|
364
|
+
: `refresh ${name}`;
|
|
365
|
+
console.error(`MCP: Failed to ${target}: ${sanitizeTerminalText(message)}`);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
private deferRefreshTimeout(
|
|
369
|
+
name: string,
|
|
370
|
+
definition: ServerDefinition,
|
|
371
|
+
connection: ServerConnection | undefined,
|
|
372
|
+
): void {
|
|
373
|
+
if (!this.recordRetry(name, definition, connection)) return;
|
|
374
|
+
logger.debug(`MCP: keep-alive tools/list refresh timed out for ${name}; retrying after backoff`);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
private recordRetry(
|
|
378
|
+
name: string,
|
|
379
|
+
definition: ServerDefinition,
|
|
380
|
+
connection: ServerConnection | undefined,
|
|
381
|
+
): boolean {
|
|
382
|
+
// Do not recreate retry/failure state from a stale convergence pass after
|
|
383
|
+
// disposal or a same-name replacement registration.
|
|
384
|
+
if (this.keepAliveServers.get(name) !== definition) return false;
|
|
385
|
+
const attempts = (this.retryStates.get(name)?.attempts ?? 0) + 1;
|
|
386
|
+
const delay = Math.min(
|
|
387
|
+
KEEP_ALIVE_RETRY_BASE_MS * 2 ** Math.min(attempts - 1, 10),
|
|
388
|
+
KEEP_ALIVE_RETRY_MAX_MS,
|
|
389
|
+
);
|
|
390
|
+
this.retryStates.set(name, {
|
|
391
|
+
attempts,
|
|
392
|
+
nextAttemptAt: Date.now() + delay,
|
|
393
|
+
connection,
|
|
394
|
+
status: connection?.status,
|
|
395
|
+
});
|
|
396
|
+
return true;
|
|
397
|
+
}
|
|
398
|
+
|
|
120
399
|
private getIdleTimeout(name: string): number {
|
|
121
400
|
const perServer = this.serverSettings.get(name)?.idleTimeout;
|
|
122
401
|
if (perServer !== undefined) return perServer * 60 * 1000;
|
|
@@ -137,11 +416,23 @@ export class McpLifecycleManager {
|
|
|
137
416
|
this.removeHealthAbortListener = undefined;
|
|
138
417
|
await this.activeHealthCheck;
|
|
139
418
|
this.activeHealthCheck = undefined;
|
|
419
|
+
await this.activeConvergence;
|
|
420
|
+
this.activeConvergence = undefined;
|
|
140
421
|
this.onReconnect = undefined;
|
|
141
422
|
this.onReconnectFailure = undefined;
|
|
423
|
+
this.onHealthRestored = undefined;
|
|
424
|
+
this.onAuthRequired = undefined;
|
|
142
425
|
this.onIdleShutdown = undefined;
|
|
426
|
+
this.retryStates.clear();
|
|
427
|
+
this.pendingMetadataPublications.clear();
|
|
143
428
|
if (typeof this.manager.closeAll === "function") {
|
|
144
429
|
await this.manager.closeAll();
|
|
145
430
|
}
|
|
146
431
|
}
|
|
147
432
|
}
|
|
433
|
+
|
|
434
|
+
function shouldReconnectAfterRefresh(error: unknown, hadSessionId: boolean): boolean {
|
|
435
|
+
if (isTerminatedSession(error, hadSessionId)) return true;
|
|
436
|
+
return error instanceof SdkError
|
|
437
|
+
&& (error.code === SdkErrorCode.NotConnected || error.code === SdkErrorCode.ConnectionClosed);
|
|
438
|
+
}
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
waitForCallback,
|
|
19
19
|
cancelPendingCallback,
|
|
20
20
|
stopCallbackServer,
|
|
21
|
+
stopCallbackServerIfIdle,
|
|
21
22
|
releaseCallbackServer,
|
|
22
23
|
} from "./mcp-callback-server.ts"
|
|
23
24
|
import {
|
|
@@ -387,7 +388,7 @@ export async function startAuth(
|
|
|
387
388
|
} catch (error) {
|
|
388
389
|
releaseCallbackServer(oauthState)
|
|
389
390
|
try {
|
|
390
|
-
await clearOAuthState(serverName, authStorageOptions)
|
|
391
|
+
await cleanupAndReleaseCallbackServerIfIdle(() => clearOAuthState(serverName, authStorageOptions))
|
|
391
392
|
} catch (cleanupError) {
|
|
392
393
|
throw new AggregateError([error, cleanupError], "OAuth startup cleanup failed")
|
|
393
394
|
}
|
|
@@ -429,6 +430,7 @@ export async function startAuth(
|
|
|
429
430
|
authProvider.deactivate()
|
|
430
431
|
releaseCallbackServer(oauthState)
|
|
431
432
|
await clearOAuthState(serverName, authStorageOptions)
|
|
433
|
+
await stopCallbackServerIfIdle()
|
|
432
434
|
return { authorizationUrl: "" }
|
|
433
435
|
}
|
|
434
436
|
if (!capturedUrl) {
|
|
@@ -439,7 +441,7 @@ export async function startAuth(
|
|
|
439
441
|
} catch (error) {
|
|
440
442
|
authProvider.deactivate()
|
|
441
443
|
try {
|
|
442
|
-
await
|
|
444
|
+
await clearPendingAuthAndReleaseIfIdle(runtime, serverName, oauthState, authStorageOptions)
|
|
443
445
|
} catch (cleanupError) {
|
|
444
446
|
throw new AggregateError([error, cleanupError], "OAuth startup cleanup failed")
|
|
445
447
|
}
|
|
@@ -463,7 +465,7 @@ async function setPendingAuth(
|
|
|
463
465
|
state.pendingAuths.set(key, pendingAuth)
|
|
464
466
|
state.pendingAuthStates.set(key, oauthState)
|
|
465
467
|
const cleanupTimer = setTimeout(() => {
|
|
466
|
-
void
|
|
468
|
+
void clearPendingAuthAndReleaseIfIdle(runtime, serverName, oauthState, pendingAuth.authStorageOptions).catch(error => {
|
|
467
469
|
console.error(`MCP Auth: Timed-out flow cleanup failed: ${formatTerminalError(error)}`)
|
|
468
470
|
})
|
|
469
471
|
}, MANUAL_AUTH_TIMEOUT_MS)
|
|
@@ -498,6 +500,37 @@ async function clearPendingAuth(runtime: McpOAuthRuntime, serverName: string, oa
|
|
|
498
500
|
}
|
|
499
501
|
}
|
|
500
502
|
|
|
503
|
+
async function clearPendingAuthAndReleaseIfIdle(
|
|
504
|
+
runtime: McpOAuthRuntime,
|
|
505
|
+
serverName: string,
|
|
506
|
+
oauthState: string | undefined,
|
|
507
|
+
fallbackStorageOptions: AuthStorageOptions = {},
|
|
508
|
+
): Promise<void> {
|
|
509
|
+
await cleanupAndReleaseCallbackServerIfIdle(
|
|
510
|
+
() => clearPendingAuth(runtime, serverName, oauthState, fallbackStorageOptions),
|
|
511
|
+
)
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
async function cleanupAndReleaseCallbackServerIfIdle(cleanup: () => void | Promise<void>): Promise<void> {
|
|
515
|
+
let cleanupFailure: { error: unknown } | undefined
|
|
516
|
+
try {
|
|
517
|
+
await cleanup()
|
|
518
|
+
} catch (error) {
|
|
519
|
+
cleanupFailure = { error }
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
try {
|
|
523
|
+
await stopCallbackServerIfIdle()
|
|
524
|
+
} catch (releaseError) {
|
|
525
|
+
if (cleanupFailure) {
|
|
526
|
+
throw new AggregateError([cleanupFailure.error, releaseError], "OAuth callback cleanup failed")
|
|
527
|
+
}
|
|
528
|
+
throw releaseError
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (cleanupFailure) throw cleanupFailure.error
|
|
532
|
+
}
|
|
533
|
+
|
|
501
534
|
function getSearchParamsFromInput(input: string): URLSearchParams | undefined {
|
|
502
535
|
try {
|
|
503
536
|
const url = new URL(input)
|
|
@@ -701,7 +734,7 @@ export async function completeAuth(
|
|
|
701
734
|
} finally {
|
|
702
735
|
if (!keepPendingForRetry) {
|
|
703
736
|
try {
|
|
704
|
-
await
|
|
737
|
+
await clearPendingAuthAndReleaseIfIdle(runtime, serverName, oauthState, authStorageOptions)
|
|
705
738
|
} catch (cleanupError) {
|
|
706
739
|
if (caughtError !== undefined) {
|
|
707
740
|
throw new AggregateError([caughtError, cleanupError], "OAuth completion cleanup failed")
|
|
@@ -801,7 +834,7 @@ export async function authenticate(
|
|
|
801
834
|
} catch (error) {
|
|
802
835
|
if (oauthState) cancelPendingCallback(oauthState)
|
|
803
836
|
try {
|
|
804
|
-
await
|
|
837
|
+
await clearPendingAuthAndReleaseIfIdle(runtime, serverName, oauthState, authStorageOptions)
|
|
805
838
|
} catch (cleanupError) {
|
|
806
839
|
throw new AggregateError([error, cleanupError], "OAuth cancellation cleanup failed")
|
|
807
840
|
}
|
|
@@ -922,7 +955,7 @@ export async function removeAuth(serverName: string, options: AuthenticateOption
|
|
|
922
955
|
if (oauthState) {
|
|
923
956
|
cancelPendingCallback(oauthState)
|
|
924
957
|
}
|
|
925
|
-
await
|
|
958
|
+
await clearPendingAuthAndReleaseIfIdle(runtime, serverName, oauthState, authStorageOptions)
|
|
926
959
|
throwIfAborted(signal)
|
|
927
960
|
clearAllCredentials(serverName, authStorageOptions)
|
|
928
961
|
await clearOAuthState(serverName, authStorageOptions)
|