@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
@@ -1,25 +1,36 @@
1
1
  import * as nodeFs from "node:fs";
2
2
  import * as path from "node:path";
3
+ import { resetBoundedTelemetry } from "./bounded-telemetry.js";
4
+ import { createDeadline, yieldIfOverBudget } from "./cooperative-budget.js";
3
5
  import { deadCodeIssueCount } from "./dead-code-client.js";
4
6
  import { logDeadCodeScan } from "./dead-code-logger.js";
7
+ import { incrementDegradationCount, resetDegradationLedger, } from "./degradation-ledger.js";
5
8
  import { getDiagnosticTracker } from "./diagnostic-tracker.js";
6
- import { resetDegradationLedger } from "./degradation-ledger.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";
7
12
  import { resetDispatchAvailabilityState } from "./dispatch/runners/utils/runner-helpers.js";
8
13
  import { clearAllSessions as clearFileTimeSessions } from "./file-time.js";
9
- import { createDeadline, yieldIfOverBudget } from "./cooperative-budget.js";
10
14
  import { getGlobalPiLensDir, getKnipIgnorePatterns, getProjectDataDir, } from "./file-utils.js";
11
15
  import { GitleaksClient } from "./gitleaks-client.js";
12
16
  import { GovulncheckClient, } from "./govulncheck-client.js";
17
+ import { sweepAtomicWriteStages } from "./instance-reaper.js";
13
18
  import { canRunStartupHeavyScans } from "./language-policy.js";
14
19
  import { detectProjectLanguageProfile, getDefaultStartupTools, } from "./language-profile.js";
15
20
  import { logLatency } from "./latency-logger.js";
16
21
  import { runLogCleanup } from "./log-cleanup.js";
17
22
  import { initLSPConfig, loadLSPConfig } from "./lsp/config.js";
23
+ import { resetWorkspaceDiagnosticsCacheSession } from "./lsp/workspace-diagnostics-session.js";
24
+ import { resetDirectLspCommandAvailability } from "./lsp/server.js";
18
25
  import { loadLspService } from "./lsp-lazy.js";
26
+ import { resetManagedToolRefreshSession } from "./installer/managed-tool-refresh-session.js";
27
+ import { resetResolvedPathCache } from "./installer/index.js";
28
+ import { _resetPackageManagerCache } from "./package-manager.js";
29
+ import { clearFormatterCache } from "./formatters.js";
19
30
  import { isAtOrAboveHomeDir } from "./path-utils.js";
20
31
  import { isPrintMode } from "./print-mode.js";
21
32
  import { readLatestProjectSequence, readLatestProjectSequenceAsync, } from "./project-changes.js";
22
- import { getProjectSnapshotPath, hydrateRuntimeFromProjectSnapshot, isProjectSnapshotFresh, isProjectSnapshotMetaStale, loadProjectSnapshot, PROJECT_SNAPSHOT_VERSION, readProjectSnapshotMeta, saveRuntimeProjectSnapshot, } from "./project-snapshot.js";
33
+ import { getProjectSnapshotPath, getProjectSnapshotLegacyPath, hydrateRuntimeFromProjectSnapshot, hydrateRuntimeFromProjectSnapshotIfIdle, isProjectSnapshotFresh, isProjectSnapshotMetaStale, loadProjectSnapshot, loadProjectSnapshotExportsAndRules, PROJECT_SNAPSHOT_VERSION, readProjectSnapshotMeta, saveRuntimeProjectSnapshot, } from "./project-snapshot.js";
23
34
  import { clearTsconfigPathsCache } from "./review-graph/tsconfig-paths.js";
24
35
  import { scanProjectRules } from "./rules-scanner.js";
25
36
  import { resetSafeSpawnWindowsCommandCache } from "./safe-spawn.js";
@@ -27,12 +38,32 @@ import { getSlowFsVerdict, isSlowFs, slowFsDegradationNotice, } from "./slow-fs.
27
38
  import { countRecentSmells, formatSmellsSessionStartLine, resetSmellsSessionState, } from "./smells-rollup.js";
28
39
  import { findNearestProjectRoot, getStartupScanMaxEntries, isStartupScanVerdictFresh, resolveStartupScanContext, } from "./startup-scan.js";
29
40
  import { getSubagentIdentity, isSubagentSession, subagentLightModeNotice, } from "./subagent-mode.js";
30
- import { sweepAtomicWriteStages } from "./instance-reaper.js";
31
41
  import { TrivyClient } from "./trivy-client.js";
32
42
  import { isWarmAttached } from "./warm-attach.js";
33
43
  import { setSessionLanguages } from "./widget-state.js";
34
44
  import { logWordIndex } from "./word-index-logger.js";
35
45
  import { resetWorkspaceTopology } from "./workspace-topology.js";
46
+ import { resetZizmorTokenAvailability } from "./zizmor-config.js";
47
+ const HOST_STALL_THRESHOLD_MS = 30_000;
48
+ function logHostReadyDelay(deps, cwd) {
49
+ if (!deps.emitHostReadyDelay ||
50
+ deps.sessionStartMonotonicAt === undefined ||
51
+ deps.extensionLoadedAt === undefined) {
52
+ return;
53
+ }
54
+ const durationMs = Math.max(0, deps.sessionStartMonotonicAt - deps.extensionLoadedAt);
55
+ logLatency({
56
+ type: "phase",
57
+ filePath: cwd,
58
+ phase: "host_ready_delay",
59
+ durationMs,
60
+ metadata: {
61
+ hostStallSuspected: durationMs > HOST_STALL_THRESHOLD_MS,
62
+ sessionStart: "first-process-session",
63
+ reason: deps.sessionReason,
64
+ },
65
+ });
66
+ }
36
67
  function resolveSnapshotRoot(cwd) {
37
68
  const resolvedCwd = path.resolve(cwd);
38
69
  const nearest = findNearestProjectRoot(resolvedCwd);
@@ -79,6 +110,91 @@ function snapshotSequenceBase(root) {
79
110
  sinceSeq: meta.seq,
80
111
  };
81
112
  }
