@herbertgao/pi-extensions 2026.8.6 → 2026.8.8

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 (385) hide show
  1. package/README.md +8 -6
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -2
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +320 -22
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +79 -17
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +27 -0
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +66 -17
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
  15. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  16. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
  17. package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
  18. package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
  19. package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
  20. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
  21. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
  22. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +17 -13
  23. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
  24. package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
  25. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
  26. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
  27. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
  28. package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
  29. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
  30. package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
  31. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +21 -6
  32. package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
  33. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +20 -0
  34. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +5 -4
  35. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +174 -100
  36. package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
  37. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
  38. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +5 -1
  39. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +10 -2
  40. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +10 -6
  41. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/tool-schema.md +11 -5
  42. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +5 -2
  43. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
  44. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
  45. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
  46. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +140 -97
  47. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +63 -28
  48. package/node_modules/@juicesharp/rpiv-ask-user-question/state/selectors/projections.ts +22 -15
  49. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state-reducer.ts +11 -1
  50. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
  51. package/node_modules/@juicesharp/rpiv-ask-user-question/tool/response-envelope.ts +11 -0
  52. package/node_modules/@juicesharp/rpiv-ask-user-question/tool/types.ts +11 -0
  53. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/multi-select-view.ts +31 -17
  54. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/preview/preview-block-renderer.ts +15 -6
  55. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/wrapping-select.ts +15 -6
  56. package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +86 -38
  57. package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +76 -9
  58. package/node_modules/@narumitw/pi-btw/README.md +120 -111
  59. package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
  60. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
  61. package/node_modules/@narumitw/pi-btw/package.json +10 -6
  62. package/node_modules/@narumitw/pi-btw/src/btw.ts +36 -40
  63. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
  64. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
  65. package/node_modules/@narumitw/pi-btw/src/menu.ts +45 -2
  66. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
  67. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +44 -1856
  68. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
  69. package/node_modules/@pi-plugins/fast-mode/package.json +4 -6
  70. package/node_modules/pi-lens/CHANGELOG.md +999 -0
  71. package/node_modules/pi-lens/README.md +117 -3
  72. package/node_modules/pi-lens/config/ruff/core.toml +42 -0
  73. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
  74. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +126 -13
  75. package/node_modules/pi-lens/dist/clients/agent-nudge.js +67 -11
  76. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
  77. package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
  78. package/node_modules/pi-lens/dist/clients/bash-file-access.js +381 -13
  79. package/node_modules/pi-lens/dist/clients/biome-client.js +61 -30
  80. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
  81. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +129 -0
  82. package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
  83. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
  84. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +206 -0
  85. package/node_modules/pi-lens/dist/clients/cache-observability.js +353 -76
  86. package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
  87. package/node_modules/pi-lens/dist/clients/cascade-format.js +20 -1
  88. package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
  89. package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
  90. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
  91. package/node_modules/pi-lens/dist/clients/dead-code-client.js +123 -31
  92. package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
  93. package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
  94. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +84 -14
  95. package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +155 -0
  96. package/node_modules/pi-lens/dist/clients/dependency-checker.js +25 -3
  97. package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +8 -2
  98. package/node_modules/pi-lens/dist/clients/deps/typebox.js +8 -3
  99. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +147 -33
  100. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
  101. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +52 -2
  102. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +256 -88
  103. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
  104. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +9 -6
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +9 -7
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +8 -6
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +60 -53
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -2
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +25 -2
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +20 -5
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +44 -11
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +17 -8
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +32 -4
  129. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +47 -3
  130. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +14 -4
  131. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +127 -28
  132. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +12 -7
  133. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
  134. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +275 -13
  135. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +21 -6
  136. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
  137. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +734 -64
  138. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
  139. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +188 -0
  140. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +9 -0
  141. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +21 -10
  142. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -2
  143. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
  144. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
  145. package/node_modules/pi-lens/dist/clients/extension-log.js +22 -1
  146. package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
  147. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +316 -0
  148. package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
  149. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
  150. package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
  151. package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
  152. package/node_modules/pi-lens/dist/clients/git-guard.js +31 -0
  153. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
  154. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +109 -9
  155. package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
  156. package/node_modules/pi-lens/dist/clients/host-ports.js +8 -0
  157. package/node_modules/pi-lens/dist/clients/installer/index.js +1114 -100
  158. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
  159. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
  160. package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
  161. package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
  162. package/node_modules/pi-lens/dist/clients/jscpd-client.js +87 -8
  163. package/node_modules/pi-lens/dist/clients/knip-client.js +310 -10
  164. package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
  165. package/node_modules/pi-lens/dist/clients/language-profile.js +9 -2
  166. package/node_modules/pi-lens/dist/clients/latency-logger.js +440 -11
  167. package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
  168. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
  169. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
  170. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +128 -8
  171. package/node_modules/pi-lens/dist/clients/lsp/client.js +1660 -164
  172. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +83 -1
  173. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
  174. package/node_modules/pi-lens/dist/clients/lsp/index.js +2423 -151
  175. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
  176. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
  177. package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
  178. package/node_modules/pi-lens/dist/clients/lsp/server.js +277 -16
  179. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
  180. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
  181. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +79 -3
  182. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +43 -0
  183. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
  184. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
  185. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
  186. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
  187. package/node_modules/pi-lens/dist/clients/mcp/session.js +10 -2
  188. package/node_modules/pi-lens/dist/clients/module-report.js +22 -1
  189. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +114 -23
  190. package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
  191. package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
  192. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
  193. package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
  194. package/node_modules/pi-lens/dist/clients/pipeline.js +154 -33
  195. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
  196. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
  197. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
  198. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +84 -14
  199. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
  200. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
  201. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +23 -9
  202. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  203. package/node_modules/pi-lens/dist/clients/project-report.js +62 -27
  204. package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
  205. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
  206. package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
  207. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +8 -3
  208. package/node_modules/pi-lens/dist/clients/read-guard.js +253 -10
  209. package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
  210. package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
  211. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +194 -31
  212. package/node_modules/pi-lens/dist/clients/review-graph/query.js +52 -4
  213. package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +21 -0
  214. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +6 -3
  215. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +96 -7
  216. package/node_modules/pi-lens/dist/clients/runtime-context.js +8 -1
  217. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +352 -45
  218. package/node_modules/pi-lens/dist/clients/runtime-session.js +373 -9
  219. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
  220. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +212 -79
  221. package/node_modules/pi-lens/dist/clients/runtime-turn.js +620 -67
  222. package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
  223. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
  224. package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
  225. package/node_modules/pi-lens/dist/clients/security-scan-client.js +157 -24
  226. package/node_modules/pi-lens/dist/clients/session-event-guard.js +184 -0
  227. package/node_modules/pi-lens/dist/clients/sg-runner.js +116 -22
  228. package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
  229. package/node_modules/pi-lens/dist/clients/source-filter.js +6 -2
  230. package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
  231. package/node_modules/pi-lens/dist/clients/startup-scan.js +5 -2
  232. package/node_modules/pi-lens/dist/clients/startup-timing.js +19 -1
  233. package/node_modules/pi-lens/dist/clients/test-runner-client.js +151 -16
  234. package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
  235. package/node_modules/pi-lens/dist/clients/tool-policy.js +125 -20
  236. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
  237. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
  238. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +1 -0
  239. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
  240. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
  241. package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
  242. package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
  243. package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
  244. package/node_modules/pi-lens/dist/index.js +61407 -48577
  245. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +476 -57
  246. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +132 -43
  247. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
  248. package/node_modules/pi-lens/docs/agent-guide.md +6 -1
  249. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +515 -506
  250. package/node_modules/pi-lens/docs/dispositions.md +21 -0
  251. package/node_modules/pi-lens/docs/features.md +19 -5
  252. package/node_modules/pi-lens/docs/globalconfig.md +3 -0
  253. package/node_modules/pi-lens/docs/language-coverage.md +71 -5
  254. package/node_modules/pi-lens/docs/mcp.md +2 -2
  255. package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
  256. package/node_modules/pi-lens/docs/settings.md +19 -1
  257. package/node_modules/pi-lens/docs/usage.md +1 -0
  258. package/node_modules/pi-lens/package.json +27 -10
  259. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
  260. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
  261. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
  262. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
  263. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +15 -1
  264. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
  265. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
  266. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
  267. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
  268. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
  269. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
  270. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +2 -0
  271. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +2 -0
  272. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +2 -1
  273. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +2 -1
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +57 -12
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +22 -2
  300. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +23 -2
  301. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +33 -5
  302. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +34 -8
  303. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
  304. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
  305. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
  306. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
  307. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
  308. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
  309. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +6 -1
  310. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
  311. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
  312. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
  313. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
  314. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
  315. package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
  316. package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
  317. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
  318. package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
  319. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +53 -1
  320. package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
  321. package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
  322. package/node_modules/pi-lens/scripts/install-selftest.mjs +99 -11
  323. package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +75 -0
  324. package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +285 -0
  325. package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +233 -0
  326. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +2 -0
  327. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +80 -3
  328. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +41 -0
  329. package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
  330. package/node_modules/pi-mcp-adapter/CHANGELOG.md +36 -0
  331. package/node_modules/pi-mcp-adapter/README.md +47 -5
  332. package/node_modules/pi-mcp-adapter/cli.js +163 -1
  333. package/node_modules/pi-mcp-adapter/commands.ts +128 -22
  334. package/node_modules/pi-mcp-adapter/config.ts +25 -7
  335. package/node_modules/pi-mcp-adapter/direct-tools.ts +5 -4
  336. package/node_modules/pi-mcp-adapter/index.ts +219 -32
  337. package/node_modules/pi-mcp-adapter/init.ts +36 -4
  338. package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
  339. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
  340. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
  341. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
  342. package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
  343. package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
  344. package/node_modules/pi-mcp-adapter/metadata-cache.ts +32 -21
  345. package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
  346. package/node_modules/pi-mcp-adapter/package.json +3 -1
  347. package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
  348. package/node_modules/pi-mcp-adapter/request-headers-command.ts +31 -37
  349. package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
  350. package/node_modules/pi-mcp-adapter/server-manager.ts +140 -2
  351. package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
  352. package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
  353. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
  354. package/node_modules/pi-mcp-adapter/types.ts +2 -0
  355. package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
  356. package/node_modules/pi-mcp-adapter/utils.ts +70 -0
  357. package/node_modules/pi-web-access/CHANGELOG.md +37 -0
  358. package/node_modules/pi-web-access/README.md +32 -13
  359. package/node_modules/pi-web-access/anysearch.ts +4 -2
  360. package/node_modules/pi-web-access/brave.ts +16 -4
  361. package/node_modules/pi-web-access/chrome-cookies.ts +86 -15
  362. package/node_modules/pi-web-access/curator-page.ts +6 -3
  363. package/node_modules/pi-web-access/curator-server.ts +4 -1
  364. package/node_modules/pi-web-access/data-uri-sanitize.ts +406 -0
  365. package/node_modules/pi-web-access/exa.ts +18 -7
  366. package/node_modules/pi-web-access/extract.ts +40 -5
  367. package/node_modules/pi-web-access/firecrawl.ts +18 -2
  368. package/node_modules/pi-web-access/gemini-search.ts +16 -4
  369. package/node_modules/pi-web-access/github-extract.ts +65 -36
  370. package/node_modules/pi-web-access/index.ts +60 -61
  371. package/node_modules/pi-web-access/openai-search.ts +17 -6
  372. package/node_modules/pi-web-access/package.json +1 -1
  373. package/node_modules/pi-web-access/page-query.ts +4 -2
  374. package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
  375. package/node_modules/pi-web-access/parallel.ts +2 -2
  376. package/node_modules/pi-web-access/pdf-extract.ts +18 -5
  377. package/node_modules/pi-web-access/query-rewrite.ts +51 -0
  378. package/node_modules/pi-web-access/serper.ts +211 -0
  379. package/node_modules/pi-web-access/ssrf-protection.ts +12 -2
  380. package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
  381. package/node_modules/pi-web-access/summary-review.ts +44 -22
  382. package/node_modules/pi-web-access/tavily.ts +16 -4
  383. package/node_modules/pi-web-access/utils.ts +87 -0
  384. package/node_modules/pi-web-access/valyu.ts +199 -0
  385. package/package.json +14 -14
