@herbertgao/pi-extensions 2026.8.5 → 2026.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (389) hide show
  1. package/README.md +5 -5
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -2
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +272 -22
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +48 -6
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +1 -1
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +14 -0
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -17
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
  15. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
  16. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
  17. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  18. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +16 -0
  19. package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
  20. package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
  21. package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
  22. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
  23. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
  24. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +22 -15
  25. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
  26. package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
  27. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
  28. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
  29. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
  30. package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
  31. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
  32. package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
  33. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +34 -10
  34. package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
  35. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +29 -5
  36. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +3 -0
  37. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +46 -10
  38. package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
  39. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
  40. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +8 -1
  41. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +3 -1
  42. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +2 -2
  43. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
  44. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
  45. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
  46. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +3 -1
  47. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +30 -7
  48. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
  49. package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +30 -4
  50. package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +26 -5
  51. package/node_modules/@narumitw/pi-btw/README.md +119 -103
  52. package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
  53. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
  54. package/node_modules/@narumitw/pi-btw/package.json +13 -9
  55. package/node_modules/@narumitw/pi-btw/src/btw.ts +40 -42
  56. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
  57. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
  58. package/node_modules/@narumitw/pi-btw/src/menu.ts +78 -15
  59. package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
  60. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
  61. package/node_modules/pi-lens/CHANGELOG.md +1004 -0
  62. package/node_modules/pi-lens/README.md +11 -3
  63. package/node_modules/pi-lens/config/ruff/core.toml +42 -0
  64. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
  65. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +427 -0
  66. package/node_modules/pi-lens/dist/clients/agent-nudge.js +79 -16
  67. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
  68. package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
  69. package/node_modules/pi-lens/dist/clients/bash-file-access.js +285 -8
  70. package/node_modules/pi-lens/dist/clients/biome-client.js +176 -37
  71. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
  72. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +119 -0
  73. package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
  74. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
  75. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +192 -0
  76. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
  77. package/node_modules/pi-lens/dist/clients/bus-publish.js +11 -3
  78. package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
  79. package/node_modules/pi-lens/dist/clients/cascade-format.js +76 -2
  80. package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
  81. package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
  82. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
  83. package/node_modules/pi-lens/dist/clients/console-guard-install.js +16 -4
  84. package/node_modules/pi-lens/dist/clients/dead-code-client.js +249 -52
  85. package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
  86. package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
  87. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +62 -13
  88. package/node_modules/pi-lens/dist/clients/dependency-checker.js +43 -9
  89. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +153 -37
  90. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
  91. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
  92. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +165 -11
  93. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +325 -72
  94. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
  95. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +81 -41
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -0
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +23 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +14 -0
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +20 -0
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +22 -0
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +10 -0
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +18 -6
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +488 -0
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +84 -0
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +943 -93
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +72 -0
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +106 -0
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +9 -0
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -1
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
  129. package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
  130. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
  131. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
  132. package/node_modules/pi-lens/dist/clients/extension-log.js +317 -3
  133. package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
  134. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +294 -0
  135. package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
  136. package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
  137. package/node_modules/pi-lens/dist/clients/format-events-publish.js +39 -8
  138. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
  139. package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
  140. package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
  141. package/node_modules/pi-lens/dist/clients/git-guard.js +49 -21
  142. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
  143. package/node_modules/pi-lens/dist/clients/go-client.js +21 -39
  144. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +219 -10
  145. package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
  146. package/node_modules/pi-lens/dist/clients/host-ports.js +9 -1
  147. package/node_modules/pi-lens/dist/clients/installer/index.js +1119 -101
  148. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
  149. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
  150. package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
  151. package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
  152. package/node_modules/pi-lens/dist/clients/jscpd-client.js +86 -15
  153. package/node_modules/pi-lens/dist/clients/knip-client.js +361 -24
  154. package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
  155. package/node_modules/pi-lens/dist/clients/language-profile.js +2 -0
  156. package/node_modules/pi-lens/dist/clients/latency-logger.js +487 -9
  157. package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
  158. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
  159. package/node_modules/pi-lens/dist/clients/lens-events.js +56 -25
  160. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
  161. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +45 -2
  162. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
  163. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +160 -20
  164. package/node_modules/pi-lens/dist/clients/lsp/client.js +1666 -148
  165. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +111 -0
  166. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
  167. package/node_modules/pi-lens/dist/clients/lsp/index.js +2496 -171
  168. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
  169. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
  170. package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
  171. package/node_modules/pi-lens/dist/clients/lsp/server.js +517 -69
  172. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
  173. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
  174. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +172 -0
  175. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
  176. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +61 -3
  177. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
  178. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
  179. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
  180. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
  181. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +4 -0
  182. package/node_modules/pi-lens/dist/clients/mcp/session.js +39 -18
  183. package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
  184. package/node_modules/pi-lens/dist/clients/module-report.js +8 -0
  185. package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
  186. package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
  187. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
  188. package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
  189. package/node_modules/pi-lens/dist/clients/pipeline.js +178 -30
  190. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
  191. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
  192. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
  193. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +85 -15
  194. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
  195. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
  196. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
  197. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -0
  198. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  199. package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
  200. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
  201. package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
  202. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +1 -1
  203. package/node_modules/pi-lens/dist/clients/read-guard.js +184 -10
  204. package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
  205. package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
  206. package/node_modules/pi-lens/dist/clients/review-graph/query.js +76 -4
  207. package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
  208. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +248 -13
  209. package/node_modules/pi-lens/dist/clients/runtime-context.js +112 -12
  210. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +472 -61
  211. package/node_modules/pi-lens/dist/clients/runtime-session.js +359 -13
  212. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
  213. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +269 -22
  214. package/node_modules/pi-lens/dist/clients/runtime-turn.js +948 -87
  215. package/node_modules/pi-lens/dist/clients/rust-client.js +21 -37
  216. package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
  217. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
  218. package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
  219. package/node_modules/pi-lens/dist/clients/security-scan-client.js +224 -8
  220. package/node_modules/pi-lens/dist/clients/sg-runner.js +249 -37
  221. package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
  222. package/node_modules/pi-lens/dist/clients/smells-rollup.js +18 -11
  223. package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
  224. package/node_modules/pi-lens/dist/clients/startup-timing.js +26 -2
  225. package/node_modules/pi-lens/dist/clients/test-runner-client.js +574 -32
  226. package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
  227. package/node_modules/pi-lens/dist/clients/tool-policy.js +127 -20
  228. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
  229. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
  230. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
  231. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
  232. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
  233. package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
  234. package/node_modules/pi-lens/dist/clients/warm-attach.js +17 -0
  235. package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
  236. package/node_modules/pi-lens/dist/clients/word-index.js +305 -33
  237. package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
  238. package/node_modules/pi-lens/dist/index.js +64677 -49484
  239. package/node_modules/pi-lens/dist/mcp/server.js +8 -2
  240. package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
  241. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
  242. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
  243. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +469 -56
  244. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +190 -46
  245. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
  246. package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
  247. package/node_modules/pi-lens/docs/agent-guide.md +44 -14
  248. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +518 -501
  249. package/node_modules/pi-lens/docs/dispositions.md +21 -0
  250. package/node_modules/pi-lens/docs/features.md +33 -6
  251. package/node_modules/pi-lens/docs/globalconfig.md +14 -0
  252. package/node_modules/pi-lens/docs/language-coverage.md +71 -5
  253. package/node_modules/pi-lens/docs/mcp.md +2 -2
  254. package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
  255. package/node_modules/pi-lens/docs/settings.md +25 -1
  256. package/node_modules/pi-lens/docs/usage.md +24 -5
  257. package/node_modules/pi-lens/docs/word-index.md +35 -0
  258. package/node_modules/pi-lens/package.json +10 -6
  259. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
  260. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
  261. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
  262. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
  263. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +22 -0
  264. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
  265. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
  266. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
  267. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
  268. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
  269. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
  270. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
  271. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
  272. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +9 -0
  273. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +9 -0
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +9 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +9 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +66 -0
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
  300. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
  301. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
  302. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
  303. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
  304. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +29 -0
  305. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +30 -0
  306. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +41 -0
  307. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +42 -0
  308. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
  309. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
  310. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
  311. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
  312. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
  313. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
  314. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +32 -0
  315. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
  316. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
  317. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
  318. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
  319. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
  320. package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
  321. package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
  322. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
  323. package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
  324. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +132 -1
  325. package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
  326. package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
  327. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +12 -11
  328. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
  329. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +87 -116
  330. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +170 -0
  331. package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
  332. package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
  333. package/node_modules/pi-mcp-adapter/CHANGELOG.md +49 -0
  334. package/node_modules/pi-mcp-adapter/README.md +51 -6
  335. package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
  336. package/node_modules/pi-mcp-adapter/cli.js +188 -5
  337. package/node_modules/pi-mcp-adapter/commands.ts +128 -22
  338. package/node_modules/pi-mcp-adapter/config.ts +28 -10
  339. package/node_modules/pi-mcp-adapter/direct-tools.ts +23 -19
  340. package/node_modules/pi-mcp-adapter/index.ts +219 -32
  341. package/node_modules/pi-mcp-adapter/init.ts +36 -4
  342. package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
  343. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
  344. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
  345. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
  346. package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
  347. package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
  348. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
  349. package/node_modules/pi-mcp-adapter/metadata-cache.ts +46 -25
  350. package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
  351. package/node_modules/pi-mcp-adapter/package.json +4 -1
  352. package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
  353. package/node_modules/pi-mcp-adapter/request-headers-command.ts +330 -0
  354. package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
  355. package/node_modules/pi-mcp-adapter/server-manager.ts +145 -2
  356. package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
  357. package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
  358. package/node_modules/pi-mcp-adapter/tool-metadata.ts +38 -18
  359. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
  360. package/node_modules/pi-mcp-adapter/types.ts +95 -10
  361. package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
  362. package/node_modules/pi-mcp-adapter/utils.ts +70 -0
  363. package/node_modules/pi-web-access/CHANGELOG.md +37 -0
  364. package/node_modules/pi-web-access/README.md +38 -14
  365. package/node_modules/pi-web-access/anysearch.ts +4 -2
  366. package/node_modules/pi-web-access/auth-fetch.ts +148 -0
  367. package/node_modules/pi-web-access/brave.ts +16 -4
  368. package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
  369. package/node_modules/pi-web-access/curator-page.ts +8 -3
  370. package/node_modules/pi-web-access/curator-server.ts +5 -1
  371. package/node_modules/pi-web-access/exa.ts +18 -7
  372. package/node_modules/pi-web-access/extract.ts +134 -37
  373. package/node_modules/pi-web-access/fetch-params.ts +17 -3
  374. package/node_modules/pi-web-access/firecrawl.ts +172 -12
  375. package/node_modules/pi-web-access/gemini-search.ts +33 -7
  376. package/node_modules/pi-web-access/index.ts +160 -104
  377. package/node_modules/pi-web-access/package.json +2 -2
  378. package/node_modules/pi-web-access/page-query.ts +4 -2
  379. package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
  380. package/node_modules/pi-web-access/parallel.ts +2 -2
  381. package/node_modules/pi-web-access/query-rewrite.ts +51 -0
  382. package/node_modules/pi-web-access/serper.ts +211 -0
  383. package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
  384. package/node_modules/pi-web-access/summary-review.ts +51 -23
  385. package/node_modules/pi-web-access/tavily.ts +16 -4
  386. package/node_modules/pi-web-access/utils.ts +87 -0
  387. package/node_modules/pi-web-access/valyu.ts +199 -0
  388. package/node_modules/pi-web-access/youtube-extract.ts +2 -2
  389. package/package.json +13 -13
