@herbertgao/pi-extensions 2026.8.3 → 2026.8.5
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 +45 -4
- package/examples/pi-footer.json +275 -0
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +10 -2
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +10 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +24 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +91 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +7 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +7 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +21 -12
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +4 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +5 -8
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +22 -12
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +2 -4
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +8 -0
- package/node_modules/@herbertgao/pi-subagents/package.json +8 -4
- package/node_modules/@herbertgao/pi-subagents/src/agent-color.ts +72 -67
- package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +255 -0
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +191 -3
- package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +15 -2
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +289 -113
- package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +24 -1
- package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +24 -2
- package/node_modules/@herbertgao/pi-subagents/src/ui/schedule-menu.ts +9 -8
- package/node_modules/@herbertgao/pi-subagents/src/ui/select-item.ts +48 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +7 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +7 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +16 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +4 -2
- package/node_modules/@narumitw/pi-btw/README.md +8 -7
- package/node_modules/@narumitw/pi-btw/package.json +2 -2
- package/node_modules/@narumitw/pi-btw/src/menu.ts +1 -0
- package/node_modules/pi-footer/CHANGELOG.md +145 -0
- package/node_modules/pi-footer/LICENSE +21 -0
- package/node_modules/pi-footer/README.md +354 -0
- package/node_modules/pi-footer/package.json +68 -0
- package/node_modules/pi-footer/src/cache.ts +106 -0
- package/node_modules/pi-footer/src/colors.ts +237 -0
- package/node_modules/pi-footer/src/config.ts +202 -0
- package/node_modules/pi-footer/src/event-widgets.ts +41 -0
- package/node_modules/pi-footer/src/extension-statuses.ts +102 -0
- package/node_modules/pi-footer/src/git.ts +135 -0
- package/node_modules/pi-footer/src/index.ts +268 -0
- package/node_modules/pi-footer/src/metrics.ts +141 -0
- package/node_modules/pi-footer/src/presets.ts +383 -0
- package/node_modules/pi-footer/src/render.ts +114 -0
- package/node_modules/pi-footer/src/separators.ts +52 -0
- package/node_modules/pi-footer/src/types.ts +122 -0
- package/node_modules/pi-footer/src/ui/color-level-confirm.ts +22 -0
- package/node_modules/pi-footer/src/ui/color-options.ts +195 -0
- package/node_modules/pi-footer/src/ui/config-lifecycle.ts +61 -0
- package/node_modules/pi-footer/src/ui/edit-colors.ts +13 -0
- package/node_modules/pi-footer/src/ui/events.ts +35 -0
- package/node_modules/pi-footer/src/ui/extension-status-picker.ts +60 -0
- package/node_modules/pi-footer/src/ui/extension-statuses.ts +73 -0
- package/node_modules/pi-footer/src/ui/fields.ts +252 -0
- package/node_modules/pi-footer/src/ui/global-menu.ts +126 -0
- package/node_modules/pi-footer/src/ui/helpers.ts +44 -0
- package/node_modules/pi-footer/src/ui/layout.ts +17 -0
- package/node_modules/pi-footer/src/ui/line-list.ts +19 -0
- package/node_modules/pi-footer/src/ui/model.ts +64 -0
- package/node_modules/pi-footer/src/ui/navigation.ts +29 -0
- package/node_modules/pi-footer/src/ui/option-edit.ts +93 -0
- package/node_modules/pi-footer/src/ui/overlay-render.ts +103 -0
- package/node_modules/pi-footer/src/ui/screen-context.ts +18 -0
- package/node_modules/pi-footer/src/ui/screen-controller.ts +27 -0
- package/node_modules/pi-footer/src/ui/screen-render.ts +32 -0
- package/node_modules/pi-footer/src/ui/screen-state.ts +20 -0
- package/node_modules/pi-footer/src/ui/screen.ts +243 -0
- package/node_modules/pi-footer/src/ui/screens/add-widget.ts +92 -0
- package/node_modules/pi-footer/src/ui/screens/confirm-exit.ts +52 -0
- package/node_modules/pi-footer/src/ui/screens/controller.ts +12 -0
- package/node_modules/pi-footer/src/ui/screens/edit-colors.ts +68 -0
- package/node_modules/pi-footer/src/ui/screens/edit-widget.ts +115 -0
- package/node_modules/pi-footer/src/ui/screens/extension-status-row.ts +61 -0
- package/node_modules/pi-footer/src/ui/screens/global.ts +75 -0
- package/node_modules/pi-footer/src/ui/screens/line-list.ts +108 -0
- package/node_modules/pi-footer/src/ui/screens/main.ts +99 -0
- package/node_modules/pi-footer/src/ui/screens/terminal.ts +121 -0
- package/node_modules/pi-footer/src/ui/screens/widget-list.ts +154 -0
- package/node_modules/pi-footer/src/ui/terminal-menu.ts +32 -0
- package/node_modules/pi-footer/src/ui/theme.ts +48 -0
- package/node_modules/pi-footer/src/ui/title-bar.ts +100 -0
- package/node_modules/pi-footer/src/ui/widget-actions.ts +76 -0
- package/node_modules/pi-footer/src/ui.ts +66 -0
- package/node_modules/pi-footer/src/widgets/context.ts +28 -0
- package/node_modules/pi-footer/src/widgets/core/active-tools.ts +17 -0
- package/node_modules/pi-footer/src/widgets/core/context-window.ts +24 -0
- package/node_modules/pi-footer/src/widgets/core/cwd-basename.ts +19 -0
- package/node_modules/pi-footer/src/widgets/core/cwd.ts +108 -0
- package/node_modules/pi-footer/src/widgets/core/event.ts +34 -0
- package/node_modules/pi-footer/src/widgets/core/external-status.ts +102 -0
- package/node_modules/pi-footer/src/widgets/core/model-provider.ts +20 -0
- package/node_modules/pi-footer/src/widgets/core/model.ts +35 -0
- package/node_modules/pi-footer/src/widgets/core/provider.ts +17 -0
- package/node_modules/pi-footer/src/widgets/core/session-name.ts +17 -0
- package/node_modules/pi-footer/src/widgets/core/text-verbosity.ts +17 -0
- package/node_modules/pi-footer/src/widgets/core/thinking-level.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/ahead-behind.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/branch.ts +81 -0
- package/node_modules/pi-footer/src/widgets/git/clean.ts +19 -0
- package/node_modules/pi-footer/src/widgets/git/deletions.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/diff.ts +38 -0
- package/node_modules/pi-footer/src/widgets/git/insertions.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/remote.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/root.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/sha.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/staged.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/status.ts +19 -0
- package/node_modules/pi-footer/src/widgets/git/unstaged.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/untracked.ts +17 -0
- package/node_modules/pi-footer/src/widgets/instance.ts +118 -0
- package/node_modules/pi-footer/src/widgets/layout/custom-text.ts +18 -0
- package/node_modules/pi-footer/src/widgets/layout/flex-separator.ts +32 -0
- package/node_modules/pi-footer/src/widgets/layout/separator.ts +57 -0
- package/node_modules/pi-footer/src/widgets/layout/spacer.ts +31 -0
- package/node_modules/pi-footer/src/widgets/options.ts +122 -0
- package/node_modules/pi-footer/src/widgets/project/runtime.ts +361 -0
- package/node_modules/pi-footer/src/widgets/registry.ts +223 -0
- package/node_modules/pi-footer/src/widgets/session/assistant-messages.ts +17 -0
- package/node_modules/pi-footer/src/widgets/session/compactions.ts +17 -0
- package/node_modules/pi-footer/src/widgets/session/elapsed.ts +18 -0
- package/node_modules/pi-footer/src/widgets/session/last-activity.ts +18 -0
- package/node_modules/pi-footer/src/widgets/session/messages.ts +19 -0
- package/node_modules/pi-footer/src/widgets/session/session-id.ts +17 -0
- package/node_modules/pi-footer/src/widgets/session/session-start.ts +18 -0
- package/node_modules/pi-footer/src/widgets/session/tool-results.ts +17 -0
- package/node_modules/pi-footer/src/widgets/session/total-messages.ts +19 -0
- package/node_modules/pi-footer/src/widgets/session/total-time.ts +18 -0
- package/node_modules/pi-footer/src/widgets/session/user-messages.ts +17 -0
- package/node_modules/pi-footer/src/widgets/store.ts +26 -0
- package/node_modules/pi-footer/src/widgets/tokens/cache-hit-rate.ts +55 -0
- package/node_modules/pi-footer/src/widgets/tokens/cache-read.ts +18 -0
- package/node_modules/pi-footer/src/widgets/tokens/cache-write.ts +18 -0
- package/node_modules/pi-footer/src/widgets/tokens/context-bar.ts +108 -0
- package/node_modules/pi-footer/src/widgets/tokens/context-length.ts +26 -0
- package/node_modules/pi-footer/src/widgets/tokens/context-remaining.ts +29 -0
- package/node_modules/pi-footer/src/widgets/tokens/context.ts +29 -0
- package/node_modules/pi-footer/src/widgets/tokens/cost.ts +51 -0
- package/node_modules/pi-footer/src/widgets/tokens/input-speed.ts +25 -0
- package/node_modules/pi-footer/src/widgets/tokens/input-tokens.ts +18 -0
- package/node_modules/pi-footer/src/widgets/tokens/output-speed.ts +25 -0
- package/node_modules/pi-footer/src/widgets/tokens/output-tokens.ts +18 -0
- package/node_modules/pi-footer/src/widgets/tokens/tokens.ts +20 -0
- package/node_modules/pi-footer/src/widgets/tokens/total-speed.ts +25 -0
- package/node_modules/pi-footer/src/widgets/tokens/total-tokens.ts +18 -0
- package/node_modules/pi-footer/src/widgets/types.ts +181 -0
- package/node_modules/pi-footer/src/widgets/utils/colors.ts +11 -0
- package/node_modules/pi-footer/src/widgets/utils/context.ts +112 -0
- package/node_modules/pi-footer/src/widgets/utils/session.ts +19 -0
- package/node_modules/pi-footer/src/widgets/utils/token-format.ts +78 -0
- package/node_modules/pi-lens/CHANGELOG.md +2440 -954
- package/node_modules/pi-lens/README.md +13 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +73 -18
- package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +21 -4
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +68 -16
- package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -1
- package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +37 -10
- package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +63 -0
- package/node_modules/pi-lens/dist/clients/atomic-write.js +114 -14
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +193 -23
- package/node_modules/pi-lens/dist/clients/biome-client.js +29 -25
- package/node_modules/pi-lens/dist/clients/bootstrap.js +11 -2
- package/node_modules/pi-lens/dist/clients/bounded-cache.js +34 -0
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +192 -0
- package/node_modules/pi-lens/dist/clients/bus-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +47 -5
- package/node_modules/pi-lens/dist/clients/cache-manager.js +97 -9
- package/node_modules/pi-lens/dist/clients/cache-observability.js +301 -14
- package/node_modules/pi-lens/dist/clients/call-graph.js +449 -117
- package/node_modules/pi-lens/dist/clients/child-unref.js +85 -0
- package/node_modules/pi-lens/dist/clients/codebase-model.js +52 -23
- package/node_modules/pi-lens/dist/clients/collateral-test-role.js +40 -0
- package/node_modules/pi-lens/dist/clients/complexity-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/console-guard-install.js +14 -0
- package/node_modules/pi-lens/dist/clients/cooperative-budget.js +42 -0
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/debug-handles.js +206 -0
- package/node_modules/pi-lens/dist/clients/debug-heap.js +167 -0
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +153 -0
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +347 -75
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +61 -11
- package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +63 -20
- package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +23 -7
- package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +8 -18
- package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +8 -5
- package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +35 -0
- package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +17 -8
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +1085 -681
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +14 -0
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +11 -1
- package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +50 -0
- package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +135 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +23 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +5 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +154 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +5 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +14 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +9 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +4 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +163 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +37 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +13 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +277 -26
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
- package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/durable-store.js +97 -0
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +94 -3
- package/node_modules/pi-lens/dist/clients/extension-log.js +139 -0
- package/node_modules/pi-lens/dist/clients/extension-mode.js +73 -0
- package/node_modules/pi-lens/dist/clients/file-kinds.js +57 -0
- package/node_modules/pi-lens/dist/clients/file-role.js +18 -3
- package/node_modules/pi-lens/dist/clients/file-utils.js +56 -34
- package/node_modules/pi-lens/dist/clients/format-events-publish.js +16 -3
- package/node_modules/pi-lens/dist/clients/format-service.js +24 -8
- package/node_modules/pi-lens/dist/clients/formatters-lazy.js +8 -0
- package/node_modules/pi-lens/dist/clients/formatters.js +358 -61
- package/node_modules/pi-lens/dist/clients/generated-artifacts.js +148 -15
- package/node_modules/pi-lens/dist/clients/git-guard.js +681 -11
- package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +4 -0
- package/node_modules/pi-lens/dist/clients/go-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +8 -0
- package/node_modules/pi-lens/dist/clients/grammar-source.js +12 -4
- package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +21 -3
- package/node_modules/pi-lens/dist/clients/host-ports.js +31 -0
- package/node_modules/pi-lens/dist/clients/installer/index.js +390 -99
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +142 -172
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +31 -54
- package/node_modules/pi-lens/dist/clients/knip-client.js +15 -36
- package/node_modules/pi-lens/dist/clients/language-policy.js +14 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +13 -7
- package/node_modules/pi-lens/dist/clients/latency-logger.js +23 -1
- package/node_modules/pi-lens/dist/clients/lens-config.js +12 -1
- package/node_modules/pi-lens/dist/clients/lens-engine.js +86 -17
- package/node_modules/pi-lens/dist/clients/lens-events.js +27 -4
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
- package/node_modules/pi-lens/dist/clients/lens-map.js +94 -19
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +30 -0
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/client.js +493 -48
- package/node_modules/pi-lens/dist/clients/lsp/config.js +13 -2
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +86 -0
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +1012 -121
- package/node_modules/pi-lens/dist/clients/lsp/index.js +1807 -780
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +32 -26
- package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +83 -0
- package/node_modules/pi-lens/dist/clients/lsp/server.js +196 -32
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
- package/node_modules/pi-lens/dist/clients/lsp-lazy.js +8 -0
- package/node_modules/pi-lens/dist/clients/lsp-mutation.js +248 -0
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +123 -32
- package/node_modules/pi-lens/dist/clients/mcp/ipc.js +157 -24
- package/node_modules/pi-lens/dist/clients/mcp/session.js +216 -8
- package/node_modules/pi-lens/dist/clients/memory-sampler.js +115 -0
- package/node_modules/pi-lens/dist/clients/metrics-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/metrics-history.js +2 -1
- package/node_modules/pi-lens/dist/clients/module-report-lsp.js +5 -0
- package/node_modules/pi-lens/dist/clients/module-report.js +168 -3
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +319 -118
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +1 -1
- package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +120 -5
- package/node_modules/pi-lens/dist/clients/path-utils.js +110 -9
- package/node_modules/pi-lens/dist/clients/pipeline.js +47 -13
- package/node_modules/pi-lens/dist/clients/print-mode.js +21 -0
- package/node_modules/pi-lens/dist/clients/project-changes.js +40 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +3 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +8 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +21 -1
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +86 -9
- package/node_modules/pi-lens/dist/clients/project-report.js +27 -13
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +175 -20
- package/node_modules/pi-lens/dist/clients/project-trust.js +181 -0
- package/node_modules/pi-lens/dist/clients/quiet-window.js +18 -4
- package/node_modules/pi-lens/dist/clients/read-bridge.js +147 -0
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +159 -1
- package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +150 -29
- package/node_modules/pi-lens/dist/clients/read-guard.js +185 -4
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +70 -84
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +1122 -221
- package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +2 -1
- package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +21 -10
- package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +46 -0
- package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +57 -6
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +51 -3
- package/node_modules/pi-lens/dist/clients/review-graph-logger.js +30 -1
- package/node_modules/pi-lens/dist/clients/ruff-client.js +27 -44
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +107 -73
- package/node_modules/pi-lens/dist/clients/runtime-context.js +36 -12
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +74 -6
- package/node_modules/pi-lens/dist/clients/runtime-session.js +405 -117
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +71 -48
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +218 -23
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +237 -64
- package/node_modules/pi-lens/dist/clients/rust-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +777 -62
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/session-state-store.js +3 -2
- package/node_modules/pi-lens/dist/clients/sg-runner.js +284 -174
- package/node_modules/pi-lens/dist/clients/slow-fs.js +2 -1
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +224 -0
- package/node_modules/pi-lens/dist/clients/source-filter.js +184 -20
- package/node_modules/pi-lens/dist/clients/source-walker.js +66 -10
- package/node_modules/pi-lens/dist/clients/startup-scan.js +15 -16
- package/node_modules/pi-lens/dist/clients/startup-timing.js +1 -1
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +38 -8
- package/node_modules/pi-lens/dist/clients/tool-event.js +35 -0
- package/node_modules/pi-lens/dist/clients/tool-policy.js +388 -39
- package/node_modules/pi-lens/dist/clients/tool-render.js +155 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +6 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +419 -50
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +21 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +7 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +7 -2
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +44 -7
- package/node_modules/pi-lens/dist/clients/user-notify.js +51 -0
- package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +57 -0
- package/node_modules/pi-lens/dist/clients/warm-attach.js +33 -13
- package/node_modules/pi-lens/dist/clients/widget-state.js +300 -43
- package/node_modules/pi-lens/dist/clients/word-index.js +391 -64
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +89 -6
- package/node_modules/pi-lens/dist/index.js +49361 -36888
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +182 -29
- package/node_modules/pi-lens/dist/mcp/server.js +211 -44
- package/node_modules/pi-lens/dist/tools/ast-grep-search.js +93 -27
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +200 -36
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +153 -40
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -3
- package/node_modules/pi-lens/dist/tools/module-report.js +9 -0
- package/node_modules/pi-lens/docs/agent-guide.md +2 -2
- package/node_modules/pi-lens/docs/agent-tools.md +21 -10
- package/node_modules/pi-lens/docs/analysisall.md +192 -0
- package/node_modules/pi-lens/docs/api-ports-inventory.md +165 -0
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +5 -2
- package/node_modules/pi-lens/docs/durable-store-audit-1202.md +32 -0
- package/node_modules/pi-lens/docs/environment-variables.md +44 -0
- package/node_modules/pi-lens/docs/fable.md +27 -0
- package/node_modules/pi-lens/docs/features.md +31 -2
- package/node_modules/pi-lens/docs/globalconfig.md +45 -1
- package/node_modules/pi-lens/docs/language-coverage.md +4 -3
- package/node_modules/pi-lens/docs/lsp-capability-matrix.md +2 -2
- package/node_modules/pi-lens/docs/mcp.md +37 -4
- package/node_modules/pi-lens/docs/servercapabilities.md +3 -3
- package/node_modules/pi-lens/docs/settings.md +12 -1
- package/node_modules/pi-lens/docs/tools.md +4 -1
- package/node_modules/pi-lens/docs/usage.md +18 -0
- package/node_modules/pi-lens/package.json +10 -10
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
- package/node_modules/pi-lens/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
- package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +2 -2
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +55 -2
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +25 -0
- package/node_modules/pi-mcp-adapter/README.md +34 -3
- package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +1 -1
- package/node_modules/pi-mcp-adapter/commands.ts +15 -7
- package/node_modules/pi-mcp-adapter/direct-tools.ts +54 -7
- package/node_modules/pi-mcp-adapter/host-html-template.ts +0 -8
- package/node_modules/pi-mcp-adapter/index.ts +8 -3
- package/node_modules/pi-mcp-adapter/init.ts +29 -5
- package/node_modules/pi-mcp-adapter/lifecycle.ts +0 -4
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +1 -2
- package/node_modules/pi-mcp-adapter/mcp-auth.ts +60 -2
- package/node_modules/pi-mcp-adapter/mcp-panel.ts +45 -9
- package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +22 -20
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +27 -4
- package/node_modules/pi-mcp-adapter/npx-resolver.ts +81 -26
- package/node_modules/pi-mcp-adapter/package.json +3 -3
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +108 -26
- package/node_modules/pi-mcp-adapter/search-ranking.ts +86 -7
- package/node_modules/pi-mcp-adapter/server-manager.ts +36 -7
- package/node_modules/pi-mcp-adapter/session-recovery.ts +8 -2
- package/node_modules/pi-mcp-adapter/tool-approval.ts +48 -7
- package/node_modules/pi-mcp-adapter/tool-metadata.ts +42 -4
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +138 -12
- package/node_modules/pi-mcp-adapter/types.ts +93 -27
- package/node_modules/pi-mcp-adapter/ui-server.ts +20 -1
- package/package.json +17 -13
- package/node_modules/pi-lens/dist/clients/ast-grep-parser.js +0 -86
- package/node_modules/pi-lens/dist/clients/lsp/interactive-install.js +0 -367
- package/node_modules/pi-lens/dist/clients/lsp/lsp-index.js +0 -10
- package/node_modules/pi-lens/dist/clients/lsp/server-strategies.js +0 -5
- package/node_modules/pi-lens/dist/clients/project-metadata.js +0 -690
- package/node_modules/pi-lens/dist/clients/source-groups.js +0 -96
- package/node_modules/pi-lens/dist/clients/startup-marker.js +0 -6
- package/node_modules/pi-lens/dist/clients/types.js +0 -11
|
@@ -16,7 +16,7 @@ import { maybeStartUiSession, summarizeUiSessionResult, type UiSessionRuntime }
|
|
|
16
16
|
import { formatAuthRequiredMessage, formatMcpStatus, resolveServerUrl, truncateAtWord } from "./utils.ts";
|
|
17
17
|
import { authenticate, completeAuthFromInput, startAuth, supportsOAuth } from "./mcp-auth-flow.ts";
|
|
18
18
|
import { SessionRecoveryAuthRequiredError, withSessionRecovery } from "./session-recovery.ts";
|
|
19
|
-
import { paginate, rankSuggestions, rankToolMatches } from "./search-ranking.ts";
|
|
19
|
+
import { paginate, rankSuggestions, rankToolMatches, resolveSearchKeywords } from "./search-ranking.ts";
|
|
20
20
|
import { ensureToolCallApproved, isToolCallApprovalRequired } from "./tool-approval.ts";
|
|
21
21
|
|
|
22
22
|
type ProxyToolResult = AgentToolResult<Record<string, unknown>>;
|
|
@@ -37,6 +37,36 @@ type AutoAuthResult =
|
|
|
37
37
|
| { status: "success" }
|
|
38
38
|
| { status: "failed"; message: string };
|
|
39
39
|
|
|
40
|
+
function getToolMatches(metadata: ToolMetadata[] | undefined, toolName: string, exact: boolean): ToolMetadata[] {
|
|
41
|
+
if (!metadata) return [];
|
|
42
|
+
if (exact) return metadata.filter(tool => tool.name === toolName);
|
|
43
|
+
const normalizedName = toolName.replace(/-/g, "_");
|
|
44
|
+
return metadata.filter(tool => tool.name.replace(/-/g, "_") === normalizedName);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getEnabledToolMatches(state: McpExtensionState, toolName: string, exact: boolean): { server: string; tool: ToolMetadata }[] {
|
|
48
|
+
const matches: { server: string; tool: ToolMetadata }[] = [];
|
|
49
|
+
for (const [server, metadata] of state.toolMetadata) {
|
|
50
|
+
if (isServerDisabled(state.config.mcpServers[server])) continue;
|
|
51
|
+
for (const tool of getToolMatches(metadata, toolName, exact)) matches.push({ server, tool });
|
|
52
|
+
}
|
|
53
|
+
return matches;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getSingleToolMatch(metadata: ToolMetadata[] | undefined, toolName: string): ToolMetadata | "ambiguous" | undefined {
|
|
57
|
+
const exactMatches = getToolMatches(metadata, toolName, true);
|
|
58
|
+
const matches = exactMatches.length > 0 ? exactMatches : getToolMatches(metadata, toolName, false);
|
|
59
|
+
return matches.length > 1 ? "ambiguous" : matches[0];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function ambiguousToolResult(mode: "call" | "describe", toolName: string): ProxyToolResult {
|
|
63
|
+
const message = `Tool "${toolName}" matches multiple servers. Specify a server.`;
|
|
64
|
+
return {
|
|
65
|
+
content: [{ type: "text" as const, text: message }],
|
|
66
|
+
details: { mode, error: "ambiguous_tool", requestedTool: toolName, message },
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
40
70
|
function disabledResult(mode: string, serverName: string): ProxyToolResult {
|
|
41
71
|
const message = `Server "${serverName}" is disabled. Run /mcp enable ${serverName} and /reload to enable it.`;
|
|
42
72
|
return {
|
|
@@ -404,20 +434,28 @@ export async function executeAuthComplete(state: McpExtensionState, serverName:
|
|
|
404
434
|
}
|
|
405
435
|
|
|
406
436
|
export function executeDescribe(state: McpExtensionState, toolName: string): ProxyToolResult {
|
|
407
|
-
|
|
408
|
-
|
|
437
|
+
const exactMatches = getEnabledToolMatches(state, toolName, true);
|
|
438
|
+
if (exactMatches.length > 1) return ambiguousToolResult("describe", toolName);
|
|
439
|
+
if (exactMatches.length === 0 && getEnabledToolMatches(state, toolName, false).length > 1) {
|
|
440
|
+
return ambiguousToolResult("describe", toolName);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
let serverName = exactMatches[0]?.server;
|
|
444
|
+
let toolMeta = exactMatches[0]?.tool;
|
|
409
445
|
let disabledMatch: string | undefined;
|
|
410
446
|
|
|
411
|
-
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
447
|
+
if (!toolMeta) {
|
|
448
|
+
for (const [server, metadata] of state.toolMetadata.entries()) {
|
|
449
|
+
const found = findToolByName(metadata, toolName);
|
|
450
|
+
if (!found) continue;
|
|
451
|
+
if (isServerDisabled(state.config.mcpServers[server])) {
|
|
452
|
+
disabledMatch ??= server;
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
serverName = server;
|
|
456
|
+
toolMeta = found;
|
|
457
|
+
break;
|
|
417
458
|
}
|
|
418
|
-
serverName = server;
|
|
419
|
-
toolMeta = found;
|
|
420
|
-
break;
|
|
421
459
|
}
|
|
422
460
|
|
|
423
461
|
if (!serverName || !toolMeta) {
|
|
@@ -430,7 +468,7 @@ export function executeDescribe(state: McpExtensionState, toolName: string): Pro
|
|
|
430
468
|
};
|
|
431
469
|
}
|
|
432
470
|
|
|
433
|
-
const approvalMarker = isToolCallApprovalRequired(state.config, serverName, toolMeta)
|
|
471
|
+
const approvalMarker = isToolCallApprovalRequired(state.config, serverName, toolMeta, state.toolMetadata)
|
|
434
472
|
? " (requires approval)"
|
|
435
473
|
: "";
|
|
436
474
|
let text = `${toolMeta.name}${approvalMarker}\n`;
|
|
@@ -503,11 +541,15 @@ export function executeSearch(
|
|
|
503
541
|
}
|
|
504
542
|
|
|
505
543
|
matches = [];
|
|
544
|
+
const globalPrefix = state.config.settings?.toolPrefix ?? "server";
|
|
506
545
|
for (const [serverName, metadata] of state.toolMetadata.entries()) {
|
|
507
|
-
|
|
546
|
+
const definition = state.config.mcpServers[serverName];
|
|
547
|
+
if (isServerDisabled(definition)) continue;
|
|
508
548
|
if (server && serverName !== server) continue;
|
|
509
549
|
for (const tool of metadata) {
|
|
510
|
-
|
|
550
|
+
const matched = pattern.test(tool.name) || pattern.test(tool.description)
|
|
551
|
+
|| resolveSearchKeywords(definition, tool.originalName, serverName, globalPrefix).some(keyword => pattern.test(keyword));
|
|
552
|
+
if (matched) matches.push({ server: serverName, tool, score: 0 });
|
|
511
553
|
}
|
|
512
554
|
}
|
|
513
555
|
} else if (query.trim().length === 0) {
|
|
@@ -553,7 +595,7 @@ export function executeSearch(
|
|
|
553
595
|
|
|
554
596
|
let text = `Found ${page.total} tool${page.total === 1 ? "" : "s"} matching "${query}":\n\n`;
|
|
555
597
|
for (const match of page.items) {
|
|
556
|
-
const approvalMarker = isToolCallApprovalRequired(state.config, match.server, match.tool)
|
|
598
|
+
const approvalMarker = isToolCallApprovalRequired(state.config, match.server, match.tool, state.toolMetadata)
|
|
557
599
|
? " (requires approval)"
|
|
558
600
|
: "";
|
|
559
601
|
if (showSchemas) {
|
|
@@ -733,7 +775,7 @@ export async function executeConnect(state: McpExtensionState, serverName: strin
|
|
|
733
775
|
}
|
|
734
776
|
}
|
|
735
777
|
const prefix = state.config.settings?.toolPrefix ?? "server";
|
|
736
|
-
const { metadata } = buildToolMetadata(connection.tools, connection.resources, definition, serverName, prefix);
|
|
778
|
+
const { metadata } = buildToolMetadata(connection.tools, connection.resources, definition, serverName, prefix, state.config.mcpServers, state.toolMetadata);
|
|
737
779
|
state.toolMetadata.set(serverName, metadata);
|
|
738
780
|
if (!connection.promptDiscoveryFailed) {
|
|
739
781
|
state.promptMetadata?.set(serverName, reconstructPromptMetadata(serverName, connection.prompts ?? [], prefix, definition));
|
|
@@ -801,14 +843,22 @@ export async function executeCall(
|
|
|
801
843
|
};
|
|
802
844
|
}
|
|
803
845
|
if (serverName) {
|
|
804
|
-
|
|
846
|
+
const match = getSingleToolMatch(state.toolMetadata.get(serverName), toolName);
|
|
847
|
+
if (match === "ambiguous") return ambiguousToolResult("call", toolName);
|
|
848
|
+
toolMeta = match;
|
|
805
849
|
if (isServerDisabled(state.config.mcpServers[serverName])) {
|
|
806
850
|
return disabledCallResult(serverName, toolMeta);
|
|
807
851
|
}
|
|
808
852
|
} else {
|
|
853
|
+
const exactMatches = getEnabledToolMatches(state, toolName, true);
|
|
854
|
+
if (exactMatches.length > 1) return ambiguousToolResult("call", toolName);
|
|
855
|
+
if (exactMatches.length === 0 && getEnabledToolMatches(state, toolName, false).length > 1) {
|
|
856
|
+
return ambiguousToolResult("call", toolName);
|
|
857
|
+
}
|
|
858
|
+
|
|
809
859
|
let disabledMatch: { serverName: string; toolMeta: ToolMetadata } | undefined;
|
|
810
860
|
for (const [server, metadata] of state.toolMetadata.entries()) {
|
|
811
|
-
const found =
|
|
861
|
+
const found = metadata.find(tool => tool.name === toolName);
|
|
812
862
|
if (!found) continue;
|
|
813
863
|
if (isServerDisabled(state.config.mcpServers[server])) {
|
|
814
864
|
disabledMatch ??= { serverName: server, toolMeta: found };
|
|
@@ -818,13 +868,28 @@ export async function executeCall(
|
|
|
818
868
|
toolMeta = found;
|
|
819
869
|
break;
|
|
820
870
|
}
|
|
871
|
+
if (!toolMeta && !disabledMatch) {
|
|
872
|
+
for (const [server, metadata] of state.toolMetadata.entries()) {
|
|
873
|
+
const found = findToolByName(metadata, toolName);
|
|
874
|
+
if (!found) continue;
|
|
875
|
+
if (isServerDisabled(state.config.mcpServers[server])) {
|
|
876
|
+
disabledMatch ??= { serverName: server, toolMeta: found };
|
|
877
|
+
continue;
|
|
878
|
+
}
|
|
879
|
+
serverName = server;
|
|
880
|
+
toolMeta = found;
|
|
881
|
+
break;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
821
884
|
if (!toolMeta && disabledMatch) return disabledCallResult(disabledMatch.serverName, disabledMatch.toolMeta);
|
|
822
885
|
}
|
|
823
886
|
|
|
824
887
|
if (serverName && !toolMeta) {
|
|
825
888
|
const connected = await lazyConnect(state, serverName, ownedSignal);
|
|
826
889
|
if (connected) {
|
|
827
|
-
|
|
890
|
+
const match = getSingleToolMatch(state.toolMetadata.get(serverName), toolName);
|
|
891
|
+
if (match === "ambiguous") return ambiguousToolResult("call", toolName);
|
|
892
|
+
toolMeta = match;
|
|
828
893
|
} else {
|
|
829
894
|
const needsAuthConnection = state.manager.getConnection(serverName);
|
|
830
895
|
if (needsAuthConnection?.status === "needs-auth") {
|
|
@@ -842,7 +907,9 @@ export async function executeCall(
|
|
|
842
907
|
clearFailure(state, serverName);
|
|
843
908
|
const connectedAfterAuth = await lazyConnect(state, serverName, ownedSignal);
|
|
844
909
|
if (connectedAfterAuth) {
|
|
845
|
-
|
|
910
|
+
const match = getSingleToolMatch(state.toolMetadata.get(serverName), toolName);
|
|
911
|
+
if (match === "ambiguous") return ambiguousToolResult("call", toolName);
|
|
912
|
+
toolMeta = match;
|
|
846
913
|
if (!toolMeta) {
|
|
847
914
|
const suggestions = rankSuggestions(state, toolName, 5);
|
|
848
915
|
const suggestionText = suggestions.length > 0 ? ` Did you mean: ${suggestions.join(", ")}` : "";
|
|
@@ -879,6 +946,8 @@ export async function executeCall(
|
|
|
879
946
|
let prefixMatchedServer: string | undefined;
|
|
880
947
|
|
|
881
948
|
if (!serverName && !toolMeta && prefixMode !== "none") {
|
|
949
|
+
const lazyExactMatches: { serverName: string; toolMeta: ToolMetadata }[] = [];
|
|
950
|
+
const lazyFallbackMatches: { serverName: string; toolMeta: ToolMetadata }[] = [];
|
|
882
951
|
const candidates = Object.keys(state.config.mcpServers)
|
|
883
952
|
.filter(name => !isServerDisabled(state.config.mcpServers[name]))
|
|
884
953
|
.map(name => ({ name, prefix: getServerPrefix(name, prefixMode) }))
|
|
@@ -909,11 +978,22 @@ export async function executeCall(
|
|
|
909
978
|
|
|
910
979
|
if (!connected) continue;
|
|
911
980
|
if (!prefixMatchedServer) prefixMatchedServer = configuredServer;
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
981
|
+
const metadata = state.toolMetadata.get(configuredServer);
|
|
982
|
+
const exactMatches = getToolMatches(metadata, toolName, true);
|
|
983
|
+
if (exactMatches.length > 1) return ambiguousToolResult("call", toolName);
|
|
984
|
+
if (exactMatches.length === 1) {
|
|
985
|
+
lazyExactMatches.push({ serverName: configuredServer, toolMeta: exactMatches[0]! });
|
|
986
|
+
continue;
|
|
916
987
|
}
|
|
988
|
+
const fallbackMatches = getToolMatches(metadata, toolName, false);
|
|
989
|
+
if (fallbackMatches.length > 1) return ambiguousToolResult("call", toolName);
|
|
990
|
+
if (fallbackMatches.length === 1) lazyFallbackMatches.push({ serverName: configuredServer, toolMeta: fallbackMatches[0]! });
|
|
991
|
+
}
|
|
992
|
+
const lazyMatches = lazyExactMatches.length > 0 ? lazyExactMatches : lazyFallbackMatches;
|
|
993
|
+
if (lazyMatches.length > 1) return ambiguousToolResult("call", toolName);
|
|
994
|
+
if (lazyMatches.length === 1) {
|
|
995
|
+
serverName = lazyMatches[0]!.serverName;
|
|
996
|
+
toolMeta = lazyMatches[0]!.toolMeta;
|
|
917
997
|
}
|
|
918
998
|
}
|
|
919
999
|
|
|
@@ -1026,7 +1106,9 @@ export async function executeCall(
|
|
|
1026
1106
|
notifyToolMetadataUpdated(state, serverName, "proxy-call-reconnect");
|
|
1027
1107
|
markKeepAliveAfterConnect(state, serverName);
|
|
1028
1108
|
updateStatusBar(state);
|
|
1029
|
-
|
|
1109
|
+
const match = getSingleToolMatch(state.toolMetadata.get(serverName), toolName);
|
|
1110
|
+
if (match === "ambiguous") return ambiguousToolResult("call", toolName);
|
|
1111
|
+
toolMeta = match;
|
|
1030
1112
|
if (!toolMeta) {
|
|
1031
1113
|
const available = getToolNames(state, serverName);
|
|
1032
1114
|
const hint = available.length > 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { McpExtensionState } from "./state.ts";
|
|
2
|
-
import type { ToolMetadata } from "./types.ts";
|
|
3
|
-
import { getServerPrefix, isServerDisabled } from "./types.ts";
|
|
2
|
+
import type { ServerEntry, ToolMetadata, ToolPrefix } from "./types.ts";
|
|
3
|
+
import { getServerPrefix, getToolNameCandidates, isServerDisabled, matchesToolPattern, resolveToolPrefix } from "./types.ts";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Shortest field token allowed to stem-match a longer query token.
|
|
@@ -14,6 +14,7 @@ const FIELD_WEIGHTS = {
|
|
|
14
14
|
originalName: 10,
|
|
15
15
|
server: 8,
|
|
16
16
|
description: 5,
|
|
17
|
+
keywords: 5,
|
|
17
18
|
} as const;
|
|
18
19
|
|
|
19
20
|
export interface RankedToolMatch {
|
|
@@ -22,6 +23,36 @@ export interface RankedToolMatch {
|
|
|
22
23
|
score: number;
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the configured searchKeywords entries that apply to a tool.
|
|
28
|
+
* Keys match by original name, prefixed name, or glob — the same candidate
|
|
29
|
+
* set includeTools/excludeTools use — and all matching entries are unioned.
|
|
30
|
+
*/
|
|
31
|
+
export function resolveSearchKeywords(
|
|
32
|
+
definition: ServerEntry | undefined,
|
|
33
|
+
toolOriginalName: string,
|
|
34
|
+
serverName: string,
|
|
35
|
+
globalPrefix: ToolPrefix,
|
|
36
|
+
): string[] {
|
|
37
|
+
const map = definition?.searchKeywords;
|
|
38
|
+
if (!map || typeof map !== "object" || Array.isArray(map)) return [];
|
|
39
|
+
const candidates = getToolNameCandidates(toolOriginalName, serverName, resolveToolPrefix(definition, globalPrefix));
|
|
40
|
+
const keywords: string[] = [];
|
|
41
|
+
const seen = new Set<string>();
|
|
42
|
+
for (const [pattern, values] of Object.entries(map)) {
|
|
43
|
+
if (!Array.isArray(values)) continue;
|
|
44
|
+
if (!matchesToolPattern(candidates, [pattern])) continue;
|
|
45
|
+
for (const value of values) {
|
|
46
|
+
if (typeof value !== "string") continue;
|
|
47
|
+
const trimmed = value.trim();
|
|
48
|
+
if (!trimmed || seen.has(trimmed)) continue;
|
|
49
|
+
seen.add(trimmed);
|
|
50
|
+
keywords.push(trimmed);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return keywords;
|
|
54
|
+
}
|
|
55
|
+
|
|
25
56
|
export function normalizeSearchText(value: string): string {
|
|
26
57
|
return value
|
|
27
58
|
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
@@ -33,7 +64,7 @@ export function tokenize(value: string): string[] {
|
|
|
33
64
|
return normalizeSearchText(value).split(/[^a-z0-9]+/).filter(Boolean);
|
|
34
65
|
}
|
|
35
66
|
|
|
36
|
-
export function scoreToolMatch(tool: ToolMetadata, server: string, query: string): number | null {
|
|
67
|
+
export function scoreToolMatch(tool: ToolMetadata, server: string, query: string, keywords?: string[]): number | null {
|
|
37
68
|
const normalizedQuery = normalizeSearchText(query).trim();
|
|
38
69
|
const queryTokens = tokenize(query);
|
|
39
70
|
if (queryTokens.length === 0) return null;
|
|
@@ -78,6 +109,43 @@ export function scoreToolMatch(tool: ToolMetadata, server: string, query: string
|
|
|
78
109
|
}
|
|
79
110
|
}
|
|
80
111
|
|
|
112
|
+
// Configured keywords are discrete phrases, so the phrase-level bonus is
|
|
113
|
+
// computed per phrase (best match wins) rather than on a joined string,
|
|
114
|
+
// which would phrase-match queries spanning two unrelated keywords.
|
|
115
|
+
if (keywords !== undefined && keywords.length > 0) {
|
|
116
|
+
const weight = FIELD_WEIGHTS.keywords;
|
|
117
|
+
const phrases = keywords.map(keyword => normalizeSearchText(keyword).trim()).filter(Boolean);
|
|
118
|
+
let phraseScore = 0;
|
|
119
|
+
for (const phrase of phrases) {
|
|
120
|
+
if (phrase === normalizedQuery) {
|
|
121
|
+
phraseScore = Math.max(phraseScore, weight * 14);
|
|
122
|
+
phraseMatched = true;
|
|
123
|
+
wholeFieldExact = true;
|
|
124
|
+
} else if (phrase.startsWith(normalizedQuery)) {
|
|
125
|
+
phraseScore = Math.max(phraseScore, weight * 9);
|
|
126
|
+
phraseMatched = true;
|
|
127
|
+
} else if (phrase.includes(normalizedQuery)) {
|
|
128
|
+
phraseScore = Math.max(phraseScore, weight * 6);
|
|
129
|
+
phraseMatched = true;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
score += phraseScore;
|
|
133
|
+
|
|
134
|
+
const keywordTokens = phrases.flatMap(tokenize);
|
|
135
|
+
for (const token of queryTokens) {
|
|
136
|
+
if (keywordTokens.includes(token)) {
|
|
137
|
+
score += weight * 4;
|
|
138
|
+
matchedTokens.add(token);
|
|
139
|
+
} else if (keywordTokens.some(keywordToken => keywordToken.startsWith(token) || (keywordToken.length >= MIN_STEM_LENGTH && token.startsWith(keywordToken)))) {
|
|
140
|
+
score += weight * 2;
|
|
141
|
+
matchedTokens.add(token);
|
|
142
|
+
} else if (phrases.some(phrase => phrase.includes(token))) {
|
|
143
|
+
score += weight;
|
|
144
|
+
matchedTokens.add(token);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
81
149
|
const coverage = matchedTokens.size / queryTokens.length;
|
|
82
150
|
if (!phraseMatched && (queryTokens.length <= 2 ? coverage !== 1 : coverage < 0.6)) return null;
|
|
83
151
|
|
|
@@ -88,13 +156,24 @@ export function scoreToolMatch(tool: ToolMetadata, server: string, query: string
|
|
|
88
156
|
return score;
|
|
89
157
|
}
|
|
90
158
|
|
|
91
|
-
export function rankToolMatches(
|
|
159
|
+
export function rankToolMatches(
|
|
160
|
+
state: McpExtensionState,
|
|
161
|
+
query: string,
|
|
162
|
+
server?: string,
|
|
163
|
+
includeKeywords = true,
|
|
164
|
+
): RankedToolMatch[] {
|
|
92
165
|
const matches: RankedToolMatch[] = [];
|
|
166
|
+
const globalPrefix = state.config.settings?.toolPrefix ?? "server";
|
|
93
167
|
for (const [serverName, metadata] of state.toolMetadata.entries()) {
|
|
94
168
|
if (server && serverName !== server) continue;
|
|
95
|
-
|
|
169
|
+
const definition = state.config.mcpServers[serverName];
|
|
170
|
+
if (isServerDisabled(definition)) continue;
|
|
171
|
+
const hasKeywords = includeKeywords && definition?.searchKeywords !== undefined;
|
|
96
172
|
for (const tool of metadata) {
|
|
97
|
-
const
|
|
173
|
+
const keywords = hasKeywords
|
|
174
|
+
? resolveSearchKeywords(definition, tool.originalName, serverName, globalPrefix)
|
|
175
|
+
: undefined;
|
|
176
|
+
const score = scoreToolMatch(tool, serverName, query, keywords);
|
|
98
177
|
if (score !== null) matches.push({ server: serverName, tool, score });
|
|
99
178
|
}
|
|
100
179
|
}
|
|
@@ -123,5 +202,5 @@ export function rankSuggestions(state: McpExtensionState, name: string, limit: n
|
|
|
123
202
|
.sort((a, b) => b.length - a.length)
|
|
124
203
|
.map(candidate => name.slice(candidate.length + 1));
|
|
125
204
|
const query = stripped[0] ?? name;
|
|
126
|
-
return rankToolMatches(state, query).slice(0, limit).map(match => match.tool.name);
|
|
205
|
+
return rankToolMatches(state, query, undefined, false).slice(0, limit).map(match => match.tool.name);
|
|
127
206
|
}
|
|
@@ -31,7 +31,7 @@ import { createJsonSchemaValidator } from "./json-schema-validator.ts";
|
|
|
31
31
|
import { logger } from "./logger.ts";
|
|
32
32
|
import { McpOAuthProvider } from "./mcp-oauth-provider.ts";
|
|
33
33
|
import { extractOAuthConfig, supportsOAuth, type McpOAuthRuntime } from "./mcp-auth-flow.ts";
|
|
34
|
-
import type
|
|
34
|
+
import { invalidateAuthEntryCache, type AuthStorageOptions } from "./mcp-auth.ts";
|
|
35
35
|
import { registerSamplingHandler, type ServerSamplingConfig } from "./sampling-handler.ts";
|
|
36
36
|
import {
|
|
37
37
|
handleUrlElicitation,
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
type ServerElicitationConfig,
|
|
40
40
|
} from "./elicitation-handler.ts";
|
|
41
41
|
import {
|
|
42
|
+
interpolateEnvVars,
|
|
42
43
|
resolveBearerToken,
|
|
43
44
|
resolveCommandSecret,
|
|
44
45
|
resolveCommandSecretsRecord,
|
|
@@ -129,8 +130,11 @@ export interface ServerConnection {
|
|
|
129
130
|
lastUsedAt: number;
|
|
130
131
|
inFlight: number;
|
|
131
132
|
status: "connected" | "closed" | "needs-auth";
|
|
133
|
+
/** True once this needs-auth episode discarded the cached credential. */
|
|
134
|
+
credentialsInvalidated?: boolean;
|
|
132
135
|
}
|
|
133
136
|
|
|
137
|
+
|
|
134
138
|
type UiStreamListener = (serverName: string, notification: ServerStreamResultPatchNotification["params"]) => void;
|
|
135
139
|
type MetadataListChangedListener = (serverName: string, reason: string) => void;
|
|
136
140
|
|
|
@@ -238,10 +242,12 @@ export class McpServerManager {
|
|
|
238
242
|
return existing;
|
|
239
243
|
}
|
|
240
244
|
|
|
245
|
+
const credentialsInvalidated = existing?.status === "needs-auth"
|
|
246
|
+
&& existing.credentialsInvalidated === true;
|
|
241
247
|
const generation = this.closeGenerations.get(name) ?? 0;
|
|
242
248
|
const attemptController = new AbortController();
|
|
243
249
|
const attemptSignal = combineAbortSignals(ownedSignal, attemptController.signal);
|
|
244
|
-
const connectionAttempt = this.createConnection(name, definition, attemptSignal, ownedSignal);
|
|
250
|
+
const connectionAttempt = this.createConnection(name, definition, attemptSignal, ownedSignal, credentialsInvalidated);
|
|
245
251
|
const promise = definition.url
|
|
246
252
|
? connectionAttempt.catch(async error => { throw await this.enrichHttpConnectionError(definition, error); })
|
|
247
253
|
: connectionAttempt;
|
|
@@ -324,6 +330,7 @@ export class McpServerManager {
|
|
|
324
330
|
definition: ServerDefinition,
|
|
325
331
|
signal?: AbortSignal,
|
|
326
332
|
requestSignal?: AbortSignal,
|
|
333
|
+
credentialsInvalidated = false,
|
|
327
334
|
): Promise<ServerConnection> {
|
|
328
335
|
throwIfAborted(signal);
|
|
329
336
|
|
|
@@ -338,6 +345,7 @@ export class McpServerManager {
|
|
|
338
345
|
let client: Client;
|
|
339
346
|
let transport: Transport;
|
|
340
347
|
let clientConnected = false;
|
|
348
|
+
let invalidated = credentialsInvalidated;
|
|
341
349
|
let transportAlreadyTraced = false;
|
|
342
350
|
let stderrTail: Buffer<ArrayBufferLike> = Buffer.alloc(0);
|
|
343
351
|
const configuredTransports = [definition.command, definition.url, definition.socket]
|
|
@@ -351,7 +359,7 @@ export class McpServerManager {
|
|
|
351
359
|
if (definition.command) {
|
|
352
360
|
client = this.createClient(name, definition);
|
|
353
361
|
let command = definition.command;
|
|
354
|
-
let args = definition.args ?? [];
|
|
362
|
+
let args = (definition.args ?? []).map(interpolateEnvVars);
|
|
355
363
|
|
|
356
364
|
if (command === "npx" || command === "npm") {
|
|
357
365
|
const resolved = await resolveNpxBinary(command, args, signal);
|
|
@@ -387,9 +395,11 @@ export class McpServerManager {
|
|
|
387
395
|
requestOptions,
|
|
388
396
|
signal,
|
|
389
397
|
traceObserver,
|
|
398
|
+
invalidated,
|
|
390
399
|
);
|
|
391
400
|
client = httpConnection.client;
|
|
392
401
|
transport = httpConnection.transport;
|
|
402
|
+
invalidated = httpConnection.credentialsInvalidated;
|
|
393
403
|
if (httpConnection.status === "needs-auth") {
|
|
394
404
|
return {
|
|
395
405
|
client,
|
|
@@ -401,6 +411,7 @@ export class McpServerManager {
|
|
|
401
411
|
lastUsedAt: Date.now(),
|
|
402
412
|
inFlight: 0,
|
|
403
413
|
status: "needs-auth",
|
|
414
|
+
credentialsInvalidated: invalidated,
|
|
404
415
|
};
|
|
405
416
|
}
|
|
406
417
|
clientConnected = true;
|
|
@@ -477,6 +488,10 @@ export class McpServerManager {
|
|
|
477
488
|
// A cleanup failure remains a setup failure rather than being hidden
|
|
478
489
|
// behind needs-auth.
|
|
479
490
|
if (isUnauthorizedHttpError(error) && supportsOAuth(definition) && cleanupFailures.length === 0) {
|
|
491
|
+
if (!invalidated) {
|
|
492
|
+
invalidateAuthEntryCache(name);
|
|
493
|
+
invalidated = true;
|
|
494
|
+
}
|
|
480
495
|
return {
|
|
481
496
|
client,
|
|
482
497
|
transport,
|
|
@@ -487,6 +502,7 @@ export class McpServerManager {
|
|
|
487
502
|
lastUsedAt: Date.now(),
|
|
488
503
|
inFlight: 0,
|
|
489
504
|
status: "needs-auth",
|
|
505
|
+
credentialsInvalidated: invalidated,
|
|
490
506
|
};
|
|
491
507
|
}
|
|
492
508
|
|
|
@@ -694,7 +710,8 @@ export class McpServerManager {
|
|
|
694
710
|
requestOptions: RequestOptions | undefined,
|
|
695
711
|
signal?: AbortSignal,
|
|
696
712
|
traceObserver?: McpTraceObserver,
|
|
697
|
-
|
|
713
|
+
credentialsInvalidated = false,
|
|
714
|
+
): Promise<{ client: Client; transport: Transport; status: "connected" | "needs-auth"; credentialsInvalidated: boolean }> {
|
|
698
715
|
throwIfAborted(signal);
|
|
699
716
|
const serverUrl = resolveServerUrl(definition)!;
|
|
700
717
|
const url = new URL(serverUrl);
|
|
@@ -791,9 +808,10 @@ export class McpServerManager {
|
|
|
791
808
|
// OAuth challenge; use SSE only for definitive endpoint incompatibility.
|
|
792
809
|
// Agent Plugins set httpTransport, and their declared transport is used without fallback.
|
|
793
810
|
let kind: "streamable-http" | "sse" = definition.httpTransport ?? "streamable-http";
|
|
811
|
+
let invalidated = credentialsInvalidated;
|
|
794
812
|
for (;;) {
|
|
795
813
|
const result = await attempt(kind);
|
|
796
|
-
if (result.status === "connected") return result;
|
|
814
|
+
if (result.status === "connected") return { ...result, credentialsInvalidated: invalidated };
|
|
797
815
|
if (result.error instanceof AggregateError
|
|
798
816
|
&& result.error.message === "MCP connection abort cleanup failed") {
|
|
799
817
|
throw result.error;
|
|
@@ -806,7 +824,16 @@ export class McpServerManager {
|
|
|
806
824
|
}
|
|
807
825
|
if (isUnauthorizedHttpError(result.error)) {
|
|
808
826
|
if (supportsOAuth(definition)) {
|
|
809
|
-
|
|
827
|
+
if (!invalidated) {
|
|
828
|
+
invalidateAuthEntryCache(serverName);
|
|
829
|
+
invalidated = true;
|
|
830
|
+
}
|
|
831
|
+
return {
|
|
832
|
+
client: result.client,
|
|
833
|
+
transport: result.transport,
|
|
834
|
+
status: "needs-auth",
|
|
835
|
+
credentialsInvalidated: invalidated,
|
|
836
|
+
};
|
|
810
837
|
}
|
|
811
838
|
throw result.error;
|
|
812
839
|
}
|
|
@@ -850,6 +877,7 @@ export class McpServerManager {
|
|
|
850
877
|
return { prompts, failed: false };
|
|
851
878
|
} catch (error) {
|
|
852
879
|
if (requestOptions?.signal?.aborted) throwIfAborted(requestOptions.signal);
|
|
880
|
+
if (isUnauthorizedHttpError(error)) throw error;
|
|
853
881
|
const message = error instanceof Error ? error.message : String(error);
|
|
854
882
|
logger.debug(`MCP: prompts/list failed: ${message}`);
|
|
855
883
|
return { prompts: [], failed: true };
|
|
@@ -871,10 +899,11 @@ export class McpServerManager {
|
|
|
871
899
|
} while (cursor);
|
|
872
900
|
|
|
873
901
|
return allResources;
|
|
874
|
-
} catch {
|
|
902
|
+
} catch (error) {
|
|
875
903
|
if (requestOptions?.signal?.aborted) {
|
|
876
904
|
throwIfAborted(requestOptions.signal);
|
|
877
905
|
}
|
|
906
|
+
if (isUnauthorizedHttpError(error)) throw error;
|
|
878
907
|
// The server advertises resources but the listing failed
|
|
879
908
|
return [];
|
|
880
909
|
}
|
|
@@ -20,10 +20,12 @@
|
|
|
20
20
|
// many things other than "your session is gone"
|
|
21
21
|
// - treat generic -32000/ConnectionClosed errors as session expiry
|
|
22
22
|
// - treat AbortError/cancellation as a session failure
|
|
23
|
-
import { ProtocolError, SdkHttpError } from "@modelcontextprotocol/client";
|
|
23
|
+
import { ProtocolError, SdkHttpError, UnauthorizedError } from "@modelcontextprotocol/client";
|
|
24
24
|
import { logger } from "./logger.ts";
|
|
25
25
|
import { throwIfAborted } from "./abort.ts";
|
|
26
26
|
import { isServerDisabled, type McpConfig } from "./types.ts";
|
|
27
|
+
import { supportsOAuth } from "./mcp-auth-flow.ts";
|
|
28
|
+
import { invalidateAuthEntryCache } from "./mcp-auth.ts";
|
|
27
29
|
import type { McpServerManager, ServerConnection } from "./server-manager.ts";
|
|
28
30
|
|
|
29
31
|
/**
|
|
@@ -106,6 +108,11 @@ export async function withSessionRecovery<T>(
|
|
|
106
108
|
try {
|
|
107
109
|
return await fn(connection);
|
|
108
110
|
} catch (err) {
|
|
111
|
+
const definition = deps.config.mcpServers[serverName];
|
|
112
|
+
if (definition && supportsOAuth(definition)
|
|
113
|
+
&& (err instanceof UnauthorizedError || (err instanceof SdkHttpError && err.status === 401))) {
|
|
114
|
+
invalidateAuthEntryCache(serverName);
|
|
115
|
+
}
|
|
109
116
|
if (!isTerminatedSession(err, hadSessionId)) {
|
|
110
117
|
throw err;
|
|
111
118
|
}
|
|
@@ -114,7 +121,6 @@ export async function withSessionRecovery<T>(
|
|
|
114
121
|
// connection's definition, in case config changed since connect. If the
|
|
115
122
|
// server was removed from config in the meantime there is nothing to
|
|
116
123
|
// reconnect to, so surface the original error.
|
|
117
|
-
const definition = deps.config.mcpServers[serverName];
|
|
118
124
|
if (!definition) {
|
|
119
125
|
throw err;
|
|
120
126
|
}
|
|
@@ -24,19 +24,59 @@ export function isToolCallApprovalRequired(
|
|
|
24
24
|
config: McpConfig,
|
|
25
25
|
serverName: string,
|
|
26
26
|
toolMeta: Pick<ToolMetadata, "originalName">,
|
|
27
|
+
toolMetadata?: ReadonlyMap<string, readonly ToolMetadata[]>,
|
|
27
28
|
): boolean {
|
|
28
29
|
const definition = config.mcpServers[serverName];
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
: config.settings?.approveTools;
|
|
30
|
+
const serverApproval = definition?.approveTools;
|
|
31
|
+
const approval = serverApproval !== undefined ? serverApproval : config.settings?.approveTools;
|
|
32
32
|
|
|
33
33
|
if (approval === true) return true;
|
|
34
34
|
if (!Array.isArray(approval) || approval.length === 0) return false;
|
|
35
35
|
|
|
36
36
|
const prefix = resolveToolPrefix(definition, config.settings?.toolPrefix);
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
approval
|
|
37
|
+
const currentCandidates = getToolNameCandidates(toolMeta.originalName, serverName, prefix, false);
|
|
38
|
+
if (serverApproval !== undefined) {
|
|
39
|
+
if (matchesToolPattern(currentCandidates, approval)) return true;
|
|
40
|
+
if (!toolMetadata) return matchesToolPattern(getToolNameCandidates(toolMeta.originalName, serverName, prefix), approval);
|
|
41
|
+
const legacyCandidates = getToolNameCandidates(toolMeta.originalName, serverName, prefix);
|
|
42
|
+
const legacyEmittedName = [...currentCandidates].find(candidate => candidate !== toolMeta.originalName)?.replace(/-/g, "_");
|
|
43
|
+
if (legacyEmittedName) legacyCandidates.add(legacyEmittedName);
|
|
44
|
+
for (const candidate of currentCandidates) legacyCandidates.delete(candidate);
|
|
45
|
+
const otherCurrentCandidates = new Set<string>();
|
|
46
|
+
for (const tool of toolMetadata.get(serverName) ?? []) {
|
|
47
|
+
for (const candidate of getToolNameCandidates(tool.originalName, serverName, prefix, false)) {
|
|
48
|
+
otherCurrentCandidates.add(candidate);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for (const candidate of currentCandidates) otherCurrentCandidates.delete(candidate);
|
|
52
|
+
return approval.some(pattern =>
|
|
53
|
+
matchesToolPattern(legacyCandidates, [pattern])
|
|
54
|
+
&& !matchesToolPattern(otherCurrentCandidates, [pattern]),
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
if (matchesToolPattern(currentCandidates, approval)) return true;
|
|
60
|
+
if (!toolMetadata) return false;
|
|
61
|
+
|
|
62
|
+
const legacyCandidates = getToolNameCandidates(toolMeta.originalName, serverName, prefix);
|
|
63
|
+
const legacyEmittedName = [...currentCandidates].find(candidate => candidate !== toolMeta.originalName)?.replace(/-/g, "_");
|
|
64
|
+
if (legacyEmittedName) legacyCandidates.add(legacyEmittedName);
|
|
65
|
+
for (const candidate of currentCandidates) legacyCandidates.delete(candidate);
|
|
66
|
+
const otherCurrentCandidates = new Set<string>();
|
|
67
|
+
for (const [name, metadata] of toolMetadata) {
|
|
68
|
+
const otherPrefix = resolveToolPrefix(config.mcpServers[name], config.settings?.toolPrefix);
|
|
69
|
+
for (const tool of metadata) {
|
|
70
|
+
for (const candidate of getToolNameCandidates(tool.originalName, name, otherPrefix, false)) {
|
|
71
|
+
otherCurrentCandidates.add(candidate);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
for (const candidate of currentCandidates) otherCurrentCandidates.delete(candidate);
|
|
76
|
+
|
|
77
|
+
return approval.some(pattern =>
|
|
78
|
+
matchesToolPattern(legacyCandidates, [pattern])
|
|
79
|
+
&& !matchesToolPattern(otherCurrentCandidates, [pattern]),
|
|
40
80
|
);
|
|
41
81
|
}
|
|
42
82
|
|
|
@@ -94,6 +134,7 @@ export async function ensureToolCallApproved(
|
|
|
94
134
|
args: Record<string, unknown> | undefined,
|
|
95
135
|
signal?: AbortSignal,
|
|
96
136
|
origin: McpToolApprovalOrigin = toolMeta.resourceUri ? "resource" : "proxy",
|
|
137
|
+
approvalMetadata?: ReadonlyMap<string, readonly ToolMetadata[]>,
|
|
97
138
|
): Promise<ToolCallApprovalResult> {
|
|
98
139
|
const cacheKey = `${serverName}\u0000${toolMeta.originalName}`;
|
|
99
140
|
const approvedToolCalls = state.approvedToolCalls ??= new Map<string, true>();
|
|
@@ -109,7 +150,7 @@ export async function ensureToolCallApproved(
|
|
|
109
150
|
}
|
|
110
151
|
if (brokerDecision === "deny") return { ok: false, reason: "denied" };
|
|
111
152
|
|
|
112
|
-
if (!isToolCallApprovalRequired(state.config, serverName, toolMeta)) {
|
|
153
|
+
if (!isToolCallApprovalRequired(state.config, serverName, toolMeta, approvalMetadata ?? state.toolMetadata)) {
|
|
113
154
|
return { ok: true };
|
|
114
155
|
}
|
|
115
156
|
|