@herbertgao/pi-extensions 2026.8.6 → 2026.8.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (385) hide show
  1. package/README.md +8 -6
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -2
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +320 -22
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +79 -17
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +27 -0
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +66 -17
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
  15. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  16. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
  17. package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
  18. package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
  19. package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
  20. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
  21. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
  22. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +17 -13
  23. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
  24. package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
  25. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
  26. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
  27. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
  28. package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
  29. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
  30. package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
  31. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +21 -6
  32. package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
  33. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +20 -0
  34. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +5 -4
  35. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +174 -100
  36. package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
  37. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
  38. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +5 -1
  39. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +10 -2
  40. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +10 -6
  41. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/tool-schema.md +11 -5
  42. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +5 -2
  43. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
  44. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
  45. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
  46. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +140 -97
  47. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +63 -28
  48. package/node_modules/@juicesharp/rpiv-ask-user-question/state/selectors/projections.ts +22 -15
  49. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state-reducer.ts +11 -1
  50. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
  51. package/node_modules/@juicesharp/rpiv-ask-user-question/tool/response-envelope.ts +11 -0
  52. package/node_modules/@juicesharp/rpiv-ask-user-question/tool/types.ts +11 -0
  53. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/multi-select-view.ts +31 -17
  54. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/preview/preview-block-renderer.ts +15 -6
  55. package/node_modules/@juicesharp/rpiv-ask-user-question/view/components/wrapping-select.ts +15 -6
  56. package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +86 -38
  57. package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +76 -9
  58. package/node_modules/@narumitw/pi-btw/README.md +120 -111
  59. package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
  60. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
  61. package/node_modules/@narumitw/pi-btw/package.json +10 -6
  62. package/node_modules/@narumitw/pi-btw/src/btw.ts +36 -40
  63. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
  64. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
  65. package/node_modules/@narumitw/pi-btw/src/menu.ts +45 -2
  66. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
  67. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +44 -1856
  68. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -1
  69. package/node_modules/@pi-plugins/fast-mode/package.json +4 -6
  70. package/node_modules/pi-lens/CHANGELOG.md +999 -0
  71. package/node_modules/pi-lens/README.md +117 -3
  72. package/node_modules/pi-lens/config/ruff/core.toml +42 -0
  73. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
  74. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +126 -13
  75. package/node_modules/pi-lens/dist/clients/agent-nudge.js +67 -11
  76. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
  77. package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
  78. package/node_modules/pi-lens/dist/clients/bash-file-access.js +381 -13
  79. package/node_modules/pi-lens/dist/clients/biome-client.js +61 -30
  80. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
  81. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +129 -0
  82. package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
  83. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
  84. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +206 -0
  85. package/node_modules/pi-lens/dist/clients/cache-observability.js +353 -76
  86. package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
  87. package/node_modules/pi-lens/dist/clients/cascade-format.js +20 -1
  88. package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
  89. package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
  90. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
  91. package/node_modules/pi-lens/dist/clients/dead-code-client.js +123 -31
  92. package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
  93. package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
  94. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +84 -14
  95. package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +155 -0
  96. package/node_modules/pi-lens/dist/clients/dependency-checker.js +25 -3
  97. package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +8 -2
  98. package/node_modules/pi-lens/dist/clients/deps/typebox.js +8 -3
  99. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +147 -33
  100. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
  101. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +52 -2
  102. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +256 -88
  103. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
  104. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +9 -6
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +9 -7
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +8 -6
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +60 -53
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -2
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +25 -2
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +20 -5
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +44 -11
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +17 -8
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +32 -4
  129. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +47 -3
  130. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +14 -4
  131. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +127 -28
  132. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +12 -7
  133. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
  134. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +275 -13
  135. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +21 -6
  136. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
  137. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +734 -64
  138. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
  139. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +188 -0
  140. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +9 -0
  141. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +21 -10
  142. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -2
  143. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
  144. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
  145. package/node_modules/pi-lens/dist/clients/extension-log.js +22 -1
  146. package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
  147. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +316 -0
  148. package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
  149. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
  150. package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
  151. package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
  152. package/node_modules/pi-lens/dist/clients/git-guard.js +31 -0
  153. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
  154. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +109 -9
  155. package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
  156. package/node_modules/pi-lens/dist/clients/host-ports.js +8 -0
  157. package/node_modules/pi-lens/dist/clients/installer/index.js +1114 -100
  158. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
  159. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
  160. package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
  161. package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
  162. package/node_modules/pi-lens/dist/clients/jscpd-client.js +87 -8
  163. package/node_modules/pi-lens/dist/clients/knip-client.js +310 -10
  164. package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
  165. package/node_modules/pi-lens/dist/clients/language-profile.js +9 -2
  166. package/node_modules/pi-lens/dist/clients/latency-logger.js +440 -11
  167. package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
  168. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
  169. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
  170. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +128 -8
  171. package/node_modules/pi-lens/dist/clients/lsp/client.js +1660 -164
  172. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +83 -1
  173. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
  174. package/node_modules/pi-lens/dist/clients/lsp/index.js +2423 -151
  175. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
  176. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
  177. package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
  178. package/node_modules/pi-lens/dist/clients/lsp/server.js +277 -16
  179. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
  180. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
  181. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +79 -3
  182. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +43 -0
  183. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
  184. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
  185. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
  186. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
  187. package/node_modules/pi-lens/dist/clients/mcp/session.js +10 -2
  188. package/node_modules/pi-lens/dist/clients/module-report.js +22 -1
  189. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +114 -23
  190. package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
  191. package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
  192. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
  193. package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
  194. package/node_modules/pi-lens/dist/clients/pipeline.js +154 -33
  195. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
  196. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
  197. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
  198. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +84 -14
  199. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
  200. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
  201. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +23 -9
  202. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  203. package/node_modules/pi-lens/dist/clients/project-report.js +62 -27
  204. package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
  205. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
  206. package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
  207. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +8 -3
  208. package/node_modules/pi-lens/dist/clients/read-guard.js +253 -10
  209. package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
  210. package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
  211. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +194 -31
  212. package/node_modules/pi-lens/dist/clients/review-graph/query.js +52 -4
  213. package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +21 -0
  214. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +6 -3
  215. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +96 -7
  216. package/node_modules/pi-lens/dist/clients/runtime-context.js +8 -1
  217. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +352 -45
  218. package/node_modules/pi-lens/dist/clients/runtime-session.js +373 -9
  219. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
  220. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +212 -79
  221. package/node_modules/pi-lens/dist/clients/runtime-turn.js +620 -67
  222. package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
  223. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
  224. package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
  225. package/node_modules/pi-lens/dist/clients/security-scan-client.js +157 -24
  226. package/node_modules/pi-lens/dist/clients/session-event-guard.js +184 -0
  227. package/node_modules/pi-lens/dist/clients/sg-runner.js +116 -22
  228. package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
  229. package/node_modules/pi-lens/dist/clients/source-filter.js +6 -2
  230. package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
  231. package/node_modules/pi-lens/dist/clients/startup-scan.js +5 -2
  232. package/node_modules/pi-lens/dist/clients/startup-timing.js +19 -1
  233. package/node_modules/pi-lens/dist/clients/test-runner-client.js +151 -16
  234. package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
  235. package/node_modules/pi-lens/dist/clients/tool-policy.js +125 -20
  236. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
  237. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
  238. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +1 -0
  239. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
  240. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
  241. package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
  242. package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
  243. package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
  244. package/node_modules/pi-lens/dist/index.js +61407 -48577
  245. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +476 -57
  246. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +132 -43
  247. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
  248. package/node_modules/pi-lens/docs/agent-guide.md +6 -1
  249. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +515 -506
  250. package/node_modules/pi-lens/docs/dispositions.md +21 -0
  251. package/node_modules/pi-lens/docs/features.md +19 -5
  252. package/node_modules/pi-lens/docs/globalconfig.md +3 -0
  253. package/node_modules/pi-lens/docs/language-coverage.md +71 -5
  254. package/node_modules/pi-lens/docs/mcp.md +2 -2
  255. package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
  256. package/node_modules/pi-lens/docs/settings.md +19 -1
  257. package/node_modules/pi-lens/docs/usage.md +1 -0
  258. package/node_modules/pi-lens/package.json +27 -10
  259. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
  260. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
  261. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
  262. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
  263. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +15 -1
  264. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
  265. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
  266. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
  267. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
  268. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
  269. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
  270. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +2 -0
  271. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +2 -0
  272. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +2 -1
  273. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +2 -1
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +57 -12
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +22 -2
  300. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +23 -2
  301. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +33 -5
  302. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +34 -8
  303. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
  304. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
  305. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
  306. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
  307. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
  308. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
  309. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +6 -1
  310. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
  311. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
  312. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
  313. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
  314. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
  315. package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
  316. package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
  317. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
  318. package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
  319. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +53 -1
  320. package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
  321. package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
  322. package/node_modules/pi-lens/scripts/install-selftest.mjs +99 -11
  323. package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +75 -0
  324. package/node_modules/pi-lens/scripts/lib/warm-loader-cache.mjs +285 -0
  325. package/node_modules/pi-lens/scripts/warm-loader-cache.mjs +233 -0
  326. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +2 -0
  327. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +80 -3
  328. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +41 -0
  329. package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
  330. package/node_modules/pi-mcp-adapter/CHANGELOG.md +36 -0
  331. package/node_modules/pi-mcp-adapter/README.md +47 -5
  332. package/node_modules/pi-mcp-adapter/cli.js +163 -1
  333. package/node_modules/pi-mcp-adapter/commands.ts +128 -22
  334. package/node_modules/pi-mcp-adapter/config.ts +25 -7
  335. package/node_modules/pi-mcp-adapter/direct-tools.ts +5 -4
  336. package/node_modules/pi-mcp-adapter/index.ts +219 -32
  337. package/node_modules/pi-mcp-adapter/init.ts +36 -4
  338. package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
  339. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
  340. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
  341. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
  342. package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
  343. package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
  344. package/node_modules/pi-mcp-adapter/metadata-cache.ts +32 -21
  345. package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
  346. package/node_modules/pi-mcp-adapter/package.json +3 -1
  347. package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
  348. package/node_modules/pi-mcp-adapter/request-headers-command.ts +31 -37
  349. package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
  350. package/node_modules/pi-mcp-adapter/server-manager.ts +140 -2
  351. package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
  352. package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
  353. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
  354. package/node_modules/pi-mcp-adapter/types.ts +2 -0
  355. package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
  356. package/node_modules/pi-mcp-adapter/utils.ts +70 -0
  357. package/node_modules/pi-web-access/CHANGELOG.md +37 -0
  358. package/node_modules/pi-web-access/README.md +32 -13
  359. package/node_modules/pi-web-access/anysearch.ts +4 -2
  360. package/node_modules/pi-web-access/brave.ts +16 -4
  361. package/node_modules/pi-web-access/chrome-cookies.ts +86 -15
  362. package/node_modules/pi-web-access/curator-page.ts +6 -3
  363. package/node_modules/pi-web-access/curator-server.ts +4 -1
  364. package/node_modules/pi-web-access/data-uri-sanitize.ts +406 -0
  365. package/node_modules/pi-web-access/exa.ts +18 -7
  366. package/node_modules/pi-web-access/extract.ts +40 -5
  367. package/node_modules/pi-web-access/firecrawl.ts +18 -2
  368. package/node_modules/pi-web-access/gemini-search.ts +16 -4
  369. package/node_modules/pi-web-access/github-extract.ts +65 -36
  370. package/node_modules/pi-web-access/index.ts +60 -61
  371. package/node_modules/pi-web-access/openai-search.ts +17 -6
  372. package/node_modules/pi-web-access/package.json +1 -1
  373. package/node_modules/pi-web-access/page-query.ts +4 -2
  374. package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
  375. package/node_modules/pi-web-access/parallel.ts +2 -2
  376. package/node_modules/pi-web-access/pdf-extract.ts +18 -5
  377. package/node_modules/pi-web-access/query-rewrite.ts +51 -0
  378. package/node_modules/pi-web-access/serper.ts +211 -0
  379. package/node_modules/pi-web-access/ssrf-protection.ts +12 -2
  380. package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
  381. package/node_modules/pi-web-access/summary-review.ts +44 -22
  382. package/node_modules/pi-web-access/tavily.ts +16 -4
  383. package/node_modules/pi-web-access/utils.ts +87 -0
  384. package/node_modules/pi-web-access/valyu.ts +199 -0
  385. package/package.json +14 -14