@@ -164,6 +164,22 @@ export const LENS_FLAGS = [
164
164
  default: false,
165
165
  scope: "global",
166
166
  },
167
+ {
168
+ name: "no-lazy-tools",
169
+ description: "Keep all pi-lens tools active to avoid tool-list cache changes. Also via tools.lazy=false in ~/.pi-lens/config.json.",
170
+ configKey: "tools.lazy",
171
+ negated: true,
172
+ default: false,
173
+ scope: "global",
174
+ },
175
+ {
176
+ name: "lens-turn-end-madge",
177
+ description: "Run the per-turn-end madge circular-dependency check on import-changed files. Off by default: the pass only writes debug output, and user-facing madge diagnostics come from the session-start scan cache + lens_diagnostics. Also via turnEnd.madge.enabled=true in ~/.pi-lens/config.json.",
178
+ configKey: "turnEnd.madge.enabled",
179
+ negated: false,
180
+ default: false,
181
+ scope: "global",
182
+ },
167
183
  ];
168
184
  const byName = new Map(LENS_FLAGS.map((spec) => [spec.name, spec]));
169
185
  export function getLensFlagSpec(name) {
@@ -1,4 +1,6 @@
1
1
  import { recordDegradation } from "./degradation-ledger.js";
2
+ import { probeCtxActive } from "./session-lifecycle.js";
3
+ import { logBusEvent, } from "./bus-events-logger.js";
2
4
  const STALE_CTX_MESSAGE = "This extension ctx is stale after session replacement";
3
5
  /** Ledger a dead activation once, at the publisher's occurrence-window guard. */
4
6
  export function recordStaleBusFailure(subject, error) {
@@ -19,8 +21,26 @@ export function createLiveBusEmitter() {
19
21
  getter = next;
20
22
  emit = undefined;
21
23
  },
22
- get() {
23
- return getter?.() ?? emit;
24
+ resolve() {
25
+ // Invoke the getter for every delivery so session_start rewiring can
26
+ // replace a captured pre-await activation with the current primary. When
27
+ // the current target is nevertheless confirmed stale, never invoke it.
28
+ const target = getter?.() ?? emit;
29
+ if (!target)
30
+ return { outcome: "unwired", ctxSource: "unwired" };
31
+ if (typeof target === "function") {
32
+ return { outcome: "ready", emit: target, ctxSource: "global-fallback" };
33
+ }
34
+ // Object targets are always "own"-sourced (only `wire(fn)`'s bare
35
+ // function arm is "global-fallback", and it never reaches this
36
+ // branch) — compute it once so the ready/stale-session outcomes
37
+ // below both carry the same value instead of restating the
38
+ // literal independently.
39
+ const ctxSource = "own";
40
+ if (probeCtxActive(target.ctx) === false) {
41
+ return { outcome: "stale-session", ctxSource };
42
+ }
43
+ return { outcome: "ready", emit: target.emit, ctxSource };
24
44
  },
25
45
  reset() {
26
46
  emit = undefined;
@@ -28,3 +48,26 @@ export function createLiveBusEmitter() {
28
48
  },
29
49
  };
30
50
  }
51
+ /**
52
+ * Resolve through the shared stale-session guard and record a declined
53
+ * target.
54
+ *
55
+ * `entry` is a THUNK, not a value (M1, #1415 review): building the log entry
56
+ * (every producer's version normalizes `cwd` via `normalizeFilePath`, a sync
57
+ * `realpathSync.native` call on Windows) is real per-publish cost that used
58
+ * to be paid on EVERY call regardless of outcome, even though it is only
59
+ * consumed on the `stale-session` branch. Invoking the thunk only there means
60
+ * the common `ready` path pays nothing for it.
61
+ */
62
+ export function resolveLiveBusEmitter(liveEmitter, entry) {
63
+ const resolution = liveEmitter.resolve();
64
+ if (resolution.outcome === "stale-session") {
65
+ logBusEvent({
66
+ ...entry(),
67
+ outcome: "skipped_stale_session",
68
+ level: "info",
69
+ ctxSource: resolution.ctxSource,
70
+ });
71
+ }
72
+ return resolution;
73
+ }
@@ -26,9 +26,12 @@
26
26
  * receive `options.auxGraceMs` before the race finalises. Aux results within
27
27
  * that window are included; later arrivals are dropped. When no descriptors
28
28
  * carry `role:"auxiliary"` the aux-grace path is never entered.
29
- * @param options.auxGraceMs - Grace period given to auxiliary promises after all
30
- * primary promises have settled. Only relevant when at least one descriptor
31
- * carries `role:"auxiliary"`. Defaults to 500ms.
29
+ * @param options.auxGraceMs - Ceiling on the grace period given to auxiliary
30
+ * promises after all primary promises have settled. Only relevant when at
31
+ * least one descriptor carries `role:"auxiliary"`. Defaults to 500ms. When a
32
+ * still-pending auxiliary's descriptor carries `budgetMs`, the actual window
33
+ * is `min(auxGraceMs, max(budgetMs of still-pending auxiliaries))` — see
34
+ * `PromiseDescriptor.budgetMs`.
32
35
  */
33
36
  export async function raceToCompletion(promises, shouldComplete, options = { timeoutMs: 1500 }) {
34
37
  const results = new Array(promises.length).fill(undefined);
@@ -76,7 +79,30 @@ export async function raceToCompletion(promises, shouldComplete, options = { tim
76
79
  if (auxGraceStarted || completed)
77
80
  return;
78
81
  auxGraceStarted = true;
79
- const graceMs = options.auxGraceMs ?? 500;
82
+ const ceilingMs = options.auxGraceMs ?? 500;
83
+ // #1458 S2: cap the grace window by the still-pending auxiliaries' own
84
+ // declared budgets, not just the flat ceiling — mirrors touchFile's
85
+ // per-server `min(declared, ceiling)`.
86
+ //
87
+ // This is ONE shared timer, not a deadline per promise, so it grants
88
+ // every pending auxiliary the window of its slowest pending sibling.
89
+ // That is safe here only because of an invariant the caller upholds,
90
+ // not because of anything this function enforces: every promise
91
+ // `getDiagnostics` passes in is already self-bounded by the same
92
+ // `strategy.aggregateWaitMs` it declares as `budgetMs`, so a fast
93
+ // auxiliary resolves on its own deadline long before a slow sibling's
94
+ // window expires. A caller that passes a promise NOT self-bounded by
95
+ // its descriptor's `budgetMs` — or one that outlives it, the way
96
+ // `expectSemanticSecondPush` extends a primary — would get a window
97
+ // wider than it declared. `PromiseDescriptor` is public, so treat that
98
+ // as a real constraint on new callers rather than a theoretical one.
99
+ const pendingAuxBudgets = [...auxIndices]
100
+ .filter((i) => results[i] === undefined)
101
+ .map((i) => descriptors[i]?.budgetMs)
102
+ .filter((b) => typeof b === "number" && b >= 0);
103
+ const graceMs = pendingAuxBudgets.length > 0
104
+ ? Math.min(ceilingMs, Math.max(...pendingAuxBudgets))
105
+ : ceilingMs;
80
106
  if (graceMs <= 0) {
81
107
  finalize();
82
108
  return;
@@ -32,6 +32,12 @@
32
32
  * NOT tier-3 — the caller keeps today's full in-lane wait. Fail-safe is
33
33
  * always "wait like before".
34
34
  *
35
+ * Native TS7 is the cascade-only exception. It is not silent on clean, but
36
+ * its publication does not settle inside the cold-snapshot budget. Cascade
37
+ * classifies it as `collect-later`, sends the same no-wait touch, and
38
+ * reconciles its later per-file push or pull publication. The shared server
39
+ * policy remains `waits`, so main-lane behavior does not change.
40
+ *
35
41
  * #524/#529/#541/#558: a server id can now be backed by more than one actual
36
42
  * binary — "typescript" is classic typescript-language-server OR TS7's
37
43
  * native `tsc --lsp --stdio` (PR #526). PR #526 originally routed the
@@ -46,18 +52,46 @@
46
52
  * confirmed still silent (`cleanPubs=0(v:0)`) in the same run. This is
47
53
  * therefore an EVIDENCE-BASED revert, not the original unverified caution:
48
54
  * native-ts7's clean-signal behavior IS known, and it is "publishes, not
49
- * silent". The snapshot's `launchVariant` marker again routes a native-ts7
50
- * snapshot through "waits" while the shared `silentOnClean` flag stays
51
- * `true` for classic. `scripts/probe-clean-signal.mjs`'s drift check no
55
+ * silent". The shared classifier still routes a native-ts7 snapshot through
56
+ * `waits`. The cascade-only wrapper routes it through `collect-later` because
57
+ * the measured publication arrives after the in-lane budget. The shared
58
+ * `silentOnClean` flag stays `true` for classic.
59
+ * `scripts/probe-clean-signal.mjs`'s drift check no
52
60
  * longer compares native-ts7 rows against the shared marker (it now expects
53
61
  * `false` for them explicitly) — see that file's header for the regression
54
62
  * watch this sets up for a future TS7 build that becomes silent again.
55
63
  */
56
64
  import { logCascade } from "../cascade-logger.js";
65
+ import { incrementDegradationCount } from "../degradation-ledger.js";
57
66
  import { logLatency } from "../latency-logger.js";
58
67
  import { normalizeMapKey } from "../path-utils.js";
59
68
  import { registerQuietWindowTask } from "../quiet-window.js";
60
- export { classifyCascadeWaitTier, classifyServerWaitTier, } from "./wait-policy/classification.js";
69
+ import { classifyCascadeWaitTier as classifySharedCascadeWaitTier, classifyServerWaitTier, resolvePrimaryServerForWaitPolicy, } from "./wait-policy/classification.js";
70
+ export { classifyServerWaitTier };
71
+ /**
72
+ * The cascade lane's wait tier for `filePath`. DELEGATES to the shared
73
+ * `wait-policy/classification.ts` rule — this wrapper adds exactly one
74
+ * cascade-only override on top of it (native TS7's push-only snapshot →
75
+ * `collect-later`) and never re-implements the classification itself.
76
+ *
77
+ * #1444 coverage tradeoff: the no-wait touch this tier selects is
78
+ * `clientScope: "primary"`, and the tier itself is decided from the PRIMARY
79
+ * (non-auxiliary) server alone. That tradeoff already existed for
80
+ * `tier3-silent`; the override enlarges the population it applies to — every
81
+ * native-TS7 TypeScript neighbour now takes the no-wait path too, so an
82
+ * auxiliary server configured for those files no longer gets touched in-lane
83
+ * for them (its findings arrive via the next per-edit dispatch, as they
84
+ * already did for classic tier-3 files).
85
+ */
86
+ export function classifyCascadeWaitTier(lspService, filePath, snapshots) {
87
+ const primary = resolvePrimaryServerForWaitPolicy(filePath, snapshots);
88
+ if (primary?.serverId === "typescript" &&
89
+ primary.snapshot?.launchVariant === "native-ts7" &&
90
+ primary.snapshot.workspaceDiagnosticsSupport?.mode === "push-only") {
91
+ return "collect-later";
92
+ }
93
+ return classifySharedCascadeWaitTier(lspService, filePath, snapshots);
94
+ }
61
95
  // --- Kill switch (lazy, memoized — house style per clients/runtime-config.ts /
62
96
  // clients/quiet-window.ts's isQuietWindowEnabled) ---
63
97
  let _enabledCache;
@@ -78,20 +112,88 @@ export function _resetTierAwareCascadeEnabledForTests() {
78
112
  // replaces the earlier entry (only the most recent touch matters — an
79
113
  // older touch's diagnostics, if they ever arrive, are still a strict superset
80
114
  // concern the newer touch already re-supersedes via didOpen/didChange).
115
+ //
116
+ // #1899: the registry is drained in full by every reconcile sweep, but nothing
117
+ // bounded it BETWEEN sweeps. The sweep runs on pi's `agent_settled` quiet
118
+ // window, and a session can go a long time without one — dogfood logs show
119
+ // sweeps up to 52 minutes apart. So the two bounds below are the registry's
120
+ // own, independent of when a sweep arrives:
121
+ //
122
+ // - `MAX_OUTSTANDING_TOUCHES` caps the entry count. Entries are held in
123
+ // touch order (see `recordOutstandingCascadeTouch`), so eviction takes the
124
+ // oldest touch at O(1).
125
+ // - `OUTSTANDING_TOUCH_MAX_AGE_MS` caps how long a touch stays reconcilable.
126
+ // Past it, the touch is discarded rather than answered: re-injecting a
127
+ // neighbour error, or clearing a footer entry, from a touch fired that
128
+ // long ago reports a state the agent has already moved past.
129
+ //
130
+ // Both constants are sized from the 2026-08-09..20 dogfood window (cascade.log,
131
+ // 67 sweeps / 756 entries): the largest single sweep held 53 entries, and a
132
+ // 15-minute age bound retains 72 of the 80 outcomes that ever resolved (90%)
133
+ // while discarding the tail that resolved after ~26-52 minutes.
134
+ const MAX_OUTSTANDING_TOUCHES = 256;
135
+ const OUTSTANDING_TOUCH_MAX_AGE_MS = 15 * 60_000;
81
136
  const _outstandingTouches = new Map();
137
+ /** Entries dropped by the age bound since the last sweep reported. */
138
+ let _expiredSinceLastSweep = 0;
139
+ /** Entries dropped by the size cap since the last sweep reported. */
140
+ let _evictedSinceLastSweep = 0;
141
+ /**
142
+ * Drop every entry older than `OUTSTANDING_TOUCH_MAX_AGE_MS`. The map is held
143
+ * in touch order, so the expired entries are a prefix and the walk stops at
144
+ * the first live one.
145
+ */
146
+ function pruneExpiredOutstandingTouches(now) {
147
+ for (const [key, touch] of _outstandingTouches) {
148
+ if (now - touch.touchedAt <= OUTSTANDING_TOUCH_MAX_AGE_MS)
149
+ break;
150
+ _outstandingTouches.delete(key);
151
+ _expiredSinceLastSweep++;
152
+ }
153
+ }
82
154
  /**
83
155
  * Record a Tier-3 cascade touch that skipped its in-lane wait. Called right
84
156
  * after the (still-performed) didOpen/didChange notify, before returning
85
157
  * without waiting. `touchedAt` must be sampled BEFORE the notify (see the
86
158
  * field doc) so the reconcile comparison can never misread a publish that
87
159
  * raced the record as pre-touch.
160
+ *
161
+ * #1899: also enforces the registry's two bounds. The `delete` before the
162
+ * `set` is load-bearing — `Map.set` on an existing key keeps the key's
163
+ * ORIGINAL insertion position, so without it a re-touched file would keep a
164
+ * stale position and both the age prefix walk and the oldest-first eviction
165
+ * would pick the wrong entry.
88
166
  */
89
167
  export function recordOutstandingCascadeTouch(entry) {
90
- _outstandingTouches.set(normalizeMapKey(entry.filePath), entry);
168
+ const key = normalizeMapKey(entry.filePath);
169
+ _outstandingTouches.delete(key);
170
+ // `Date.now()`, not `entry.touchedAt`: the caller samples `touchedAt` before
171
+ // its notify, so it trails real time, and the age bound is a statement about
172
+ // the clock rather than about the newest touch's own stamp.
173
+ pruneExpiredOutstandingTouches(Date.now());
174
+ _outstandingTouches.set(key, entry);
175
+ while (_outstandingTouches.size > MAX_OUTSTANDING_TOUCHES) {
176
+ const oldest = _outstandingTouches.keys().next();
177
+ if (oldest.done)
178
+ break;
179
+ _outstandingTouches.delete(oldest.value);
180
+ _evictedSinceLastSweep++;
181
+ // Bounded by construction: one ledger tally per session, not per
182
+ // eviction event. The subject is the cap itself rather than the evicted
183
+ // path — the discriminating fact is "this session overflowed the tier-3
184
+ // backlog", and per-path subjects would unbound the ledger group.
185
+ incrementDegradationCount({
186
+ kind: "cascade-tier3-backlog-evicted",
187
+ subject: `cap=${MAX_OUTSTANDING_TOUCHES}`,
188
+ reason: "tier-3 outstanding-touch registry hit its cap before a quiet-window reconcile drained it; the oldest touch was dropped unanswered",
189
+ });
190
+ }
91
191
  }
92
192
  /** Test-only: clear the outstanding-touch registry between test cases. */
93
193
  export function _resetOutstandingCascadeTouchesForTests() {
94
194
  _outstandingTouches.clear();
195
+ _expiredSinceLastSweep = 0;
196
+ _evictedSinceLastSweep = 0;
95
197
  }
96
198
  /** Test-only: peek at the registry without mutating it. */
97
199
  export function _getOutstandingCascadeTouchesForTests() {
@@ -122,6 +224,10 @@ export function _getOutstandingCascadeTouchesForTests() {
122
224
  */
123
225
  export async function reconcileOutstandingCascadeTouches(lspService) {
124
226
  const outcomes = [];
227
+ // #1899: the age bound applies at sweep time too, not only at record time —
228
+ // a registry that received no further touches would otherwise hand the sweep
229
+ // entries of unbounded age.
230
+ pruneExpiredOutstandingTouches(Date.now());
125
231
  const entries = [..._outstandingTouches.entries()];
126
232
  _outstandingTouches.clear();
127
233
  for (const [key, touch] of entries) {
@@ -133,6 +239,7 @@ export async function reconcileOutstandingCascadeTouches(lspService) {
133
239
  filePath: touch.filePath,
134
240
  serverId: touch.serverId,
135
241
  outcome: "unresolved",
242
+ unresolvedReason: spawned ? "server-mismatch" : "warm-miss",
136
243
  ageMs,
137
244
  });
138
245
  continue;
@@ -147,6 +254,7 @@ export async function reconcileOutstandingCascadeTouches(lspService) {
147
254
  filePath: touch.filePath,
148
255
  serverId: touch.serverId,
149
256
  outcome: "unresolved",
257
+ unresolvedReason: entry ? "no-publish-since-touch" : "no-publish",
150
258
  ageMs,
151
259
  });
152
260
  continue;
@@ -158,6 +266,7 @@ export async function reconcileOutstandingCascadeTouches(lspService) {
158
266
  outcome: found ? "resolved-found" : "resolved-clean",
159
267
  ageMs,
160
268
  diagnosticCount: entry.diags.length,
269
+ publishedAt: entry.ts,
161
270
  // #1023: carry the diagnostics so the task can re-surface them.
162
271
  ...(found && { diagnostics: entry.diags }),
163
272
  });
@@ -167,6 +276,7 @@ export async function reconcileOutstandingCascadeTouches(lspService) {
167
276
  filePath: touch.filePath,
168
277
  serverId: touch.serverId,
169
278
  outcome: "unresolved",
279
+ unresolvedReason: "error",
170
280
  ageMs,
171
281
  });
172
282
  logLatency({
@@ -193,23 +303,30 @@ export function registerCascadeTierReconcileTask(getLspService, options = {}) {
193
303
  registerQuietWindowTask("cascade_tier3_reconcile", async () => {
194
304
  if (!isTierAwareCascadeEnabled())
195
305
  return;
196
- if (_outstandingTouches.size === 0)
197
- return;
198
306
  const outcomes = await reconcileOutstandingCascadeTouches(getLspService());
199
- if (outcomes.length === 0)
200
- return;
201
307
  // #1023: re-inject each resolved-found neighbor error so it reaches the
202
- // agent (previously logs-only). Isolated per-outcome — a throwing callback
203
- // must not drop the log line or the sibling re-injections.
308
+ // agent (previously logs-only). #1444: hand each resolved-CLEAN outcome to
309
+ // the footer reconcile for the mirror-image case. Isolated per-outcome — a
310
+ // throwing callback must not drop the log line or the sibling deliveries.
204
311
  for (const o of outcomes) {
205
- if (o.outcome !== "resolved-found" || !o.diagnostics?.length)
206
- continue;
207
312
  try {
208
- options.onResolvedFound?.({
209
- filePath: o.filePath,
210
- serverId: o.serverId,
211
- diagnostics: o.diagnostics,
212
- });
313
+ if (o.outcome === "resolved-found" && o.diagnostics?.length) {
314
+ options.onResolvedFound?.({
315
+ filePath: o.filePath,
316
+ serverId: o.serverId,
317
+ diagnostics: o.diagnostics,
318
+ });
319
+ }
320
+ else if (o.outcome === "resolved-clean" && o.publishedAt != null) {
321
+ // #1444: the stale-footer half of the same honesty problem — the
322
+ // neighbour proved clean, but only after the in-lane wait was
323
+ // skipped, so nothing has cleared its earlier error entries.
324
+ options.onResolvedClean?.({
325
+ filePath: o.filePath,
326
+ serverId: o.serverId,
327
+ publishedAt: o.publishedAt,
328
+ });
329
+ }
213
330
  }
214
331
  catch {
215
332
  // best-effort surfacing; the log below is the durable record.
@@ -219,16 +336,31 @@ export function registerCascadeTierReconcileTask(getLspService, options = {}) {
219
336
  let resolvedClean = 0;
220
337
  let unresolved = 0;
221
338
  let ageSumMs = 0;
339
+ let maxAgeMs = 0;
340
+ const unresolvedByReason = {};
222
341
  for (const o of outcomes) {
223
342
  if (o.outcome === "resolved-found")
224
343
  resolvedFound++;
225
344
  else if (o.outcome === "resolved-clean")
226
345
  resolvedClean++;
227
- else
346
+ else {
228
347
  unresolved++;
348
+ const reason = o.unresolvedReason ?? "no-publish";
349
+ unresolvedByReason[reason] = (unresolvedByReason[reason] ?? 0) + 1;
350
+ }
229
351
  ageSumMs += o.ageMs;
352
+ if (o.ageMs > maxAgeMs)
353
+ maxAgeMs = o.ageMs;
230
354
  }
231
- const avgAgeMs = Math.round(ageSumMs / outcomes.length);
355
+ const avgAgeMs = outcomes.length
356
+ ? Math.round(ageSumMs / outcomes.length)
357
+ : 0;
358
+ // #1899: emitted on EVERY sweep, including the empty one. The backlog's
359
+ // size was previously observable only when it was non-empty, so "the
360
+ // queue is drained" and "the sweep never ran" read identically in
361
+ // cascade.log. This record is the backlog gauge: what the sweep drained,
362
+ // what the two bounds dropped since the last sweep, and what the bounds
363
+ // are, so a reader never has to know the constants to judge the numbers.
232
364
  logCascade({
233
365
  phase: "cascade_tier3_reconcile",
234
366
  filePath: "<quiet-window>",
@@ -237,10 +369,18 @@ export function registerCascadeTierReconcileTask(getLspService, options = {}) {
237
369
  resolvedFound,
238
370
  resolvedClean,
239
371
  unresolved,
372
+ unresolvedByReason,
240
373
  avgAgeMs,
374
+ maxAgeMs,
375
+ expired: _expiredSinceLastSweep,
376
+ evicted: _evictedSinceLastSweep,
377
+ capacity: MAX_OUTSTANDING_TOUCHES,
378
+ maxAgeCapMs: OUTSTANDING_TOUCH_MAX_AGE_MS,
241
379
  outcomes,
242
380
  },
243
381
  });
382
+ _expiredSinceLastSweep = 0;
383
+ _evictedSinceLastSweep = 0;
244
384
  });
245
385
  }
246
386
  /** Test-only: undo registerCascadeTierReconcileTask's idempotency guard. */