@herbertgao/pi-extensions 2026.8.15 → 2026.9.1

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 (351) hide show
  1. package/README.md +4 -1
  2. package/THIRD_PARTY_NOTICES.md +26 -0
  3. package/node_modules/@czottmann/pi-automode/CHANGELOG.md +13 -1
  4. package/node_modules/@czottmann/pi-automode/README.md +20 -0
  5. package/node_modules/@czottmann/pi-automode/docs/GLOSSARY.md +1 -1
  6. package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +2 -1
  7. package/node_modules/@czottmann/pi-automode/docs/defaults.md +3 -2
  8. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +38 -2
  9. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +5 -3
  10. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +11 -3
  11. package/node_modules/@czottmann/pi-automode/package.json +1 -1
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +20 -0
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +46 -3
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -17
  15. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/packets.ts +6 -0
  16. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/scroll.ts +5 -0
  17. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +34 -30
  18. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  19. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  20. package/node_modules/@narumitw/pi-btw/README.md +5 -1
  21. package/node_modules/@narumitw/pi-btw/dist/index.ts +143 -14
  22. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +3 -3
  23. package/node_modules/@narumitw/pi-btw/package.json +4 -4
  24. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +158 -7
  25. package/node_modules/@narumitw/pi-btw/src/text.ts +18 -0
  26. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +5 -16
  27. package/node_modules/@narumitw/pi-caffeinate/LICENSE +21 -0
  28. package/node_modules/@narumitw/pi-caffeinate/README.md +224 -0
  29. package/node_modules/@narumitw/pi-caffeinate/dist/index.ts +1112 -0
  30. package/node_modules/@narumitw/pi-caffeinate/dist/index.ts.map +7 -0
  31. package/node_modules/@narumitw/pi-caffeinate/package.json +53 -0
  32. package/node_modules/@narumitw/pi-caffeinate/src/caffeinate.ts +811 -0
  33. package/node_modules/@narumitw/pi-caffeinate/src/dbus-inhibit.ts +153 -0
  34. package/node_modules/@narumitw/pi-caffeinate/src/index.ts +1 -0
  35. package/node_modules/@narumitw/pi-caffeinate/src/inhibitor-process.ts +43 -0
  36. package/node_modules/@narumitw/pi-caffeinate/src/inhibitors.ts +167 -0
  37. package/node_modules/@narumitw/pi-caffeinate/src/settings.ts +202 -0
  38. package/node_modules/@pi-plugins/fast-mode/README.md +5 -3
  39. package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts.map +1 -1
  40. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +20 -55
  41. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
  42. package/node_modules/@pi-plugins/fast-mode/package.json +1 -1
  43. package/node_modules/@tifan/pi-handoff/README.md +6 -3
  44. package/node_modules/@tifan/pi-handoff/package.json +1 -1
  45. package/node_modules/@tifan/pi-handoff/src/index.ts +8 -3
  46. package/node_modules/pi-lens/CHANGELOG.md +988 -0
  47. package/node_modules/pi-lens/README.md +3 -0
  48. package/node_modules/pi-lens/config/biome/core.jsonc +11 -2
  49. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +13 -1
  50. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +2 -2
  51. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +927 -80
  52. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +13 -4
  53. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +123 -3
  54. package/node_modules/pi-lens/dist/clients/ast-grep-rule-manager.js +60 -5
  55. package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +2 -2
  56. package/node_modules/pi-lens/dist/clients/bash-file-access.js +1 -3
  57. package/node_modules/pi-lens/dist/clients/biome-client.js +9 -2
  58. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +14 -0
  59. package/node_modules/pi-lens/dist/clients/bootstrap.js +509 -73
  60. package/node_modules/pi-lens/dist/clients/bounded-cache.js +152 -12
  61. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +59 -4
  62. package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +113 -0
  63. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +2 -2
  64. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +31 -4
  65. package/node_modules/pi-lens/dist/clients/cache-manager.js +105 -6
  66. package/node_modules/pi-lens/dist/clients/cache-observability.js +44 -18
  67. package/node_modules/pi-lens/dist/clients/cargo-manifest.js +422 -0
  68. package/node_modules/pi-lens/dist/clients/cascade-logger.js +2 -2
  69. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +13 -3
  70. package/node_modules/pi-lens/dist/clients/complexity-client.js +16 -5
  71. package/node_modules/pi-lens/dist/clients/config-core/deny.js +221 -0
  72. package/node_modules/pi-lens/dist/clients/config-core/index.js +47 -0
  73. package/node_modules/pi-lens/dist/clients/config-core/merge.js +357 -0
  74. package/node_modules/pi-lens/dist/clients/config-core/normalize.js +340 -0
  75. package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +248 -0
  76. package/node_modules/pi-lens/dist/clients/config-core/provenance.js +171 -0
  77. package/node_modules/pi-lens/dist/clients/config-core/records.js +218 -0
  78. package/node_modules/pi-lens/dist/clients/config-core/resolve.js +125 -0
  79. package/node_modules/pi-lens/dist/clients/config-core/safe-object.js +78 -0
  80. package/node_modules/pi-lens/dist/clients/config-core/schema.js +167 -0
  81. package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +281 -0
  82. package/node_modules/pi-lens/dist/clients/config-locations.js +160 -0
  83. package/node_modules/pi-lens/dist/clients/config-resolve.js +789 -0
  84. package/node_modules/pi-lens/dist/clients/config-schema.js +197 -0
  85. package/node_modules/pi-lens/dist/clients/config-warn.js +407 -0
  86. package/node_modules/pi-lens/dist/clients/dead-code-client.js +19 -14
  87. package/node_modules/pi-lens/dist/clients/dead-code-logger.js +2 -2
  88. package/node_modules/pi-lens/dist/clients/deadline-utils.js +178 -0
  89. package/node_modules/pi-lens/dist/clients/debug-handles.js +2 -2
  90. package/node_modules/pi-lens/dist/clients/debug-heap.js +3 -3
  91. package/node_modules/pi-lens/dist/clients/deferred-lsp-work.js +106 -0
  92. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +113 -7
  93. package/node_modules/pi-lens/dist/clients/dependency-checker.js +8 -19
  94. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +12 -9
  95. package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +14 -4
  96. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +162 -18
  97. package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +97 -0
  98. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +9 -7
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +0 -1
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +380 -26
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +0 -1
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +0 -1
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +0 -1
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +0 -1
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -1
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +0 -1
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -1
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +0 -1
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +0 -1
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/fact-rules.js +0 -1
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +0 -1
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +0 -1
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +1 -3
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -1
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +0 -1
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +0 -1
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +0 -1
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +0 -1
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +0 -1
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +0 -1
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +0 -1
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +0 -1
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +0 -1
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +13 -18
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +0 -1
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +0 -1
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -1
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -1
  129. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +7 -4
  130. package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +0 -1
  131. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +0 -1
  132. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +1 -3
  133. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +0 -1
  134. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -1
  135. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +0 -1
  136. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +0 -1
  137. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -1
  138. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +0 -1
  139. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +0 -1
  140. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +0 -1
  141. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +0 -1
  142. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +0 -1
  143. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -1
  144. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +0 -1
  145. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +3 -0
  146. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +3 -4
  147. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +194 -80
  148. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +84 -15
  149. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -1
  150. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +0 -1
  151. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -1
  152. package/node_modules/pi-lens/dist/clients/disposition-logger.js +3 -3
  153. package/node_modules/pi-lens/dist/clients/effective-config.js +403 -0
  154. package/node_modules/pi-lens/dist/clients/error-class.js +23 -0
  155. package/node_modules/pi-lens/dist/clients/event-loop-hold.js +274 -0
  156. package/node_modules/pi-lens/dist/clients/extension-log.js +2 -2
  157. package/node_modules/pi-lens/dist/clients/feature-hints.js +2 -1
  158. package/node_modules/pi-lens/dist/clients/file-time.js +6 -1
  159. package/node_modules/pi-lens/dist/clients/file-utils.js +112 -9
  160. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +50 -13
  161. package/node_modules/pi-lens/dist/clients/format-service.js +6 -1
  162. package/node_modules/pi-lens/dist/clients/formatters.js +276 -102
  163. package/node_modules/pi-lens/dist/clients/freshness-cadence.js +17 -0
  164. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +114 -19
  165. package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -9
  166. package/node_modules/pi-lens/dist/clients/git-guard.js +1 -1
  167. package/node_modules/pi-lens/dist/clients/go-client.js +39 -0
  168. package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +252 -0
  169. package/node_modules/pi-lens/dist/clients/hashline-anchor.js +424 -0
  170. package/node_modules/pi-lens/dist/clients/hook-budgets.js +76 -0
  171. package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +5 -2
  172. package/node_modules/pi-lens/dist/clients/host-ports.js +1 -1
  173. package/node_modules/pi-lens/dist/clients/installer/index.js +71 -52
  174. package/node_modules/pi-lens/dist/clients/instance-reaper.js +104 -230
  175. package/node_modules/pi-lens/dist/clients/instance-registry.js +6 -3
  176. package/node_modules/pi-lens/dist/clients/language-registry.js +595 -0
  177. package/node_modules/pi-lens/dist/clients/latency-logger.js +160 -2
  178. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +13 -0
  179. package/node_modules/pi-lens/dist/clients/lens-config.js +141 -30
  180. package/node_modules/pi-lens/dist/clients/lens-engine.js +30 -37
  181. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +59 -6
  182. package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -2
  183. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +3 -1
  184. package/node_modules/pi-lens/dist/clients/lsp/client.js +168 -30
  185. package/node_modules/pi-lens/dist/clients/lsp/config.js +483 -158
  186. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +1 -1
  187. package/node_modules/pi-lens/dist/clients/lsp/edits.js +15 -2
  188. package/node_modules/pi-lens/dist/clients/lsp/index.js +676 -71
  189. package/node_modules/pi-lens/dist/clients/lsp/language.js +18 -169
  190. package/node_modules/pi-lens/dist/clients/lsp/launch.js +2 -1
  191. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +5 -16
  192. package/node_modules/pi-lens/dist/clients/lsp/server.js +284 -147
  193. package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +116 -23
  194. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +2 -7
  195. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +9 -2
  196. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +1 -1
  197. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +163 -21
  198. package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +36 -0
  199. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +33 -4
  200. package/node_modules/pi-lens/dist/clients/mcp/session.js +5 -16
  201. package/node_modules/pi-lens/dist/clients/memory-sampler.js +8 -3
  202. package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +2 -4
  203. package/node_modules/pi-lens/dist/clients/module-report.js +20 -45
  204. package/node_modules/pi-lens/dist/clients/mutating-tool.js +651 -0
  205. package/node_modules/pi-lens/dist/clients/mutation-attribution.js +368 -0
  206. package/node_modules/pi-lens/dist/clients/mutation-bridge.js +240 -0
  207. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +247 -20
  208. package/node_modules/pi-lens/dist/clients/observed-mutation-sources.js +101 -0
  209. package/node_modules/pi-lens/dist/clients/observed-mutation.js +1215 -0
  210. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +68 -22
  211. package/node_modules/pi-lens/dist/clients/opengrep-config.js +4 -0
  212. package/node_modules/pi-lens/dist/clients/package-manager.js +195 -26
  213. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +359 -80
  214. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +23 -7
  215. package/node_modules/pi-lens/dist/clients/path-keyed-map.js +21 -2
  216. package/node_modules/pi-lens/dist/clients/path-utils.js +428 -13
  217. package/node_modules/pi-lens/dist/clients/persist-debounce.js +8 -1
  218. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +114 -0
  219. package/node_modules/pi-lens/dist/clients/pipeline.js +77 -16
  220. package/node_modules/pi-lens/dist/clients/probe-home-state.js +228 -0
  221. package/node_modules/pi-lens/dist/clients/process-bridge.js +66 -0
  222. package/node_modules/pi-lens/dist/clients/process-snapshot.js +68 -0
  223. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +4 -3
  224. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +27 -3
  225. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +51 -20
  226. package/node_modules/pi-lens/dist/clients/project-lens-config.js +393 -122
  227. package/node_modules/pi-lens/dist/clients/project-snapshot.js +52 -24
  228. package/node_modules/pi-lens/dist/clients/python-environment.js +217 -0
  229. package/node_modules/pi-lens/dist/clients/python-provenance.js +639 -0
  230. package/node_modules/pi-lens/dist/clients/quiet-window.js +6 -1
  231. package/node_modules/pi-lens/dist/clients/read-bridge.js +13 -21
  232. package/node_modules/pi-lens/dist/clients/read-expansion.js +9 -46
  233. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +15 -4
  234. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +364 -212
  235. package/node_modules/pi-lens/dist/clients/read-guard.js +21 -0
  236. package/node_modules/pi-lens/dist/clients/resource-sampler.js +195 -96
  237. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +152 -109
  238. package/node_modules/pi-lens/dist/clients/review-graph/service.js +55 -3
  239. package/node_modules/pi-lens/dist/clients/review-graph/shared-extraction-ir.js +7 -15
  240. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +10 -74
  241. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +9 -3
  242. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +18 -2
  243. package/node_modules/pi-lens/dist/clients/runtime-context.js +49 -16
  244. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +25 -6
  245. package/node_modules/pi-lens/dist/clients/runtime-session.js +139 -22
  246. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +168 -35
  247. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +797 -243
  248. package/node_modules/pi-lens/dist/clients/runtime-turn.js +1028 -53
  249. package/node_modules/pi-lens/dist/clients/rust-client.js +31 -0
  250. package/node_modules/pi-lens/dist/clients/safe-spawn.js +33 -10
  251. package/node_modules/pi-lens/dist/clients/sanitize.js +7 -1
  252. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +1 -3
  253. package/node_modules/pi-lens/dist/clients/security-scan-client.js +3 -0
  254. package/node_modules/pi-lens/dist/clients/session-lifecycle.js +1 -1
  255. package/node_modules/pi-lens/dist/clients/session-start-observability.js +79 -0
  256. package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +12 -3
  257. package/node_modules/pi-lens/dist/clients/sgconfig.js +6 -1
  258. package/node_modules/pi-lens/dist/clients/skills-resolver.js +105 -0
  259. package/node_modules/pi-lens/dist/clients/smells-rollup.js +2 -2
  260. package/node_modules/pi-lens/dist/clients/string-utils.js +13 -0
  261. package/node_modules/pi-lens/dist/clients/subagent-mode.js +17 -4
  262. package/node_modules/pi-lens/dist/clients/test-runner-client.js +176 -39
  263. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +239 -0
  264. package/node_modules/pi-lens/dist/clients/tool-definition.js +41 -1
  265. package/node_modules/pi-lens/dist/clients/tool-policy.js +91 -21
  266. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +40 -40
  267. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +86 -48
  268. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +2 -2
  269. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +81 -1
  270. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +51 -46
  271. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +28 -0
  272. package/node_modules/pi-lens/dist/clients/typos-config.js +5 -0
  273. package/node_modules/pi-lens/dist/clients/user-notify.js +6 -2
  274. package/node_modules/pi-lens/dist/clients/widget-state.js +191 -26
  275. package/node_modules/pi-lens/dist/clients/word-index-logger.js +2 -2
  276. package/node_modules/pi-lens/dist/clients/word-index-store.js +20 -5
  277. package/node_modules/pi-lens/dist/clients/word-index.js +243 -31
  278. package/node_modules/pi-lens/dist/clients/workspace-topology.js +8 -1
  279. package/node_modules/pi-lens/dist/clients/zizmor-config.js +3 -0
  280. package/node_modules/pi-lens/dist/index.js +64335 -54300
  281. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -1
  282. package/node_modules/pi-lens/dist/mcp/server.js +127 -5
  283. package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +583 -0
  284. package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +129 -0
  285. package/node_modules/pi-lens/dist/tools/effective-config.js +89 -0
  286. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +95 -24
  287. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +67 -80
  288. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +37 -16
  289. package/node_modules/pi-lens/dist/tools/shared.js +0 -1
  290. package/node_modules/pi-lens/docs/agent-guide.md +64 -1
  291. package/node_modules/pi-lens/docs/configuration.md +222 -0
  292. package/node_modules/pi-lens/docs/dependencies.md +3 -3
  293. package/node_modules/pi-lens/docs/features.md +44 -5
  294. package/node_modules/pi-lens/docs/language-coverage.md +2 -2
  295. package/node_modules/pi-lens/docs/pi-lens-fixer.md +25 -0
  296. package/node_modules/pi-lens/docs/pi-lens-investigator.md +25 -0
  297. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +27 -0
  298. package/node_modules/pi-lens/docs/pi-lens-subagent.md +38 -0
  299. package/node_modules/pi-lens/docs/pi-lens-warden.md +55 -0
  300. package/node_modules/pi-lens/docs/public-api-stability.md +359 -0
  301. package/node_modules/pi-lens/docs/release-qa-baseline.md +182 -0
  302. package/node_modules/pi-lens/docs/subagent-compat.md +110 -30
  303. package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +2 -2
  304. package/node_modules/pi-lens/package.json +15 -23
  305. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-cross-language-method.yml +3 -3
  306. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-hallucinated-import.yml +2 -2
  307. package/node_modules/pi-lens/rules/tree-sitter-queries/python/python-sql-injection.yml +12 -2
  308. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +192 -1
  309. package/node_modules/pi-lens/scripts/install-selftest.mjs +58 -2
  310. package/node_modules/pi-lens/scripts/lib/skills-predicate.mjs +129 -0
  311. package/node_modules/pi-lens/scripts/rpc-load-check.mjs +3 -1
  312. package/node_modules/pi-mcp-adapter/CHANGELOG.md +27 -0
  313. package/node_modules/pi-mcp-adapter/README.md +13 -13
  314. package/node_modules/pi-mcp-adapter/commands.ts +58 -21
  315. package/node_modules/pi-mcp-adapter/config.ts +18 -4
  316. package/node_modules/pi-mcp-adapter/direct-tools.ts +26 -6
  317. package/node_modules/pi-mcp-adapter/dist/config.d.ts +4 -0
  318. package/node_modules/pi-mcp-adapter/dist/config.js +13 -4
  319. package/node_modules/pi-mcp-adapter/dist/config.js.map +1 -1
  320. package/node_modules/pi-mcp-adapter/dist/types.d.ts +11 -3
  321. package/node_modules/pi-mcp-adapter/dist/types.js.map +1 -1
  322. package/node_modules/pi-mcp-adapter/error-signal.ts +15 -4
  323. package/node_modules/pi-mcp-adapter/errors.ts +141 -0
  324. package/node_modules/pi-mcp-adapter/host-html-template.ts +58 -5
  325. package/node_modules/pi-mcp-adapter/index.ts +2 -3
  326. package/node_modules/pi-mcp-adapter/init.ts +5 -0
  327. package/node_modules/pi-mcp-adapter/mcp-panel.ts +30 -9
  328. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +66 -28
  329. package/node_modules/pi-mcp-adapter/mcp-status.ts +2 -0
  330. package/node_modules/pi-mcp-adapter/package.json +2 -1
  331. package/node_modules/pi-mcp-adapter/proxy-modes.ts +66 -13
  332. package/node_modules/pi-mcp-adapter/sandbox-proxy-template.ts +217 -0
  333. package/node_modules/pi-mcp-adapter/server-manager.ts +337 -6
  334. package/node_modules/pi-mcp-adapter/skills/mcp-scripting/SKILL.md +1 -0
  335. package/node_modules/pi-mcp-adapter/types.ts +18 -3
  336. package/node_modules/pi-mcp-adapter/ui-resource-handler.ts +18 -2
  337. package/node_modules/pi-mcp-adapter/ui-server.ts +179 -54
  338. package/node_modules/pi-mcp-adapter/ui-session.ts +28 -8
  339. package/node_modules/pi-web-access/CHANGELOG.md +28 -0
  340. package/node_modules/pi-web-access/README.md +62 -15
  341. package/node_modules/pi-web-access/curator-page.ts +3 -1
  342. package/node_modules/pi-web-access/curator-server.ts +5 -1
  343. package/node_modules/pi-web-access/gemini-search.ts +8 -4
  344. package/node_modules/pi-web-access/github-extract.ts +242 -1
  345. package/node_modules/pi-web-access/index.ts +118 -64
  346. package/node_modules/pi-web-access/mistral-search.ts +281 -0
  347. package/node_modules/pi-web-access/package.json +2 -2
  348. package/node_modules/pi-web-access/perplexity.ts +14 -1
  349. package/node_modules/pi-web-access/utils.ts +23 -6
  350. package/node_modules/pi-web-access/xai-search.ts +96 -33
  351. package/package.json +15 -11