@@ -12,34 +12,73 @@
12
12
  *
13
13
  * Refs: #130, #131, #132
14
14
  */
15
+ import * as path from "node:path";
15
16
  import { createSubsystemLogger } from "./extension-log.js";
16
17
  import { safeSpawnAsync } from "./safe-spawn.js";
17
- import { classifyProbeFailure, createAvailabilityLatch, logAvailabilityDecision, startHostStallSampler, } from "./dispatch/runners/utils/availability-policy.js";
18
+ import { classifyProbeFailure, createAvailabilityLatch, describeInstallAttempt, describeProbeEvidence, logAvailabilityDecision, startHostStallSampler, } from "./dispatch/runners/utils/availability-policy.js";
18
19
  export class SecurityScanClient {
19
20
  toolName;
20
21
  /**
21
22
  * Availability memo, backed by the shared transient-aware latch (#1467).
22
23
  *
23
24
  * Assigning `false` still means "durable: this machine does not have the
24
- * tool" — every existing subclass write keeps its meaning. A probe that
25
- * merely timed out must go through `markTransientlyUnavailable` instead, so
26
- * it expires and the tool can come back without a host restart.
25
+ * tool" — every existing subclass write keeps its meaning. A transient
26
+ * failure must expire instead: `probeVersion` latches its own transient
27
+ * verdicts (#1501), and any other transient failure (a scan or install
28
+ * timeout) goes through `markTransientlyUnavailable`, so the tool can come
29
+ * back without a host restart.
27
30
  */
28
31
  availabilityLatch = createAvailabilityLatch();
29
32
  get available() {
30
33
  return this.availabilityLatch.read();
31
34
  }
32
35
  set available(value) {
33
- if (value === true)
36
+ if (value === true) {
34
37
  this.availabilityLatch.noteAvailable();
35
- else if (value === false)
36
- this.availabilityLatch.noteUnavailable("missing", "not-found");
37
- else
38
+ return;
39
+ }
40
+ if (value !== false) {
38
41
  this.availabilityLatch.reset();
42
+ return;
43
+ }
44
+ this.availabilityLatch.noteUnavailable("missing", "not-found");
45
+ this.logDurableAbsence();
46
+ }
47
+ /**
48
+ * Latch a durable absence WITH the facts behind it. Prefer this over
49
+ * `available = false` wherever the call site knows what actually failed — a
50
+ * `missing` row carrying `install: "failed"` is a retry candidate, one
51
+ * carrying `install: "not-attempted"` is a policy decision, and a bare one is
52
+ * a plain absence. Only the record can tell them apart.
53
+ */
54
+ noteDurableAbsence(evidence, options = {}) {
55
+ this.availabilityLatch.noteUnavailable("missing", "not-found");
56
+ this.logDurableAbsence(evidence, options.elapsedMs);
39
57
  }
40
- /** Outcome/cause of the most recent `probeVersion` call. */
58
+ /**
59
+ * One record per durable-absence ASSERTION, whether or not the call site had
60
+ * evidence. Before this, `available = false` after a failed install was a
61
+ * silent latch: the tool went quiet for the session with nothing in
62
+ * latency.log to audit (#1500). Bounded — the verdict latches, so at most one
63
+ * row per client per session.
64
+ *
65
+ * `elapsedMs` is whatever the caller measured (an install attempt's duration);
66
+ * there is no probe here, so it is 0 unless the caller has something real.
67
+ */
68
+ logDurableAbsence(evidence, elapsedMs = 0) {
69
+ logAvailabilityDecision({
70
+ tool: this.toolName,
71
+ verdict: "unavailable",
72
+ outcome: "missing",
73
+ cause: "not-found",
74
+ elapsedMs,
75
+ latched: true,
76
+ classifiedBy: "caller",
77
+ ...(evidence !== undefined && { evidence }),
78
+ });
79
+ }
80
+ /** Outcome of the most recent `probeVersion` call. */
41
81
  lastProbeOutcome = null;
42
- lastProbeCause = null;
43
82
  ensureInFlight = null;
44
83
  inFlight = new Map();
45
84
  binaryPath = null;
@@ -74,10 +113,17 @@ export class SecurityScanClient {
74
113
  }
75
114
  /**
76
115
  * Spawn `toolName <versionArgs>` and report whether it answered cleanly.
77
- * Does NOT mutate `this.available` — callers decide what a hit/miss means —
78
- * but it does classify the failure (`lastProbeOutcome`/`lastProbeCause`) and
79
- * emit one availability-decision record, so a probe that keeps timing out is
80
- * visible in latency.log rather than inferred from silence (#1467).
116
+ * Classifies the failure (`lastProbeOutcome`) and emits one
117
+ * availability-decision record, so a probe that keeps timing out is visible
118
+ * in latency.log rather than inferred from silence (#1467).
119
+ *
120
+ * Latch ownership is split by outcome: success and durable failures leave
121
+ * `this.available` to the caller (a missing binary may still be
122
+ * auto-installed), while a TRANSIENT failure is latched by the seam itself
123
+ * with its cooldown — the retry schedule only exists once the latch owns
124
+ * the verdict, and the record is incomplete without it (#1501). Callers
125
+ * must not re-mark a probe transient (a second noteUnavailable would
126
+ * double-escalate the cooldown).
81
127
  */
82
128
  async probeVersion(versionArgs) {
83
129
  const sampler = startHostStallSampler();
@@ -95,7 +141,6 @@ export class SecurityScanClient {
95
141
  const elapsedMs = Date.now() - startedAt;
96
142
  if (!probe.error && probe.status === 0) {
97
143
  this.lastProbeOutcome = "success";
98
- this.lastProbeCause = "ok";
99
144
  this.log(`${this.toolName} found: ${probe.stdout.trim().split("\n")[0]}`);
100
145
  logAvailabilityDecision({
101
146
  tool: this.toolName,
@@ -106,12 +151,22 @@ export class SecurityScanClient {
106
151
  latched: true,
107
152
  hostStallMs,
108
153
  budgetMs: 5000,
154
+ classifiedBy: "probe",
155
+ evidence: describeProbeEvidence(probe),
109
156
  });
110
157
  return true;
111
158
  }
112
- const { outcome, cause } = classifyProbeFailure(probe, { hostStallMs });
159
+ const { outcome, cause, evidence } = classifyProbeFailure(probe, {
160
+ hostStallMs,
161
+ });
113
162
  this.lastProbeOutcome = outcome;
114
- this.lastProbeCause = cause;
163
+ // A transient verdict's record is incomplete without the retry schedule,
164
+ // and the schedule only exists once the latch owns the verdict — so the
165
+ // seam writes the expiring verdict itself, at log time (#1501). Durable
166
+ // outcomes stay caller-owned (a missing binary may still be installed).
167
+ const retryAfterMs = outcome === "transient"
168
+ ? this.availabilityLatch.noteUnavailable("transient", cause)
169
+ : 0;
115
170
  logAvailabilityDecision({
116
171
  tool: this.toolName,
117
172
  verdict: "unavailable",
@@ -120,7 +175,10 @@ export class SecurityScanClient {
120
175
  elapsedMs,
121
176
  latched: outcome !== "transient",
122
177
  hostStallMs,
178
+ ...(retryAfterMs > 0 && { retryAfterMs }),
123
179
  budgetMs: 5000,
180
+ classifiedBy: "probe",
181
+ evidence,
124
182
  });
125
183
  return false;
126
184
  }
@@ -135,9 +193,47 @@ export class SecurityScanClient {
135
193
  * Returns that cooldown in ms so the caller can put it in its decision
136
194
  * record. A latch you can read in `latency.log` without the retry schedule
137
195
  * beside it only tells you the tool is off, not when it comes back.
196
+ *
197
+ * For NON-probe transient failures only (scan/install timeouts):
198
+ * `probeVersion` latches and logs its own transient verdicts (#1501), so
199
+ * re-marking one here would double-escalate the cooldown.
200
+ *
201
+ * Pass `opts.operationClass: "install"` when the failed operation was a
202
+ * network install/compile rather than a cheap probe (#1497): the retry
203
+ * escalates on the install-class schedule, on its OWN cooldown slot (a
204
+ * cheap probe failure can never shorten it), and latches for the session at
205
+ * the attempt ceiling, in which case the return is 0 (latched).
206
+ */
207
+ markTransientlyUnavailable(cause = "probe-timeout", opts) {
208
+ return this.availabilityLatch.noteUnavailable("transient", cause, opts);
209
+ }
210
+ /**
211
+ * The cause the LATCH settled on, which is not always the cause the caller
212
+ * passed in: at the install-class ceiling the latch rewrites it to
213
+ * `install-retry-exhausted` (#1497). A decision record built from the
214
+ * caller's own cause would say `probe-timeout` for a verdict that is no
215
+ * longer being retried, so the record reads it back from here.
138
216
  */
139
- markTransientlyUnavailable(cause = "probe-timeout") {
140
- return this.availabilityLatch.noteUnavailable("transient", cause);
217
+ latchedCause() {
218
+ return this.availabilityLatch.getCause();
219
+ }
220
+ /**
221
+ * #1623: public window onto the availability verdict for callers OUTSIDE
222
+ * the dispatch graph (mode=full's fresh-fetch) that need to say WHY
223
+ * `ensureAvailable()` most recently returned false — using the SAME
224
+ * `AvailabilityOutcome`/`AvailabilityCause` taxonomy every dispatch-side
225
+ * message is built from, rather than a re-guessed "binary unavailable"
226
+ * that can't tell a transient retry-cooldown probe from a durable absence.
227
+ * `latchedCause()`/`lastProbeOutcome` above stay `protected` for
228
+ * dispatch-internal callers; this is the one public seam for everyone
229
+ * else.
230
+ */
231
+ getAvailabilityVerdict() {
232
+ return {
233
+ outcome: this.availabilityLatch.getOutcome(),
234
+ cause: this.availabilityLatch.getCause(),
235
+ retryAtMs: this.availabilityLatch.getRetryAtMs(),
236
+ };
141
237
  }
142
238
  /**
143
239
  * Standard availability path for the GitHub-release tools (gitleaks, trivy):
@@ -151,22 +247,59 @@ export class SecurityScanClient {
151
247
  }
152
248
  if (this.probeWasTransient()) {
153
249
  // A timed-out probe is not evidence the tool is absent, so it must not
154
- // trigger an install NOR latch a permanent `false`.
250
+ // trigger an install NOR latch a permanent `false`. probeVersion has
251
+ // already recorded the expiring verdict and its retry schedule
252
+ // (#1501) — re-marking it here would double-escalate the cooldown.
155
253
  this.log(`${this.toolName} availability probe timed out; not installing, will retry`);
156
- this.markTransientlyUnavailable(this.lastProbeCause ?? "probe-timeout");
157
254
  return false;
158
255
  }
159
256
  this.log(`${this.toolName} not found, attempting auto-install`);
160
- const { ensureTool } = await import("./installer/index.js");
257
+ const { ensureTool, getInstallAttempt } = await import("./installer/index.js");
258
+ const installStartedAt = Date.now();
161
259
  const installed = await ensureTool(this.toolName);
162
260
  if (!installed) {
163
- this.log(`${this.toolName} auto-install failed`);
164
- this.available = false;
261
+ // #1500: this classification is ASSERTED, not derived. It is justified —
262
+ // the probe already classified a genuine absence, and a failed repair
263
+ // leaves the tool absent — but the install failure has no taxonomy of
264
+ // its own, so a network blip and a permanently unavailable release look
265
+ // identical from here. Record what happened rather than only the verdict.
266
+ //
267
+ // `ensureTool` answers the same empty result whether it TRIED and failed
268
+ // or declined to try (auto-install off, project trust denied), so the
269
+ // attempt is read from the reason it records rather than assumed — the
270
+ // review found `install: "failed"` being written for attempts that never
271
+ // happened.
272
+ const attempt = getInstallAttempt(this.toolName);
273
+ this.log(attempt?.outcome === "failed"
274
+ ? `${this.toolName} auto-install failed: ${attempt.reason ?? "unknown reason"}`
275
+ : `${this.toolName} auto-install did not run: ${attempt?.reason ?? "no attempt recorded"}`);
276
+ this.noteDurableAbsence(describeInstallAttempt(attempt), {
277
+ elapsedMs: Date.now() - installStartedAt,
278
+ });
165
279
  return false;
166
280
  }
167
281
  this.binaryPath = installed;
168
282
  this.available = true;
169
283
  this.log(`${this.toolName} auto-installed at ${installed}`);
284
+ // The probe already wrote a latched `unavailable` row (#1500's durable
285
+ // assertion doesn't fire here — install SUCCEEDED — but probeVersion's
286
+ // verdict still did, before this call ever ran). Without a compensating
287
+ // row, the durable record says the lane is off when it just came back
288
+ // on: the exact defect #1606 was filed over.
289
+ logAvailabilityDecision({
290
+ tool: this.toolName,
291
+ verdict: "available",
292
+ outcome: "success",
293
+ cause: "ok",
294
+ elapsedMs: Date.now() - installStartedAt,
295
+ latched: true,
296
+ classifiedBy: "caller",
297
+ evidence: {
298
+ install: "succeeded",
299
+ binary: path.basename(installed),
300
+ source: "managed-dir",
301
+ },
302
+ });
170
303
  return true;
171
304
  }
172
305
  /**
@@ -0,0 +1,184 @@
1
+ /**
2
+ * One home for "this event arrived on a ctx the SDK already invalidated"
3
+ * (#1925).
4
+ *
5
+ * pi invalidates a captured extension ctx when the session is replaced —
6
+ * `ctx.newSession()`, `ctx.fork()`, `ctx.switchSession()`, `ctx.reload()`.
7
+ * Every accessor on that ctx then throws from the SDK's `assertActive()`
8
+ * (`core/extensions/loader.js` in the installed
9
+ * `@earendil-works/pi-coding-agent`). An event already queued when the swap
10
+ * happens still reaches pi-lens, carrying the dead ctx, and the first
11
+ * unguarded `ctx.signal` / `ctx.ui` / `ctx.cwd` read throws.
12
+ *
13
+ * That throw does not escape into pi's loop. `ExtensionRunner.emit` wraps every
14
+ * handler call in a try/catch and routes the error to `emitError`
15
+ * (`core/extensions/runner.js:586-606` in the installed SDK), so the throw
16
+ * surfaces as an extension error report against pi-lens. The report names the
17
+ * wrong cause. It reads as a pi-lens handler bug when the real event is a
18
+ * benign race with a session swap, it says nothing about which handler keeps
19
+ * losing, and it counts nothing. This wrapper converts that noisy misattributed
20
+ * error into a counted, bounded, attributable skip.
21
+ *
22
+ * #1924 fixed that for `agent_settled` with an inline try/catch. #1925 found
23
+ * four more handlers with the same shape. Five inline copies of one policy is
24
+ * the parallel-state defect AGENTS.md names, so the policy lives here instead.
25
+ * `agent_settled` is a consumer of this path, not a special case.
26
+ *
27
+ * ## What this module actually covers
28
+ *
29
+ * Not every `pi.on` registration in `index.ts` goes through here, and the doc
30
+ * used to read as if they all did. Seven do today:
31
+ *
32
+ * - {@link wrapSessionEventHandler} — `tool_result`, `turn_start`,
33
+ * `agent_end`, `turn_end`, `agent_settled` (#1925) and `session_start`
34
+ * (#1929). All six return nothing, so a skipped event resolving to
35
+ * `undefined` is exactly their own early-exit value.
36
+ * - {@link wrapSessionEventHandlerWithResult} — `context` (#1929), which must
37
+ * hand the host back a message list on the live path.
38
+ *
39
+ * Five registrations stay unwrapped on purpose: `resources_discover`,
40
+ * `session_before_fork`, `tool_call`, `session_shutdown`, and `message_end`.
41
+ * `tests/clients/session-event-guard-sweep.test.ts` is the source of truth for
42
+ * that split. It scans every registration in `index.ts` and reds unless the
43
+ * handler is wrapped or carries a written reason, so this paragraph can go
44
+ * stale but the contract cannot.
45
+ *
46
+ * The wrapper does three things and nothing else:
47
+ *
48
+ * 1. **Probes once, before dispatch.** {@link probeCtxActive} answers `false`
49
+ * only when an `assertActive()`-wrapped accessor threw the SDK's own stale
50
+ * message. `undefined` (no ctx, unexpected shape, unrecognised throw) is
51
+ * inconclusive and always dispatches — never guess a session dead.
52
+ * 2. **Still classifies a stale throw that races in mid-handler.** The probe
53
+ * is a point-in-time read; the swap can land between the probe and the
54
+ * handler's own first ctx read, and for an async handler it can land at any
55
+ * await. Both the synchronous throw and the rejected promise are
56
+ * classified by {@link isStaleExtensionCtxError}.
57
+ * 3. **Makes the skip visible.** One bounded record and one ledger kind for
58
+ * the whole class, keyed by the event name so aggregation still answers
59
+ * WHICH handler is being skipped. A silent guard and a guard that never
60
+ * fires read identically from a log, which is exactly how vacuous guards
61
+ * survive review.
62
+ *
63
+ * Anything that is NOT the SDK's stale-ctx error propagates unchanged. The
64
+ * wrapper narrows nothing else: a handler bug must stay as loud as it was.
65
+ */
66
+ import { emitBounded } from "./bounded-telemetry.js";
67
+ import { probeCtxActive } from "./session-lifecycle.js";
68
+ /**
69
+ * The pi SDK invalidates a captured `pi`/command ctx after a session
70
+ * replacement or reload; every later `pi.*` call then throws with this
71
+ * signature. Matched by MESSAGE — not by `===` against a captured instance —
72
+ * so a fire-and-forget task that races a session swap can recognise the benign
73
+ * stale-ctx throw and degrade to a no-op. Substring-matched on the stable
74
+ * "stale after session replacement or reload" phrase so it survives incidental
75
+ * wording changes around it.
76
+ *
77
+ * Deliberately a NARROWER match than {@link probeCtxActive}'s (which accepts
78
+ * the shorter "stale after session replacement", the message
79
+ * `ExtensionRunner.invalidate()` uses for its own probe path). Widening this
80
+ * one is a behavior change: it decides which throws get swallowed.
81
+ */
82
+ export function isStaleExtensionCtxError(err) {
83
+ return (err instanceof Error &&
84
+ err.message.includes("stale after session replacement or reload"));
85
+ }
86
+ /**
87
+ * Record one skipped session event. Bounded per event name: the ledger counts
88
+ * every occurrence exactly, and only the first per event name this session
89
+ * also writes the detailed `latency.log` row, so a replaced session whose
90
+ * queue drains a hundred stale events cannot storm the log.
91
+ */
92
+ function recordStaleSkip(eventName, detectedAt) {
93
+ emitBounded("session_event_stale_ctx_skip", eventName, {
94
+ durationMs: 0,
95
+ metadata: { event: eventName, detectedAt },
96
+ }, {
97
+ ledgerKind: "extension-ctx-stale",
98
+ risingEdgePer: "identity",
99
+ reason: `${eventName} skipped: extension ctx is stale (${detectedAt})`,
100
+ });
101
+ }
102
+ function isThenable(value) {
103
+ return (typeof value?.then === "function");
104
+ }
105
+ /**
106
+ * The one policy. Both public wrappers are this function with a different
107
+ * stale-path value, so the probe point, the classification, and the record can
108
+ * never drift apart between a void handler and a value-returning one.
109
+ */
110
+ function guardSessionEvent(eventName, handler, onStaleResult, options) {
111
+ const skip = (event, detectedAt) => {
112
+ recordStaleSkip(eventName, detectedAt);
113
+ try {
114
+ options.dbg?.(`${eventName} skipped: extension ctx is stale after session replacement or reload`);
115
+ }
116
+ catch {
117
+ // A debug sink must never decide whether an event is handled.
118
+ }
119
+ return onStaleResult(event);
120
+ };
121
+ return (event, ctx) => {
122
+ // `false` is the only confirmed verdict. `undefined` means the probe
123
+ // could not tell, and an inconclusive probe must dispatch.
124
+ if (probeCtxActive(ctx) === false)
125
+ // SAFETY: `R` is the handler's declared return type, which is either a
126
+ // plain value or a promise of one. `skip` yields `Awaited<R>`, the
127
+ // settled form. TypeScript cannot see that a host awaiting an `R` is
128
+ // equally satisfied by the settled value, but every `pi.on` caller
129
+ // either awaits the result or ignores it, so both forms behave the
130
+ // same at the call site. The same reasoning covers the two casts
131
+ // below.
132
+ return skip(event, "pre-dispatch");
133
+ try {
134
+ const result = handler(event, ctx);
135
+ if (isThenable(result)) {
136
+ // Recover the rejection in place. The host awaits the same promise
137
+ // it would have awaited anyway; it just resolves instead.
138
+ // SAFETY: the recovered promise settles to `Awaited<R>`, which the
139
+ // host consumes exactly as it would an `R` — see the note above.
140
+ return Promise.resolve(result).catch((err) => {
141
+ if (isStaleExtensionCtxError(err))
142
+ return skip(event, "mid-handler");
143
+ throw err;
144
+ });
145
+ }
146
+ return result;
147
+ }
148
+ catch (err) {
149
+ if (isStaleExtensionCtxError(err))
150
+ // SAFETY: a synchronous handler's `R` is already its settled form,
151
+ // so `Awaited<R>` and `R` coincide here — see the note above.
152
+ return skip(event, "mid-handler");
153
+ throw err;
154
+ }
155
+ };
156
+ }
157
+ /**
158
+ * Wrap one `pi.on` handler so a stale ctx becomes an observable no-op instead
159
+ * of a throw into the host.
160
+ *
161
+ * The returned function keeps the handler's own signature and its return
162
+ * value, so a wrapped registration is a drop-in for the bare one. A skipped
163
+ * event resolves to `undefined`, which is what every handler wrapped this way
164
+ * already returns on its early-exit paths.
165
+ *
166
+ * Use {@link wrapSessionEventHandlerWithResult} when `undefined` is NOT the
167
+ * handler's own no-op value.
168
+ */
169
+ export function wrapSessionEventHandler(eventName, handler, options = {}) {
170
+ return guardSessionEvent(eventName, handler, () => undefined, options);
171
+ }
172
+ /**
173
+ * {@link wrapSessionEventHandler} for a handler whose return value the host
174
+ * consumes (#1929).
175
+ *
176
+ * The live path returns exactly what the handler returned. The stale path
177
+ * returns `onStaleResult(event)` instead of assuming `undefined`, so a handler
178
+ * whose no-op value is something else does not get one silently substituted.
179
+ * Probe, classification, and the bounded record are identical to the void
180
+ * wrapper's — they are the same function underneath.
181
+ */
182
+ export function wrapSessionEventHandlerWithResult(eventName, handler, options) {
183
+ return guardSessionEvent(eventName, handler, options.onStaleResult, options);
184
+ }