@@ -1,10 +1,14 @@
1
1
  import * as nodeFs from "node:fs";
2
2
  import * as path from "node:path";
3
+ import { resetBoundedTelemetry } from "./bounded-telemetry.js";
3
4
  import { createDeadline, yieldIfOverBudget } from "./cooperative-budget.js";
4
5
  import { deadCodeIssueCount } from "./dead-code-client.js";
5
6
  import { logDeadCodeScan } from "./dead-code-logger.js";
6
- import { resetDegradationLedger } from "./degradation-ledger.js";
7
+ import { incrementDegradationCount, resetDegradationLedger, } from "./degradation-ledger.js";
7
8
  import { getDiagnosticTracker } from "./diagnostic-tracker.js";
9
+ import { resetPsScriptAnalyzerAvailability } from "./dispatch/runners/psscriptanalyzer.js";
10
+ import { resetInstallRetryLatches } from "./dispatch/runners/utils/availability-policy.js";
11
+ import { resetLazyInstallAttempts } from "./dispatch/runners/utils/lazy-installer.js";
8
12
  import { resetDispatchAvailabilityState } from "./dispatch/runners/utils/runner-helpers.js";
9
13
  import { clearAllSessions as clearFileTimeSessions } from "./file-time.js";
10
14
  import { getGlobalPiLensDir, getKnipIgnorePatterns, getProjectDataDir, } from "./file-utils.js";
