@herbertgao/pi-extensions 2026.8.11 → 2026.8.13
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 +23 -10
- package/{node_modules/@herbertgao/pi-mermaid-open/LICENSE → THIRD_PARTY_NOTICES.md} +19 -1
- package/node_modules/@czottmann/pi-automode/CHANGELOG.md +15 -0
- package/node_modules/@czottmann/pi-automode/README.md +5 -3
- package/node_modules/@czottmann/pi-automode/docs/adr/ADR-001-permission-precedence-and-trust-boundaries.md +2 -0
- package/node_modules/@czottmann/pi-automode/docs/adr/ADR-002-global-config-in-extension-data-directory.md +60 -0
- package/node_modules/@czottmann/pi-automode/docs/adr/INDEX.md +1 -0
- package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +4 -4
- package/node_modules/@czottmann/pi-automode/docs/configuration.md +20 -4
- package/node_modules/@czottmann/pi-automode/docs/observability-logging.md +1 -1
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/bash.ts +692 -0
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/config.ts +181 -3
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +4 -1
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +143 -36
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/hard-deny.ts +103 -148
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/paths.ts +32 -6
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/permissions.ts +344 -22
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode.ts +1 -0
- package/node_modules/@czottmann/pi-automode/package.json +4 -1
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -1
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +4 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +21 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/core.ts +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +19 -19
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +4 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +2 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +10 -2
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +10 -0
- package/node_modules/@herbertgao/pi-subagents/README.md +21 -15
- package/node_modules/@herbertgao/pi-subagents/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +11 -8
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +184 -106
- package/node_modules/@herbertgao/pi-subagents/src/cross-extension-rpc.ts +31 -11
- package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +9 -1
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +156 -31
- package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +17 -0
- package/node_modules/@herbertgao/pi-subagents/src/model-resolver.ts +14 -0
- package/node_modules/@herbertgao/pi-subagents/src/schedule.ts +8 -0
- package/node_modules/@herbertgao/pi-subagents/src/settings.ts +32 -1
- package/node_modules/@herbertgao/pi-subagents/src/types.ts +17 -2
- package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +23 -2
- package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +259 -24
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
- package/node_modules/@luxusai/pi-hindsight/CHANGELOG.md +376 -0
- package/node_modules/@luxusai/pi-hindsight/README.md +92 -0
- package/node_modules/@luxusai/pi-hindsight/docs/adr/001-memory-lifecycle-and-scope.md +56 -0
- package/node_modules/@luxusai/pi-hindsight/docs/adr/002-explicit-routing-strategy-seam.md +169 -0
- package/node_modules/@luxusai/pi-hindsight/docs/adr/003-tui-memory-mode-vocabulary.md +107 -0
- package/node_modules/@luxusai/pi-hindsight/docs/adr/004-lifeos-dual-bank-design.md +89 -0
- package/node_modules/@luxusai/pi-hindsight/docs/adr/005-domain-banks-and-agent-first-surface.md +189 -0
- package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark.webp +0 -0
- package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark@2x.webp +0 -0
- package/node_modules/@luxusai/pi-hindsight/docs/coding-memory-evaluation.md +54 -0
- package/node_modules/@luxusai/pi-hindsight/docs/compatibility.md +75 -0
- package/node_modules/@luxusai/pi-hindsight/docs/hindsight-core-functions.md +300 -0
- package/node_modules/@luxusai/pi-hindsight/docs/mission-and-mental-model-quality.md +158 -0
- package/node_modules/@luxusai/pi-hindsight/docs/next-opt-out-design.md +164 -0
- package/node_modules/@luxusai/pi-hindsight/docs/risky-memory-modes.md +139 -0
- package/node_modules/@luxusai/pi-hindsight/docs/starter-mental-model-suggestions.md +74 -0
- package/node_modules/@luxusai/pi-hindsight/docs/surface-reference.md +261 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-operations.ts +151 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-selection.ts +18 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-settings-presenter.ts +46 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-templates.ts +383 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/banks/banking.ts +240 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/banks/knowledge-page-seed.ts +176 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/banks/retain-strategies.ts +178 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/client/client-retry.ts +41 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/client/client.ts +386 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/client/fetch-compat.ts +39 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/client/timeout.ts +34 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/config/config-defaults.ts +140 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-model.ts +61 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-registry.ts +925 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/config/config-field-paths.ts +247 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/config/config-normalize.ts +547 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/config/config-writer.ts +480 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/config/config.ts +182 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/config/setup-gate.ts +86 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-execute.ts +1010 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-parse.ts +175 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-plan.ts +425 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-presentation.ts +210 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-sessions.ts +817 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/index.ts +25 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/git-seed.ts +319 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-recall.ts +196 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-retain.ts +189 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-runtime.ts +65 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle.ts +260 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/mental-models.ts +244 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/observation-scopes.ts +59 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-cleanup.ts +82 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-visibility.ts +45 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall.ts +331 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-cursor.ts +354 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-job-builder.ts +66 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-receipts.ts +132 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain.ts +228 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-bank-template-operations.ts +74 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-config-operations.ts +16 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-control-operations.ts +648 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-diagnostics-operations.ts +105 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-identity.ts +68 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-service.ts +71 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-types.ts +13 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-recall-operations.ts +162 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-retain-operations.ts +131 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-scope.ts +104 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-session-operations.ts +37 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/operation-catalog.ts +893 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/reflect-presenter.ts +35 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/scope-migrate.ts +177 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/operations/tools.ts +7 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/queue/flush-presenter.ts +21 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/queue/jsonl-queue-store.ts +112 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-delivery.ts +111 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-lock.ts +214 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-operations.ts +70 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue.ts +392 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/bank-template-presentation.ts +45 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/commands.ts +9 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/guided-setup.ts +860 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/prefill-input.ts +105 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-flow.ts +189 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-server-probe.ts +299 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-actions.ts +201 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-facts.ts +47 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-render.ts +239 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-types.ts +50 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui.ts +259 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/tui/tool-presenters.ts +77 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/types.ts +534 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/diagnostics.ts +319 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/messages.ts +325 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/sanitize.ts +42 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-memory-meta.ts +244 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-operations.ts +56 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/session.ts +50 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-fields.ts +167 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-health.ts +207 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/utils/status.ts +120 -0
- package/node_modules/@luxusai/pi-hindsight/extensions/version.ts +8 -0
- package/node_modules/@luxusai/pi-hindsight/package.json +118 -0
- package/node_modules/@luxusai/pi-hindsight/skills/hindsight-memory-doctor/SKILL.md +75 -0
- package/node_modules/@narumitw/pi-btw/dist/index.ts +143 -46
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +3 -3
- package/node_modules/@narumitw/pi-btw/package.json +6 -6
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +136 -22
- package/node_modules/{@herbertgao → @tifan}/pi-copy-response/README.md +2 -4
- package/node_modules/{@herbertgao → @tifan}/pi-copy-response/package.json +5 -15
- package/node_modules/{@herbertgao → @tifan}/pi-handoff/README.md +4 -6
- package/node_modules/@tifan/pi-handoff/package.json +43 -0
- package/node_modules/{@herbertgao → @tifan}/pi-handoff/src/index.ts +1 -1
- package/node_modules/{@herbertgao → @tifan}/pi-inline-skills/README.md +2 -4
- package/node_modules/{@herbertgao → @tifan}/pi-inline-skills/package.json +5 -15
- package/node_modules/{@herbertgao → @tifan}/pi-mermaid-open/README.md +5 -7
- package/node_modules/{@herbertgao → @tifan}/pi-mermaid-open/package.json +5 -19
- package/node_modules/{@herbertgao → @tifan}/pi-preferred-thinking/README.md +4 -6
- package/node_modules/{@herbertgao → @tifan}/pi-preferred-thinking/package.json +6 -16
- package/node_modules/{@herbertgao → @tifan}/pi-preferred-thinking/src/index.ts +1 -6
- package/node_modules/{@herbertgao → @tifan}/pi-recap/README.md +4 -6
- package/node_modules/@tifan/pi-recap/package.json +42 -0
- package/node_modules/{@herbertgao → @tifan}/pi-recap/src/models.ts +1 -6
- package/node_modules/{@herbertgao → @tifan}/pi-rename/README.md +13 -8
- package/node_modules/@tifan/pi-rename/package.json +47 -0
- package/node_modules/@tifan/pi-rename/src/herdr-label.ts +4 -0
- package/node_modules/{@herbertgao → @tifan}/pi-rename/src/index.ts +4 -16
- package/node_modules/{@herbertgao → @tifan}/pi-rename/src/models.ts +1 -6
- package/node_modules/{@herbertgao → @tifan}/pi-titlebar-spinner/README.md +2 -4
- package/node_modules/{@herbertgao → @tifan}/pi-titlebar-spinner/package.json +5 -15
- package/node_modules/pi-lens/CHANGELOG.md +96 -1
- package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +2 -1
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +14 -5
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +48 -18
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +1 -3
- package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +16 -4
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +3 -1
- package/node_modules/pi-lens/dist/clients/biome-client.js +1 -3
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +21 -8
- package/node_modules/pi-lens/dist/clients/bounded-cache.js +24 -8
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +4 -3
- package/node_modules/pi-lens/dist/clients/cache-manager.js +6 -7
- package/node_modules/pi-lens/dist/clients/cache-observability.js +461 -66
- package/node_modules/pi-lens/dist/clients/call-graph.js +238 -63
- package/node_modules/pi-lens/dist/clients/codebase-model.js +13 -5
- package/node_modules/pi-lens/dist/clients/complexity-client.js +1 -3
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +19 -8
- package/node_modules/pi-lens/dist/clients/dead-code-logger.js +1 -2
- package/node_modules/pi-lens/dist/clients/deadline-utils.js +3 -1
- package/node_modules/pi-lens/dist/clients/debug-heap.js +8 -1
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +1 -1
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +11 -7
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +39 -2
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +5 -1
- package/node_modules/pi-lens/dist/clients/dispatch/auxiliary-lsp.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +13 -1
- package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +9 -4
- package/node_modules/pi-lens/dist/clients/dispatch/rules/error-swallowing.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/rules/pass-through-wrappers.js +6 -2
- package/node_modules/pi-lens/dist/clients/dispatch/rules/unsafe-boundary.js +31 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +5 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +10 -14
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +5 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +25 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +13 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +4 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +25 -10
- package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +28 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +6 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +212 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +8 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +10 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +23 -14
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +7 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +5 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +7 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +6 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +13 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +13 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +17 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +7 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +24 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +35 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +1 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +6 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/suppress-writer.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/types.js +6 -1
- package/node_modules/pi-lens/dist/clients/dispatch/utils/lsp-diagnostics.js +6 -2
- package/node_modules/pi-lens/dist/clients/extension-log.js +5 -3
- package/node_modules/pi-lens/dist/clients/feature-hints.js +8 -1
- package/node_modules/pi-lens/dist/clients/file-kinds.js +34 -129
- package/node_modules/pi-lens/dist/clients/file-utils.js +17 -1
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +57 -2
- package/node_modules/pi-lens/dist/clients/formatters.js +144 -27
- package/node_modules/pi-lens/dist/clients/freshness.js +38 -0
- package/node_modules/pi-lens/dist/clients/git-guard.js +43 -13
- package/node_modules/pi-lens/dist/clients/go-client.js +1 -3
- package/node_modules/pi-lens/dist/clients/grammar-source.js +1 -1
- package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +30 -4
- package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +1 -3
- package/node_modules/pi-lens/dist/clients/install-diagnostics.js +4 -2
- package/node_modules/pi-lens/dist/clients/installer/index.js +86 -77
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +5 -2
- package/node_modules/pi-lens/dist/clients/instance-registry.js +4 -2
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +6 -1
- package/node_modules/pi-lens/dist/clients/knip-client.js +29 -15
- package/node_modules/pi-lens/dist/clients/language-profile.js +4 -1
- package/node_modules/pi-lens/dist/clients/latency-logger.js +13 -2
- package/node_modules/pi-lens/dist/clients/lens-config.js +1 -1
- package/node_modules/pi-lens/dist/clients/lens-map.js +8 -3
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/config.js +4 -6
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +119 -32
- package/node_modules/pi-lens/dist/clients/lsp/index.js +94 -35
- package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +4 -1
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +11 -3
- package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +152 -0
- package/node_modules/pi-lens/dist/clients/lsp/server.js +19 -7
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +10 -2
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +9 -4
- package/node_modules/pi-lens/dist/clients/lsp-document-symbols.js +1 -3
- package/node_modules/pi-lens/dist/clients/lsp-mutation.js +10 -20
- package/node_modules/pi-lens/dist/clients/mcp/host-shim.js +1 -1
- package/node_modules/pi-lens/dist/clients/memory-sampler.js +73 -7
- package/node_modules/pi-lens/dist/clients/metrics-client.js +1 -3
- package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +8 -3
- package/node_modules/pi-lens/dist/clients/module-report-lsp.js +7 -1
- package/node_modules/pi-lens/dist/clients/module-report.js +14 -17
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +3 -5
- package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +220 -0
- package/node_modules/pi-lens/dist/clients/package-manager.js +6 -1
- package/node_modules/pi-lens/dist/clients/pipeline.js +57 -9
- package/node_modules/pi-lens/dist/clients/project-conventions.js +2 -8
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +10 -7
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +2 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +3 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +13 -9
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +3 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -2
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +12 -5
- package/node_modules/pi-lens/dist/clients/project-snapshot-fingerprint.js +48 -0
- package/node_modules/pi-lens/dist/clients/project-snapshot-persist-worker.js +15 -1
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +472 -66
- package/node_modules/pi-lens/dist/clients/read-expansion.js +12 -2
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +5 -4
- package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +5 -2
- package/node_modules/pi-lens/dist/clients/read-guard.js +1 -1
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +1 -1
- package/node_modules/pi-lens/dist/clients/reverse-deps.js +6 -5
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +42 -17
- package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +8 -2
- package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +10 -1
- package/node_modules/pi-lens/dist/clients/review-graph/query.js +2 -1
- package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +4 -2
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +2 -2
- package/node_modules/pi-lens/dist/clients/ruff-client.js +6 -5
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +63 -31
- package/node_modules/pi-lens/dist/clients/runtime-context.js +12 -4
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +83 -4
- package/node_modules/pi-lens/dist/clients/runtime-session.js +14 -0
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +59 -3
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +148 -34
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +147 -6
- package/node_modules/pi-lens/dist/clients/rust-client.js +1 -3
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +150 -11
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +1 -3
- package/node_modules/pi-lens/dist/clients/session-summary.js +3 -1
- package/node_modules/pi-lens/dist/clients/sg-runner.js +1 -3
- package/node_modules/pi-lens/dist/clients/source-filter.js +10 -6
- package/node_modules/pi-lens/dist/clients/source-walker.js +1 -1
- package/node_modules/pi-lens/dist/clients/spawn-timeout-cooldown.js +96 -0
- package/node_modules/pi-lens/dist/clients/startup-scan.js +4 -1
- package/node_modules/pi-lens/dist/clients/tool-definition.js +7 -0
- package/node_modules/pi-lens/dist/clients/tool-policy.js +26 -11
- package/node_modules/pi-lens/dist/clients/tool-render.js +2 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +10 -4
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +2 -2
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +12 -5
- package/node_modules/pi-lens/dist/clients/warm-attach.js +12 -5
- package/node_modules/pi-lens/dist/clients/widget-state.js +27 -11
- package/node_modules/pi-lens/dist/clients/word-index.js +3 -1
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +8 -2
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +9 -1
- package/node_modules/pi-lens/dist/i18n.js +13 -5
- package/node_modules/pi-lens/dist/index.js +10797 -8627
- package/node_modules/pi-lens/dist/mcp/server.js +3 -1
- package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +68 -12
- package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +21 -7
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +164 -35
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +24 -7
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +3 -1
- package/node_modules/pi-lens/dist/tools/module-report.js +1 -1
- package/node_modules/pi-lens/dist/tools/project-report.js +1 -1
- package/node_modules/pi-lens/dist/tools/symbol-search.js +5 -2
- package/node_modules/pi-lens/docs/durable-store-audit-1202.md +1 -1
- package/node_modules/pi-lens/docs/servercapabilities.md +1 -1
- package/node_modules/pi-lens/package.json +160 -156
- package/node_modules/pi-lens/rules/typos/_typos.toml +2 -2
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +31 -13
- package/node_modules/pi-lens/scripts/download-grammars.js +9 -3
- package/node_modules/pi-lens/scripts/rpc-load-check.mjs +30 -9
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +47 -0
- package/node_modules/pi-mcp-adapter/README.md +42 -13
- package/node_modules/pi-mcp-adapter/commands.ts +4 -2
- package/node_modules/pi-mcp-adapter/config.ts +45 -5
- package/node_modules/pi-mcp-adapter/direct-tools.ts +103 -92
- package/node_modules/pi-mcp-adapter/dist/agent-dir.d.ts +10 -0
- package/node_modules/pi-mcp-adapter/dist/agent-dir.js +63 -0
- package/node_modules/pi-mcp-adapter/dist/agent-dir.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/agent-plugin-loader.d.ts +8 -0
- package/node_modules/pi-mcp-adapter/dist/agent-plugin-loader.js +356 -0
- package/node_modules/pi-mcp-adapter/dist/agent-plugin-loader.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/config.d.ts +123 -0
- package/node_modules/pi-mcp-adapter/dist/config.js +1087 -0
- package/node_modules/pi-mcp-adapter/dist/config.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/metadata-cache.d.ts +19 -0
- package/node_modules/pi-mcp-adapter/dist/metadata-cache.js +319 -0
- package/node_modules/pi-mcp-adapter/dist/metadata-cache.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/package-mcp-loader.d.ts +2 -0
- package/node_modules/pi-mcp-adapter/dist/package-mcp-loader.js +156 -0
- package/node_modules/pi-mcp-adapter/dist/package-mcp-loader.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/resource-tools.d.ts +1 -0
- package/node_modules/pi-mcp-adapter/dist/resource-tools.js +15 -0
- package/node_modules/pi-mcp-adapter/dist/resource-tools.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/types.d.ts +564 -0
- package/node_modules/pi-mcp-adapter/dist/types.js +287 -0
- package/node_modules/pi-mcp-adapter/dist/types.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/ui-app-bridge-helpers.d.ts +6 -0
- package/node_modules/pi-mcp-adapter/dist/ui-app-bridge-helpers.js +37 -0
- package/node_modules/pi-mcp-adapter/dist/ui-app-bridge-helpers.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/ui-stream-types.d.ts +104 -0
- package/node_modules/pi-mcp-adapter/dist/ui-stream-types.js +59 -0
- package/node_modules/pi-mcp-adapter/dist/ui-stream-types.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/ui-tool-visibility.d.ts +4 -0
- package/node_modules/pi-mcp-adapter/dist/ui-tool-visibility.js +27 -0
- package/node_modules/pi-mcp-adapter/dist/ui-tool-visibility.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/utils.d.ts +32 -0
- package/node_modules/pi-mcp-adapter/dist/utils.js +338 -0
- package/node_modules/pi-mcp-adapter/dist/utils.js.map +1 -0
- package/node_modules/pi-mcp-adapter/failure-backoff.ts +23 -0
- package/node_modules/pi-mcp-adapter/index.ts +204 -82
- package/node_modules/pi-mcp-adapter/init.ts +41 -30
- package/node_modules/pi-mcp-adapter/lifecycle.ts +16 -8
- package/node_modules/pi-mcp-adapter/mcp-code.ts +2 -0
- package/node_modules/pi-mcp-adapter/mcp-oauth-provider.ts +66 -16
- package/node_modules/pi-mcp-adapter/mcp-output-guard.ts +95 -7
- package/node_modules/pi-mcp-adapter/mcp-probe.ts +18 -4
- package/node_modules/pi-mcp-adapter/mcp-references.ts +368 -0
- package/node_modules/pi-mcp-adapter/mcp-status.ts +8 -19
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +20 -12
- package/node_modules/pi-mcp-adapter/namespace-tools.ts +276 -0
- package/node_modules/pi-mcp-adapter/package.json +24 -6
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +66 -10
- package/node_modules/pi-mcp-adapter/search-ranking.ts +2 -0
- package/node_modules/pi-mcp-adapter/server-manager.ts +63 -12
- package/node_modules/pi-mcp-adapter/state.ts +1 -3
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +16 -2
- package/node_modules/pi-mcp-adapter/types.ts +48 -1
- package/node_modules/pi-mcp-adapter/ui-server.ts +3 -19
- package/node_modules/pi-mcp-adapter/ui-session.ts +2 -1
- package/node_modules/pi-mcp-adapter/utils.ts +21 -19
- package/node_modules/pi-web-access/CHANGELOG.md +26 -0
- package/node_modules/pi-web-access/README.md +71 -14
- package/node_modules/pi-web-access/chrome-cookies.ts +115 -35
- package/node_modules/pi-web-access/credential-source.ts +6 -2
- package/node_modules/pi-web-access/curator-page.ts +5 -3
- package/node_modules/pi-web-access/curator-server.ts +2 -1
- package/node_modules/pi-web-access/extract.ts +89 -9
- package/node_modules/pi-web-access/fetch-params.ts +13 -0
- package/node_modules/pi-web-access/gemini-adc.ts +298 -0
- package/node_modules/pi-web-access/gemini-api.ts +59 -15
- package/node_modules/pi-web-access/gemini-search.ts +58 -18
- package/node_modules/pi-web-access/gemini-url-context.ts +3 -2
- package/node_modules/pi-web-access/gemini-web-config.ts +57 -7
- package/node_modules/pi-web-access/gemini-web.ts +9 -3
- package/node_modules/pi-web-access/github-api.ts +5 -4
- package/node_modules/pi-web-access/github-issue-pr.ts +700 -0
- package/node_modules/pi-web-access/index.ts +403 -297
- package/node_modules/pi-web-access/kimi-search.ts +234 -0
- package/node_modules/pi-web-access/openai-search.ts +118 -21
- package/node_modules/pi-web-access/package.json +3 -2
- package/node_modules/pi-web-access/ssrf-protection.ts +4 -1
- package/node_modules/pi-web-access/utils.ts +307 -1
- package/node_modules/pi-web-access/youtube-extract.ts +2 -2
- package/package.json +46 -37
- package/node_modules/@herbertgao/pi-copy-response/CHANGELOG.md +0 -49
- package/node_modules/@herbertgao/pi-copy-response/LICENSE +0 -22
- package/node_modules/@herbertgao/pi-handoff/CHANGELOG.md +0 -83
- package/node_modules/@herbertgao/pi-handoff/LICENSE +0 -22
- package/node_modules/@herbertgao/pi-handoff/package.json +0 -60
- package/node_modules/@herbertgao/pi-inline-skills/CHANGELOG.md +0 -57
- package/node_modules/@herbertgao/pi-inline-skills/LICENSE +0 -22
- package/node_modules/@herbertgao/pi-mermaid-open/CHANGELOG.md +0 -39
- package/node_modules/@herbertgao/pi-preferred-thinking/CHANGELOG.md +0 -63
- package/node_modules/@herbertgao/pi-preferred-thinking/LICENSE +0 -22
- package/node_modules/@herbertgao/pi-recap/CHANGELOG.md +0 -101
- package/node_modules/@herbertgao/pi-recap/LICENSE +0 -22
- package/node_modules/@herbertgao/pi-recap/package.json +0 -55
- package/node_modules/@herbertgao/pi-rename/CHANGELOG.md +0 -49
- package/node_modules/@herbertgao/pi-rename/LICENSE +0 -22
- package/node_modules/@herbertgao/pi-rename/package.json +0 -63
- package/node_modules/@herbertgao/pi-titlebar-spinner/CHANGELOG.md +0 -25
- package/node_modules/@herbertgao/pi-titlebar-spinner/LICENSE +0 -22
- /package/node_modules/{@herbertgao → @tifan}/pi-copy-response/src/index.ts +0 -0
- /package/node_modules/{@herbertgao → @tifan}/pi-handoff/src/complete-text.ts +0 -0
- /package/node_modules/{@herbertgao → @tifan}/pi-handoff/src/session-query.ts +0 -0
- /package/node_modules/{@herbertgao → @tifan}/pi-inline-skills/src/index.ts +0 -0
- /package/node_modules/{@herbertgao → @tifan}/pi-mermaid-open/herdr-plugin/herdr-plugin.toml +0 -0
- /package/node_modules/{@herbertgao → @tifan}/pi-mermaid-open/herdr-plugin/viewer.mjs +0 -0
- /package/node_modules/{@herbertgao → @tifan}/pi-mermaid-open/src/index.ts +0 -0
- /package/node_modules/{@herbertgao → @tifan}/pi-recap/src/index.ts +0 -0
- /package/node_modules/{@herbertgao → @tifan}/pi-recap/src/model-picker.ts +0 -0
- /package/node_modules/{@herbertgao → @tifan}/pi-recap/src/sanitize.ts +0 -0
- /package/node_modules/{@herbertgao → @tifan}/pi-recap/src/tui.ts +0 -0
- /package/node_modules/{@herbertgao → @tifan}/pi-rename/src/model-picker.ts +0 -0
- /package/node_modules/{@herbertgao → @tifan}/pi-rename/src/naming.ts +0 -0
- /package/node_modules/{@herbertgao → @tifan}/pi-rename/src/sanitize.ts +0 -0
- /package/node_modules/{@herbertgao → @tifan}/pi-titlebar-spinner/src/index.ts +0 -0
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
serializeTools,
|
|
21
21
|
type ServerCacheEntry,
|
|
22
22
|
} from "./metadata-cache.ts";
|
|
23
|
-
import { McpServerManager } from "./server-manager.ts";
|
|
23
|
+
import { McpServerManager, isTransientHttpConnectError } from "./server-manager.ts";
|
|
24
24
|
import { buildToolMetadata, totalToolCount } from "./tool-metadata.ts";
|
|
25
25
|
import { resourceNameToToolName } from "./resource-tools.ts";
|
|
26
26
|
import { UiResourceHandler } from "./ui-resource-handler.ts";
|
|
@@ -37,8 +37,9 @@ import {
|
|
|
37
37
|
type McpRuntimeOwner,
|
|
38
38
|
} from "./runtime-owner.ts";
|
|
39
39
|
import { publishMcpStatusSnapshot } from "./mcp-status.ts";
|
|
40
|
+
import { FAILURE_BACKOFF_MS, getFailureAgeSeconds } from "./failure-backoff.ts";
|
|
41
|
+
export { getFailureAgeSeconds, getFailureMessage, isServerInActiveFailureBackoff } from "./failure-backoff.ts";
|
|
40
42
|
|
|
41
|
-
const FAILURE_BACKOFF_MS = 60 * 1000;
|
|
42
43
|
const MAX_FAILURE_MESSAGE_CHARS = 8 * 1024;
|
|
43
44
|
const failureExpiryTimers = new WeakMap<McpExtensionState, Map<string, ReturnType<typeof setTimeout>>>();
|
|
44
45
|
|
|
@@ -51,13 +52,19 @@ function getFailureExpiryTimers(state: McpExtensionState): Map<string, ReturnTyp
|
|
|
51
52
|
return timers;
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
export function clearFailure(state: McpExtensionState, serverName: string):
|
|
55
|
+
export function clearFailure(state: McpExtensionState, serverName: string, restoredReason?: string): boolean {
|
|
56
|
+
const wasActive = getFailureAgeSeconds(state, serverName) !== null;
|
|
55
57
|
state.failureTracker.delete(serverName);
|
|
56
58
|
state.failureMessages?.delete(serverName);
|
|
57
59
|
const timers = failureExpiryTimers.get(state);
|
|
58
60
|
const timer = timers?.get(serverName);
|
|
59
61
|
if (timer) clearTimeout(timer);
|
|
60
62
|
timers?.delete(serverName);
|
|
63
|
+
if (restoredReason && wasActive) {
|
|
64
|
+
notifyToolMetadataUpdated(state, serverName, restoredReason);
|
|
65
|
+
publishMcpStatusSnapshot(state);
|
|
66
|
+
}
|
|
67
|
+
return wasActive;
|
|
61
68
|
}
|
|
62
69
|
|
|
63
70
|
export function recordFailure(state: McpExtensionState, serverName: string, message: string): void {
|
|
@@ -73,12 +80,15 @@ export function recordFailure(state: McpExtensionState, serverName: string, mess
|
|
|
73
80
|
if (state.failureTracker.get(serverName) === failedAt) {
|
|
74
81
|
state.failureTracker.delete(serverName);
|
|
75
82
|
state.failureMessages?.delete(serverName);
|
|
83
|
+
notifyToolMetadataUpdated(state, serverName, "failure-backoff-expired");
|
|
76
84
|
publishMcpStatusSnapshot(state);
|
|
77
85
|
}
|
|
78
86
|
getFailureExpiryTimers(state).delete(serverName);
|
|
79
87
|
}, FAILURE_BACKOFF_MS);
|
|
80
88
|
timer.unref?.();
|
|
81
89
|
getFailureExpiryTimers(state).set(serverName, timer);
|
|
90
|
+
notifyToolMetadataUpdated(state, serverName, "failure-backoff-started");
|
|
91
|
+
publishMcpStatusSnapshot(state);
|
|
82
92
|
}
|
|
83
93
|
|
|
84
94
|
export function isTuiMode(ctx: Pick<ExtensionContext, "hasUI" | "mode">): boolean {
|
|
@@ -293,16 +303,17 @@ export async function initializeMcp(
|
|
|
293
303
|
try {
|
|
294
304
|
const connection = await manager.connect(name, definition, runtimeSignal);
|
|
295
305
|
if (connection.status === "needs-auth") {
|
|
296
|
-
return { name, definition, connection: null, error: `OAuth authentication required. Run /mcp-auth ${name}
|
|
306
|
+
return { name, definition, connection: null, error: `OAuth authentication required. Run /mcp-auth ${name}.`, transient: false };
|
|
297
307
|
}
|
|
298
|
-
return { name, definition, connection, error: null };
|
|
308
|
+
return { name, definition, connection, error: null, transient: false };
|
|
299
309
|
} catch (error) {
|
|
300
310
|
if (isAbortError(error, runtimeSignal)) {
|
|
301
311
|
if (owner.signal.aborted) throw error;
|
|
302
|
-
return { name, definition, connection: null, error: null };
|
|
312
|
+
return { name, definition, connection: null, error: null, transient: false };
|
|
303
313
|
}
|
|
314
|
+
const transient = isTransientHttpConnectError(error);
|
|
304
315
|
const message = error instanceof Error ? error.message : String(error);
|
|
305
|
-
return { name, definition, connection: null, error: message };
|
|
316
|
+
return { name, definition, connection: null, error: message, transient };
|
|
306
317
|
}
|
|
307
318
|
});
|
|
308
319
|
|
|
@@ -332,11 +343,18 @@ export async function initializeMcp(
|
|
|
332
343
|
startupKnownMetadata.set(name, metadata);
|
|
333
344
|
}
|
|
334
345
|
|
|
335
|
-
for (const { name, definition, connection, error } of results) {
|
|
346
|
+
for (const { name, definition, connection, error, transient } of results) {
|
|
336
347
|
owner.throwIfInactive();
|
|
337
348
|
if (error || !connection) {
|
|
338
349
|
if (initialSignal?.aborted) continue;
|
|
339
350
|
if (error) recordFailure(state, name, error);
|
|
351
|
+
if (transient) {
|
|
352
|
+
const notice = `MCP: ${name} temporarily unavailable (HTTP 503); retry later`;
|
|
353
|
+
logger.debug(`MCP: startup connect hit transient upstream outage for ${name}; will retry`);
|
|
354
|
+
if (ui) ui.notify(notice, "warning");
|
|
355
|
+
else console.error(notice);
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
340
358
|
const displayError = sanitizeTerminalText(error ?? "Unknown connection failure");
|
|
341
359
|
if (ui) {
|
|
342
360
|
ui.notify(`MCP: Failed to connect to ${name}: ${displayError}`, "error");
|
|
@@ -399,9 +417,9 @@ export async function initializeMcp(
|
|
|
399
417
|
}
|
|
400
418
|
updateServerMetadata(state, name);
|
|
401
419
|
updateMetadataCache(state, name);
|
|
402
|
-
|
|
420
|
+
const restored = clearFailure(state, name, "direct-tools-bootstrap");
|
|
421
|
+
if (!restored) notifyToolMetadataUpdated(state, name, "direct-tools-bootstrap");
|
|
403
422
|
markKeepAliveAfterConnect(state, name);
|
|
404
|
-
clearFailure(state, name);
|
|
405
423
|
return { name, ok: true };
|
|
406
424
|
} catch (error) {
|
|
407
425
|
if (isAbortError(error, runtimeSignal)) {
|
|
@@ -427,8 +445,8 @@ export async function initializeMcp(
|
|
|
427
445
|
if (!owner.isActive()) return;
|
|
428
446
|
updateServerMetadata(state, serverName);
|
|
429
447
|
updateMetadataCache(state, serverName);
|
|
430
|
-
|
|
431
|
-
|
|
448
|
+
const restored = clearFailure(state, serverName, "lifecycle-reconnect");
|
|
449
|
+
if (!restored) notifyToolMetadataUpdated(state, serverName, "lifecycle-reconnect");
|
|
432
450
|
updateStatusBar(state);
|
|
433
451
|
});
|
|
434
452
|
|
|
@@ -441,13 +459,13 @@ export async function initializeMcp(
|
|
|
441
459
|
|
|
442
460
|
lifecycle.setHealthRestoredCallback((serverName) => {
|
|
443
461
|
if (!owner.isActive()) return;
|
|
444
|
-
clearFailure(state, serverName);
|
|
462
|
+
clearFailure(state, serverName, "health-restored");
|
|
445
463
|
updateStatusBar(state);
|
|
446
464
|
});
|
|
447
465
|
|
|
448
466
|
lifecycle.setAuthRequiredCallback((serverName) => {
|
|
449
467
|
if (!owner.isActive()) return;
|
|
450
|
-
clearFailure(state, serverName);
|
|
468
|
+
clearFailure(state, serverName, "auth-required");
|
|
451
469
|
updateStatusBar(state);
|
|
452
470
|
});
|
|
453
471
|
|
|
@@ -544,6 +562,8 @@ export function updateMetadataCache(
|
|
|
544
562
|
resources,
|
|
545
563
|
...(prompts !== undefined ? { prompts } : {}),
|
|
546
564
|
...(connection.instructions !== undefined ? { instructions: connection.instructions } : {}),
|
|
565
|
+
...(connection.toolListHints?.ttlMs !== undefined ? { ttlMs: connection.toolListHints.ttlMs } : {}),
|
|
566
|
+
...(connection.toolListHints?.cacheScope !== undefined ? { cacheScope: connection.toolListHints.cacheScope } : {}),
|
|
547
567
|
cachedAt: Date.now(),
|
|
548
568
|
};
|
|
549
569
|
|
|
@@ -606,20 +626,11 @@ export function updateStatusBar(state: McpExtensionState): void {
|
|
|
606
626
|
ui.setStatus("mcp", undefined);
|
|
607
627
|
return;
|
|
608
628
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
if (!failedAt) return null;
|
|
615
|
-
const ageMs = Date.now() - failedAt;
|
|
616
|
-
if (ageMs > FAILURE_BACKOFF_MS) return null;
|
|
617
|
-
return Math.round(ageMs / 1000);
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
export function getFailureMessage(state: McpExtensionState, serverName: string): string | null {
|
|
621
|
-
if (getFailureAgeSeconds(state, serverName) === null) return null;
|
|
622
|
-
return state.failureMessages?.get(serverName) ?? null;
|
|
629
|
+
const theme = ui.theme;
|
|
630
|
+
const styledStatus = typeof theme?.fg === "function"
|
|
631
|
+
? theme.fg("accent", formattedStatus)
|
|
632
|
+
: formattedStatus;
|
|
633
|
+
ui.setStatus("mcp", styledStatus);
|
|
623
634
|
}
|
|
624
635
|
|
|
625
636
|
export async function lazyConnect(state: McpExtensionState, serverName: string, signal?: AbortSignal): Promise<boolean> {
|
|
@@ -650,10 +661,10 @@ export async function lazyConnect(state: McpExtensionState, serverName: string,
|
|
|
650
661
|
if (newConnection.status === "needs-auth") {
|
|
651
662
|
return false;
|
|
652
663
|
}
|
|
653
|
-
clearFailure(state, serverName);
|
|
654
664
|
updateServerMetadata(state, serverName);
|
|
655
665
|
updateMetadataCache(state, serverName);
|
|
656
|
-
|
|
666
|
+
const restored = clearFailure(state, serverName, "lazy-connect");
|
|
667
|
+
if (!restored) notifyToolMetadataUpdated(state, serverName, "lazy-connect");
|
|
657
668
|
markKeepAliveAfterConnect(state, serverName);
|
|
658
669
|
updateStatusBar(state);
|
|
659
670
|
return true;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SdkError, SdkErrorCode } from "@modelcontextprotocol/client";
|
|
2
2
|
import { isServerDisabled, type ServerDefinition } from "./types.ts";
|
|
3
|
-
import type
|
|
3
|
+
import { isTransientHttpConnectError, type McpServerManager, type ServerConnection } from "./server-manager.ts";
|
|
4
4
|
import { hasPendingAuth } from "./mcp-auth-flow.ts";
|
|
5
5
|
import { logger } from "./logger.ts";
|
|
6
6
|
import { formatTerminalError, parallelLimit, sanitizeTerminalText } from "./utils.ts";
|
|
@@ -20,6 +20,7 @@ interface RetryState {
|
|
|
20
20
|
nextAttemptAt: number;
|
|
21
21
|
connection: ServerConnection | undefined;
|
|
22
22
|
status: ServerConnection["status"] | undefined;
|
|
23
|
+
warningReported: boolean;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export class McpLifecycleManager {
|
|
@@ -347,6 +348,12 @@ export class McpLifecycleManager {
|
|
|
347
348
|
return Date.now() >= retry.nextAttemptAt;
|
|
348
349
|
}
|
|
349
350
|
|
|
351
|
+
private connectionFailureTarget(action: "refresh" | "reconnect" | "publish", name: string): string {
|
|
352
|
+
if (action === "reconnect") return `reconnect to ${name}`;
|
|
353
|
+
if (action === "publish") return `publish metadata for ${name}`;
|
|
354
|
+
return `refresh ${name}`;
|
|
355
|
+
}
|
|
356
|
+
|
|
350
357
|
private reportConnectionFailure(
|
|
351
358
|
name: string,
|
|
352
359
|
definition: ServerDefinition,
|
|
@@ -356,13 +363,12 @@ export class McpLifecycleManager {
|
|
|
356
363
|
): void {
|
|
357
364
|
if (!this.recordRetry(name, definition, connection)) return;
|
|
358
365
|
this.onReconnectFailure?.(name, error);
|
|
366
|
+
if (isTransientHttpConnectError(error)) return;
|
|
367
|
+
const retry = this.retryStates.get(name);
|
|
368
|
+
if (retry?.warningReported) return;
|
|
369
|
+
if (retry) retry.warningReported = true;
|
|
359
370
|
const message = error instanceof Error ? error.message : String(error);
|
|
360
|
-
|
|
361
|
-
? `reconnect to ${name}`
|
|
362
|
-
: action === "publish"
|
|
363
|
-
? `publish metadata for ${name}`
|
|
364
|
-
: `refresh ${name}`;
|
|
365
|
-
console.error(`MCP: Failed to ${target}: ${sanitizeTerminalText(message)}`);
|
|
371
|
+
console.error(`MCP: Failed to ${this.connectionFailureTarget(action, name)}: ${sanitizeTerminalText(message)}`);
|
|
366
372
|
}
|
|
367
373
|
|
|
368
374
|
private deferRefreshTimeout(
|
|
@@ -382,7 +388,8 @@ export class McpLifecycleManager {
|
|
|
382
388
|
// Do not recreate retry/failure state from a stale convergence pass after
|
|
383
389
|
// disposal or a same-name replacement registration.
|
|
384
390
|
if (this.keepAliveServers.get(name) !== definition) return false;
|
|
385
|
-
const
|
|
391
|
+
const previous = this.retryStates.get(name);
|
|
392
|
+
const attempts = (previous?.attempts ?? 0) + 1;
|
|
386
393
|
const delay = Math.min(
|
|
387
394
|
KEEP_ALIVE_RETRY_BASE_MS * 2 ** Math.min(attempts - 1, 10),
|
|
388
395
|
KEEP_ALIVE_RETRY_MAX_MS,
|
|
@@ -392,6 +399,7 @@ export class McpLifecycleManager {
|
|
|
392
399
|
nextAttemptAt: Date.now() + delay,
|
|
393
400
|
connection,
|
|
394
401
|
status: connection?.status,
|
|
402
|
+
warningReported: previous?.warningReported ?? false,
|
|
395
403
|
});
|
|
396
404
|
return true;
|
|
397
405
|
}
|
|
@@ -5,6 +5,7 @@ import { guardMcpOutput, guardedMcpDetails, resolveMcpOutputGuardOptions } from
|
|
|
5
5
|
import { executeCall } from "./proxy-modes.ts";
|
|
6
6
|
import { combineAbortSignals } from "./runtime-owner.ts";
|
|
7
7
|
import { paginate, rankSuggestions, rankToolMatches } from "./search-ranking.ts";
|
|
8
|
+
import { isServerInActiveFailureBackoff } from "./failure-backoff.ts";
|
|
8
9
|
import type { McpExtensionState } from "./state.ts";
|
|
9
10
|
import { findToolByName, formatSchema } from "./tool-metadata.ts";
|
|
10
11
|
import { renderTsShape } from "./ts-shape.ts";
|
|
@@ -202,6 +203,7 @@ export async function runMcpScript(
|
|
|
202
203
|
let error: unknown;
|
|
203
204
|
try {
|
|
204
205
|
for (const [server, metadata] of state.toolMetadata) {
|
|
206
|
+
if (isServerInActiveFailureBackoff(state, server)) continue;
|
|
205
207
|
const tool = findToolByName(metadata, path);
|
|
206
208
|
if (!tool) continue;
|
|
207
209
|
const inputTypeScript = tool.inputSchema
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import {
|
|
9
9
|
UnauthorizedError,
|
|
10
10
|
type AddClientAuthentication,
|
|
11
|
+
type OAuthClientInformationContext,
|
|
11
12
|
type OAuthClientProvider,
|
|
12
13
|
type OAuthDiscoveryState,
|
|
13
14
|
} from "@modelcontextprotocol/client"
|
|
@@ -21,9 +22,8 @@ import {
|
|
|
21
22
|
updateTokens,
|
|
22
23
|
updateClientInfo,
|
|
23
24
|
clearAllCredentials,
|
|
24
|
-
clearClientInfo,
|
|
25
25
|
clearCodeVerifier,
|
|
26
|
-
|
|
26
|
+
invalidateAuthEntryCache,
|
|
27
27
|
type AuthEntry,
|
|
28
28
|
type AuthStorageOptions,
|
|
29
29
|
type StoredTokens,
|
|
@@ -71,6 +71,18 @@ function issuersMatch(first: string, second: string): boolean {
|
|
|
71
71
|
|| (second.endsWith("/") && second.slice(0, -1) === first)
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
function toOAuthTokens(tokens: StoredTokens): IssuerBoundTokens {
|
|
75
|
+
const result: IssuerBoundTokens = {
|
|
76
|
+
access_token: tokens.accessToken,
|
|
77
|
+
token_type: "Bearer",
|
|
78
|
+
}
|
|
79
|
+
if (tokens.refreshToken !== undefined) result.refresh_token = tokens.refreshToken
|
|
80
|
+
if (tokens.expiresAt !== undefined) result.expires_in = Math.max(0, Math.floor(tokens.expiresAt - Date.now() / 1000))
|
|
81
|
+
if (tokens.scope !== undefined) result.scope = tokens.scope
|
|
82
|
+
if (tokens.issuer !== undefined) result.issuer = tokens.issuer
|
|
83
|
+
return result
|
|
84
|
+
}
|
|
85
|
+
|
|
74
86
|
// Callback server configuration
|
|
75
87
|
const DEFAULT_OAUTH_CALLBACK_PORT = 19876
|
|
76
88
|
const DEFAULT_OAUTH_CALLBACK_PATH = "/callback"
|
|
@@ -161,6 +173,11 @@ export class McpOAuthProvider implements OAuthClientProvider {
|
|
|
161
173
|
private flowDiscoveryState: OAuthDiscoveryState | undefined
|
|
162
174
|
private flowIssuerMismatch = false
|
|
163
175
|
private flowState: string | undefined
|
|
176
|
+
private invalidatedAccessToken: string | undefined
|
|
177
|
+
private invalidatedClientId: string | undefined
|
|
178
|
+
private lastObservedClientId: string | undefined
|
|
179
|
+
private lastSavedAccessToken: string | undefined
|
|
180
|
+
private pendingAuthAccessToken: string | undefined
|
|
164
181
|
|
|
165
182
|
constructor(
|
|
166
183
|
private serverName: string,
|
|
@@ -188,6 +205,11 @@ export class McpOAuthProvider implements OAuthClientProvider {
|
|
|
188
205
|
|
|
189
206
|
deactivate(): void {
|
|
190
207
|
this.active = false
|
|
208
|
+
this.invalidatedAccessToken = undefined
|
|
209
|
+
this.invalidatedClientId = undefined
|
|
210
|
+
this.lastObservedClientId = undefined
|
|
211
|
+
this.lastSavedAccessToken = undefined
|
|
212
|
+
this.pendingAuthAccessToken = undefined
|
|
191
213
|
}
|
|
192
214
|
|
|
193
215
|
private assertStoredIssuerBindings(entry: AuthEntry | undefined, issuer: string | undefined): void {
|
|
@@ -264,6 +286,9 @@ export class McpOAuthProvider implements OAuthClientProvider {
|
|
|
264
286
|
* Returns undefined if no client info exists or if the server URL has changed.
|
|
265
287
|
*/
|
|
266
288
|
async clientInformation(): Promise<OAuthClientInformationMixed | undefined> {
|
|
289
|
+
if (this.invalidatedClientId !== undefined) {
|
|
290
|
+
invalidateAuthEntryCache(this.serverName)
|
|
291
|
+
}
|
|
267
292
|
const issuer = this.discoveredIssuer
|
|
268
293
|
const stored = await getAuthForUrl(this.serverName, this.serverUrl, this.storageOptions)
|
|
269
294
|
this.assertStoredIssuerBindings(stored, issuer)
|
|
@@ -298,7 +323,9 @@ export class McpOAuthProvider implements OAuthClientProvider {
|
|
|
298
323
|
// Keep client registration associated with this in-flight flow even if
|
|
299
324
|
// another runtime writes the shared persistent entry for the same name.
|
|
300
325
|
const clientInfo = this.flowClientInfo ?? stored?.clientInfo
|
|
326
|
+
if (clientInfo?.clientId === this.invalidatedClientId) return undefined
|
|
301
327
|
if (clientInfo) {
|
|
328
|
+
this.invalidatedClientId = undefined
|
|
302
329
|
// A stored SEP-2352 issuer stub for a config-pre-registered client
|
|
303
330
|
// (identified by the explicit marker, or by the legacy stub shape of
|
|
304
331
|
// {clientId, issuer} with no registration metadata) is only meaningful
|
|
@@ -329,6 +356,7 @@ export class McpOAuthProvider implements OAuthClientProvider {
|
|
|
329
356
|
}
|
|
330
357
|
// Return all registration metadata and the local issuer extension.
|
|
331
358
|
// This keeps the SDK OAuth view and the stored issuer binding consistent.
|
|
359
|
+
this.lastObservedClientId = clientInfo.clientId
|
|
332
360
|
return {
|
|
333
361
|
client_id: clientInfo.clientId,
|
|
334
362
|
client_secret: clientInfo.clientSecret,
|
|
@@ -380,6 +408,8 @@ export class McpOAuthProvider implements OAuthClientProvider {
|
|
|
380
408
|
...(issuer !== undefined ? { issuer } : {}),
|
|
381
409
|
}
|
|
382
410
|
this.flowClientInfo = clientInfo
|
|
411
|
+
this.invalidatedClientId = undefined
|
|
412
|
+
this.lastObservedClientId = clientInfo.clientId
|
|
383
413
|
updateClientInfo(this.serverName, clientInfo, this.serverUrl, this.storageOptions)
|
|
384
414
|
}
|
|
385
415
|
|
|
@@ -387,27 +417,28 @@ export class McpOAuthProvider implements OAuthClientProvider {
|
|
|
387
417
|
* Get stored OAuth tokens.
|
|
388
418
|
* Returns undefined if no tokens exist or if the server URL has changed.
|
|
389
419
|
*/
|
|
390
|
-
async tokens(): Promise<OAuthTokens | undefined> {
|
|
420
|
+
async tokens(ctx?: OAuthClientInformationContext): Promise<OAuthTokens | undefined> {
|
|
421
|
+
// Once this provider rejects a token, bypass its process-local cache until
|
|
422
|
+
// another process replaces that token in shared secure storage.
|
|
423
|
+
if (this.invalidatedAccessToken !== undefined) {
|
|
424
|
+
invalidateAuthEntryCache(this.serverName)
|
|
425
|
+
}
|
|
426
|
+
|
|
391
427
|
// Use getAuthForUrl to validate tokens are for the current server URL.
|
|
392
428
|
const entry = await getAuthForUrl(this.serverName, this.serverUrl, this.storageOptions)
|
|
393
|
-
if (!entry?.tokens) return undefined
|
|
429
|
+
if (!entry?.tokens || entry.tokens.accessToken === this.invalidatedAccessToken) return undefined
|
|
430
|
+
this.invalidatedAccessToken = undefined
|
|
394
431
|
const issuer = this.discoveredIssuer
|
|
395
432
|
this.assertStoredIssuerBindings(entry, issuer)
|
|
396
433
|
if (issuer && entry.tokens.issuer === undefined) {
|
|
397
434
|
entry.tokens.issuer = issuer
|
|
398
435
|
updateTokens(this.serverName, entry.tokens, this.serverUrl, this.storageOptions)
|
|
399
436
|
}
|
|
437
|
+
if (ctx !== undefined) {
|
|
438
|
+
this.pendingAuthAccessToken = entry.tokens.accessToken
|
|
439
|
+
}
|
|
400
440
|
|
|
401
|
-
return
|
|
402
|
-
access_token: entry.tokens.accessToken,
|
|
403
|
-
token_type: "Bearer",
|
|
404
|
-
refresh_token: entry.tokens.refreshToken,
|
|
405
|
-
expires_in: entry.tokens.expiresAt
|
|
406
|
-
? Math.max(0, Math.floor(entry.tokens.expiresAt - Date.now() / 1000))
|
|
407
|
-
: undefined,
|
|
408
|
-
scope: entry.tokens.scope,
|
|
409
|
-
...(entry.tokens.issuer !== undefined ? { issuer: entry.tokens.issuer } : {}),
|
|
410
|
-
} as IssuerBoundTokens
|
|
441
|
+
return toOAuthTokens(entry.tokens)
|
|
411
442
|
}
|
|
412
443
|
|
|
413
444
|
/**
|
|
@@ -427,6 +458,8 @@ export class McpOAuthProvider implements OAuthClientProvider {
|
|
|
427
458
|
}
|
|
428
459
|
this.throwIfInactive()
|
|
429
460
|
updateTokens(this.serverName, storedTokens, this.serverUrl, this.storageOptions)
|
|
461
|
+
this.invalidatedAccessToken = undefined
|
|
462
|
+
this.lastSavedAccessToken = storedTokens.accessToken
|
|
430
463
|
// Discovery must survive the browser redirect so the callback can verify
|
|
431
464
|
// the authorization server that minted the code. Once token issuance
|
|
432
465
|
// succeeds, clear it so a later 401 re-reads PRM and can observe an
|
|
@@ -533,14 +566,31 @@ export class McpOAuthProvider implements OAuthClientProvider {
|
|
|
533
566
|
this.flowDiscoveryState = undefined
|
|
534
567
|
this.flowIssuerMismatch = false
|
|
535
568
|
this.flowState = undefined
|
|
569
|
+
this.invalidatedAccessToken = undefined
|
|
570
|
+
this.invalidatedClientId = undefined
|
|
571
|
+
this.lastObservedClientId = undefined
|
|
572
|
+
this.lastSavedAccessToken = undefined
|
|
573
|
+
this.pendingAuthAccessToken = undefined
|
|
536
574
|
clearAllCredentials(this.serverName, this.storageOptions)
|
|
537
575
|
break
|
|
538
576
|
case "client":
|
|
577
|
+
// Dynamic client registrations share the same credential-store entry as
|
|
578
|
+
// tokens. Keep SDK invalidation local so a stale process cannot rewrite
|
|
579
|
+
// that entry over credentials another process just authorized.
|
|
580
|
+
this.invalidatedClientId = this.lastObservedClientId
|
|
581
|
+
this.lastObservedClientId = undefined
|
|
539
582
|
this.flowClientInfo = undefined
|
|
540
|
-
|
|
583
|
+
invalidateAuthEntryCache(this.serverName)
|
|
541
584
|
break
|
|
542
585
|
case "tokens":
|
|
543
|
-
|
|
586
|
+
// Invalidation is provider-local. Persistently deleting a shared token
|
|
587
|
+
// here lets a process refreshing stale cached credentials erase a token
|
|
588
|
+
// that another process just authorized. A later tokens() call bypasses
|
|
589
|
+
// the local cache and adopts a replacement token when one exists.
|
|
590
|
+
this.invalidatedAccessToken = this.pendingAuthAccessToken ?? this.lastSavedAccessToken
|
|
591
|
+
this.lastSavedAccessToken = undefined
|
|
592
|
+
this.pendingAuthAccessToken = undefined
|
|
593
|
+
invalidateAuthEntryCache(this.serverName)
|
|
544
594
|
break
|
|
545
595
|
case "verifier":
|
|
546
596
|
clearCodeVerifier(this.serverName, this.storageOptions)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
|
-
import { mkdtemp, writeFile } from "node:fs/promises";
|
|
2
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
5
|
import type { ContentBlock, McpSettings } from "./types.ts";
|
|
6
6
|
|
|
7
7
|
export const DEFAULT_MCP_OUTPUT_MAX_BYTES = 50 * 1024;
|
|
@@ -10,7 +10,9 @@ export const DEFAULT_MCP_DETAILS_MAX_BYTES = 16 * 1024;
|
|
|
10
10
|
|
|
11
11
|
const CONTENT_SUMMARY_LIMIT = 20;
|
|
12
12
|
const KEY_PREVIEW_LIMIT = 20;
|
|
13
|
-
const
|
|
13
|
+
const KEY_MAX_BYTES = 120;
|
|
14
|
+
const STRUCTURED_CONTENT_PRESERVE_MAX_BYTES = 4 * 1024;
|
|
15
|
+
const STRUCTURED_CONTENT_FIELD_PRESERVE_MAX_BYTES = 512;
|
|
14
16
|
|
|
15
17
|
type Recordish = Record<string, unknown>;
|
|
16
18
|
|
|
@@ -269,7 +271,33 @@ async function boundMcpResult(result: unknown, detailsMaxBytes: number): Promise
|
|
|
269
271
|
const raw = safeStringify(result);
|
|
270
272
|
const rawBytes = byteLength(raw);
|
|
271
273
|
if (rawBytes <= detailsMaxBytes) return result;
|
|
272
|
-
|
|
274
|
+
const marker = { omitted: true } as const;
|
|
275
|
+
if (byteLength(safeStringify(marker)) > detailsMaxBytes) return undefined;
|
|
276
|
+
|
|
277
|
+
const summary = await summarizeMcpResult(result, raw, rawBytes);
|
|
278
|
+
if (byteLength(safeStringify(summary)) <= detailsMaxBytes) return summary;
|
|
279
|
+
return compactMcpResultOmission(summary, marker, detailsMaxBytes);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async function compactMcpResultOmission(
|
|
283
|
+
summary: McpResultSummary,
|
|
284
|
+
marker: Readonly<{ omitted: true }>,
|
|
285
|
+
maxBytes: number,
|
|
286
|
+
): Promise<unknown> {
|
|
287
|
+
if (summary.fullResultPath) {
|
|
288
|
+
const withPathAndSize = {
|
|
289
|
+
...marker,
|
|
290
|
+
rawResultBytes: summary.rawResultBytes,
|
|
291
|
+
fullResultPath: summary.fullResultPath,
|
|
292
|
+
};
|
|
293
|
+
if (byteLength(safeStringify(withPathAndSize)) <= maxBytes) return withPathAndSize;
|
|
294
|
+
const withPath = { ...marker, fullResultPath: summary.fullResultPath };
|
|
295
|
+
if (byteLength(safeStringify(withPath)) <= maxBytes) return withPath;
|
|
296
|
+
await discardArtifact(summary.fullResultPath);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const withSize = { ...marker, rawResultBytes: summary.rawResultBytes };
|
|
300
|
+
return byteLength(safeStringify(withSize)) <= maxBytes ? withSize : marker;
|
|
273
301
|
}
|
|
274
302
|
|
|
275
303
|
async function summarizeMcpResult(result: unknown, raw: string, rawBytes: number): Promise<McpResultSummary> {
|
|
@@ -289,7 +317,7 @@ async function summarizeMcpResult(result: unknown, raw: string, rawBytes: number
|
|
|
289
317
|
};
|
|
290
318
|
|
|
291
319
|
if (record && "structuredContent" in record) {
|
|
292
|
-
summary.structuredContent =
|
|
320
|
+
summary.structuredContent = summarizeStructuredContent(record.structuredContent);
|
|
293
321
|
}
|
|
294
322
|
if (record && "_meta" in record) {
|
|
295
323
|
summary.meta = summarizeValue(record._meta);
|
|
@@ -336,11 +364,41 @@ function summarizeValue(value: unknown): Record<string, unknown> {
|
|
|
336
364
|
type: Array.isArray(value) ? "array" : "object",
|
|
337
365
|
estimatedBytes: estimateValueBytes(value),
|
|
338
366
|
keyCount: keys.length,
|
|
339
|
-
keysPreview: keys.slice(0, KEY_PREVIEW_LIMIT)
|
|
367
|
+
keysPreview: uniqueBoundedKeys(keys.slice(0, KEY_PREVIEW_LIMIT)),
|
|
340
368
|
omitted: true,
|
|
341
369
|
};
|
|
342
370
|
}
|
|
343
371
|
|
|
372
|
+
function summarizeStructuredContent(value: unknown): Record<string, unknown> {
|
|
373
|
+
const record = asRecord(value);
|
|
374
|
+
if (!record || Array.isArray(value)) return summarizeValue(value);
|
|
375
|
+
const keys = Object.keys(record);
|
|
376
|
+
const entries = Object.entries(record).slice(0, KEY_PREVIEW_LIMIT);
|
|
377
|
+
const previewKeys = uniqueBoundedKeys(entries.map(([key]) => key));
|
|
378
|
+
const fields: Record<string, unknown> = {};
|
|
379
|
+
let preservedBytes = byteLength("{}");
|
|
380
|
+
for (const [key, field] of entries) {
|
|
381
|
+
const fieldBytes = byteLength(safeStringify(field));
|
|
382
|
+
const candidate = fieldBytes <= STRUCTURED_CONTENT_FIELD_PRESERVE_MAX_BYTES
|
|
383
|
+
? field
|
|
384
|
+
: summarizeValue(field);
|
|
385
|
+
const entryBytes = serializedObjectEntryBytes(key, candidate, Object.keys(fields).length > 0);
|
|
386
|
+
if (preservedBytes + entryBytes > STRUCTURED_CONTENT_PRESERVE_MAX_BYTES) continue;
|
|
387
|
+
fields[key] = candidate;
|
|
388
|
+
preservedBytes += entryBytes;
|
|
389
|
+
}
|
|
390
|
+
return {
|
|
391
|
+
preservedFields: fields,
|
|
392
|
+
summary: {
|
|
393
|
+
type: "object",
|
|
394
|
+
estimatedBytes: estimateValueBytes(value),
|
|
395
|
+
keyCount: keys.length,
|
|
396
|
+
keysPreview: previewKeys,
|
|
397
|
+
omitted: true,
|
|
398
|
+
},
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
344
402
|
function estimateValueBytes(value: unknown, depth = 0): number {
|
|
345
403
|
if (value === null || value === undefined) return 0;
|
|
346
404
|
if (typeof value === "string") return byteLength(value);
|
|
@@ -352,7 +410,29 @@ function estimateValueBytes(value: unknown, depth = 0): number {
|
|
|
352
410
|
}
|
|
353
411
|
|
|
354
412
|
function truncateKey(key: string): string {
|
|
355
|
-
|
|
413
|
+
if (byteLength(key) <= KEY_MAX_BYTES) return key;
|
|
414
|
+
const suffix = "…";
|
|
415
|
+
return `${truncateStringToBytes(key, KEY_MAX_BYTES - byteLength(suffix))}${suffix}`;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function uniqueBoundedKeys(keys: string[]): string[] {
|
|
419
|
+
const used = new Set<string>();
|
|
420
|
+
return keys.map((key) => {
|
|
421
|
+
let candidate = truncateKey(key);
|
|
422
|
+
let ordinal = 2;
|
|
423
|
+
while (used.has(candidate)) {
|
|
424
|
+
const suffix = `~${ordinal}`;
|
|
425
|
+
candidate = `${truncateStringToBytes(key, KEY_MAX_BYTES - byteLength(suffix))}${suffix}`;
|
|
426
|
+
ordinal += 1;
|
|
427
|
+
}
|
|
428
|
+
used.add(candidate);
|
|
429
|
+
return candidate;
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function serializedObjectEntryBytes(key: string, value: unknown, hasPrevious: boolean): number {
|
|
434
|
+
const serialized = safeStringify({ [key]: value });
|
|
435
|
+
return Math.max(0, byteLength(serialized) - byteLength("{}")) + (hasPrevious ? 1 : 0);
|
|
356
436
|
}
|
|
357
437
|
|
|
358
438
|
async function saveArtifact(kind: string, text: string): Promise<{ path?: string; error?: string }> {
|
|
@@ -366,6 +446,14 @@ async function saveArtifact(kind: string, text: string): Promise<{ path?: string
|
|
|
366
446
|
}
|
|
367
447
|
}
|
|
368
448
|
|
|
449
|
+
async function discardArtifact(path: string): Promise<void> {
|
|
450
|
+
try {
|
|
451
|
+
await rm(dirname(path), { recursive: true, force: true });
|
|
452
|
+
} catch {
|
|
453
|
+
// Cleanup cannot increase the returned details payload.
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
369
457
|
function asRecord(value: unknown): Recordish | undefined {
|
|
370
458
|
return typeof value === "object" && value !== null ? value as Recordish : undefined;
|
|
371
459
|
}
|
|
@@ -5,6 +5,7 @@ const JSON_ACCEPT = "application/json, text/event-stream";
|
|
|
5
5
|
const SSE_ACCEPT = "text/event-stream";
|
|
6
6
|
const MODERN_FALLBACK_STATUSES = new Set([400, 401, 404, 405, 406, 415]);
|
|
7
7
|
const POST_ENDPOINT_MISMATCH_STATUSES = new Set([404, 405, 406, 415]);
|
|
8
|
+
const AMBIGUOUS_STATUSES = new Set([202, 401, 503]);
|
|
8
9
|
|
|
9
10
|
export interface McpProbeResult {
|
|
10
11
|
isMcp: boolean;
|
|
@@ -163,25 +164,38 @@ async function probe(url: string | URL, strategy: ProbeStrategy): Promise<ProbeR
|
|
|
163
164
|
}
|
|
164
165
|
|
|
165
166
|
function notMcp(response: Response): McpProbeResult {
|
|
167
|
+
const responseDescription = `endpoint returned ${responseKind(response)} (${response.status})`;
|
|
166
168
|
return {
|
|
167
169
|
isMcp: false,
|
|
168
|
-
classification:
|
|
170
|
+
classification: response.status === 503
|
|
171
|
+
? `${responseDescription} — server is temporarily unavailable; MCP endpoint shape could not be determined`
|
|
172
|
+
: response.status === 202
|
|
173
|
+
? `${responseDescription} — MCP endpoint shape could not be determined`
|
|
174
|
+
: response.status === 401
|
|
175
|
+
? `${responseDescription} — authentication may be required; MCP endpoint shape could not be determined`
|
|
176
|
+
: `${responseDescription} — this URL does not appear to speak MCP`,
|
|
169
177
|
};
|
|
170
178
|
}
|
|
171
179
|
|
|
180
|
+
function ambiguousNotMcp(response: Response): McpProbeResult | undefined {
|
|
181
|
+
return AMBIGUOUS_STATUSES.has(response.status) ? notMcp(response) : undefined;
|
|
182
|
+
}
|
|
183
|
+
|
|
172
184
|
/** Makes one unauthenticated metadata-only request to identify an HTTP endpoint's protocol shape. */
|
|
173
185
|
export async function probeMcpEndpoint(url: string | URL): Promise<McpProbeResult> {
|
|
174
186
|
const { response: modernResponse, outcome: modernOutcome } = await probe(url, MODERN_STRATEGY);
|
|
175
187
|
if (modernOutcome.kind === "mcp") return modernOutcome.result;
|
|
188
|
+
let ambiguousResult = ambiguousNotMcp(modernResponse);
|
|
176
189
|
|
|
177
190
|
if (modernOutcome.kind !== "unsupported-modern" && !MODERN_FALLBACK_STATUSES.has(modernResponse.status)) {
|
|
178
|
-
return notMcp(modernResponse);
|
|
191
|
+
return ambiguousResult ?? notMcp(modernResponse);
|
|
179
192
|
}
|
|
180
193
|
|
|
181
194
|
const { response: postResponse, outcome: postOutcome } = await probe(url, LEGACY_POST_STRATEGY);
|
|
182
195
|
if (postOutcome.kind === "mcp") return postOutcome.result;
|
|
183
|
-
|
|
196
|
+
ambiguousResult ??= ambiguousNotMcp(postResponse);
|
|
197
|
+
if (!POST_ENDPOINT_MISMATCH_STATUSES.has(postResponse.status)) return ambiguousNotMcp(postResponse) ?? ambiguousResult ?? notMcp(postResponse);
|
|
184
198
|
|
|
185
199
|
const { response: getResponse, outcome: getOutcome } = await probe(url, LEGACY_SSE_STRATEGY);
|
|
186
|
-
return getOutcome.kind === "mcp" ? getOutcome.result : notMcp(getResponse);
|
|
200
|
+
return getOutcome.kind === "mcp" ? getOutcome.result : ambiguousNotMcp(getResponse) ?? ambiguousResult ?? notMcp(getResponse);
|
|
187
201
|
}
|