@@ -2,7 +2,7 @@ import { matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tu
2
2
  import { createPanelKeys, type PanelKeybindings, type PanelKeys } from "./panel-keys.ts";
3
3
  import type { ImportKind } from "./types.ts";
4
4
  import { getConfigDirName } from "./agent-dir.ts";
5
- import { KNOWN_SERVER_PRESETS, type ConfigWritePreview, type KnownServerPreset, type McpDiscoverySummary } from "./config.ts";
5
+ import { KNOWN_SERVER_PRESETS, type ConfigWritePreview, type KnownServerPreset, type McpDiscoverySummary, type SharedConfigTarget } from "./config.ts";
6
6
  import type { McpOnboardingState } from "./onboarding-state.ts";
7
7
 
8
8
  interface SetupTheme {
@@ -54,13 +54,13 @@ function wrapText(text: string, width: number): string[] {
54
54
 
55
55
  export interface SetupPanelCallbacks {
56
56
  previewImports: (imports: ImportKind[]) => ConfigWritePreview;
57
- previewStarterProject: () => ConfigWritePreview;
58
- previewRepoPrompt: () => ConfigWritePreview | null;
59
- previewKnownServer: (preset: KnownServerPreset) => ConfigWritePreview;
57
+ previewStarterConfig: (target: SharedConfigTarget) => ConfigWritePreview;
58
+ previewRepoPrompt: (target: SharedConfigTarget) => ConfigWritePreview | null;
59
+ previewKnownServer: (preset: KnownServerPreset, target: SharedConfigTarget) => ConfigWritePreview;
60
60
  adoptImports: (imports: ImportKind[]) => Promise<{ added: ImportKind[]; path: string }>;
61
- scaffoldProjectConfig: () => Promise<{ path: string }>;
62
- addRepoPrompt: () => Promise<{ path: string; serverName: string }>;
63
- addKnownServer: (preset: KnownServerPreset) => Promise<{ path: string; serverName: string }>;
61
+ scaffoldConfig: (target: SharedConfigTarget) => Promise<{ path: string }>;
62
+ addRepoPrompt: (target: SharedConfigTarget) => Promise<{ path: string; serverName: string }>;
63
+ addKnownServer: (preset: KnownServerPreset, target: SharedConfigTarget) => Promise<{ path: string; serverName: string }>;
64
64
  openPath: (path: string) => Promise<void>;
65
65
  markSetupCompleted: () => void;
66
66
  }
@@ -75,13 +75,14 @@ type Screen = "empty" | "setup" | "imports" | "paths";
75
75
 
76
76
  type ActionId =
77
77
  | "run-setup"
78
+ | "select-shared-target"
78
79
  | "adopt-imports"
79
80
  | "view-example"
80
81
  | "show-precedence"
81
82
  | "open-paths"
82
83
  | "add-repoprompt"
83
84
  | "add-known-server"
84
- | "scaffold-project"
85
+ | "scaffold-shared-config"
85
86
  | "close";
86
87
 
87
88
  interface Action {
@@ -89,6 +90,7 @@ interface Action {
89
90
  label: string;
90
91
  description: string;
91
92
  preset?: KnownServerPreset;
93
+ target?: SharedConfigTarget;
92
94
  }
93
95
 
94
96
  export class McpSetupPanel {
@@ -96,6 +98,7 @@ export class McpSetupPanel {
96
98
  private actionCursor = 0;
97
99
  private importCursor = 0;
98
100
  private pathCursor = 0;
101
+ private sharedConfigTarget: SharedConfigTarget = "project";
99
102
  private selectedImports = new Set<ImportKind>();
100
103
  private busy = false;
101
104
  private notice: { text: string; tone: "success" | "warning" | "muted" } | null = null;
@@ -144,9 +147,13 @@ export class McpSetupPanel {
144
147
  if (this.discovery.imports.length > 0) {
145
148
  actions.push({ id: "adopt-imports", label: "Adopt detected compatibility imports", description: `Choose which host-specific MCP configs Pi should import into its own override file. ${this.discovery.imports.length} source${this.discovery.imports.length === 1 ? "" : "s"} found.` });
146
149
  }
147
- actions.push({ id: "view-example", label: "View example `.mcp.json`", description: "Preview a working shared MCP config you can paste or adapt." });
148
- if (!this.discovery.sources.some((source) => source.id === "shared-project" && source.exists)) {
149
- actions.push({ id: "scaffold-project", label: "Scaffold project `.mcp.json`", description: "Write a minimal project config using the standard shared MCP file path, then reload Pi." });
150
+ actions.push(
151
+ { id: "select-shared-target", label: `${this.sharedConfigTarget === "project" ? "●" : "○"} Add to this project (.mcp.json)`, description: "Write new shared MCP servers to the project/team config.", target: "project" },
152
+ { id: "select-shared-target", label: `${this.sharedConfigTarget === "global" ? "●" : "○"} Add globally (~/.config/mcp/mcp.json)`, description: "Write new shared MCP servers to your all-projects config.", target: "global" },
153
+ );
154
+ actions.push({ id: "view-example", label: "View example shared config", description: "Preview a working shared MCP config you can paste or adapt." });
155
+ if (!this.selectedSharedConfigExists()) {
156
+ actions.push({ id: "scaffold-shared-config", label: `Scaffold ${this.sharedTargetLabel()}`, description: "Write a minimal config at the selected normal MCP setup path, then reload Pi." });
150
157
  }
151
158
  actions.push({ id: "show-precedence", label: "Explain config precedence", description: "Show the read order and where Pi writes compatibility settings." });
152
159
  if (this.getDetectedPaths().length > 0) {
@@ -156,7 +163,7 @@ export class McpSetupPanel {
156
163
  actions.push({ id: "add-known-server", label: preset.name, description: preset.summary, preset });
157
164
  }
158
165
  if (!this.discovery.repoPrompt.configured && this.discovery.repoPrompt.executablePath && this.discovery.repoPrompt.targetPath && this.discovery.repoPrompt.entry && this.discovery.repoPrompt.serverName) {
159
- actions.push({ id: "add-repoprompt", label: "Add RepoPrompt to shared MCP config", description: "Write a standard MCP entry for RepoPrompt to the recommended shared target, then reload MCP in-session." });
166
+ actions.push({ id: "add-repoprompt", label: "Add RepoPrompt to selected shared config", description: "Write a standard MCP entry for RepoPrompt to the selected normal setup path, then reload MCP in-session." });
160
167
  }
161
168
  actions.push({ id: "close", label: "Close", description: "Exit the onboarding flow." });
162
169
  return actions;
@@ -170,6 +177,15 @@ export class McpSetupPanel {
170
177
  return [...new Set(paths)];
171
178
  }
172
179
 
180
+ private sharedTargetLabel(): string {
181
+ return this.sharedConfigTarget === "project" ? "project .mcp.json" : "global ~/.config/mcp/mcp.json";
182
+ }
183
+
184
+ private selectedSharedConfigExists(): boolean {
185
+ const sourceId = this.sharedConfigTarget === "project" ? "shared-project" : "shared-global";
186
+ return this.discovery.sources.some((source) => source.id === sourceId && source.exists);
187
+ }
188
+
173
189
  private getSelectedAction(): Action | undefined {
174
190
  const actions = this.getActions();
175
191
  return actions[this.actionCursor];
@@ -293,9 +309,15 @@ export class McpSetupPanel {
293
309
  this.tui.requestRender();
294
310
  return;
295
311
  }
296
- if (action.id === "scaffold-project") {
312
+ if (action.id === "select-shared-target" && action.target) {
313
+ this.sharedConfigTarget = action.target;
314
+ this.notice = { text: `New shared servers will be written to ${this.sharedTargetLabel()}.`, tone: "muted" };
315
+ this.tui.requestRender();
316
+ return;
317
+ }
318
+ if (action.id === "scaffold-shared-config") {
297
319
  await this.runBusy(async () => {
298
- const result = await this.callbacks.scaffoldProjectConfig();
320
+ const result = await this.callbacks.scaffoldConfig(this.sharedConfigTarget);
299
321
  this.callbacks.markSetupCompleted();
300
322
  this.notice = { text: `Wrote starter config to ${result.path}. Pi will reload after this panel closes.`, tone: "success" };
301
323
  });
@@ -303,7 +325,7 @@ export class McpSetupPanel {
303
325
  }
304
326
  if (action.id === "add-repoprompt") {
305
327
  await this.runBusy(async () => {
306
- const result = await this.callbacks.addRepoPrompt();
328
+ const result = await this.callbacks.addRepoPrompt(this.sharedConfigTarget);
307
329
  this.callbacks.markSetupCompleted();
308
330
  this.notice = { text: `Added ${result.serverName} to ${result.path}. Pi will reload after this panel closes.`, tone: "success" };
309
331
  });
@@ -312,7 +334,7 @@ export class McpSetupPanel {
312
334
  if (action.id === "add-known-server" && action.preset) {
313
335
  const preset = action.preset;
314
336
  await this.runBusy(async () => {
315
- const result = await this.callbacks.addKnownServer(preset);
337
+ const result = await this.callbacks.addKnownServer(preset, this.sharedConfigTarget);
316
338
  this.callbacks.markSetupCompleted();
317
339
  this.notice = { text: `Added ${result.serverName} to ${result.path}. Pi will reload after this panel closes.`, tone: "success" };
318
340
  });
@@ -416,8 +438,11 @@ export class McpSetupPanel {
416
438
  for (let index = start; index < end; index++) {
417
439
  const action = actions[index];
418
440
  if (!action) continue;
441
+ if (action.id === "select-shared-target" && (index === start || actions[index - 1]?.id !== "select-shared-target")) {
442
+ lines.push(this.padLine(fg(this.t.title, "Choose where new shared servers go"), innerW));
443
+ }
419
444
  if (action.id === "add-known-server" && (index === start || actions[index - 1]?.id !== "add-known-server")) {
420
- lines.push(this.padLine(fg(this.t.title, "Add a known server"), innerW));
445
+ lines.push(this.padLine(fg(this.t.title, `Add a known server to ${this.sharedTargetLabel()}`), innerW));
421
446
  }
422
447
  const selected = index === this.actionCursor;
423
448
  const cursor = selected ? fg(this.t.selected, "›") : " ";
@@ -495,12 +520,12 @@ export class McpSetupPanel {
495
520
  ? ` ${this.discovery.conflicts.length} same-name conflict${this.discovery.conflicts.length === 1 ? "" : "s"} reported.`
496
521
  : "";
497
522
  if (!this.discovery.hasAnyConfig) {
498
- return `Create a shared .mcp.json, adopt host imports, or quick-add RepoPrompt from this screen.${hostNote}${conflictNote}`;
523
+ return `Add shared servers to .mcp.json for this project/team or ~/.config/mcp/mcp.json for all projects. Adopt host imports or quick-add RepoPrompt from this screen.${hostNote}${conflictNote}`;
499
524
  }
500
525
  if (this.discovery.totalServerCount === 0 && this.discovery.imports.length > 0) {
501
526
  return `Detected ${this.discovery.imports.length} compatibility import source${this.discovery.imports.length === 1 ? "" : "s"}. Adopt them into Pi or inspect the underlying files.${hostNote}${conflictNote}`;
502
527
  }
503
- return `Shared MCP files are preferred. Pi-owned files are only for compatibility imports and adapter-specific overrides.${hostNote}${conflictNote}`;
528
+ return `Use .mcp.json for project/team servers or ~/.config/mcp/mcp.json for all projects. Pi-owned files are for compatibility imports and adapter-specific overrides, not another normal setup path.${hostNote}${conflictNote}`;
504
529
  }
505
530
 
506
531
  private visibleActionRange(total: number): { start: number; end: number } {
@@ -534,6 +559,12 @@ export class McpSetupPanel {
534
559
  ],
535
560
  previewW,
536
561
  );
562
+ case "select-shared-target":
563
+ return this.formatPreview([
564
+ action.target === "project" ? "Project target: .mcp.json" : "Global target: ~/.config/mcp/mcp.json",
565
+ "Known server presets and starter configs will be written to the selected normal MCP setup path.",
566
+ "Pi-owned mcp.json files remain compatibility and adapter-only override state.",
567
+ ], previewW);
537
568
  case "view-example":
538
569
  return this.formatPreview([
539
570
  "Example shared `.mcp.json`:",
@@ -546,10 +577,17 @@ export class McpSetupPanel {
546
577
  " }",
547
578
  "}",
548
579
  "",
549
- "Use Scaffold project `.mcp.json` when you want a safe empty shell instead of a live example server.",
580
+ "Use Scaffold selected config when you want a safe empty shell instead of a live example server.",
550
581
  ], previewW);
551
582
  case "show-precedence":
552
583
  return this.formatPreview([
584
+ "Recommended shared config:",
585
+ " project/team: .mcp.json",
586
+ " all projects: ~/.config/mcp/mcp.json",
587
+ "",
588
+ "Advanced compatibility and Pi-owned layers:",
589
+ " host imports, .agents files, package MCP manifests, and Pi overrides",
590
+ "",
553
591
  "Read order (later entries win):",
554
592
  "0. detected host configs (opt-in lowest-precedence fallback)",
555
593
  "1. ~/.config/mcp/mcp.json",
@@ -570,7 +608,7 @@ export class McpSetupPanel {
570
608
  : ["No config paths were detected."], previewW);
571
609
  case "add-repoprompt": {
572
610
  const repoPrompt = this.discovery.repoPrompt;
573
- const preview = this.callbacks.previewRepoPrompt();
611
+ const preview = this.callbacks.previewRepoPrompt(this.sharedConfigTarget);
574
612
  if (!preview) {
575
613
  return this.formatPreview(["RepoPrompt is not available to add from this setup screen."], previewW);
576
614
  }
@@ -579,7 +617,7 @@ export class McpSetupPanel {
579
617
  preview,
580
618
  [
581
619
  `Executable: ${repoPrompt.executablePath ?? "not found"}`,
582
- `Target: ${repoPrompt.targetPath ?? "n/a"}`,
620
+ `Target: ${this.sharedTargetLabel()}`,
583
621
  `Server name: ${repoPrompt.serverName ?? "repoprompt"}`,
584
622
  ],
585
623
  previewW,
@@ -590,17 +628,17 @@ export class McpSetupPanel {
590
628
  if (!preset) return this.formatPreview(["Known server preset is unavailable."], previewW);
591
629
  return this.formatWritePreview(
592
630
  `${preset.name} write preview`,
593
- this.callbacks.previewKnownServer(preset),
594
- [preset.summary],
631
+ this.callbacks.previewKnownServer(preset, this.sharedConfigTarget),
632
+ [preset.summary, `Target: ${this.sharedTargetLabel()}`],
595
633
  previewW,
596
634
  );
597
635
  }
598
- case "scaffold-project":
636
+ case "scaffold-shared-config":
599
637
  return this.formatWritePreview(
600
- "Starter project `.mcp.json` write preview",
601
- this.callbacks.previewStarterProject(),
638
+ `${this.sharedTargetLabel()} starter write preview`,
639
+ this.callbacks.previewStarterConfig(this.sharedConfigTarget),
602
640
  [
603
- "This writes a minimal `.mcp.json` in the current project using the shared MCP layout.",
641
+ "This writes a minimal config at the selected normal MCP setup path.",
604
642
  "It intentionally avoids adding a fake placeholder server that would fail on first reload.",
605
643
  ],
606
644
  previewW,
@@ -48,7 +48,9 @@ export function createMcpStatusSnapshot(state: McpExtensionState): McpStatusSnap
48
48
  servers.push({
49
49
  name,
50
50
  status,
51
+ listenState: connection?.status === "connected" ? connection.listenState : "disconnected",
51
52
  toolCount,
53
+ ...(connection?.status === "connected" && connection.listenCatalogStale ? { catalogStale: true } : {}),
52
54
  ...(resourceCount !== undefined ? { resourceCount } : {}),
53
55
  ...(status === "failed" && failedAgoSeconds !== null ? { failedAgoSeconds } : {}),
54
56
  disabled,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-mcp-adapter",
3
- "version": "2.31.0",
3
+ "version": "2.32.1",
4
4
  "description": "MCP (Model Context Protocol) adapter extension for Pi coding agent",
5
5
  "type": "module",
6
6
  "types": "./index.ts",
@@ -125,6 +125,7 @@
125
125
  "mcp-status.ts",
126
126
  "metadata-cache.ts",
127
127
  "host-html-template.ts",
128
+ "sandbox-proxy-template.ts",
128
129
  "ui-resource-handler.ts",
129
130
  "consent-manager.ts",
130
131
  "ui-server.ts",
@@ -19,6 +19,7 @@ import { SessionRecoveryAuthRequiredError, withSessionRecovery } from "./session
19
19
  import { paginate, rankSuggestions, rankToolMatches, resolveSearchKeywords } from "./search-ranking.ts";
20
20
  import { ensureToolCallApproved, isToolCallApprovalRequired } from "./tool-approval.ts";
21
21
  import { isServerInActiveFailureBackoff } from "./failure-backoff.ts";
22
+ import { getInputRequiredNeedsUiDetails } from "./errors.ts";
22
23
 
23
24
  type ProxyToolResult = AgentToolResult<Record<string, unknown>>;
24
25
  type ClientCallToolResult = Awaited<ReturnType<Client["callTool"]>>;
@@ -339,7 +340,7 @@ export function executeUiMessages(state: McpExtensionState): ProxyToolResult {
339
340
  }
340
341
 
341
342
  export function executeStatus(state: McpExtensionState): ProxyToolResult {
342
- const servers: Array<{ name: string; status: string; toolCount: number; failedAgo: number | null; disabled?: boolean }> = [];
343
+ const servers: Array<{ name: string; status: string; listenState: string; catalogStale?: boolean; toolCount: number; failedAgo: number | null; disabled?: boolean }> = [];
343
344
 
344
345
  for (const name of Object.keys(state.config.mcpServers)) {
345
346
  const definition = state.config.mcpServers[name];
@@ -360,7 +361,16 @@ export function executeStatus(state: McpExtensionState): ProxyToolResult {
360
361
 
361
362
  const toolCount = status === "failed" ? 0 : metadata?.length ?? 0;
362
363
 
363
- servers.push({ name, status, toolCount, failedAgo, ...(disabled ? { disabled: true } : {}) });
364
+ const listenState = connection?.status === "connected" ? connection.listenState : "disconnected";
365
+ servers.push({
366
+ name,
367
+ status,
368
+ listenState,
369
+ ...(connection?.status === "connected" && connection.listenCatalogStale ? { catalogStale: true } : {}),
370
+ toolCount,
371
+ failedAgo,
372
+ ...(disabled ? { disabled: true } : {}),
373
+ });
364
374
  }
365
375
 
366
376
  const disabledCount = servers.filter(s => s.disabled).length;
@@ -377,7 +387,18 @@ export function executeStatus(state: McpExtensionState): ProxyToolResult {
377
387
  continue;
378
388
  }
379
389
  if (server.status === "connected") {
380
- text += `✓ ${server.name} (${server.toolCount} tools)\n`;
390
+ const listen = server.listenState === "active"
391
+ ? server.catalogStale
392
+ ? ", listen active, catalog may be stale"
393
+ : ", listen active"
394
+ : server.listenState === "dropped"
395
+ ? ", catalog may be stale; will reconcile on next keep-alive or tool use"
396
+ : server.listenState === "re-establishing"
397
+ ? ", re-establishing listen"
398
+ : server.listenState === "legacy"
399
+ ? ", legacy notification path"
400
+ : ", not listening for catalog updates";
401
+ text += `✓ ${server.name} (${server.toolCount} tools${listen})\n`;
381
402
  continue;
382
403
  }
383
404
  if (server.status === "needs-auth") {
@@ -385,14 +406,19 @@ export function executeStatus(state: McpExtensionState): ProxyToolResult {
385
406
  continue;
386
407
  }
387
408
  if (server.status === "cached") {
388
- text += `○ ${server.name} (${server.toolCount} tools, cached)\n`;
409
+ text += `○ ${server.name} (${server.toolCount} tools, cached; not listening)\n`;
389
410
  continue;
390
411
  }
391
412
  if (server.status === "failed") {
392
413
  text += `✗ ${server.name} (failed ${server.failedAgo ?? 0}s ago)\n`;
393
414
  continue;
394
415
  }
395
- text += `○ ${server.name} (not connected)\n`;
416
+ text += `○ ${server.name} (not listening; disconnected)\n`;
417
+ }
418
+
419
+ const directToolsFrozen = state.config.settings?.freezeDirectTools === true;
420
+ if (directToolsFrozen) {
421
+ text += "\nDirect tools frozen; active registrations may differ from current metadata.\n";
396
422
  }
397
423
 
398
424
  if (servers.length > 0) {
@@ -401,7 +427,7 @@ export function executeStatus(state: McpExtensionState): ProxyToolResult {
401
427
 
402
428
  return {
403
429
  content: [{ type: "text" as const, text: text.trim() }],
404
- details: { mode: "status", servers, totalTools, connectedCount, disabledCount },
430
+ details: { mode: "status", servers, totalTools, connectedCount, disabledCount, directToolsFrozen },
405
431
  };
406
432
  }
407
433
 
@@ -751,7 +777,17 @@ export function executeList(state: McpExtensionState, server: string): ProxyTool
751
777
  };
752
778
  }
753
779
 
754
- const cachedNote = connection?.status === "connected" ? "" : " (not connected, cached)";
780
+ // "not connected" alone reads as an outage while the tools listed below are
781
+ // real and the connection is simply lazy (tools served from cache).
782
+ // Distinguish auth from plain laziness so the caller knows which remedy applies.
783
+ let cachedNote = "";
784
+ if (connection?.status !== "connected") {
785
+ if (connection?.status === "needs-auth") {
786
+ cachedNote = ` (needs auth — run mcp({ action: "auth-start", server: "${server}" }))`;
787
+ } else {
788
+ cachedNote = ` (lazy: tools from cache, not connected yet — mcp({ connect: "${server}" }) to connect)`;
789
+ }
790
+ }
755
791
  let text = `${server} (${toolNames.length} tools${cachedNote}):\n\n`;
756
792
 
757
793
  const descMap = new Map<string, string>();
@@ -1300,7 +1336,13 @@ export async function executeCall(
1300
1336
  onNeedsAuth: recoverAuthConnection,
1301
1337
  },
1302
1338
  serverName,
1303
- (conn) => conn.client.readResource({ uri: toolMeta.resourceUri! }, requestOptions),
1339
+ async (conn) => {
1340
+ const refreshRead = await state.manager.prepareResourceUse?.(serverName, toolMeta.resourceUri!, conn);
1341
+ return conn.client.readResource(
1342
+ { uri: toolMeta.resourceUri! },
1343
+ refreshRead ? { ...requestOptions, cacheMode: "refresh" } : requestOptions,
1344
+ );
1345
+ },
1304
1346
  );
1305
1347
  const content = transformMcpResourceContents(result.contents ?? [], state.owner?.signal);
1306
1348
  const guarded = await guardMcpOutput(content.length > 0 ? content : [{ type: "text" as const, text: "(empty resource)" }], outputGuardOptions);
@@ -1330,11 +1372,14 @@ export async function executeCall(
1330
1372
  onNeedsAuth: recoverAuthConnection,
1331
1373
  },
1332
1374
  serverName,
1333
- (conn) => abortable(conn.client.callTool({
1334
- name: toolMeta.originalName,
1335
- arguments: normalizedArgs,
1336
- _meta: uiSession?.requestMeta,
1337
- }, requestOptions), ownedSignal),
1375
+ async (conn) => {
1376
+ await state.manager.ensureListen?.(serverName, conn);
1377
+ return abortable(conn.client.callTool({
1378
+ name: toolMeta.originalName,
1379
+ arguments: normalizedArgs,
1380
+ _meta: uiSession?.requestMeta,
1381
+ }, requestOptions), ownedSignal);
1382
+ },
1338
1383
  );
1339
1384
 
1340
1385
  if (toolMeta.uiResourceUri) {
@@ -1408,6 +1453,14 @@ export async function executeCall(
1408
1453
  details: { mode: "call", error: "url_elicitation_required", ...callIdentity, action },
1409
1454
  };
1410
1455
  }
1456
+ const inputRequired = getInputRequiredNeedsUiDetails(error, callIdentity);
1457
+ if (inputRequired) {
1458
+ uiSession?.sendToolCancelled(inputRequired.message);
1459
+ return {
1460
+ content: [{ type: "text" as const, text: inputRequired.message }],
1461
+ details: { mode: "call", ...inputRequired },
1462
+ };
1463
+ }
1411
1464
  const message = error instanceof Error ? error.message : String(error);
1412
1465
  uiSession?.sendToolCancelled(message);
1413
1466
 
@@ -0,0 +1,217 @@
1
+ /**
2
+ * The proxy is served from a different loopback origin than the trusted host.
3
+ * `allow-same-origin` is safe here because this document never contains the
4
+ * host's session capability; provider HTML is loaded into the nested frame.
5
+ */
6
+ export const SANDBOX_PROXY_SANDBOX =
7
+ "allow-scripts allow-forms allow-modals allow-popups allow-downloads allow-same-origin";
8
+ export const SANDBOX_INNER_SANDBOX = SANDBOX_PROXY_SANDBOX;
9
+ export const SANDBOX_PROXY_PATH = "/sandbox";
10
+
11
+ export interface SandboxProxyTemplateInput {
12
+ parentOrigin: string;
13
+ }
14
+
15
+ /**
16
+ * Build the static document used by the trusted, second-origin sandbox proxy.
17
+ * The only per-session value is the exact origin of the parent host.
18
+ */
19
+ export function buildSandboxProxyHtml(input: SandboxProxyTemplateInput): string {
20
+ const parentOrigin = safeInlineJSON(input.parentOrigin);
21
+
22
+ return `<!doctype html>
23
+ <html lang="en">
24
+ <head>
25
+ <meta charset="utf-8" />
26
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
27
+ <title>MCP App Sandbox</title>
28
+ <style>
29
+ html, body { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background: transparent; }
30
+ iframe { display: block; width: 100%; height: 100%; border: 0; }
31
+ </style>
32
+ </head>
33
+ <body>
34
+ <iframe id="mcp-app" title="MCP App" sandbox="${SANDBOX_INNER_SANDBOX}" referrerpolicy="no-referrer"></iframe>
35
+ <script>
36
+ const EXPECTED_PARENT_ORIGIN = ${parentOrigin};
37
+ const SANDBOX_PROXY_READY_METHOD = "ui/notifications/sandbox-proxy-ready";
38
+ const SANDBOX_RESOURCE_READY_METHOD = "ui/notifications/sandbox-resource-ready";
39
+ const INNER_SANDBOX = ${safeInlineJSON(SANDBOX_INNER_SANDBOX)};
40
+ const MAX_PENDING_MESSAGES = 64;
41
+ const innerFrame = document.getElementById("mcp-app");
42
+ const pendingToInner = [];
43
+ let innerReady = false;
44
+
45
+ const isObject = (value) => value !== null && typeof value === "object";
46
+ const isMessageFromParent = (event) =>
47
+ event.source === window.parent && event.origin === EXPECTED_PARENT_ORIGIN;
48
+ const isMessageFromInner = (event) =>
49
+ event.source === innerFrame.contentWindow && event.origin === window.location.origin;
50
+
51
+ const postToParent = (data) => {
52
+ window.parent.postMessage(data, EXPECTED_PARENT_ORIGIN);
53
+ };
54
+
55
+ const postToInner = (data) => {
56
+ if (!innerReady || !innerFrame.contentWindow) {
57
+ if (pendingToInner.length < MAX_PENDING_MESSAGES) pendingToInner.push(data);
58
+ return;
59
+ }
60
+ innerFrame.contentWindow.postMessage(data, window.location.origin);
61
+ };
62
+
63
+ const flushPendingMessages = () => {
64
+ if (!innerFrame.contentWindow) return;
65
+ innerReady = true;
66
+ for (const data of pendingToInner.splice(0)) {
67
+ innerFrame.contentWindow.postMessage(data, window.location.origin);
68
+ }
69
+ };
70
+
71
+ const sanitizeDomains = (domains) => {
72
+ if (!Array.isArray(domains)) return [];
73
+ return [...new Set(domains.filter((domain) =>
74
+ typeof domain === "string" &&
75
+ domain.length > 0 &&
76
+ /^[\\x21-\\x7E]+$/.test(domain) &&
77
+ !/[;'\"]/.test(domain),
78
+ ))];
79
+ };
80
+
81
+ const toDirective = (name, trustedSources, domains) =>
82
+ name + " " + [...new Set([...trustedSources, ...domains])].join(" ");
83
+
84
+ const buildResourceCsp = (csp) => {
85
+ const resourceDomains = sanitizeDomains(csp?.resourceDomains);
86
+ const connectDomains = sanitizeDomains(csp?.connectDomains);
87
+ const frameDomains = sanitizeDomains(csp?.frameDomains);
88
+ const baseUriDomains = sanitizeDomains(csp?.baseUriDomains);
89
+ return [
90
+ "default-src 'none'",
91
+ "sandbox " + INNER_SANDBOX,
92
+ toDirective("script-src", ["'self'", "'unsafe-inline'"], resourceDomains),
93
+ toDirective("style-src", ["'self'", "'unsafe-inline'"], resourceDomains),
94
+ toDirective("font-src", ["'self'"], resourceDomains),
95
+ toDirective("img-src", ["'self'", "data:"], resourceDomains),
96
+ toDirective("media-src", ["'self'", "data:"], resourceDomains),
97
+ connectDomains.length > 0 ? "connect-src " + connectDomains.join(" ") : "connect-src 'none'",
98
+ frameDomains.length > 0 ? "frame-src " + frameDomains.join(" ") : "frame-src 'none'",
99
+ "worker-src 'none'",
100
+ "object-src 'none'",
101
+ baseUriDomains.length > 0 ? "base-uri " + baseUriDomains.join(" ") : "base-uri 'self'",
102
+ ].join("; ");
103
+ };
104
+
105
+ const escapeAttribute = (value) => value
106
+ .replace(/&/g, "&amp;")
107
+ .replace(/\"/g, "&quot;")
108
+ .replace(/</g, "&lt;")
109
+ .replace(/>/g, "&gt;");
110
+
111
+ const injectCsp = (html, csp) => {
112
+ const meta = '<meta http-equiv="Content-Security-Policy" content="' + escapeAttribute(csp) + '">';
113
+ const head = html.match(/<head(?:\\s[^>]*)?>/i);
114
+ if (head && head.index !== undefined) {
115
+ const end = head.index + head[0].length;
116
+ return html.slice(0, end) + meta + html.slice(end);
117
+ }
118
+ return meta + html;
119
+ };
120
+
121
+ const safeSandbox = (requested) => {
122
+ const requestedTokens = typeof requested === "string" ? requested.split(/\\s+/) : [];
123
+ const allowedTokens = new Set(INNER_SANDBOX.split(" "));
124
+ const tokens = requestedTokens.filter((token) => allowedTokens.has(token));
125
+ // Provider HTML needs both script execution and a real proxy-origin
126
+ // storage context. Never accept popup escape or top-navigation tokens.
127
+ return [...new Set([
128
+ "allow-scripts",
129
+ "allow-same-origin",
130
+ ...tokens,
131
+ ])].filter((token) => allowedTokens.has(token)).join(" ");
132
+ };
133
+
134
+ const buildAllowAttribute = (permissions) => {
135
+ if (!isObject(permissions) || Array.isArray(permissions)) return "";
136
+ const allowed = [];
137
+ if (permissions.camera) allowed.push("camera");
138
+ if (permissions.microphone) allowed.push("microphone");
139
+ if (permissions.geolocation) allowed.push("geolocation");
140
+ if (permissions.clipboardWrite) allowed.push("clipboard-write");
141
+ return allowed.join("; ");
142
+ };
143
+
144
+ const loadResource = (params) => {
145
+ if (!isObject(params) || typeof params.html !== "string" || !innerFrame.contentDocument) return;
146
+ innerFrame.setAttribute("sandbox", safeSandbox(params.sandbox));
147
+ const allow = buildAllowAttribute(params.permissions);
148
+ if (allow) innerFrame.setAttribute("allow", allow);
149
+ else innerFrame.removeAttribute("allow");
150
+ innerReady = false;
151
+ const document = innerFrame.contentDocument;
152
+ document.open();
153
+ document.write(injectCsp(params.html, buildResourceCsp(params.csp)));
154
+ document.close();
155
+ flushPendingMessages();
156
+ };
157
+
158
+ window.addEventListener("message", (event) => {
159
+ if (isMessageFromParent(event)) {
160
+ const data = event.data;
161
+ if (!isObject(data)) return;
162
+ if (data.method === SANDBOX_RESOURCE_READY_METHOD) {
163
+ loadResource(data.params);
164
+ return;
165
+ }
166
+ if (data.method === SANDBOX_PROXY_READY_METHOD) return;
167
+ if (typeof data.method === "string" && data.method.startsWith("ui/notifications/sandbox-")) return;
168
+ postToInner(data);
169
+ return;
170
+ }
171
+
172
+ if (!isMessageFromInner(event)) return;
173
+ const data = event.data;
174
+ if (!isObject(data)) return;
175
+ if (typeof data.method === "string" && data.method.startsWith("ui/notifications/sandbox-")) return;
176
+ postToParent(data);
177
+ });
178
+
179
+ postToParent({
180
+ jsonrpc: "2.0",
181
+ method: SANDBOX_PROXY_READY_METHOD,
182
+ params: {},
183
+ });
184
+ </script>
185
+ </body>
186
+ </html>`;
187
+ }
188
+
189
+ /**
190
+ * CSP for the proxy document itself. It contains only the inline relay script
191
+ * and a nested about:blank frame; provider policy is applied separately after
192
+ * the host sends the resource-ready notification.
193
+ */
194
+ export function buildSandboxProxyCsp(): string {
195
+ return [
196
+ "default-src 'none'",
197
+ "script-src 'unsafe-inline'",
198
+ "style-src 'unsafe-inline'",
199
+ "frame-src 'self'",
200
+ "connect-src 'none'",
201
+ "worker-src 'none'",
202
+ "object-src 'none'",
203
+ "base-uri 'none'",
204
+ `sandbox ${SANDBOX_PROXY_SANDBOX}`,
205
+ ].join("; ");
206
+ }
207
+
208
+ function safeInlineJSON(value: unknown): string {
209
+ const json = JSON.stringify(value);
210
+ if (json === undefined) return "undefined";
211
+ return json
212
+ .replace(/</g, "\\u003c")
213
+ .replace(/>/g, "\\u003e")
214
+ .replace(/&/g, "\\u0026")
215
+ .replace(/\u2028/g, "\\u2028")
216
+ .replace(/\u2029/g, "\\u2029");
217
+ }