@herbertgao/pi-extensions 2026.8.5 → 2026.8.7

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