113
+ /**
114
+ * #1785: one bounded ledger record per timed-out sequence read, so a real
115
+ * occurrence is diagnosable from the ledger alone rather than only from a
116
+ * `dbg` line that most hosts never surface. Carries the project root and the
117
+ * on-disk snapshot's age/size (when one exists) — the identity AC1 asks for:
118
+ * which project, how stale/large the snapshot that got skipped was.
119
+ */
120
+ function recordSnapshotSequenceTimeout(args) {
121
+ let reason = "sequence read timed out; no snapshot on disk";
122
+ try {
123
+ const stat = nodeFs.statSync(args.snapshotPath);
124
+ reason = `sequence read timed out; snapshot age=${Math.round(Date.now() - stat.mtimeMs)}ms size=${stat.size}b`;
125
+ }
126
+ catch {
127
+ // No snapshot on disk yet — the reason above already covers it.
128
+ }
129
+ incrementDegradationCount({
130
+ kind: "snapshot-sequence-read-timeout",
131
+ subject: args.snapshotRoot,
132
+ reason,
133
+ });
134
+ }
135
+ /**
136
+ * #1785: the synchronous freshness check made a conservative call with the
137
+ * `UNKNOWN_PROJECT_SEQ` sentinel (never hydrate when the real sequence is
138
+ * unknown). Once the deferred read resolves — a deterministic completion
139
+ * signal, not a sleep — this re-checks freshness against the now-known
140
+ * sequence and hydrates late when the snapshot really was current, instead of
141
+ * leaving the runtime without `cachedExports`/`projectRulesScan` for the rest
142
+ * of the session.
143
+ *
144
+ * #1785 review round F5 (design (a), replacing rounds 1-3's synchronous
145
+ * capture entirely): quick mode's own cold-start warmup ALREADY loads the
146
+ * on-disk snapshot for its own purposes — `cachedSnapshot` at the top of the
147
+ * warmup body, used to reuse a still-fresh `startupScan` verdict — strictly
148
+ * BEFORE the warmup's own possible save (`saveRuntimeProjectSnapshot`, only
149
+ * reached when that verdict was NOT fresh). Reusing THAT already-loaded
150
+ * value costs nothing new: the read was always going to happen regardless of
151
+ * this fix, and it is guaranteed to predate any overwrite the SAME warmup
152
+ * invocation might go on to make — a read-before-write invariant of the
153
+ * warmup's own code, not something this function has to defend against with
154
+ * a race window of its own. `getWarmupOwnSnapshotRead` is a closure over a
155
+ * per-`handleSessionStart`-call LOCAL variable (never module-scope — #1785
156
+ * F6 is exactly the class of bug a module-scope holder here would
157
+ * reintroduce) that the warmup publishes into once its own read completes.
158
+ *
159
+ * When the getter still returns `undefined` — the deferred read resolved
160
+ * before the warmup got that far (or no warmup was ever armed by this call:
161
+ * a later quick-mode call in the same process, print mode, full mode) —
162
+ * there is nothing published yet, so this falls back to a live re-load via
163
+ * `loadProjectSnapshotExportsAndRules`. That fallback is exactly as safe as
164
+ * the primary path: if the warmup hasn't reached its own read yet, it
165
+ * certainly hasn't saved yet either (same invariant), so disk still holds
166
+ * whatever was there before any warmup activity.
167
+ *
168
+ * Both the published value and the fallback re-load are the narrow,
169
+ * postings-free `ProjectSnapshotExportsAndRules` shape — never the full
170
+ * `loadProjectSnapshot`. This path only ever hydrates
171
+ * `cachedExports`/`projectRulesScan`; it can NOT hydrate `wordIndex` even
172
+ * when the underlying snapshot would otherwise have one, because the narrow
173
+ * type never carries it. That is intentional, not a regression: F2's hazard
174
+ * was this exact path nulling a `wordIndex` that quick mode's warmup had, in
175
+ * the meantime, built for real — the warmup remains the sole source of a
176
+ * late-arriving `wordIndex` for the interactive path.
177
+ */
178
+ function retroactivelyHydrateAfterDeferredSequence(args) {
179
+ return (latestSeq) => {
180
+ const published = args.getWarmupOwnSnapshotRead();
181
+ const snapshot = published !== undefined
182
+ ? published
183
+ : loadProjectSnapshotExportsAndRules(args.snapshotRoot);
184
+ if (!snapshot ||
185
+ snapshot.version !== PROJECT_SNAPSHOT_VERSION ||
186
+ snapshot.seq !== latestSeq.projectSeq) {
187
+ return;
188
+ }
189
+ const hydrated = hydrateRuntimeFromProjectSnapshotIfIdle(args.runtime, snapshot);
190
+ if (hydrated) {
191
+ args.dbg(`session_start: deferred sequence read confirmed snapshot freshness — hydrating cachedExports/projectRulesScan late (seq=${snapshot.seq})`);
192
+ }
193
+ else {
194
+ 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`);
195
+ }
196
+ };
197
+ }
82
198
  function loadSnapshotBodyUnlessStale(args) {
83
199
  const meta = readProjectSnapshotMeta(args.root);
84
200
  if (meta && isProjectSnapshotMetaStale(meta, args.currentProjectSeq)) {
@@ -90,12 +206,17 @@ function loadSnapshotBodyUnlessStale(args) {
90
206
  skippedStale: false,
91
207
  };
92
208
  }
93
- function describeSnapshotMiss(snapshot, currentProjectSeq) {
209
+ function describeSnapshotMiss(snapshot, currentProjectSeq, args) {
210
+ if (args.skippedStale)
211
+ return "stale-meta-gate";
94
212
  if (!snapshot)
95
- return "missing";
213
+ return args.bodyPresent ? "invalid-body" : "missing";
96
214
  if (snapshot.seq !== currentProjectSeq) {
97
215
  return `stale(seq=${snapshot.seq}, current=${currentProjectSeq})`;
98
216
  }
217
+ // Defensive-only arm: parseSnapshot rejects incompatible versions, and a
218
+ // same-sequence parsed snapshot is fresh. Keep this classification explicit
219
+ // so a future loader change cannot collapse it into a generic miss.
99
220
  return "incompatible";
100
221
  }
101
222
  function logProjectSnapshotProbe(args) {
@@ -104,7 +225,7 @@ function logProjectSnapshotProbe(args) {
104
225
  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
226
  }
106
227
  else {
107
- args.dbg(`project_snapshot: miss reason=${describeSnapshotMiss(args.snapshot, args.currentProjectSeq)}`);
228
+ args.dbg(`project_snapshot: miss reason=${args.missReason}`);
108
229
  }
109
230
  }
110
231
  function resolveStartupMode() {
@@ -171,7 +292,7 @@ const UNKNOWN_PROJECT_SEQ = -1;
171
292
  * logs its own `session_start_sequence_read_deferred_reseed` phase.
172
293
  */
173
294
  async function readSequenceWithBudget(args) {
174
- const { snapshotRoot, base, cwd, runtime, sessionGeneration, dbg } = args;
295
+ const { snapshotRoot, base, cwd, runtime, sessionGeneration, dbg, onDeferredSequenceResolved, } = args;
175
296
  const readPromise = readLatestProjectSequenceAsync(snapshotRoot, base);
176
297
  let timeoutHandle;
177
298
  const timeoutPromise = new Promise((resolve) => {
@@ -224,6 +345,7 @@ async function readSequenceWithBudget(args) {
224
345
  metadata: { entries: latestSeq.fileSeqByPath.size, deferred: true },
225
346
  });
226
347
  dbg(`session_start: deferred sequence read completed — reseeded projectSeq=${latestSeq.projectSeq} fileSeqEntries=${latestSeq.fileSeqByPath.size}`);
348
+ onDeferredSequenceResolved?.(latestSeq);
227
349
  })
228
350
  .catch((err) => {
229
351
  dbg(`session_start: deferred sequence read failed: ${err}`);
@@ -375,6 +497,47 @@ function firePreinstallDefaults(ensureTool, dbg, startupDefaults) {
375
497
  });
376
498
  }
377
499
  }
500
+ /**
501
+ * Default delay before the managed-tool version refresh runs (#1730). Long
502
+ * enough that `session_start`, the startup scans, and the tool preinstalls have
503
+ * all settled — the refresh may spawn one `npm update`, and that must never
504
+ * compete with the work the user is waiting on.
505
+ */
506
+ const MANAGED_TOOL_REFRESH_DELAY_MS = 30_000;
507
+ /**
508
+ * Schedule the periodic managed-tool refresh (#1730) on an unref'd background
509
+ * timer.
510
+ *
511
+ * `session_start`'s own cost here is a single `setTimeout` registration: the
512
+ * refresh module is not even imported until the timer fires. The timer is
513
+ * unref'd so a one-shot process exits without waiting for it, matching the
514
+ * warmup timer above. Whether a refresh actually runs is decided inside
515
+ * `runManagedToolRefresh` against a PERSISTED per-tool stamp, so a launch loop
516
+ * cannot escalate the weekly cadence.
517
+ */
518
+ function scheduleManagedToolRefresh(dbg) {
519
+ const delayMs = Number(process.env.PI_LENS_TOOL_REFRESH_DELAY_MS ?? MANAGED_TOOL_REFRESH_DELAY_MS);
520
+ const timer = setTimeout(() => {
521
+ void (async () => {
522
+ try {
523
+ const refresh = await import("./installer/managed-tool-refresh.js");
524
+ const outcome = await refresh.runManagedToolRefresh();
525
+ if (outcome.skipped) {
526
+ dbg(`session_start tool-refresh: skipped (${outcome.skipped})`);
527
+ return;
528
+ }
529
+ for (const result of outcome.refreshed) {
530
+ dbg(`session_start tool-refresh: ${result.toolId} ${result.ok ? "ok" : "failed"}` +
531
+ `${result.changed ? ` ${result.previousVersion ?? "unknown"} → ${result.currentVersion}` : " (unchanged)"}`);
532
+ }
533
+ }
534
+ catch (err) {
535
+ dbg(`session_start tool-refresh: error ${err}`);
536
+ }
537
+ })();
538
+ }, Number.isFinite(delayMs) ? delayMs : MANAGED_TOOL_REFRESH_DELAY_MS);
539
+ timer.unref?.();
540
+ }
378
541
  async function probePrettierInstall(ensureTool, dbg, analysisRoot) {
379
542
  const pkgPath = path.join(analysisRoot, "package.json");
380
543
  try {
@@ -452,10 +615,14 @@ async function buildOrRefreshWordIndex(args) {
452
615
  let rebuildPreflightFiles;
453
616
  const latestSeq = readLatestProjectSequence(snapshotRoot, snapshotSequenceBase(snapshotRoot));
454
617
  const effectiveSeq = runtime.projectSeq ?? latestSeq.projectSeq;
618
+ const snapshotLoadStartMs = Date.now();
455
619
  const snapshot = loadProjectSnapshot(snapshotRoot);
620
+ const snapshotLoadMs = Date.now() - snapshotLoadStartMs;
456
621
  if (snapshot?.wordIndex) {
457
622
  const { deserializeWordIndex, refreshWordIndexIncrementally } = await import("./word-index.js");
623
+ const deserializeStartMs = Date.now();
458
624
  const index = deserializeWordIndex(snapshot.wordIndex);
625
+ const deserializeMs = Date.now() - deserializeStartMs;
459
626
  if (index) {
460
627
  try {
461
628
  const result = await refreshWordIndexIncrementally(index, analysisRoot, () => runtime.isCurrentSession(sessionGeneration));
@@ -469,10 +636,16 @@ async function buildOrRefreshWordIndex(args) {
469
636
  cwd: snapshotRoot,
470
637
  trigger: "session_start",
471
638
  reason: result.reason,
639
+ phaseDurationsMs: {
640
+ snapshotLoadMs,
641
+ deserializeMs,
642
+ ...result.timings,
643
+ },
472
644
  });
473
645
  }
474
646
  else {
475
647
  runtime.wordIndex = index;
648
+ let snapshotSaveSyncMs = 0;
476
649
  // A stale project seq must be advanced even when mtimes prove every
477
650
  // indexed document reusable. Fresh snapshots with no changes avoid
478
651
  // an unnecessary rewrite of the large shared snapshot.
@@ -480,9 +653,15 @@ async function buildOrRefreshWordIndex(args) {
480
653
  result.refreshed > 0 ||
481
654
  result.dropped > 0 ||
482
655
  snapshot.wordIndex.truncated !== index.truncated) {
656
+ const serializeSaveEnqueueStartMs = Date.now();
483
657
  saveRuntimeProjectSnapshot({ cwd: snapshotRoot, runtime, dbg });
658
+ snapshotSaveSyncMs = Date.now() - serializeSaveEnqueueStartMs;
484
659
  }
485
- dbg(`session_start word-index: incremental (seq=${effectiveSeq}, refreshed=${result.refreshed}, dropped=${result.dropped}, skipped=${result.skipped}, reused=${result.reused}, ${Date.now() - startMs}ms)`);
660
+ const phases = `snapshot-load=${snapshotLoadMs}ms, deserialize=${deserializeMs}ms, ` +
661
+ `source-walk=${result.timings.sourceWalkMs}ms, stat-walk=${result.timings.statWalkMs}ms, ` +
662
+ `refresh-reads=${result.timings.refreshReadsMs}ms, ` +
663
+ `snapshot-save-sync=${snapshotSaveSyncMs}ms`;
664
+ dbg(`session_start word-index: incremental (seq=${effectiveSeq}, refreshed=${result.refreshed}, dropped=${result.dropped}, skipped=${result.skipped}, reused=${result.reused}, ${Date.now() - startMs}ms, phases: ${phases})`);
486
665
  // M2, #958: the incremental-vs-full decision + honest coverage
487
666
  // (indexedFileCount/truncated/skipped), independent of host `dbg`.
488
667
  logWordIndex({
@@ -493,6 +672,12 @@ async function buildOrRefreshWordIndex(args) {
493
672
  indexedFileCount: index.docCount,
494
673
  tokens: index.postings.size,
495
674
  truncated: index.truncated,
675
+ phaseDurationsMs: {
676
+ snapshotLoadMs,
677
+ deserializeMs,
678
+ ...result.timings,
679
+ snapshotSaveSyncMs,
680
+ },
496
681
  refreshed: result.refreshed,
497
682
  dropped: result.dropped,
498
683
  skipped: result.skipped,
@@ -516,6 +701,7 @@ async function buildOrRefreshWordIndex(args) {
516
701
  cwd: snapshotRoot,
517
702
  trigger: "session_start",
518
703
  reason: err instanceof Error ? err.message : String(err),
704
+ phaseDurationsMs: { snapshotLoadMs, deserializeMs },
519
705
  });
520
706
  }
521
707
  }
@@ -692,7 +878,7 @@ function scheduleStartupScans(deps, runtime, sessionGeneration, analysisRoot, sn
692
878
  // KnipClient skips before probing/installing when analysisRoot is not a real
693
879
  // JS/Knip project. This avoids running knip from Unity/C#/generic repos.
694
880
  const startMs = Date.now();
695
- const knipResult = await knipClient.analyze(analysisRoot, getKnipIgnorePatterns());
881
+ const knipResult = await knipClient.analyze(analysisRoot, getKnipIgnorePatterns(), { projectSeq: runtime.projectSeq });
696
882
  if (!runtime.isCurrentSession(sessionGeneration))
697
883
  return;
698
884
  if (knipResult.success) {
@@ -1004,7 +1190,7 @@ function scheduleStartupScans(deps, runtime, sessionGeneration, analysisRoot, sn
1004
1190
  return;
1005
1191
  if (!runtime.callGraph)
1006
1192
  return;
1007
- const { buildCodebaseModel, saveCodebaseModel, DEFAULT_CODEBASE_MODEL_TOKEN_BUDGET } = await import("./codebase-model.js");
1193
+ const { buildCodebaseModel, saveCodebaseModel, DEFAULT_CODEBASE_MODEL_TOKEN_BUDGET, } = await import("./codebase-model.js");
1008
1194
  if (!callGraphIdentity)
1009
1195
  return;
1010
1196
  const model = buildCodebaseModel(runtime.callGraph, analysisRoot, DEFAULT_CODEBASE_MODEL_TOKEN_BUDGET, callGraphIdentity);
@@ -1098,6 +1284,12 @@ export const SESSION_START_GUIDANCE = [
1098
1284
  ];
1099
1285
  export async function handleSessionStart(deps) {
1100
1286
  resetDegradationLedger();
1287
+ // #1743: the bounded-telemetry per-turn counters. The rising-edge state is
1288
+ // NOT here — it is the ledger's own tally, reset on the line above. These
1289
+ // counters are keyed by turn index, and a new session restarts turn
1290
+ // numbering at 0, so without this a stale count could survive a session
1291
+ // boundary that happened to land on the same index.
1292
+ resetBoundedTelemetry();
1101
1293
  const handlerEnteredAt = Date.now();
1102
1294
  const sessionStartMs = deps.sessionStartFiredAt ?? handlerEnteredAt;
1103
1295
  const cwdForTelemetry = deps.ctxCwd ?? process.cwd();
@@ -1152,6 +1344,11 @@ export async function handleSessionStart(deps) {
1152
1344
  // (an explicit env var still takes highest precedence).
1153
1345
  // Tunable: PI_LENS_WARMUP_DELAY_MS adjusts the warmup delay.
1154
1346
  let startupMode = resolveStartupMode();
1347
+ // SAFETY: these two flags are process-lifetime state pi-lens stashes on
1348
+ // `globalThis` so a second extension instance in the same process sees the
1349
+ // first one's warmup. There is no ambient declaration for them, and adding
1350
+ // one would let any module write them. Both are declared OPTIONAL here, so
1351
+ // the reads below are `undefined`-safe on a process where nothing set them.
1155
1352
  const processGlobals = globalThis;
1156
1353
  const isFirstSessionOfProcess = !processGlobals.__piLensFirstSessionDone;
1157
1354
  if (isFirstSessionOfProcess &&
@@ -1162,6 +1359,20 @@ export async function handleSessionStart(deps) {
1162
1359
  startupMode = deps.startupModeOverride ?? "quick";
1163
1360
  }
1164
1361
  processGlobals.__piLensFirstSessionDone = true;
1362
+ // #1785 F5 (round 4, design (a)): the warmup timer body below ALREADY
1363
+ // loads the on-disk snapshot for its own purposes (`cachedSnapshot`,
1364
+ // reused for the `startupScan` verdict cache) strictly BEFORE any save it
1365
+ // might go on to make. This LOCAL variable — per-`handleSessionStart`-call,
1366
+ // deliberately NOT module-scope (see #1785 F6) — lets the warmup publish
1367
+ // that already-loaded read (narrowed to exports+rules, never the full
1368
+ // object with `wordIndex`) for `retroactivelyHydrateAfterDeferredSequence`
1369
+ // to reuse at zero additional cost, instead of this call paying for its
1370
+ // own separate synchronous capture (rounds 1-3's approach, reverted here:
1371
+ // see that function's doc comment for the full history). `undefined`
1372
+ // means the warmup hasn't reached its own read yet (or was never armed by
1373
+ // this call at all) — the quick-mode block's retroactive-hydration
1374
+ // callback then falls back to a live re-load instead.
1375
+ let warmupOwnSnapshotRead;
1165
1376
  // #1154: quick mode is entered on BOTH `pi -p`/`--print` (a one-shot that
1166
1377
  // exits right after this turn) and an interactive process's first
1167
1378
  // session_start (forced quick to protect keystroke latency, then warms
@@ -1209,6 +1420,23 @@ export async function handleSessionStart(deps) {
1209
1420
  // reuse it instead of re-walking a possibly huge tree from
1210
1421
  // scratch on every single startup.
1211
1422
  const cachedSnapshot = loadProjectSnapshot(warmupSnapshotRoot);
1423
+ // #1785 F5 (round 4): publish this ALREADY-loaded read — narrowed
1424
+ // to exports+rules, never a reference to `cachedSnapshot` itself
1425
+ // (which carries `wordIndex`/`files`/`symbols`/`reverseDeps`) — for
1426
+ // `retroactivelyHydrateAfterDeferredSequence` to reuse. This read
1427
+ // strictly precedes the warmup's own possible save a few lines
1428
+ // below, so it is guaranteed to reflect disk as it stood before
1429
+ // THIS warmup could have touched it. Zero marginal cost: this read
1430
+ // already happens unconditionally for the `startupScan` verdict
1431
+ // cache below, with or without this fix.
1432
+ warmupOwnSnapshotRead = cachedSnapshot
1433
+ ? {
1434
+ version: cachedSnapshot.version,
1435
+ seq: cachedSnapshot.seq,
1436
+ cachedExports: cachedSnapshot.cachedExports,
1437
+ projectRulesScan: cachedSnapshot.projectRulesScan,
1438
+ }
1439
+ : null;
1212
1440
  const cachedVerdict = cachedSnapshot?.startupScan;
1213
1441
  let scan;
1214
1442
  if (cachedVerdict &&
@@ -1358,7 +1586,7 @@ export async function handleSessionStart(deps) {
1358
1586
  }
1359
1587
  const allowBootstrapTasks = startupMode === "full";
1360
1588
  const quickMode = startupMode === "quick";
1361
- const { ctxCwd, getFlag, notify, dbg, log, runtime, metricsClient, cacheManager, testRunnerClient, goClient, rustClient, ensureTool, cleanStaleTsBuildInfo, resetDispatchBaselines, resetLSPService, } = deps;
1589
+ const { ctxCwd, getFlag, notify, dbg, log, runtime, metricsClient, knipClient, cacheManager, testRunnerClient, goClient, rustClient, ensureTool, cleanStaleTsBuildInfo, resetDispatchBaselines, resetLSPService, } = deps;
1362
1590
  // Lightweight phase timer — resets after each call so each log line shows
1363
1591
  // the cost of that phase alone, not cumulative time from session start.
1364
1592
  let _phaseT = Date.now();
@@ -1392,9 +1620,66 @@ export async function handleSessionStart(deps) {
1392
1620
  // the tool for the rest of the process lifetime. Clear it here, same
1393
1621
  // boundary as the other per-session caches on this line.
1394
1622
  resetDispatchAvailabilityState();
1623
+ // #1895: formatter PATH verdicts are session-scoped, but they live in
1624
+ // formatters.ts and are not covered by the dispatch generation. Re-arm them
1625
+ // here so a formatter installed or removed between sessions is observed by
1626
+ // the next session. `clearFormatterCache` drops the per-cwd selection cache
1627
+ // as well as the which latches, which is what a real re-probe needs: the
1628
+ // selection cache answers a same-cwd lookup before any probe runs, so
1629
+ // clearing the latches alone would leave the stale verdict standing in the
1630
+ // one directory the user is working in.
1631
+ clearFormatterCache();
1632
+ // #1535: same #1266 pattern, one caller earlier — the `gh auth token` latch
1633
+ // zizmor's spawn reads is process-lived storage whose durability contract is
1634
+ // per SESSION, not per process. Without this, a user who runs `gh auth
1635
+ // login` and starts a fresh session still reads the previous session's
1636
+ // stale "no token" verdict until the cooldown (if any) happens to expire.
1637
+ resetZizmorTokenAvailability();
1638
+ // psscriptanalyzer's three latches (interpreter, module, -File exec) are
1639
+ // module-local, so the generation counter above does not reach them
1640
+ // (#1490, #1540).
1641
+ resetPsScriptAnalyzerAvailability();
1642
+ // #1497: the install-class retry ceiling is terminal for a SESSION, but the
1643
+ // latches holding it live on process-lived client instances (bootstrap builds
1644
+ // them once). Same #1266/#1490/#1535 shape — without this line "terminal for
1645
+ // the session" is terminal for the process, and a repaired network never
1646
+ // re-earns its `go install`.
1647
+ resetInstallRetryLatches();
1648
+ // #1537: the lazy-install seam's hold (`gem install rubocop`, `rustup
1649
+ // component add`) is durable for a SESSION. Its map is module-local, so the
1650
+ // generation counter above does not reach it — same #1490/#1497 shape. It
1651
+ // belongs on THIS line and not on the turn_end path: the first cut cleared it
1652
+ // from `clearFormatterRuntimeState()`, which runs every turn, so a failing
1653
+ // install re-spawned every turn instead of once per session.
1654
+ resetLazyInstallAttempts();
1655
+ // #1897: direct-LSP negative availability and bare installer paths are
1656
+ // session facts. A command or PATH entry can appear between sessions.
1657
+ resetDirectLspCommandAvailability();
1658
+ resetResolvedPathCache();
1659
+ // #1653: pnpm/yarn/bun/npm's availability latches (package-manager.ts) are
1660
+ // module-local, same #1490/#1535 shape as the two lines above — the
1661
+ // generation counter above does not reach them. Without this line, a
1662
+ // pnpm/yarn/bun install done mid-day stayed invisible: a genuine "missing"
1663
+ // verdict from one session latched into the next until a process restart.
1664
+ _resetPackageManagerCache();
1665
+ // #1730: the managed-tool refresh budget is a per-SESSION allowance (at most
1666
+ // one `npm update` per session). Left process-lived, one long-running pi
1667
+ // would refresh a single tool at launch and never look at the other 21
1668
+ // again — the same latch shape as the two lines above. The weekly cadence
1669
+ // itself is NOT reset here: it lives in the persisted per-tool stamp, so
1670
+ // re-arming the budget only restores the session's right to ask.
1671
+ resetManagedToolRefreshSession();
1395
1672
  // #1123 item 3: a fresh session can re-report smells that a prior session
1396
1673
  // already surfaced once (see `checkSmellsAndNoteOnce`'s once-per-session gate).
1397
1674
  resetSmellsSessionState();
1675
+ // #1782: re-arm the workspace-diagnostics cache session clock. Entries
1676
+ // written before this instant assert findings from a session that is over,
1677
+ // so they must revalidate before they can be served as current again.
1678
+ resetWorkspaceDiagnosticsCacheSession(sessionStartMs);
1679
+ // Some embedders inject a capability-shaped Knip client rather than the
1680
+ // concrete KnipClient. Session reset is an optional lifecycle capability;
1681
+ // its absence must not make session_start fail.
1682
+ knipClient.resetSessionState?.();
1398
1683
  runtime.resetForSession(sessionStartMs);
1399
1684
  logLatency({
1400
1685
  type: "phase",
@@ -1448,11 +1733,21 @@ export async function handleSessionStart(deps) {
1448
1733
  // project data roots and machine-global registry root once per session start;
1449
1734
  // this is fire-and-forget and bounded so it never delays startup.
1450
1735
  const projectDataDir = getProjectDataDir(cwd);
1736
+ // #1609 review F1: sweepOwnStagingFiles does not recurse, so the installer's
1737
+ // bin/ and tools/ subdirectories (clients/installer/index.ts's
1738
+ // GITHUB_BIN_DIR / TOOLS_DIR, now atomic-write.js writers too) need their
1739
+ // own entries — otherwise an orphaned staging file from a kill mid-install
1740
+ // (this PR's own motivating scenario) never gets reaped, and unique
1741
+ // pid-thread-seq staging names mean repeated kills ACCUMULATE full-size
1742
+ // orphan binaries instead of being cleaned up.
1743
+ const globalDir = getGlobalPiLensDir();
1451
1744
  void sweepAtomicWriteStages([
1452
1745
  projectDataDir,
1453
1746
  path.join(projectDataDir, "cache"),
1454
1747
  path.join(projectDataDir, "sessions"),
1455
- getGlobalPiLensDir(),
1748
+ globalDir,
1749
+ path.join(globalDir, "bin"),
1750
+ path.join(globalDir, "tools"),
1456
1751
  ]).catch(() => {
1457
1752
  // best-effort lifecycle cleanup — never fail session_start
1458
1753
  });
@@ -1460,6 +1755,12 @@ export async function handleSessionStart(deps) {
1460
1755
  runtime.projectRoot = cwd;
1461
1756
  const sequenceReadStartedAt = Date.now();
1462
1757
  const snapshotRoot = resolveSnapshotRoot(cwd);
1758
+ // #1785 F5 (round 4): the getter closes over `warmupOwnSnapshotRead`
1759
+ // directly (a `let` in this same `handleSessionStart` invocation's
1760
+ // scope), so it reads whatever value is current AT DEFERRED-RESOLVE
1761
+ // TIME — not whatever it was at this wiring instant. See
1762
+ // `retroactivelyHydrateAfterDeferredSequence`'s doc comment for the
1763
+ // full design.
1463
1764
  const { latestSeq, timedOut } = await readSequenceWithBudget({
1464
1765
  snapshotRoot,
1465
1766
  base: snapshotSequenceBase(snapshotRoot),
@@ -1467,6 +1768,12 @@ export async function handleSessionStart(deps) {
1467
1768
  runtime,
1468
1769
  sessionGeneration: runtime.sessionGeneration,
1469
1770
  dbg,
1771
+ onDeferredSequenceResolved: retroactivelyHydrateAfterDeferredSequence({
1772
+ getWarmupOwnSnapshotRead: () => warmupOwnSnapshotRead,
1773
+ snapshotRoot,
1774
+ runtime,
1775
+ dbg,
1776
+ }),
1470
1777
  });
1471
1778
  logLatency({
1472
1779
  type: "phase",
@@ -1476,6 +1783,12 @@ export async function handleSessionStart(deps) {
1476
1783
  durationMs: Date.now() - sequenceReadStartedAt,
1477
1784
  metadata: { entries: latestSeq.fileSeqByPath.size, timedOut },
1478
1785
  });
1786
+ if (timedOut) {
1787
+ recordSnapshotSequenceTimeout({
1788
+ snapshotRoot,
1789
+ snapshotPath: getProjectSnapshotPath(snapshotRoot),
1790
+ });
1791
+ }
1479
1792
  runtime.seedProjectSequence?.(latestSeq.projectSeq, latestSeq.fileSeqByPath);
1480
1793
  const effectiveSeq = runtime.projectSeq ?? latestSeq.projectSeq;
1481
1794
  dbg(`session_start sequence: projectSeq=${effectiveSeq} fileSeqEntries=${latestSeq.fileSeqByPath.size}`);
@@ -1499,6 +1812,8 @@ export async function handleSessionStart(deps) {
1499
1812
  catch {
1500
1813
  // Missing snapshots are the normal cold-start case.
1501
1814
  }
1815
+ const snapshotBodyPresent = nodeFs.existsSync(snapshotPath) ||
1816
+ nodeFs.existsSync(getProjectSnapshotLegacyPath(snapshotRoot));
1502
1817
  const snapshotGate = loadSnapshotBodyUnlessStale({
1503
1818
  root: snapshotRoot,
1504
1819
  currentProjectSeq: freshnessSeq,
@@ -1506,6 +1821,10 @@ export async function handleSessionStart(deps) {
1506
1821
  });
1507
1822
  const snapshot = snapshotGate.snapshot;
1508
1823
  const snapshotFresh = isProjectSnapshotFresh(snapshot, freshnessSeq);
1824
+ const snapshotMissReason = describeSnapshotMiss(snapshot, freshnessSeq, {
1825
+ skippedStale: snapshotGate.skippedStale,
1826
+ bodyPresent: snapshotBodyPresent,
1827
+ });
1509
1828
  logLatency({
1510
1829
  type: "phase",
1511
1830
  phase: "session_start_snapshot_load",
@@ -1516,6 +1835,7 @@ export async function handleSessionStart(deps) {
1516
1835
  bytes: snapshotBytes,
1517
1836
  fresh: snapshotFresh,
1518
1837
  seq: snapshot?.seq ?? null,
1838
+ reason: snapshotFresh ? undefined : snapshotMissReason,
1519
1839
  ...(snapshotGate.skippedStale ? { skippedStale: true } : {}),
1520
1840
  ...(timedOut ? { sequenceUnknown: true } : {}),
1521
1841
  },
@@ -1525,6 +1845,7 @@ export async function handleSessionStart(deps) {
1525
1845
  root: snapshotRoot,
1526
1846
  currentProjectSeq: freshnessSeq,
1527
1847
  snapshot,
1848
+ missReason: snapshotMissReason,
1528
1849
  });
1529
1850
  if (snapshotFresh) {
1530
1851
  hydrateRuntimeFromProjectSnapshot(runtime, snapshot);
@@ -1546,6 +1867,7 @@ export async function handleSessionStart(deps) {
1546
1867
  durationMs: totalDurationMs,
1547
1868
  metadata: { mode: startupMode, reason: deps.sessionReason },
1548
1869
  });
1870
+ logHostReadyDelay(deps, cwd);
1549
1871
  emitSmellsSessionStartLine(dbg, sessionStartMs);
1550
1872
  return;
1551
1873
  }
@@ -1582,6 +1904,20 @@ export async function handleSessionStart(deps) {
1582
1904
  durationMs: Date.now() - sequenceReadStartedAt,
1583
1905
  metadata: { entries: latestSeq.fileSeqByPath.size, timedOut },
1584
1906
  });
1907
+ if (timedOut) {
1908
+ // #1785: instrumentation only here, deliberately no
1909
+ // `onDeferredSequenceResolved` — unlike quick mode's cold path (which
1910
+ // does nothing when the snapshot isn't trusted), full mode's cold path
1911
+ // actively kicks off a real rescan below to rebuild this same state.
1912
+ // Retroactively hydrating from the on-disk snapshot once the deferred
1913
+ // read lands could overwrite that fresher, actively-computed state with
1914
+ // the stale disk copy — a regression, not a fix. See the PR body for the
1915
+ // full reasoning.
1916
+ recordSnapshotSequenceTimeout({
1917
+ snapshotRoot,
1918
+ snapshotPath: getProjectSnapshotPath(snapshotRoot),
1919
+ });
1920
+ }
1585
1921
  runtime.seedProjectSequence?.(latestSeq.projectSeq, latestSeq.fileSeqByPath);
1586
1922
  const effectiveSeq = runtime.projectSeq ?? latestSeq.projectSeq;
1587
1923
  dbg(`session_start sequence: projectSeq=${effectiveSeq} fileSeqEntries=${latestSeq.fileSeqByPath.size}`);
@@ -1601,6 +1937,8 @@ export async function handleSessionStart(deps) {
1601
1937
  catch {
1602
1938
  // Missing snapshots are the normal cold-start case.
1603
1939
  }
1940
+ const snapshotBodyPresent = nodeFs.existsSync(snapshotPath) ||
1941
+ nodeFs.existsSync(getProjectSnapshotLegacyPath(snapshotRoot));
1604
1942
  const snapshotGate = loadSnapshotBodyUnlessStale({
1605
1943
  root: snapshotRoot,
1606
1944
  currentProjectSeq: freshnessSeq,
@@ -1608,6 +1946,10 @@ export async function handleSessionStart(deps) {
1608
1946
  });
1609
1947
  const snapshot = snapshotGate.snapshot;
1610
1948
  const snapshotFresh = isProjectSnapshotFresh(snapshot, freshnessSeq);
1949
+ const snapshotMissReason = describeSnapshotMiss(snapshot, freshnessSeq, {
1950
+ skippedStale: snapshotGate.skippedStale,
1951
+ bodyPresent: snapshotBodyPresent,
1952
+ });
1611
1953
  logLatency({
1612
1954
  type: "phase",
1613
1955
  phase: "session_start_snapshot_load",
@@ -1618,6 +1960,7 @@ export async function handleSessionStart(deps) {
1618
1960
  bytes: snapshotBytes,
1619
1961
  fresh: snapshotFresh,
1620
1962
  seq: snapshot?.seq ?? null,
1963
+ reason: snapshotFresh ? undefined : snapshotMissReason,
1621
1964
  ...(snapshotGate.skippedStale ? { skippedStale: true } : {}),
1622
1965
  ...(timedOut ? { sequenceUnknown: true } : {}),
1623
1966
  },
@@ -1627,6 +1970,7 @@ export async function handleSessionStart(deps) {
1627
1970
  root: snapshotRoot,
1628
1971
  currentProjectSeq: freshnessSeq,
1629
1972
  snapshot,
1973
+ missReason: snapshotMissReason,
1630
1974
  });
1631
1975
  const freshSnapshot = snapshotFresh ? snapshot : null;
1632
1976
  if (freshSnapshot) {
@@ -1754,6 +2098,7 @@ export async function handleSessionStart(deps) {
1754
2098
  const jstsHeavyScansWillRun = startupScansWillRun && canRunStartupHeavyScans(languageProfile, "jsts");
1755
2099
  if (allowBootstrapTasks) {
1756
2100
  scheduleDeferredToolProbes(deps, languageProfile, startupDefaults, jstsHeavyScansWillRun, dbg);
2101
+ scheduleManagedToolRefresh(dbg);
1757
2102
  }
1758
2103
  if (allowBootstrapTasks) {
1759
2104
  // Fire-and-forget like other tool probes
@@ -1866,6 +2211,7 @@ export async function handleSessionStart(deps) {
1866
2211
  durationMs: totalDurationMs,
1867
2212
  metadata: { mode: startupMode, reason: deps.sessionReason },
1868
2213
  });
2214
+ logHostReadyDelay(deps, cwd);
1869
2215
  emitSmellsSessionStartLine(dbg, sessionStartMs);
1870
2216
  }
1871
2217
  /**