@herbertgao/pi-extensions 2026.8.6 → 2026.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (343) hide show
  1. package/README.md +5 -5
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +1 -1
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +1 -1
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +272 -22
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +1 -1
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +14 -0
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -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 +1 -0
  35. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +26 -10
  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 +2 -1
  39. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +3 -1
  40. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +2 -2
  41. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
  42. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
  43. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
  44. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +3 -1
  45. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +30 -7
  46. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
  47. package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +30 -4
  48. package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +26 -5
  49. package/node_modules/@narumitw/pi-btw/README.md +120 -111
  50. package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
  51. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
  52. package/node_modules/@narumitw/pi-btw/package.json +10 -6
  53. package/node_modules/@narumitw/pi-btw/src/btw.ts +36 -40
  54. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
  55. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
  56. package/node_modules/@narumitw/pi-btw/src/menu.ts +45 -2
  57. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
  58. package/node_modules/pi-lens/CHANGELOG.md +909 -0
  59. package/node_modules/pi-lens/README.md +11 -3
  60. package/node_modules/pi-lens/config/ruff/core.toml +42 -0
  61. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
  62. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +126 -13
  63. package/node_modules/pi-lens/dist/clients/agent-nudge.js +67 -11
  64. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
  65. package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
  66. package/node_modules/pi-lens/dist/clients/bash-file-access.js +285 -8
  67. package/node_modules/pi-lens/dist/clients/biome-client.js +61 -30
  68. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
  69. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +119 -0
  70. package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
  71. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
  72. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +192 -0
  73. package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
  74. package/node_modules/pi-lens/dist/clients/cascade-format.js +20 -1
  75. package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
  76. package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
  77. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
  78. package/node_modules/pi-lens/dist/clients/dead-code-client.js +123 -31
  79. package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
  80. package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
  81. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +62 -13
  82. package/node_modules/pi-lens/dist/clients/dependency-checker.js +25 -3
  83. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +147 -33
  84. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
  85. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +52 -2
  86. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +256 -88
  87. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
  88. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
  89. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
  90. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
  91. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
  92. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
  93. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +60 -53
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -0
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +23 -0
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +14 -0
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +20 -0
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +22 -0
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +10 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +18 -6
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +275 -13
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +21 -6
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +734 -64
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +72 -0
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +9 -0
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +9 -0
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -1
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
  122. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
  123. package/node_modules/pi-lens/dist/clients/extension-log.js +22 -1
  124. package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
  125. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +294 -0
  126. package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
  127. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
  128. package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
  129. package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
  130. package/node_modules/pi-lens/dist/clients/git-guard.js +31 -0
  131. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
  132. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +109 -9
  133. package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
  134. package/node_modules/pi-lens/dist/clients/host-ports.js +8 -0
  135. package/node_modules/pi-lens/dist/clients/installer/index.js +1114 -100
  136. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
  137. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
  138. package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
  139. package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
  140. package/node_modules/pi-lens/dist/clients/jscpd-client.js +82 -6
  141. package/node_modules/pi-lens/dist/clients/knip-client.js +310 -10
  142. package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
  143. package/node_modules/pi-lens/dist/clients/language-profile.js +2 -0
  144. package/node_modules/pi-lens/dist/clients/latency-logger.js +440 -11
  145. package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
  146. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
  147. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
  148. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +102 -7
  149. package/node_modules/pi-lens/dist/clients/lsp/client.js +1516 -159
  150. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +83 -1
  151. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
  152. package/node_modules/pi-lens/dist/clients/lsp/index.js +2274 -147
  153. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
  154. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
  155. package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
  156. package/node_modules/pi-lens/dist/clients/lsp/server.js +277 -16
  157. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
  158. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
  159. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +79 -3
  160. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +43 -0
  161. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
  162. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
  163. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
  164. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
  165. package/node_modules/pi-lens/dist/clients/mcp/session.js +10 -2
  166. package/node_modules/pi-lens/dist/clients/module-report.js +8 -0
  167. package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
  168. package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
  169. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
  170. package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
  171. package/node_modules/pi-lens/dist/clients/pipeline.js +154 -33
  172. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
  173. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
  174. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
  175. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +84 -14
  176. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
  177. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
  178. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -0
  179. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  180. package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
  181. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
  182. package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
  183. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +1 -1
  184. package/node_modules/pi-lens/dist/clients/read-guard.js +184 -10
  185. package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
  186. package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
  187. package/node_modules/pi-lens/dist/clients/review-graph/query.js +52 -4
  188. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +96 -7
  189. package/node_modules/pi-lens/dist/clients/runtime-context.js +8 -1
  190. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +308 -45
  191. package/node_modules/pi-lens/dist/clients/runtime-session.js +332 -9
  192. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
  193. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +212 -79
  194. package/node_modules/pi-lens/dist/clients/runtime-turn.js +575 -67
  195. package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
  196. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
  197. package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
  198. package/node_modules/pi-lens/dist/clients/security-scan-client.js +157 -24
  199. package/node_modules/pi-lens/dist/clients/sg-runner.js +116 -22
  200. package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
  201. package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
  202. package/node_modules/pi-lens/dist/clients/startup-timing.js +19 -1
  203. package/node_modules/pi-lens/dist/clients/test-runner-client.js +151 -16
  204. package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
  205. package/node_modules/pi-lens/dist/clients/tool-policy.js +125 -20
  206. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
  207. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
  208. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
  209. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
  210. package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
  211. package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
  212. package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
  213. package/node_modules/pi-lens/dist/index.js +67738 -55739
  214. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +466 -54
  215. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +132 -43
  216. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
  217. package/node_modules/pi-lens/docs/agent-guide.md +6 -1
  218. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +515 -506
  219. package/node_modules/pi-lens/docs/dispositions.md +21 -0
  220. package/node_modules/pi-lens/docs/features.md +19 -5
  221. package/node_modules/pi-lens/docs/globalconfig.md +3 -0
  222. package/node_modules/pi-lens/docs/language-coverage.md +71 -5
  223. package/node_modules/pi-lens/docs/mcp.md +2 -2
  224. package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
  225. package/node_modules/pi-lens/docs/settings.md +19 -1
  226. package/node_modules/pi-lens/docs/usage.md +1 -0
  227. package/node_modules/pi-lens/package.json +10 -6
  228. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
  229. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
  230. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
  231. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
  232. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +15 -1
  233. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
  234. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
  235. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
  236. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
  237. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
  238. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
  239. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +2 -0
  240. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +2 -0
  241. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +2 -1
  242. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +2 -1
  243. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
  244. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
  245. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
  246. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
  247. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
  248. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
  249. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
  250. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
  251. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
  252. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
  253. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
  254. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
  255. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
  256. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
  257. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
  258. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
  259. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
  260. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +57 -12
  261. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
  262. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
  263. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
  264. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
  265. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
  266. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
  267. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
  268. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +22 -2
  269. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +23 -2
  270. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +33 -5
  271. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +34 -8
  272. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
  273. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +6 -1
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
  288. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +53 -1
  289. package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
  290. package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
  291. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +2 -0
  292. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +80 -3
  293. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +41 -0
  294. package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
  295. package/node_modules/pi-mcp-adapter/CHANGELOG.md +36 -0
  296. package/node_modules/pi-mcp-adapter/README.md +47 -5
  297. package/node_modules/pi-mcp-adapter/cli.js +163 -1
  298. package/node_modules/pi-mcp-adapter/commands.ts +128 -22
  299. package/node_modules/pi-mcp-adapter/config.ts +25 -7
  300. package/node_modules/pi-mcp-adapter/direct-tools.ts +5 -4
  301. package/node_modules/pi-mcp-adapter/index.ts +219 -32
  302. package/node_modules/pi-mcp-adapter/init.ts +36 -4
  303. package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
  304. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
  305. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
  306. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
  307. package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
  308. package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
  309. package/node_modules/pi-mcp-adapter/metadata-cache.ts +32 -21
  310. package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
  311. package/node_modules/pi-mcp-adapter/package.json +3 -1
  312. package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
  313. package/node_modules/pi-mcp-adapter/request-headers-command.ts +31 -37
  314. package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
  315. package/node_modules/pi-mcp-adapter/server-manager.ts +140 -2
  316. package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
  317. package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
  318. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
  319. package/node_modules/pi-mcp-adapter/types.ts +2 -0
  320. package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
  321. package/node_modules/pi-mcp-adapter/utils.ts +70 -0
  322. package/node_modules/pi-web-access/CHANGELOG.md +23 -0
  323. package/node_modules/pi-web-access/README.md +27 -9
  324. package/node_modules/pi-web-access/anysearch.ts +4 -2
  325. package/node_modules/pi-web-access/brave.ts +16 -4
  326. package/node_modules/pi-web-access/curator-page.ts +6 -3
  327. package/node_modules/pi-web-access/curator-server.ts +4 -1
  328. package/node_modules/pi-web-access/exa.ts +18 -7
  329. package/node_modules/pi-web-access/extract.ts +28 -3
  330. package/node_modules/pi-web-access/gemini-search.ts +16 -4
  331. package/node_modules/pi-web-access/index.ts +41 -57
  332. package/node_modules/pi-web-access/package.json +1 -1
  333. package/node_modules/pi-web-access/page-query.ts +4 -2
  334. package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
  335. package/node_modules/pi-web-access/parallel.ts +2 -2
  336. package/node_modules/pi-web-access/query-rewrite.ts +51 -0
  337. package/node_modules/pi-web-access/serper.ts +211 -0
  338. package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
  339. package/node_modules/pi-web-access/summary-review.ts +44 -22
  340. package/node_modules/pi-web-access/tavily.ts +16 -4
  341. package/node_modules/pi-web-access/utils.ts +87 -0
  342. package/node_modules/pi-web-access/valyu.ts +199 -0
  343. package/package.json +13 -13
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Past-EOF validity gate for the turn-end inline-blocker store (#1641
3
+ * remainder).
4
+ *
5
+ * #1664 wired `demotePastEofDiagnostics` into every surface that renders a
6
+ * `WidgetDiagnostic` — widget-state, `lens_diagnostics` (`mode=all`/`full`),
7
+ * and the TUI blocking-render loop. It deliberately deferred one surface:
8
+ * `RuntimeCoordinator`'s `_pendingInlineBlockers` map (the "Unresolved from
9
+ * this turn" blocker re-served at `turn_end`, `clients/runtime-turn.ts`).
10
+ * That store's records (`InlineBlockerRecord`) never carried a `WidgetDiagnostic`
11
+ * — just a rendered `summary` string — so the shared gate had nothing to run
12
+ * against. #1633 was meant to add the structured line field and never did;
13
+ * it merged with only the dependency-drift gate (`blocker-freshness.ts`)
14
+ * wired in. This module is that missing half.
15
+ *
16
+ * Design choice (#1641 remainder, decided here): carry a STRUCTURED `lines`
17
+ * field on `InlineBlockerRecord`, captured at write time from the same
18
+ * `Diagnostic[]` the summary text was rendered from
19
+ * (`dispatchResult.blockers` in `pipeline.ts`), rather than regex-parsing the
20
+ * cited line back out of the rendered summary prose. Re-deriving a number
21
+ * that was already in hand one call earlier — by parsing a string built for
22
+ * human display, not machine reading — is exactly the re-derivation-vs-
23
+ * correlation screen's failure shape: any future change to the summary's
24
+ * wording (a different prefix, a renamed tool label) would silently break
25
+ * the parse with no type error. Threading the number through is one field on
26
+ * an existing interface and a few lines at each hop.
27
+ *
28
+ * Composition with #1631's dependency-drift gate (`blocker-freshness.ts`,
29
+ * also swept at `turn_end`): each gate demotes with its own
30
+ * `InlineBlockerRecord.staleReason` and only re-derives (heals) entries
31
+ * carrying its OWN reason — `setInlineBlockerPastEofStale` refuses to touch a
32
+ * `"dependency-drift"` demotion, and this gate skips a record already
33
+ * demoted for that reason before it even computes a verdict. The two gates
34
+ * therefore layer rather than race: whichever ran first "wins" the record for
35
+ * this turn, and the other's condition is picked up again once the first
36
+ * heals or a fresh dispatch/retire clears the record outright.
37
+ *
38
+ * RE-ARMS, never latches (mirrors `demotePastEofDiagnostics`): the verdict is
39
+ * recomputed from the file's CURRENT line count on every turn end, so a
40
+ * transient shrink-then-restore un-demotes on its own next turn without any
41
+ * session-lifetime flag to reset.
42
+ */
43
+ import { demotePastEofDiagnostics, resyncDocumentOnPastEof, } from "./diagnostic-line-freshness.js";
44
+ /**
45
+ * Past-EOF sweep over the cached inline blockers. Called at turn end
46
+ * alongside (and, per the module doc, ordered before) the dependency-drift
47
+ * sweep — a cheap `statSync` per file is worth paying first so the pricier
48
+ * import-parsing sweep can skip anything this gate already took out of the
49
+ * authoritative channel.
50
+ *
51
+ * Never throws: a per-entry failure leaves that entry untouched rather than
52
+ * failing the turn end.
53
+ */
54
+ export function sweepInlineBlockerPastEof(runtime, cwd, options) {
55
+ const counts = {
56
+ total: 0,
57
+ checked: 0,
58
+ demoted: 0,
59
+ healed: 0,
60
+ };
61
+ const resync = options?.resync ?? resyncDocumentOnPastEof;
62
+ let entries;
63
+ try {
64
+ entries = runtime.getInlineBlockersSnapshot();
65
+ }
66
+ catch {
67
+ return counts;
68
+ }
69
+ counts.total = entries.length;
70
+ for (const entry of entries) {
71
+ try {
72
+ if (!entry.lines || entry.lines.length === 0)
73
+ continue;
74
+ // Not this gate's demotion to heal or re-assert (#1641 composition
75
+ // rule — see module doc).
76
+ if (entry.stale && entry.staleReason !== "past-eof")
77
+ continue;
78
+ counts.checked += 1;
79
+ // Reuse the shared gate's per-line verdict machinery: feed it one
80
+ // synthetic "diagnostic" per cited line, sharing this record's
81
+ // current stale/staleReason so it can detect the rising/falling
82
+ // edge itself instead of this module re-implementing that logic.
83
+ const probes = entry.lines.map((line) => ({
84
+ line,
85
+ stale: entry.stale,
86
+ staleReason: entry.staleReason,
87
+ }));
88
+ // `demotePastEofDiagnostics` already logs (`diagnostic_past_eof`) and
89
+ // fires `resync` exactly once per call on a rising edge — reusing it
90
+ // here (rather than re-implementing that bookkeeping) keeps this
91
+ // gate's telemetry and resync-on-demotion behavior identical to the
92
+ // widget/lens surfaces #1664 already wired.
93
+ const { diagnostics } = demotePastEofDiagnostics({
94
+ store: "inline-blocker",
95
+ cwd,
96
+ filePath: entry.filePath,
97
+ diagnostics: probes,
98
+ lineCountCache: options?.lineCountCache,
99
+ resync,
100
+ });
101
+ // The record cites any of its lines being past EOF as a whole-record
102
+ // demotion — one summary string, not one flag per line.
103
+ const isPastEof = diagnostics.some((d) => d.stale);
104
+ const transitioned = runtime.setInlineBlockerPastEofStale(entry.filePath, isPastEof);
105
+ if (!transitioned)
106
+ continue;
107
+ if (isPastEof) {
108
+ counts.demoted += 1;
109
+ }
110
+ else {
111
+ counts.healed += 1;
112
+ }
113
+ }
114
+ catch {
115
+ // Per-entry failure: leave the entry as-is.
116
+ }
117
+ }
118
+ return counts;
119
+ }
@@ -47,6 +47,14 @@ async function logBootstrapFailures(failures) {
47
47
  // the per-analyzer lines above already named the failures
48
48
  }
