@herbertgao/pi-extensions 2026.8.11 → 2026.8.13

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