@@ -15,12 +19,19 @@ import { canRunStartupHeavyScans } from "./language-policy.js";
15
19
  import { detectProjectLanguageProfile, getDefaultStartupTools, } from "./language-profile.js";
16
20
  import { logLatency } from "./latency-logger.js";
17
21
  import { runLogCleanup } from "./log-cleanup.js";
22
+ import { resetCascadeTierSessionState } from "./lsp/cascade-tier.js";
18
23
  import { initLSPConfig, loadLSPConfig } from "./lsp/config.js";
24
+ import { resetWorkspaceDiagnosticsCacheSession } from "./lsp/workspace-diagnostics-session.js";
25
+ import { resetDirectLspCommandAvailability } from "./lsp/server.js";
19
26
  import { loadLspService } from "./lsp-lazy.js";
27
+ import { resetManagedToolRefreshSession } from "./installer/managed-tool-refresh-session.js";
28
+ import { resetResolvedPathCache } from "./installer/index.js";
29
+ import { _resetPackageManagerCache } from "./package-manager.js";
30
+ import { clearFormatterCache } from "./formatters.js";
20
31
  import { isAtOrAboveHomeDir } from "./path-utils.js";
21
32
  import { isPrintMode } from "./print-mode.js";
22
33
  import { readLatestProjectSequence, readLatestProjectSequenceAsync, } from "./project-changes.js";
23
- import { getProjectSnapshotPath, hydrateRuntimeFromProjectSnapshot, isProjectSnapshotFresh, isProjectSnapshotMetaStale, loadProjectSnapshot, PROJECT_SNAPSHOT_VERSION, readProjectSnapshotMeta, saveRuntimeProjectSnapshot, } from "./project-snapshot.js";
34
+ import { getProjectSnapshotPath, getProjectSnapshotLegacyPath, hydrateRuntimeFromProjectSnapshot, hydrateRuntimeFromProjectSnapshotIfIdle, isProjectSnapshotFresh, isProjectSnapshotMetaStale, loadProjectSnapshot, loadProjectSnapshotExportsAndRules, PROJECT_SNAPSHOT_VERSION, readProjectSnapshotMeta, saveRuntimeProjectSnapshot, } from "./project-snapshot.js";
24
35
  import { clearTsconfigPathsCache } from "./review-graph/tsconfig-paths.js";
25
36
  import { scanProjectRules } from "./rules-scanner.js";
26
37
  import { resetSafeSpawnWindowsCommandCache } from "./safe-spawn.js";
@@ -33,6 +44,27 @@ import { isWarmAttached } from "./warm-attach.js";
33
44
  import { setSessionLanguages } from "./widget-state.js";
34
45
  import { logWordIndex } from "./word-index-logger.js";
35
46
  import { resetWorkspaceTopology } from "./workspace-topology.js";
47
+ import { resetZizmorTokenAvailability } from "./zizmor-config.js";
48
+ const HOST_STALL_THRESHOLD_MS = 30_000;
49
+ function logHostReadyDelay(deps, cwd) {
50
+ if (!deps.emitHostReadyDelay ||
51
+ deps.sessionStartMonotonicAt === undefined ||
52
+ deps.extensionLoadedAt === undefined) {
53
+ return;
54
+ }
55
+ const durationMs = Math.max(0, deps.sessionStartMonotonicAt - deps.extensionLoadedAt);
56
+ logLatency({
57
+ type: "phase",
58
+ filePath: cwd,
59
+ phase: "host_ready_delay",
60
+ durationMs,
61
+ metadata: {
62
+ hostStallSuspected: durationMs > HOST_STALL_THRESHOLD_MS,
63
+ sessionStart: "first-process-session",
64
+ reason: deps.sessionReason,
65
+ },
66
+ });
67
+ }
36
68
  function resolveSnapshotRoot(cwd) {
37
69
  const resolvedCwd = path.resolve(cwd);
38
70
  const nearest = findNearestProjectRoot(resolvedCwd);
@@ -79,6 +111,91 @@ function snapshotSequenceBase(root) {
79
111
  sinceSeq: meta.seq,
80
112
  };
81
113
  }