49
49
  }
50
+ /**
51
+ * Every per-client load below is individually fail-soft (`load`/`loadList`
52
+ * degrade to a stub instead of throwing), so this promise is not expected to
53
+ * reject in practice. It is still memoized with eviction-on-rejection —
54
+ * consistent with the other lazy-import memos (#1570) — so a genuinely
55
+ * unexpected throw (e.g. `logBootstrapFailures`) cannot latch a permanently
56
+ * rejected bootstrap for the rest of the process.
57
+ */
50
58
  export function loadBootstrapClients() {
51
59
  bootstrapPromise ??= (async () => {
52
60
  const failures = [];
@@ -112,6 +120,9 @@ export function loadBootstrapClients() {
112
120
  agentBehaviorClient,
113
121
  deadCodeClients,
114
122
  };
115
- })();
123
+ })().catch((err) => {
124
+ bootstrapPromise = null;
125
+ throw err;
126
+ });
116
127
  return bootstrapPromise;
117
128
  }
@@ -57,9 +57,18 @@ async function releaseQuarantineLock(lockPath, token) {
57
57
  }
58
58
  }
59
59
  function quarantineOwnerIsStale(owner, staleMs) {
60
- return Number.isInteger(owner.pid) && owner.pid > 0 &&
61
- Number.isFinite(owner.createdAt) &&
62
- (!ownerPidIsLive(owner.pid) || Date.now() - owner.createdAt > staleMs);
60
+ // #1816: the two staleness signals are INDEPENDENT, and the original
61
+ // conjunction made the dead-PID one unreachable. An `owner.json` with a
62
+ // valid PID but a missing or non-numeric `createdAt` (an older writer, a
63
+ // half-written file, a hand-edited one) short-circuited on the
64
+ // `Number.isFinite` guard, so a dead owner never reclaimed and the lock
65
+ // stayed poisoned for the life of the directory. This is
66
+ // `installer/index.ts:173`'s predicate: a dead PID reclaims regardless of
67
+ // `createdAt`, and an aged lock reclaims regardless of what the PID says.
68
+ const pidUsable = Number.isInteger(owner.pid) && owner.pid > 0;
69
+ if (pidUsable && !ownerPidIsLive(owner.pid))
70
+ return true;
71
+ return (Number.isFinite(owner.createdAt) && Date.now() - owner.createdAt > staleMs);
63
72
  }