114
+ /**
115
+ * #1785: one bounded ledger record per timed-out sequence read, so a real
116
+ * occurrence is diagnosable from the ledger alone rather than only from a
117
+ * `dbg` line that most hosts never surface. Carries the project root and the
118
+ * on-disk snapshot's age/size (when one exists) — the identity AC1 asks for:
119
+ * which project, how stale/large the snapshot that got skipped was.
120
+ */
121
+ function recordSnapshotSequenceTimeout(args) {
122
+ let reason = "sequence read timed out; no snapshot on disk";
123
+ try {
124
+ const stat = nodeFs.statSync(args.snapshotPath);
125
+ reason = `sequence read timed out; snapshot age=${Math.round(Date.now() - stat.mtimeMs)}ms size=${stat.size}b`;
126
+ }
127
+ catch {
128
+ // No snapshot on disk yet — the reason above already covers it.
129
+ }
130
+ incrementDegradationCount({
131
+ kind: "snapshot-sequence-read-timeout",
132
+ subject: args.snapshotRoot,
133
+ reason,
134
+ });
135
+ }
136
+ /**
137
+ * #1785: the synchronous freshness check made a conservative call with the
138
+ * `UNKNOWN_PROJECT_SEQ` sentinel (never hydrate when the real sequence is
139
+ * unknown). Once the deferred read resolves — a deterministic completion
140
+ * signal, not a sleep — this re-checks freshness against the now-known
141
+ * sequence and hydrates late when the snapshot really was current, instead of
142
+ * leaving the runtime without `cachedExports`/`projectRulesScan` for the rest
143
+ * of the session.
144
+ *
145
+ * #1785 review round F5 (design (a), replacing rounds 1-3's synchronous
146
+ * capture entirely): quick mode's own cold-start warmup ALREADY loads the
147
+ * on-disk snapshot for its own purposes — `cachedSnapshot` at the top of the
148
+ * warmup body, used to reuse a still-fresh `startupScan` verdict — strictly
149
+ * BEFORE the warmup's own possible save (`saveRuntimeProjectSnapshot`, only
150
+ * reached when that verdict was NOT fresh). Reusing THAT already-loaded
151
+ * value costs nothing new: the read was always going to happen regardless of
152
+ * this fix, and it is guaranteed to predate any overwrite the SAME warmup
153
+ * invocation might go on to make — a read-before-write invariant of the
154
+ * warmup's own code, not something this function has to defend against with
155
+ * a race window of its own. `getWarmupOwnSnapshotRead` is a closure over a
156
+ * per-`handleSessionStart`-call LOCAL variable (never module-scope — #1785
157
+ * F6 is exactly the class of bug a module-scope holder here would
158
+ * reintroduce) that the warmup publishes into once its own read completes.
159
+ *
160
+ * When the getter still returns `undefined` — the deferred read resolved
161
+ * before the warmup got that far (or no warmup was ever armed by this call:
162
+ * a later quick-mode call in the same process, print mode, full mode) —
163
+ * there is nothing published yet, so this falls back to a live re-load via
164
+ * `loadProjectSnapshotExportsAndRules`. That fallback is exactly as safe as
165
+ * the primary path: if the warmup hasn't reached its own read yet, it
166
+ * certainly hasn't saved yet either (same invariant), so disk still holds
167
+ * whatever was there before any warmup activity.
168
+ *
169
+ * Both the published value and the fallback re-load are the narrow,
170
+ * postings-free `ProjectSnapshotExportsAndRules` shape — never the full
171
+ * `loadProjectSnapshot`. This path only ever hydrates
172
+ * `cachedExports`/`projectRulesScan`; it can NOT hydrate `wordIndex` even
173
+ * when the underlying snapshot would otherwise have one, because the narrow
174
+ * type never carries it. That is intentional, not a regression: F2's hazard
175
+ * was this exact path nulling a `wordIndex` that quick mode's warmup had, in
176
+ * the meantime, built for real — the warmup remains the sole source of a
177
+ * late-arriving `wordIndex` for the interactive path.
178
+ */
179
+ function retroactivelyHydrateAfterDeferredSequence(args) {
180
+ return (latestSeq) => {
181
+ const published = args.getWarmupOwnSnapshotRead();
182
+ const snapshot = published !== undefined
183
+ ? published
184
+ : loadProjectSnapshotExportsAndRules(args.snapshotRoot);
185
+ if (!snapshot ||
186
+ snapshot.version !== PROJECT_SNAPSHOT_VERSION ||
187
+ snapshot.seq !== latestSeq.projectSeq) {
188
+ return;
189
+ }
190
+ const hydrated = hydrateRuntimeFromProjectSnapshotIfIdle(args.runtime, snapshot);
191
+ if (hydrated) {
192
+ args.dbg(`session_start: deferred sequence read confirmed snapshot freshness — hydrating cachedExports/projectRulesScan late (seq=${snapshot.seq})`);
193
+ }
194
+ else {
195
+ args.dbg(`session_start: deferred sequence read confirmed snapshot freshness (seq=${snapshot.seq}), but the runtime already has live state (warmup or another task got there first) — skipping late hydration to avoid clobbering it`);
196
+ }
197
+ };
198
+ }
82
199
  function loadSnapshotBodyUnlessStale(args) {
83
200
  const meta = readProjectSnapshotMeta(args.root);
84
201
  if (meta && isProjectSnapshotMetaStale(meta, args.currentProjectSeq)) {
@@ -90,12 +207,17 @@ function loadSnapshotBodyUnlessStale(args) {
90
207
  skippedStale: false,
91
208
  };
92
209
  }
93
- function describeSnapshotMiss(snapshot, currentProjectSeq) {
210
+ function describeSnapshotMiss(snapshot, currentProjectSeq, args) {
211
+ if (args.skippedStale)
212
+ return "stale-meta-gate";
94
213
  if (!snapshot)
95
- return "missing";
214
+ return args.bodyPresent ? "invalid-body" : "missing";
96
215
  if (snapshot.seq !== currentProjectSeq) {
97
216
  return `stale(seq=${snapshot.seq}, current=${currentProjectSeq})`;
98
217
  }
218
+ // Defensive-only arm: parseSnapshot rejects incompatible versions, and a
219
+ // same-sequence parsed snapshot is fresh. Keep this classification explicit
220
+ // so a future loader change cannot collapse it into a generic miss.
99
221
  return "incompatible";
100
222
  }
101
223
  function logProjectSnapshotProbe(args) {
@@ -104,7 +226,7 @@ function logProjectSnapshotProbe(args) {
104
226
  args.dbg(`project_snapshot: loaded seq=${args.snapshot.seq} exports=${args.snapshot.cachedExports.length} files=${Object.keys(args.snapshot.files ?? {}).length} reverseDeps=${Object.keys(args.snapshot.reverseDeps ?? {}).length} startupScan=${Boolean(args.snapshot.startupScan)} languageProfile=${Boolean(args.snapshot.languageProfile)}`);
105
227
  }
106
228
  else {
107
- args.dbg(`project_snapshot: miss reason=${describeSnapshotMiss(args.snapshot, args.currentProjectSeq)}`);
229
+ args.dbg(`project_snapshot: miss reason=${args.missReason}`);
108
230
  }
109
231
  }
110
232
  function resolveStartupMode() {
@@ -171,7 +293,7 @@ const UNKNOWN_PROJECT_SEQ = -1;
171
293
  * logs its own `session_start_sequence_read_deferred_reseed` phase.
172
294
  */
173
295
  async function readSequenceWithBudget(args) {
174
- const { snapshotRoot, base, cwd, runtime, sessionGeneration, dbg } = args;
296
+ const { snapshotRoot, base, cwd, runtime, sessionGeneration, dbg, onDeferredSequenceResolved, } = args;
175
297
  const readPromise = readLatestProjectSequenceAsync(snapshotRoot, base);
176
298
  let timeoutHandle;
177
299
  const timeoutPromise = new Promise((resolve) => {
@@ -224,6 +346,7 @@ async function readSequenceWithBudget(args) {
224
346
  metadata: { entries: latestSeq.fileSeqByPath.size, deferred: true },
225
347
  });
226
348
  dbg(`session_start: deferred sequence read completed — reseeded projectSeq=${latestSeq.projectSeq} fileSeqEntries=${latestSeq.fileSeqByPath.size}`);
349
+ onDeferredSequenceResolved?.(latestSeq);
227
350
  })
228
351
  .catch((err) => {
229
352
  dbg(`session_start: deferred sequence read failed: ${err}`);
@@ -375,6 +498,47 @@ function firePreinstallDefaults(ensureTool, dbg, startupDefaults) {
375
498
  });
376
499
  }
377
500
  }
501
+ /**
502
+ * Default delay before the managed-tool version refresh runs (#1730). Long
503
+ * enough that `session_start`, the startup scans, and the tool preinstalls have
504
+ * all settled — the refresh may spawn one `npm update`, and that must never
505
+ * compete with the work the user is waiting on.
506
+ */
507
+ const MANAGED_TOOL_REFRESH_DELAY_MS = 30_000;
508
+ /**
509
+ * Schedule the periodic managed-tool refresh (#1730) on an unref'd background
510
+ * timer.
511
+ *
512
+ * `session_start`'s own cost here is a single `setTimeout` registration: the
513
+ * refresh module is not even imported until the timer fires. The timer is
514
+ * unref'd so a one-shot process exits without waiting for it, matching the
515
+ * warmup timer above. Whether a refresh actually runs is decided inside
516
+ * `runManagedToolRefresh` against a PERSISTED per-tool stamp, so a launch loop
517
+ * cannot escalate the weekly cadence.
518
+ */
519
+ function scheduleManagedToolRefresh(dbg) {
520
+ const delayMs = Number(process.env.PI_LENS_TOOL_REFRESH_DELAY_MS ?? MANAGED_TOOL_REFRESH_DELAY_MS);
521
+ const timer = setTimeout(() => {
522
+ void (async () => {
523
+ try {
524
+ const refresh = await import("./installer/managed-tool-refresh.js");
525
+ const outcome = await refresh.runManagedToolRefresh();
526
+ if (outcome.skipped) {
527
+ dbg(`session_start tool-refresh: skipped (${outcome.skipped})`);
528
+ return;
529
+ }
530
+ for (const result of outcome.refreshed) {
531
+ dbg(`session_start tool-refresh: ${result.toolId} ${result.ok ? "ok" : "failed"}` +
532
+ `${result.changed ? ` ${result.previousVersion ?? "unknown"} → ${result.currentVersion}` : " (unchanged)"}`);
533
+ }
534
+ }
535
+ catch (err) {
536
+ dbg(`session_start tool-refresh: error ${err}`);
537
+ }
538
+ })();
539
+ }, Number.isFinite(delayMs) ? delayMs : MANAGED_TOOL_REFRESH_DELAY_MS);
540
+ timer.unref?.();
541
+ }
378
542
  async function probePrettierInstall(ensureTool, dbg, analysisRoot) {
379
543
  const pkgPath = path.join(analysisRoot, "package.json");
380
544
  try {
@@ -715,7 +879,7 @@ function scheduleStartupScans(deps, runtime, sessionGeneration, analysisRoot, sn
715
879
  // KnipClient skips before probing/installing when analysisRoot is not a real
716
880
  // JS/Knip project. This avoids running knip from Unity/C#/generic repos.
717
881
  const startMs = Date.now();
718
- const knipResult = await knipClient.analyze(analysisRoot, getKnipIgnorePatterns());
882
+ const knipResult = await knipClient.analyze(analysisRoot, getKnipIgnorePatterns(), { projectSeq: runtime.projectSeq });
719
883
  if (!runtime.isCurrentSession(sessionGeneration))
720
884
  return;
721
885
  if (knipResult.success) {
@@ -1121,6 +1285,12 @@ export const SESSION_START_GUIDANCE = [
1121
1285
  ];
1122
1286
  export async function handleSessionStart(deps) {
1123
1287
  resetDegradationLedger();
1288
+ // #1743: the bounded-telemetry per-turn counters. The rising-edge state is
1289
+ // NOT here — it is the ledger's own tally, reset on the line above. These
1290
+ // counters are keyed by turn index, and a new session restarts turn
1291
+ // numbering at 0, so without this a stale count could survive a session
1292
+ // boundary that happened to land on the same index.
1293
+ resetBoundedTelemetry();
1124
1294
  const handlerEnteredAt = Date.now();
1125
1295
  const sessionStartMs = deps.sessionStartFiredAt ?? handlerEnteredAt;
1126
1296
  const cwdForTelemetry = deps.ctxCwd ?? process.cwd();
@@ -1175,6 +1345,11 @@ export async function handleSessionStart(deps) {
1175
1345
  // (an explicit env var still takes highest precedence).
1176
1346
  // Tunable: PI_LENS_WARMUP_DELAY_MS adjusts the warmup delay.
1177
1347
  let startupMode = resolveStartupMode();
1348
+ // SAFETY: these two flags are process-lifetime state pi-lens stashes on
1349
+ // `globalThis` so a second extension instance in the same process sees the
1350
+ // first one's warmup. There is no ambient declaration for them, and adding
1351
+ // one would let any module write them. Both are declared OPTIONAL here, so
1352
+ // the reads below are `undefined`-safe on a process where nothing set them.
1178
1353
  const processGlobals = globalThis;
1179
1354
  const isFirstSessionOfProcess = !processGlobals.__piLensFirstSessionDone;
1180
1355
  if (isFirstSessionOfProcess &&
@@ -1185,6 +1360,20 @@ export async function handleSessionStart(deps) {
1185
1360
  startupMode = deps.startupModeOverride ?? "quick";
1186
1361
  }
1187
1362
  processGlobals.__piLensFirstSessionDone = true;
1363
+ // #1785 F5 (round 4, design (a)): the warmup timer body below ALREADY
1364
+ // loads the on-disk snapshot for its own purposes (`cachedSnapshot`,
1365
+ // reused for the `startupScan` verdict cache) strictly BEFORE any save it
1366
+ // might go on to make. This LOCAL variable — per-`handleSessionStart`-call,
1367
+ // deliberately NOT module-scope (see #1785 F6) — lets the warmup publish
1368
+ // that already-loaded read (narrowed to exports+rules, never the full
1369
+ // object with `wordIndex`) for `retroactivelyHydrateAfterDeferredSequence`
1370
+ // to reuse at zero additional cost, instead of this call paying for its
1371
+ // own separate synchronous capture (rounds 1-3's approach, reverted here:
1372
+ // see that function's doc comment for the full history). `undefined`
1373
+ // means the warmup hasn't reached its own read yet (or was never armed by
1374
+ // this call at all) — the quick-mode block's retroactive-hydration
1375
+ // callback then falls back to a live re-load instead.
1376
+ let warmupOwnSnapshotRead;
1188
1377
  // #1154: quick mode is entered on BOTH `pi -p`/`--print` (a one-shot that
1189
1378
  // exits right after this turn) and an interactive process's first
1190
1379
  // session_start (forced quick to protect keystroke latency, then warms
@@ -1232,6 +1421,23 @@ export async function handleSessionStart(deps) {
1232
1421
  // reuse it instead of re-walking a possibly huge tree from
1233
1422
  // scratch on every single startup.
1234
1423
  const cachedSnapshot = loadProjectSnapshot(warmupSnapshotRoot);
1424
+ // #1785 F5 (round 4): publish this ALREADY-loaded read — narrowed
1425
+ // to exports+rules, never a reference to `cachedSnapshot` itself
1426
+ // (which carries `wordIndex`/`files`/`symbols`/`reverseDeps`) — for
1427
+ // `retroactivelyHydrateAfterDeferredSequence` to reuse. This read
1428
+ // strictly precedes the warmup's own possible save a few lines
1429
+ // below, so it is guaranteed to reflect disk as it stood before
1430
+ // THIS warmup could have touched it. Zero marginal cost: this read
1431
+ // already happens unconditionally for the `startupScan` verdict
1432
+ // cache below, with or without this fix.
1433
+ warmupOwnSnapshotRead = cachedSnapshot
1434
+ ? {
1435
+ version: cachedSnapshot.version,
1436
+ seq: cachedSnapshot.seq,
1437
+ cachedExports: cachedSnapshot.cachedExports,
1438
+ projectRulesScan: cachedSnapshot.projectRulesScan,
1439
+ }
1440
+ : null;
1235
1441
  const cachedVerdict = cachedSnapshot?.startupScan;
1236
1442
  let scan;
1237
1443
  if (cachedVerdict &&
@@ -1381,7 +1587,7 @@ export async function handleSessionStart(deps) {
1381
1587
  }
1382
1588
  const allowBootstrapTasks = startupMode === "full";
1383
1589
  const quickMode = startupMode === "quick";
1384
- const { ctxCwd, getFlag, notify, dbg, log, runtime, metricsClient, cacheManager, testRunnerClient, goClient, rustClient, ensureTool, cleanStaleTsBuildInfo, resetDispatchBaselines, resetLSPService, } = deps;
1590
+ const { ctxCwd, getFlag, notify, dbg, log, runtime, metricsClient, knipClient, cacheManager, testRunnerClient, goClient, rustClient, ensureTool, cleanStaleTsBuildInfo, resetDispatchBaselines, resetLSPService, } = deps;
1385
1591
  // Lightweight phase timer — resets after each call so each log line shows
1386
1592
  // the cost of that phase alone, not cumulative time from session start.
1387
1593
  let _phaseT = Date.now();
@@ -1415,9 +1621,74 @@ export async function handleSessionStart(deps) {
1415
1621
  // the tool for the rest of the process lifetime. Clear it here, same
1416
1622
  // boundary as the other per-session caches on this line.
1417
1623
  resetDispatchAvailabilityState();
1624
+ // #1895: formatter PATH verdicts are session-scoped, but they live in
1625
+ // formatters.ts and are not covered by the dispatch generation. Re-arm them
1626
+ // here so a formatter installed or removed between sessions is observed by
1627
+ // the next session. `clearFormatterCache` drops the per-cwd selection cache
1628
+ // as well as the which latches, which is what a real re-probe needs: the
1629
+ // selection cache answers a same-cwd lookup before any probe runs, so
1630
+ // clearing the latches alone would leave the stale verdict standing in the
1631
+ // one directory the user is working in.
1632
+ clearFormatterCache();
1633
+ // #1535: same #1266 pattern, one caller earlier — the `gh auth token` latch
1634
+ // zizmor's spawn reads is process-lived storage whose durability contract is
1635
+ // per SESSION, not per process. Without this, a user who runs `gh auth
1636
+ // login` and starts a fresh session still reads the previous session's
1637
+ // stale "no token" verdict until the cooldown (if any) happens to expire.
1638
+ resetZizmorTokenAvailability();
1639
+ // psscriptanalyzer's three latches (interpreter, module, -File exec) are
1640
+ // module-local, so the generation counter above does not reach them
1641
+ // (#1490, #1540).
1642
+ resetPsScriptAnalyzerAvailability();
1643
+ // #1497: the install-class retry ceiling is terminal for a SESSION, but the
1644
+ // latches holding it live on process-lived client instances (bootstrap builds
1645
+ // them once). Same #1266/#1490/#1535 shape — without this line "terminal for
1646
+ // the session" is terminal for the process, and a repaired network never
1647
+ // re-earns its `go install`.
1648
+ resetInstallRetryLatches();
1649
+ // #1537: the lazy-install seam's hold (`gem install rubocop`, `rustup
1650
+ // component add`) is durable for a SESSION. Its map is module-local, so the
1651
+ // generation counter above does not reach it — same #1490/#1497 shape. It
1652
+ // belongs on THIS line and not on the turn_end path: the first cut cleared it
1653
+ // from `clearFormatterRuntimeState()`, which runs every turn, so a failing
1654
+ // install re-spawned every turn instead of once per session.
1655
+ resetLazyInstallAttempts();
1656
+ // #1897: direct-LSP negative availability and bare installer paths are
1657
+ // session facts. A command or PATH entry can appear between sessions.
1658
+ resetDirectLspCommandAvailability();
1659
+ resetResolvedPathCache();
1660
+ // #1653: pnpm/yarn/bun/npm's availability latches (package-manager.ts) are
1661
+ // module-local, same #1490/#1535 shape as the two lines above — the
1662
+ // generation counter above does not reach them. Without this line, a
1663
+ // pnpm/yarn/bun install done mid-day stayed invisible: a genuine "missing"
1664
+ // verdict from one session latched into the next until a process restart.
1665
+ _resetPackageManagerCache();
1666
+ // #1730: the managed-tool refresh budget is a per-SESSION allowance (at most
1667
+ // one `npm update` per session). Left process-lived, one long-running pi
1668
+ // would refresh a single tool at launch and never look at the other 21
1669
+ // again — the same latch shape as the two lines above. The weekly cadence
1670
+ // itself is NOT reset here: it lives in the persisted per-tool stamp, so
1671
+ // re-arming the budget only restores the session's right to ask.
1672
+ resetManagedToolRefreshSession();
1418
1673
  // #1123 item 3: a fresh session can re-report smells that a prior session
1419
1674
  // already surfaced once (see `checkSmellsAndNoteOnce`'s once-per-session gate).
1420
1675
  resetSmellsSessionState();
1676
+ // #1782: re-arm the workspace-diagnostics cache session clock. Entries
1677
+ // written before this instant assert findings from a session that is over,
1678
+ // so they must revalidate before they can be served as current again.
1679
+ resetWorkspaceDiagnosticsCacheSession(sessionStartMs);
1680
+ // Some embedders inject a capability-shaped Knip client rather than the
1681
+ // concrete KnipClient. Session reset is an optional lifecycle capability;
1682
+ // its absence must not make session_start fail.
1683
+ knipClient.resetSessionState?.();
1684
+ // #1910: the tier-3 cascade outstanding-touch registry and its
1685
+ // sweep-scoped expired/evicted counters (clients/lsp/cascade-tier.ts) are
1686
+ // a per-SESSION claim about touches THIS session fired. #1899 bounded the
1687
+ // registry between sweeps but, by its own review, left the session
1688
+ // boundary unwired — a session replacement inherited the prior session's
1689
+ // outstanding touches and misattributed a stray expiry/eviction to the
1690
+ // next session's first reconcile gauge.
1691
+ resetCascadeTierSessionState();
1421
1692
  runtime.resetForSession(sessionStartMs);
1422
1693
  logLatency({
1423
1694
  type: "phase",
@@ -1471,11 +1742,21 @@ export async function handleSessionStart(deps) {
1471
1742
  // project data roots and machine-global registry root once per session start;
1472
1743
  // this is fire-and-forget and bounded so it never delays startup.
1473
1744
  const projectDataDir = getProjectDataDir(cwd);
1745
+ // #1609 review F1: sweepOwnStagingFiles does not recurse, so the installer's
1746
+ // bin/ and tools/ subdirectories (clients/installer/index.ts's
1747
+ // GITHUB_BIN_DIR / TOOLS_DIR, now atomic-write.js writers too) need their
1748
+ // own entries — otherwise an orphaned staging file from a kill mid-install
1749
+ // (this PR's own motivating scenario) never gets reaped, and unique
1750
+ // pid-thread-seq staging names mean repeated kills ACCUMULATE full-size
1751
+ // orphan binaries instead of being cleaned up.
1752
+ const globalDir = getGlobalPiLensDir();
1474
1753
  void sweepAtomicWriteStages([
1475
1754
  projectDataDir,
1476
1755
  path.join(projectDataDir, "cache"),
1477
1756
  path.join(projectDataDir, "sessions"),
1478
- getGlobalPiLensDir(),
1757
+ globalDir,
1758
+ path.join(globalDir, "bin"),
1759
+ path.join(globalDir, "tools"),
1479
1760
  ]).catch(() => {
1480
1761
  // best-effort lifecycle cleanup — never fail session_start
1481
1762
  });
@@ -1483,6 +1764,12 @@ export async function handleSessionStart(deps) {
1483
1764
  runtime.projectRoot = cwd;
1484
1765
  const sequenceReadStartedAt = Date.now();
1485
1766
  const snapshotRoot = resolveSnapshotRoot(cwd);
1767
+ // #1785 F5 (round 4): the getter closes over `warmupOwnSnapshotRead`
1768
+ // directly (a `let` in this same `handleSessionStart` invocation's
1769
+ // scope), so it reads whatever value is current AT DEFERRED-RESOLVE
1770
+ // TIME — not whatever it was at this wiring instant. See
1771
+ // `retroactivelyHydrateAfterDeferredSequence`'s doc comment for the
1772
+ // full design.
1486
1773
  const { latestSeq, timedOut } = await readSequenceWithBudget({
1487
1774
  snapshotRoot,
1488
1775
  base: snapshotSequenceBase(snapshotRoot),
@@ -1490,6 +1777,12 @@ export async function handleSessionStart(deps) {
1490
1777
  runtime,
1491
1778
  sessionGeneration: runtime.sessionGeneration,
1492
1779
  dbg,
1780
+ onDeferredSequenceResolved: retroactivelyHydrateAfterDeferredSequence({
1781
+ getWarmupOwnSnapshotRead: () => warmupOwnSnapshotRead,
1782
+ snapshotRoot,
1783
+ runtime,
1784
+ dbg,
1785
+ }),
1493
1786
  });
1494
1787
  logLatency({
1495
1788
  type: "phase",
@@ -1499,6 +1792,12 @@ export async function handleSessionStart(deps) {
1499
1792
  durationMs: Date.now() - sequenceReadStartedAt,
1500
1793
  metadata: { entries: latestSeq.fileSeqByPath.size, timedOut },
1501
1794
  });
1795
+ if (timedOut) {
1796
+ recordSnapshotSequenceTimeout({
1797
+ snapshotRoot,
1798
+ snapshotPath: getProjectSnapshotPath(snapshotRoot),
1799
+ });
1800
+ }
1502
1801
  runtime.seedProjectSequence?.(latestSeq.projectSeq, latestSeq.fileSeqByPath);
1503
1802
  const effectiveSeq = runtime.projectSeq ?? latestSeq.projectSeq;
1504
1803
  dbg(`session_start sequence: projectSeq=${effectiveSeq} fileSeqEntries=${latestSeq.fileSeqByPath.size}`);
@@ -1522,6 +1821,8 @@ export async function handleSessionStart(deps) {
1522
1821
  catch {
1523
1822
  // Missing snapshots are the normal cold-start case.
1524
1823
  }
1824
+ const snapshotBodyPresent = nodeFs.existsSync(snapshotPath) ||
1825
+ nodeFs.existsSync(getProjectSnapshotLegacyPath(snapshotRoot));
1525
1826
  const snapshotGate = loadSnapshotBodyUnlessStale({
1526
1827
  root: snapshotRoot,
1527
1828
  currentProjectSeq: freshnessSeq,
@@ -1529,6 +1830,10 @@ export async function handleSessionStart(deps) {
1529
1830
  });
1530
1831
  const snapshot = snapshotGate.snapshot;
1531
1832
  const snapshotFresh = isProjectSnapshotFresh(snapshot, freshnessSeq);
1833
+ const snapshotMissReason = describeSnapshotMiss(snapshot, freshnessSeq, {
1834
+ skippedStale: snapshotGate.skippedStale,
1835
+ bodyPresent: snapshotBodyPresent,
1836
+ });
1532
1837
  logLatency({
1533
1838
  type: "phase",
1534
1839
  phase: "session_start_snapshot_load",
@@ -1539,6 +1844,7 @@ export async function handleSessionStart(deps) {
1539
1844
  bytes: snapshotBytes,
1540
1845
  fresh: snapshotFresh,
1541
1846
  seq: snapshot?.seq ?? null,
1847
+ reason: snapshotFresh ? undefined : snapshotMissReason,
1542
1848
  ...(snapshotGate.skippedStale ? { skippedStale: true } : {}),
1543
1849
  ...(timedOut ? { sequenceUnknown: true } : {}),
1544
1850
  },
@@ -1548,6 +1854,7 @@ export async function handleSessionStart(deps) {
1548
1854
  root: snapshotRoot,
1549
1855
  currentProjectSeq: freshnessSeq,
1550
1856
  snapshot,
1857
+ missReason: snapshotMissReason,
1551
1858
  });
1552
1859
  if (snapshotFresh) {
1553
1860
  hydrateRuntimeFromProjectSnapshot(runtime, snapshot);
@@ -1559,6 +1866,38 @@ export async function handleSessionStart(deps) {
1559
1866
  log(`Active tools: ${quickTools.join(", ")}`);
1560
1867
  dbg(`session_start tools: ${quickTools.join(", ") || "deferred (quick mode)"}`);
1561
1868
  dbg("session_start: quick mode active - skipping slow tool probes, language profiling, preinstall, scans, and error debt baseline");
1869
+ // #1911: the debug line above says WHICH steps were skipped, but nothing
1870
+ // bounded or structured said so — quick mode's absence of work and an
1871
+ // absent LOGGER read identically in latency.log. This record is that
1872
+ // line's structured twin: one bounded gauge per quick-mode session_start,
1873
+ // naming exactly the step set skipped, so a reader can tell "quick mode
1874
+ // correctly skipped these" from "the probes silently never ran".
1875
+ logLatency({
1876
+ type: "phase",
1877
+ phase: "session_start_skipped_steps",
1878
+ filePath: cwd,
1879
+ durationMs: 0,
1880
+ metadata: {
1881
+ mode: startupMode,
1882
+ reason: deps.sessionReason,
1883
+ // #1911 review F3: this `if (quickMode)` branch is an early return —
1884
+ // the whole quick path — so there is no enumerable list of "steps
1885
+ // run vs. skipped" this array could be derived from mechanically. It
1886
+ // is DESCRIPTIVE documentation of what the early return skips,
1887
+ // matching the `dbg()` line above word for word; keep both in sync by
1888
+ // hand if either changes. It also does NOT narrow under
1889
+ // `getFlag("no-lsp")` — quick mode skips the same five steps either
1890
+ // way; the LSP flag instead affects `quickTools` above, a separate
1891
+ // record.
1892
+ steps: [
1893
+ "slow_tool_probes",
1894
+ "language_profiling",
1895
+ "tool_preinstall",
1896
+ "startup_scans",
1897
+ "error_debt_baseline",
1898
+ ],
1899
+ },
1900
+ });
1562
1901
  const totalDurationMs = Date.now() - sessionStartMs;
1563
1902
  dbg(`session_start total: ${totalDurationMs}ms (interactive path)`);
1564
1903
  logLatency({
@@ -1569,6 +1908,7 @@ export async function handleSessionStart(deps) {
1569
1908
  durationMs: totalDurationMs,
1570
1909
  metadata: { mode: startupMode, reason: deps.sessionReason },
1571
1910
  });
1911
+ logHostReadyDelay(deps, cwd);
1572
1912
  emitSmellsSessionStartLine(dbg, sessionStartMs);
1573
1913
  return;
1574
1914
  }
@@ -1605,6 +1945,20 @@ export async function handleSessionStart(deps) {
1605
1945
  durationMs: Date.now() - sequenceReadStartedAt,
1606
1946
  metadata: { entries: latestSeq.fileSeqByPath.size, timedOut },
1607
1947
  });
1948
+ if (timedOut) {
1949
+ // #1785: instrumentation only here, deliberately no
1950
+ // `onDeferredSequenceResolved` — unlike quick mode's cold path (which
1951
+ // does nothing when the snapshot isn't trusted), full mode's cold path
1952
+ // actively kicks off a real rescan below to rebuild this same state.
1953
+ // Retroactively hydrating from the on-disk snapshot once the deferred
1954
+ // read lands could overwrite that fresher, actively-computed state with
1955
+ // the stale disk copy — a regression, not a fix. See the PR body for the
1956
+ // full reasoning.
1957
+ recordSnapshotSequenceTimeout({
1958
+ snapshotRoot,
1959
+ snapshotPath: getProjectSnapshotPath(snapshotRoot),
1960
+ });
1961
+ }
1608
1962
  runtime.seedProjectSequence?.(latestSeq.projectSeq, latestSeq.fileSeqByPath);
1609
1963
  const effectiveSeq = runtime.projectSeq ?? latestSeq.projectSeq;
1610
1964
  dbg(`session_start sequence: projectSeq=${effectiveSeq} fileSeqEntries=${latestSeq.fileSeqByPath.size}`);
@@ -1624,6 +1978,8 @@ export async function handleSessionStart(deps) {
1624
1978
  catch {
1625
1979
  // Missing snapshots are the normal cold-start case.
1626
1980
  }
1981
+ const snapshotBodyPresent = nodeFs.existsSync(snapshotPath) ||
1982
+ nodeFs.existsSync(getProjectSnapshotLegacyPath(snapshotRoot));
1627
1983
  const snapshotGate = loadSnapshotBodyUnlessStale({
1628
1984
  root: snapshotRoot,
1629
1985
  currentProjectSeq: freshnessSeq,
@@ -1631,6 +1987,10 @@ export async function handleSessionStart(deps) {
1631
1987
  });
1632
1988
  const snapshot = snapshotGate.snapshot;
1633
1989
  const snapshotFresh = isProjectSnapshotFresh(snapshot, freshnessSeq);
1990
+ const snapshotMissReason = describeSnapshotMiss(snapshot, freshnessSeq, {
1991
+ skippedStale: snapshotGate.skippedStale,
1992
+ bodyPresent: snapshotBodyPresent,
1993
+ });
1634
1994
  logLatency({
1635
1995
  type: "phase",
1636
1996
  phase: "session_start_snapshot_load",
@@ -1641,6 +2001,7 @@ export async function handleSessionStart(deps) {
1641
2001
  bytes: snapshotBytes,
1642
2002
  fresh: snapshotFresh,
1643
2003
  seq: snapshot?.seq ?? null,
2004
+ reason: snapshotFresh ? undefined : snapshotMissReason,
1644
2005
  ...(snapshotGate.skippedStale ? { skippedStale: true } : {}),
1645
2006
  ...(timedOut ? { sequenceUnknown: true } : {}),
1646
2007
  },
@@ -1650,6 +2011,7 @@ export async function handleSessionStart(deps) {
1650
2011
  root: snapshotRoot,
1651
2012
  currentProjectSeq: freshnessSeq,
1652
2013
  snapshot,
2014
+ missReason: snapshotMissReason,
1653
2015
  });
1654
2016
  const freshSnapshot = snapshotFresh ? snapshot : null;
1655
2017
  if (freshSnapshot) {
@@ -1777,6 +2139,7 @@ export async function handleSessionStart(deps) {
1777
2139
  const jstsHeavyScansWillRun = startupScansWillRun && canRunStartupHeavyScans(languageProfile, "jsts");
1778
2140
  if (allowBootstrapTasks) {
1779
2141
  scheduleDeferredToolProbes(deps, languageProfile, startupDefaults, jstsHeavyScansWillRun, dbg);
2142
+ scheduleManagedToolRefresh(dbg);
1780
2143
  }
1781
2144
  if (allowBootstrapTasks) {
1782
2145
  // Fire-and-forget like other tool probes
@@ -1889,6 +2252,7 @@ export async function handleSessionStart(deps) {
1889
2252
  durationMs: totalDurationMs,
1890
2253
  metadata: { mode: startupMode, reason: deps.sessionReason },
1891
2254
  });
2255
+ logHostReadyDelay(deps, cwd);
1892
2256
  emitSmellsSessionStartLine(dbg, sessionStartMs);
1893
2257
  }
1894
2258
  /**