64
73
  async function reclaimQuarantineLock(lockPath, staleMs) {
65
74
  const quarantined = quarantinePath(lockPath, `reclaim-${Date.now()}-${randomUUID()}`);
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Typed bounded telemetry emission (#1743).
3
+ *
4
+ * Four PRs in two days hand-built the same machinery: a failure path that
5
+ * needs a detailed `latency.log` record, an exact per-identity tally, and a
6
+ * bound so a storming subsystem cannot flood the log. #1705 (orphan
7
+ * forensics), #1717 (pull timeouts and late answers), #1726 (navRequest
8
+ * timeouts, rising edge per method and file), and #1733 (loop-block floor
9
+ * with a per-turn bound) each rebuilt it, and each needed a review round to
10
+ * get the bounding right. This module is the intersection of those four, not
11
+ * a speculative framework. Three of its four options come straight from those
12
+ * sites. The fourth, `capPerTurn`, expresses #1733's per-turn bound
13
+ * structurally but has no caller yet: that site's bound is its call cadence,
14
+ * and pinning a cap there would red its own wiring tests. It ships tested and
15
+ * mutation-proofed, awaiting its first site.
16
+ *
17
+ * Three rules the helper makes structural instead of prose:
18
+ *
19
+ * 1. **The rising edge comes from the ledger's own tally.** #1705 established
20
+ * that `incrementDegradationCount` already knows whether a
21
+ * (kind, subject) pair is being seen for the first time, and returns it.
22
+ * A caller that wants "log the detail once, count the rest" reads that
23
+ * return value. It never keeps a second `Set` — a parallel latch is state
24
+ * that must re-arm at `session_start` and will eventually be forgotten
25
+ * (the #1266–#1625 defect class).
26
+ * 2. **A per-turn cap names its turn.** `capPerTurn` carries the caller's
27
+ * `turnIndex` in the same object as the limit, so there is no separate
28
+ * reset to wire and forget: the counters clear when the observed turn
29
+ * changes. This is the same lazy clear-on-transition shape as
30
+ * `noteImportResolutionMemoTurn` (`clients/blocker-freshness.ts`).
31
+ * 3. **Identity always survives.** Every emitted record carries the
32
+ * discriminating identity in `filePath` and in `metadata.identity`, so
33
+ * aggregation can still answer "which file, which method, which record is
34
+ * stuck" (the rule AGENTS.md states, now enforced by the signature).
35
+ *
36
+ * `BOUNDED_TELEMETRY_PHASES` is the registry.
37
+ * `tests/clients/bounded-telemetry-sweep.test.ts` sweeps it registered-or-
38
+ * fail: a failure-path phase must either route through this helper or carry a
39
+ * stated reason for staying raw.
40
+ *
41
+ * Nothing here ever throws. Telemetry must not perturb the path it observes —
42
+ * every one of the four migrated sites had already decided its outcome before
43
+ * calling in.
44
+ */
45
+ import { incrementDegradationCount } from "./degradation-ledger.js";
46
+ import { logLatency } from "./latency-logger.js";
47
+ /**
48
+ * Phases that emit through this helper. The `phase` parameter is typed to
49
+ * this list, so a new bounded phase cannot be emitted without joining the
50
+ * registry, and the sweep proves the reverse direction: a registered phase
51
+ * must not also be written by a raw `logLatency` call somewhere else.
52
+ */
53
+ export const BOUNDED_TELEMETRY_PHASES = [
54
+ /** #1705: a deferred-format record whose origin worktree never claimed it. */
55
+ "agent_end_deferred_format_orphan_origin_mismatch",
56
+ /** #1713: a `textDocument`/`workspace` diagnostic pull abandoned at budget. */
57
+ "lsp_pull_diagnostic_timeout",
58
+ /** #1713: that abandoned pull answered anyway, too late to serve anyone. */
59
+ "lsp_pull_late_answer_discarded",
60
+ /** #1773: a pull skipped outright because the caller's budget was already
61
+ * exhausted — never dispatched, so it must not be confused with a genuine
62
+ * timeout. */
63
+ "lsp_pull_skipped_budget_exhausted",
64
+ /** #1774: an abandoned pull's request eventually REJECTED (e.g. a server
65
+ * `ContentModified`) rather than answering or staying silent forever —
66
+ * the third outcome late-answer telemetry previously could not see. */
67
+ "lsp_pull_late_rejection",
68
+ /** #1716: a hover/definition/references/etc. request abandoned at budget. */
69
+ "lsp_nav_request_timeout",
70
+ /** #1716: that abandoned nav request answered anyway, after the caller left. */
71
+ "lsp_nav_late_answer_discarded",
72
+ /**
73
+ * #1743 review: a per-file touch skipped a server in breaker cooldown or
74
+ * latched permanently broken. Fires once per file per touch during an
75
+ * outage, so it is rising-edge per (server, file).
76
+ */
77
+ "lsp_client_skipped_broken",
78
+ /** #1743 review: the same skip, for a temporarily unavailable command. */
79
+ "lsp_client_skipped_unavailable_command",
80
+ /**
81
+ * #1723: an event-loop block at or above the floor. Not a degradation, so
82
+ * no ledger kind; bounded by call cadence (one `turn_end` runs it once per
83
+ * turn) rather than by an option here.
84
+ */
85
+ "loop_block",
86
+ ];
87
+ const REGISTERED_PHASES = new Set(BOUNDED_TELEMETRY_PHASES);
88
+ /** Report whether a phase name is in the registry (used by the sweep). */
89
+ export function isBoundedTelemetryPhase(phase) {
90
+ return REGISTERED_PHASES.has(phase);
91
+ }
92
+ /**
93
+ * Per-turn admission counters, keyed by phase. Cleared lazily when a call
94
+ * observes a turn index different from the one the counters were built for,
95
+ * and eagerly by `resetBoundedTelemetry` at `session_start` (a new session
96
+ * restarts turn numbering at 0, so the lazy check alone could carry a stale
97
+ * count across a session boundary that happened to land on the same index).
98
+ */
99
+ let turnCounts = new Map();
100
+ let countedTurnIndex;
101
+ /**
102
+ * Decide whether this occurrence earns a detailed record, and count it in the
103
+ * ledger either way. Returns `true` when the caller should write the record.
104
+ *
105
+ * Split out from `emitBounded` for the #1705 shape: that site loops over N
106
+ * abandoned records, admits each one, and then writes ONE batched
107
+ * `logLatency` naming only the admitted ones. A helper that could only emit
108
+ * per-call would have turned one record into N.
109
+ */
110
+ export function admitBounded(phase, identity, options = {}) {
111
+ try {
112
+ let isRisingEdge = true;
113
+ if (options.ledgerKind !== undefined) {
114
+ // `subject` is the identity, unmodified: the ledger's per-subject
115
+ // entry is what tells a reader WHICH file or method is stuck after
116
+ // the detailed records stop.
117
+ isRisingEdge = incrementDegradationCount({
118
+ kind: options.ledgerKind,
119
+ subject: identity,
120
+ reason: options.reason ?? phase,
121
+ });
122
+ }
123
+ if (options.risingEdgePer !== undefined && !isRisingEdge)
124
+ return false;
125
+ return admitAgainstTurnCap(phase, options.capPerTurn);
126
+ }
127
+ catch {
128
+ // Telemetry must never break the observed path. Emitting is the safer
129
+ // failure: an extra record is noise, a swallowed one is a blind spot.
130
+ return true;
131
+ }
132
+ }
133
+ /**
134
+ * Count every occurrence in the ledger, then write ONE bounded `latency.log`
135
+ * record if this occurrence is admitted. Returns whether the record was
136
+ * written, so a caller can branch on it (all four migrated sites either
137
+ * ignore it or, in #1705's case, use `admitBounded` directly).
138
+ */
139
+ export function emitBounded(phase, identity, payload, options = {}) {
140
+ try {
141
+ if (!admitBounded(phase, identity, options))
142
+ return false;
143
+ logLatency({
144
+ ...payload,
145
+ type: payload.type ?? "phase",
146
+ phase,
147
+ // `filePath` is what existing latency.log queries filter on, so a site
148
+ // may keep a coarser one; `metadata.identity` below always carries the
149
+ // full discriminating identity. Aggregation that loses WHICH subject
150
+ // is stuck is the failure AGENTS.md names.
151
+ filePath: payload.filePath ?? identity,
152
+ metadata: { ...payload.metadata, identity },
153
+ });
154
+ return true;
155
+ }
156
+ catch {
157
+ // Telemetry must never break the observed path.
158
+ return false;
159
+ }
160
+ }
161
+ /**
162
+ * Apply the per-turn ceiling. Separated so the turn-rollover clear happens in
163
+ * exactly one place: a cap and its reset drifting apart is how #1733's
164
+ * compensating-pair guards got past review.
165
+ */
166
+ function admitAgainstTurnCap(phase, cap) {
167
+ if (cap === undefined)
168
+ return true;
169
+ if (countedTurnIndex !== cap.turnIndex) {
170
+ turnCounts = new Map();
171
+ countedTurnIndex = cap.turnIndex;
172
+ }
173
+ const used = turnCounts.get(phase) ?? 0;
174
+ if (used >= cap.limit)
175
+ return false;
176
+ turnCounts.set(phase, used + 1);
177
+ return true;
178
+ }
179
+ /**
180
+ * Session-boundary/test reset. The rising-edge state is NOT here — it lives
181
+ * in the degradation ledger, which `handleSessionStart` already re-arms via
182
+ * `resetDegradationLedger`. Only the per-turn counters are this module's own,
183
+ * and they need this because turn numbering restarts at 0 each session.
184
+ */
185
+ export function resetBoundedTelemetry() {
186
+ turnCounts = new Map();
187
+ countedTurnIndex = undefined;
188
+ }
189
+ /** Test-only: the live per-turn admission count for a phase. */
190
+ export function _boundedTurnCountForTest(phase) {
191
+ return turnCounts.get(phase) ?? 0;
192
+ }
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Cascade neighbour-budget sizing (#1462).
3
+ *
4
+ * The flat neighbour cap (40) and the turn-end settle wait (5000 ms) were set in
5
+ * two different files with nothing relating them. Dogfood 2026-08-15 measured
6
+ * what that costs: n=50 cascades, median 30 ms, max 3.88 s at nbr=40 — and with
7
+ * a `reverse_deps_cache` refresh ahead of the graph build (2046 ms for
8
+ * `logger.ts`) the run overran the turn_end cap, so `cascade_settle_wait`
9
+ * returned `settled: 0` and the finding arrived a turn late.
10
+ *
11
+ * ## What a slow run actually costs
12
+ *
13
+ * It is NOT lost. `RuntimeCoordinator.settleCascadeRuns` re-parks a promise
14
+ * still in flight at the cap, `agent_settled` gives it a second and more
15
+ * generous drain (`cascade_carry_over_settle`, `quietWindowWaitMs`, 15 000 ms),
16
+ * and `beginTurn` deliberately keeps `_pendingCascadeRuns` so anything still
17
+ * running surfaces at a later turn_end — that is #1443, and pre-#450 these
18
+ * findings were always awaited. So the real trade is:
19
+ *
20
+ * full neighbour set, one turn late vs. a narrowed set, on time,
21
+ * with the remainder lost for good
22
+ *
23
+ * Narrowing is therefore only worth paying for when it genuinely converts a
24
+ * late run into an on-time one. That gives three zones, and the derivation
25
+ * below is exactly those three:
26
+ *
27
+ * fits the full walk still fits the on-time window — nothing to do
28
+ * narrowed the RESCUE BAND: the full walk no longer fits but a shorter
29
+ * one does, so trading the tail for on-time delivery buys
30
+ * something real
31
+ * past-rescue not even a floor-sized walk fits any more, so narrowing would
32
+ * drop neighbours permanently AND still miss the window. Keep
33
+ * the full budget and let the carry-over deliver it complete.
34
+ * This is the cold-session case: a fresh graph build measures up
35
+ * to ~19 s (see `runtime-coordinator.ts`'s carry-over note), and
36
+ * a floor-sized stub there is strictly worse than pre-#1462.
37
+ *
38
+ * One more guard, for the same reason. If the on-time window could not fit a
39
+ * full walk even at zero prelude, there is no LATE run to rescue — every
40
+ * cascade would be permanently downsized because a *wait* was configured small.
41
+ * That is a budget change wearing a timeout's clothes, so the derivation stands
42
+ * down entirely (`no-rescue-window`) and the flat cap applies. This is what
43
+ * keeps `PI_LENS_CASCADE_SETTLE_WAIT_MS` from doubling as a neighbour-count
44
+ * knob: at the 5000/100/40 defaults the band is a prelude of 1.0–4.5 s, and
45
+ * shrinking the wait to 1000 ms disables narrowing rather than capping every
46
+ * cascade at ~10 neighbours.
47
+ */
48
+ import { recordDegradationOnce } from "./degradation-ledger.js";
49
+ import { toPositiveFinite } from "./env-utils.js";
50
+ // The knobs only, never the scheduler — this module is arithmetic and has no
51
+ // business depending on a task registry to read two numbers (#1462 review N4).
52
+ // Note it buys nothing at load time: `runtime-config.js` below already reaches
53
+ // `pidusage` via `lens-config.js`.
54
+ import { isQuietWindowEnabled, quietWindowWaitMs, } from "./quiet-window-config.js";
55
+ import { RUNTIME_CONFIG } from "./runtime-config.js";
56
+ /**
57
+ * Bounded wait for the turn's deferred cascade computes (#450) to settle before
58
+ * they are merged at turn_end. A late compute is carried over, never dropped.
59
+ *
60
+ * `0` is a meaningful setting (never block turn_end), so this deliberately does
61
+ * NOT use `lazyEnvNumber` — that helper treats 0 as "unset" and would silently
62
+ * restore the 5000 ms default.
63
+ */
64
+ export function cascadeSettleWaitMs() {
65
+ const raw = Number(process.env.PI_LENS_CASCADE_SETTLE_WAIT_MS);
66
+ return Number.isFinite(raw) && raw >= 0 ? raw : 5000;
67
+ }
68
+ /** The budget floor, and the minimum any env override of the cap can produce. */
69
+ const MIN_NEIGHBOUR_BUDGET = RUNTIME_CONFIG.pipeline.cascadeMaxFiles;
70
+ /**
71
+ * The flat per-run cap: the most neighbours a cascade walks. Still the ceiling
72
+ * — the derivation below only ever narrows it, and only inside the rescue band.
73
+ */
74
+ const DEFAULT_NEIGHBOUR_BUDGET = 40;
75
+ export const CASCADE_NEIGHBOUR_BUDGET = Math.max(MIN_NEIGHBOUR_BUDGET, Number.parseInt(process.env.PI_LENS_CASCADE_NEIGHBOUR_BUDGET ?? "40", 10) ||
76
+ DEFAULT_NEIGHBOUR_BUDGET);
77
+ /**
78
+ * True when `PI_LENS_CASCADE_NEIGHBOUR_BUDGET` moved the ceiling away from the
79
+ * default (#1462 review F-E). Raising it far enough can push
80
+ * `ceiling * perNeighbourMs` past the settle window, so EVERY cascade reads
81
+ * `no-rescue-window` — the whole derivation disarmed by a cap change nobody
82
+ * meant as a timeout change. Read once at load, same lifetime as the constant
83
+ * it describes.
84
+ */
85
+ const NEIGHBOUR_BUDGET_OVERRIDDEN = process.env.PI_LENS_CASCADE_NEIGHBOUR_BUDGET !== undefined &&
86
+ CASCADE_NEIGHBOUR_BUDGET !== DEFAULT_NEIGHBOUR_BUDGET;
87
+ /**
88
+ * Marginal wall-clock cost of one more neighbour in the walk, in ms. Calibrated
89
+ * on the 2026-08-15 dogfood tail: 3.88 s at nbr=40 and 3.71 s at nbr=38, both
90
+ * ~97 ms per neighbour. Touches fan out in parallel, so this is the observed
91
+ * marginal cost under LSP contention, not one touch's own latency.
92
+ *
93
+ * It is an ESTIMATE, and the derivation is sensitive to it: if the true cost is
94
+ * materially above this, a walk sized against it still overruns (the rescue
95
+ * fails and the tail was dropped for nothing). `scripts/bench-cascade-budget.mjs
96
+ * --true-cost-ms=<n>` measures that sensitivity directly. Rounded UP from the
97
+ * measurement so the estimate errs toward narrowing less.
98
+ */
99
+ const DEFAULT_NEIGHBOUR_COST_MS = 100;
100
+ /** Read per call — sized once per cascade run, never on a hot path. */
101
+ function neighbourCostMs() {
102
+ return (toPositiveFinite(process.env.PI_LENS_CASCADE_NEIGHBOUR_COST_MS) ||
103
+ DEFAULT_NEIGHBOUR_COST_MS);
104
+ }
105
+ function neighbourFloor() {
106
+ return (toPositiveFinite(process.env.PI_LENS_CASCADE_NEIGHBOUR_FLOOR) ||
107
+ MIN_NEIGHBOUR_BUDGET);
108
+ }
109
+ /**
110
+ * Size one run's neighbour walk against the on-time window supplied by its
111
+ * caller.
112
+ *
113
+ * `elapsedMs` is arithmetic input, not an observation made by this function.
114
+ * The production caller measures it against the active turn_end settle window
115
+ * and passes that elapsed time here. When that boundary is unavailable, the
116
+ * caller passes zero to preserve the flat cap. Legacy callers may still pass
117
+ * deferred run age for compatibility.
118
+ *
119
+ * Every override exists so the derivation can be tested and benchmarked as a
120
+ * pure function; the function itself does not observe a deferred run's age.
121
+ */
122
+ export function deriveCascadeNeighbourBudget(options) {
123
+ const ceiling = options.ceiling ?? CASCADE_NEIGHBOUR_BUDGET;
124
+ // The floor can never exceed the ceiling — a floor above the cap would make
125
+ // the derived budget LARGER than the flat one it exists to bound.
126
+ const floor = Math.min(ceiling, options.floor ?? neighbourFloor());
127
+ const onTimeMs = options.settleWaitMs ?? cascadeSettleWaitMs();
128
+ const perNeighbourMs = options.perNeighbourMs ?? neighbourCostMs();
129
+ const elapsedMs = toPositiveFinite(options.elapsedMs);
130
+ const remainingMs = onTimeMs - elapsedMs;
131
+ const deliveryWindowMs = onTimeMs +
132
+ (options.quietDrainMs ??
133
+ (isQuietWindowEnabled() ? quietWindowWaitMs() : 0));
134
+ const full = { budget: ceiling, ceiling, remainingMs, deliveryWindowMs };
135
+ // No on-time window at all, or one too small to ever fit a full walk. There
136
+ // is no late run to rescue here, only every run to shrink — stand down.
137
+ //
138
+ // Boundary, acknowledged rather than special-cased: at exactly
139
+ // `onTimeMs === ceiling * perNeighbourMs` this does not fire and the `fits`
140
+ // zone has zero width, so ANY prelude narrows. Not slightly, either — at
141
+ // 4000/100/40 a 2 s prelude gives 20 and a 3.5 s prelude gives the floor.
142
+ // Left alone because the narrowing is honest at that setting: with the
143
+ // window sized to exactly one full walk, a run that has spent any of it
144
+ // really would miss, so every one of those cuts is a genuine rescue rather
145
+ // than the `no-rescue-window` case this guard exists to catch. What it does
146
+ // cost is headroom — see the divisor bands in the bench's `--sweep` text.
147
+ if (onTimeMs <= 0 || onTimeMs < ceiling * perNeighbourMs) {
148
+ // #1462 review F-E: name the override, once per session, when it is the
149
+ // REASON the derivation stood down — not when a caller under test asked
150
+ // for a specific ceiling, and not on every one of the cascades this
151
+ // disarms for the rest of the session.
152
+ if (options.ceiling === undefined && NEIGHBOUR_BUDGET_OVERRIDDEN) {
153
+ recordDegradationOnce({
154
+ kind: "cascade-budget-override-disarmed",
155
+ subject: "cascade-neighbour-budget",
156
+ reason: `PI_LENS_CASCADE_NEIGHBOUR_BUDGET=${CASCADE_NEIGHBOUR_BUDGET} needs ` +
157
+ `${ceiling * perNeighbourMs}ms to walk the full cap, which exceeds ` +
158
+ `the ${onTimeMs}ms settle window — the rescue band is disarmed and ` +
159
+ `every cascade keeps the flat (overridden) cap`,
160
+ });
161
+ }
162
+ return { ...full, zone: "no-rescue-window" };
163
+ }
164
+ // The full walk still fits. Nothing to buy.
165
+ if (remainingMs >= ceiling * perNeighbourMs) {
166
+ return { ...full, zone: "fits" };
167
+ }
168
+ // Past rescue: not even a floor-sized walk fits, so narrowing would drop the
169
+ // tail permanently and still miss the window. The carry-over delivers the
170
+ // whole set one turn later instead.
171
+ //
172
+ // #1462 review F-B, kept rather than shrunk: one dogfood log measured this
173
+ // zone at 0/1090 cascades, and asked whether a 3-zone module earns its keep
174
+ // at that frequency. It was measured against the WRITE-relative clock this
175
+ // same round fixed, which charges elapsed time no cascade actually spent
176
+ // waiting — so it under-counts how often a genuinely slow compute (a cold
177
+ // graph build, ~19 s) overlaps an already-running settle window, which is
178
+ // exactly what this zone exists to catch (F3, a real regression the first
179
+ // review round found: a floor-sized stub on that cold-start case is
180
+ // strictly worse than pre-#1462). Reachability under the corrected clock
181
+ // has not been re-measured; shrinking on a stale sample would trade a
182
+ // cheap, already-tested guard against a proven regression for a paper
183
+ // simplification. Revisit if a post-fix dogfood window confirms it still
184
+ // never fires.
185
+ if (remainingMs < floor * perNeighbourMs) {
186
+ return { ...full, zone: "past-rescue" };
187
+ }
188
+ // The rescue band: a shorter walk lands on time.
189
+ return {
190
+ budget: Math.min(ceiling, Math.max(floor, Math.floor(remainingMs / perNeighbourMs))),
191
+ ceiling,
192
+ remainingMs,
193
+ deliveryWindowMs,
194
+ zone: "narrowed",
195
+ };
196
+ }
@@ -3,8 +3,18 @@ import { toRunnerDisplayPath } from "./dispatch/runner-context.js";
3
3
  export function formatCascadeNeighborDiagnostics(cwd, neighbors, options = {}) {
4
4
  const withErrors = neighbors.filter((n) => n.diagnostics.length > 0);
5
5
  const inconclusive = neighbors.filter((n) => n.inconclusive === true && n.diagnostics.length === 0);
6
- if (withErrors.length === 0 && inconclusive.length === 0)
6
+ // #1459: a neighbour whose scanner never looked at it is not a clean leaf. It
7
+ // is also not `inconclusive` — the language server answered — so it gets its
8
+ // own honest line instead of being folded into either bucket. Only the
9
+ // zero-diagnostic case needs saying: a neighbour with findings already renders.
10
+ const uncovered = neighbors.filter((n) => n.diagnostics.length === 0 &&
11
+ n.inconclusive !== true &&
12
+ (n.unconfirmedServerIds?.length ?? 0) > 0);
13
+ if (withErrors.length === 0 &&
14
+ inconclusive.length === 0 &&
15
+ uncovered.length === 0) {
7
16
  return "";
17
+ }
8
18
  const noun = options.noun ?? "neighbor";
9
19
  let out = withErrors.length > 0
10
20
  ? `📐 Cascade errors in ${withErrors.length} ${noun} file(s) — fix before finishing turn:`
@@ -29,6 +39,15 @@ export function formatCascadeNeighborDiagnostics(cwd, neighbors, options = {}) {
29
39
  out += `\n ${toRunnerDisplayPath(cwd, neighbor.filePath)}`;
30
40
  }
31
41
  }
42
+ if (uncovered.length > 0) {
43
+ if (out)
44
+ out += "\n";
45
+ out += `⚠️ Cascade scanners did not cover ${uncovered.length} ${noun} file(s) — no findings does NOT mean clean here:`;
46
+ for (const neighbor of uncovered) {
47
+ const servers = (neighbor.unconfirmedServerIds ?? []).join(", ");
48
+ out += `\n ${toRunnerDisplayPath(cwd, neighbor.filePath)} (not scanned by ${servers})`;
49
+ }
50
+ }
32
51
  return out;
33
52
  }
34
53
  /**
@@ -1 +1,9 @@
1
- export {};
1
+ /**
2
+ * How long a cascade neighbour's diagnostics stay usable (#1816).
3
+ *
4
+ * One constant for two consumers that each declared their own `240_000`:
5
+ * `clients/lsp/index.ts` (the neighbour-diagnostics cache) and
6
+ * `clients/dispatch/integration.ts` (the cascade turn scope). They must move
7
+ * together — a split pair reads as two independent policies and drifts.
8
+ */
9
+ export const CASCADE_DIAGNOSTICS_TTL_MS = 240_000;