@herbertgao/pi-extensions 2026.8.4 → 2026.8.6

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 (360) hide show
  1. package/README.md +5 -5
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +11 -3
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +11 -3
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +24 -0
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +91 -1
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +7 -3
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +7 -6
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +21 -12
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +4 -5
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +53 -14
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +22 -12
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +2 -4
  14. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  15. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +18 -0
  16. package/node_modules/@herbertgao/pi-subagents/package.json +8 -4
  17. package/node_modules/@herbertgao/pi-subagents/src/agent-color.ts +72 -67
  18. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +255 -0
  19. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +191 -3
  20. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +20 -4
  21. package/node_modules/@herbertgao/pi-subagents/src/index.ts +289 -113
  22. package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +24 -1
  23. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +24 -2
  24. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +15 -6
  25. package/node_modules/@herbertgao/pi-subagents/src/ui/schedule-menu.ts +9 -8
  26. package/node_modules/@herbertgao/pi-subagents/src/ui/select-item.ts +48 -0
  27. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +9 -5
  28. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +2 -0
  29. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +20 -0
  30. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +6 -0
  31. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +7 -0
  32. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  33. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +7 -0
  34. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +16 -5
  35. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +4 -2
  36. package/node_modules/@narumitw/pi-btw/README.md +32 -24
  37. package/node_modules/@narumitw/pi-btw/package.json +6 -6
  38. package/node_modules/@narumitw/pi-btw/src/btw.ts +4 -2
  39. package/node_modules/@narumitw/pi-btw/src/menu.ts +34 -13
  40. package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
  41. package/node_modules/pi-lens/CHANGELOG.md +2535 -954
  42. package/node_modules/pi-lens/README.md +13 -0
  43. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +73 -18
  44. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +314 -0
  45. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +21 -4
  46. package/node_modules/pi-lens/dist/clients/agent-nudge.js +14 -7
  47. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +68 -16
  48. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -1
  49. package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +37 -10
  50. package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +63 -0
  51. package/node_modules/pi-lens/dist/clients/atomic-write.js +114 -14
  52. package/node_modules/pi-lens/dist/clients/bash-file-access.js +193 -23
  53. package/node_modules/pi-lens/dist/clients/biome-client.js +136 -24
  54. package/node_modules/pi-lens/dist/clients/bootstrap.js +11 -2
  55. package/node_modules/pi-lens/dist/clients/bounded-cache.js +34 -0
  56. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +192 -0
  57. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
  58. package/node_modules/pi-lens/dist/clients/bus-publish.js +19 -4
  59. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +47 -5
  60. package/node_modules/pi-lens/dist/clients/cache-manager.js +97 -9
  61. package/node_modules/pi-lens/dist/clients/cache-observability.js +301 -14
  62. package/node_modules/pi-lens/dist/clients/call-graph.js +449 -117
  63. package/node_modules/pi-lens/dist/clients/cascade-format.js +57 -2
  64. package/node_modules/pi-lens/dist/clients/child-unref.js +85 -0
  65. package/node_modules/pi-lens/dist/clients/codebase-model.js +52 -23
  66. package/node_modules/pi-lens/dist/clients/collateral-test-role.js +40 -0
  67. package/node_modules/pi-lens/dist/clients/complexity-client.js +2 -1
  68. package/node_modules/pi-lens/dist/clients/console-guard-install.js +26 -0
  69. package/node_modules/pi-lens/dist/clients/cooperative-budget.js +42 -0
  70. package/node_modules/pi-lens/dist/clients/dead-code-client.js +137 -31
  71. package/node_modules/pi-lens/dist/clients/debug-handles.js +206 -0
  72. package/node_modules/pi-lens/dist/clients/debug-heap.js +167 -0
  73. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +153 -0
  74. package/node_modules/pi-lens/dist/clients/dependency-checker.js +361 -77
  75. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +65 -13
  76. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +18 -4
  77. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +176 -29
  78. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +23 -7
  79. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +8 -18
  80. package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +8 -5
  81. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +35 -0
  82. package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +17 -8
  83. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +1172 -683
  84. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +14 -0
  85. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +11 -1
  86. package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +50 -0
  87. package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +135 -0
  88. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +23 -5
  89. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +5 -9
  90. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +3 -2
  91. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
  92. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +154 -0
  93. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +3 -2
  94. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +5 -1
  95. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +47 -6
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -9
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +9 -5
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +4 -8
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -3
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -3
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +163 -0
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +37 -5
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +3 -1
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +13 -7
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +226 -0
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +69 -0
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +473 -42
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +97 -0
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
  114. package/node_modules/pi-lens/dist/clients/disposition-publish.js +18 -4
  115. package/node_modules/pi-lens/dist/clients/durable-store.js +97 -0
  116. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
  117. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +94 -3
  118. package/node_modules/pi-lens/dist/clients/extension-log.js +432 -0
  119. package/node_modules/pi-lens/dist/clients/extension-mode.js +73 -0
  120. package/node_modules/pi-lens/dist/clients/file-kinds.js +57 -0
  121. package/node_modules/pi-lens/dist/clients/file-role.js +18 -3
  122. package/node_modules/pi-lens/dist/clients/file-utils.js +56 -34
  123. package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
  124. package/node_modules/pi-lens/dist/clients/format-events-publish.js +51 -7
  125. package/node_modules/pi-lens/dist/clients/format-service.js +24 -8
  126. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +8 -0
  127. package/node_modules/pi-lens/dist/clients/formatters.js +358 -61
  128. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +148 -15
  129. package/node_modules/pi-lens/dist/clients/git-guard.js +678 -11
  130. package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +4 -0
  131. package/node_modules/pi-lens/dist/clients/go-client.js +23 -40
  132. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +124 -7
  133. package/node_modules/pi-lens/dist/clients/grammar-source.js +12 -4
  134. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +21 -3
  135. package/node_modules/pi-lens/dist/clients/host-ports.js +31 -0
  136. package/node_modules/pi-lens/dist/clients/installer/index.js +395 -100
  137. package/node_modules/pi-lens/dist/clients/instance-reaper.js +142 -172
  138. package/node_modules/pi-lens/dist/clients/jscpd-client.js +27 -55
  139. package/node_modules/pi-lens/dist/clients/knip-client.js +58 -42
  140. package/node_modules/pi-lens/dist/clients/language-policy.js +14 -2
  141. package/node_modules/pi-lens/dist/clients/language-profile.js +13 -7
  142. package/node_modules/pi-lens/dist/clients/latency-logger.js +72 -1
  143. package/node_modules/pi-lens/dist/clients/lens-config.js +12 -1
  144. package/node_modules/pi-lens/dist/clients/lens-engine.js +86 -17
  145. package/node_modules/pi-lens/dist/clients/lens-events.js +63 -9
  146. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
  147. package/node_modules/pi-lens/dist/clients/lens-map.js +94 -19
  148. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +73 -0
  149. package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -1
  150. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
  151. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +59 -14
  152. package/node_modules/pi-lens/dist/clients/lsp/client.js +662 -56
  153. package/node_modules/pi-lens/dist/clients/lsp/config.js +13 -2
  154. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +115 -0
  155. package/node_modules/pi-lens/dist/clients/lsp/edits.js +1012 -121
  156. package/node_modules/pi-lens/dist/clients/lsp/index.js +2025 -800
  157. package/node_modules/pi-lens/dist/clients/lsp/launch.js +32 -26
  158. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
  159. package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +83 -0
  160. package/node_modules/pi-lens/dist/clients/lsp/server.js +437 -86
  161. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +96 -0
  162. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
  163. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +18 -3
  164. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
  165. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +8 -0
  166. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +248 -0
  167. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +127 -32
  168. package/node_modules/pi-lens/dist/clients/mcp/ipc.js +157 -24
  169. package/node_modules/pi-lens/dist/clients/mcp/session.js +235 -14
  170. package/node_modules/pi-lens/dist/clients/memory-sampler.js +115 -0
  171. package/node_modules/pi-lens/dist/clients/metrics-client.js +2 -1
  172. package/node_modules/pi-lens/dist/clients/metrics-history.js +2 -1
  173. package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
  174. package/node_modules/pi-lens/dist/clients/module-report-lsp.js +5 -0
  175. package/node_modules/pi-lens/dist/clients/module-report.js +168 -3
  176. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +319 -118
  177. package/node_modules/pi-lens/dist/clients/opengrep-client.js +1 -1
  178. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +120 -5
  179. package/node_modules/pi-lens/dist/clients/path-utils.js +110 -9
  180. package/node_modules/pi-lens/dist/clients/pipeline.js +76 -15
  181. package/node_modules/pi-lens/dist/clients/print-mode.js +21 -0
  182. package/node_modules/pi-lens/dist/clients/project-changes.js +40 -0
  183. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +3 -2
  184. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +1 -1
  185. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +8 -0
  186. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
  187. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +21 -1
  188. package/node_modules/pi-lens/dist/clients/project-lens-config.js +86 -9
  189. package/node_modules/pi-lens/dist/clients/project-report.js +27 -13
  190. package/node_modules/pi-lens/dist/clients/project-snapshot.js +175 -20
  191. package/node_modules/pi-lens/dist/clients/project-trust.js +181 -0
  192. package/node_modules/pi-lens/dist/clients/quiet-window.js +18 -4
  193. package/node_modules/pi-lens/dist/clients/read-bridge.js +147 -0
  194. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +159 -1
  195. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +150 -29
  196. package/node_modules/pi-lens/dist/clients/read-guard.js +185 -4
  197. package/node_modules/pi-lens/dist/clients/resource-sampler.js +70 -84
  198. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +1122 -221
  199. package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +2 -1
  200. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +21 -10
  201. package/node_modules/pi-lens/dist/clients/review-graph/query.js +24 -0
  202. package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +46 -0
  203. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +57 -6
  204. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +51 -3
  205. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +30 -1
  206. package/node_modules/pi-lens/dist/clients/ruff-client.js +27 -44
  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 +257 -77
  209. package/node_modules/pi-lens/dist/clients/runtime-context.js +129 -12
  210. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +242 -26
  211. package/node_modules/pi-lens/dist/clients/runtime-session.js +428 -117
  212. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +71 -48
  213. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +342 -33
  214. package/node_modules/pi-lens/dist/clients/runtime-turn.js +619 -93
  215. package/node_modules/pi-lens/dist/clients/rust-client.js +23 -38
  216. package/node_modules/pi-lens/dist/clients/safe-spawn.js +777 -62
  217. package/node_modules/pi-lens/dist/clients/security-scan-client.js +90 -6
  218. package/node_modules/pi-lens/dist/clients/session-state-store.js +3 -2
  219. package/node_modules/pi-lens/dist/clients/sg-runner.js +424 -196
  220. package/node_modules/pi-lens/dist/clients/slow-fs.js +2 -1
  221. package/node_modules/pi-lens/dist/clients/smells-rollup.js +231 -0
  222. package/node_modules/pi-lens/dist/clients/source-filter.js +184 -20
  223. package/node_modules/pi-lens/dist/clients/source-walker.js +66 -10
  224. package/node_modules/pi-lens/dist/clients/startup-scan.js +15 -16
  225. package/node_modules/pi-lens/dist/clients/startup-timing.js +7 -1
  226. package/node_modules/pi-lens/dist/clients/test-runner-client.js +469 -32
  227. package/node_modules/pi-lens/dist/clients/tool-event.js +35 -0
  228. package/node_modules/pi-lens/dist/clients/tool-policy.js +390 -39
  229. package/node_modules/pi-lens/dist/clients/tool-render.js +155 -0
  230. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
  231. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +6 -1
  232. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +419 -50
  233. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +21 -0
  234. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +7 -1
  235. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +7 -2
  236. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +44 -7
  237. package/node_modules/pi-lens/dist/clients/user-notify.js +51 -0
  238. package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +57 -0
  239. package/node_modules/pi-lens/dist/clients/warm-attach.js +50 -13
  240. package/node_modules/pi-lens/dist/clients/widget-state.js +300 -43
  241. package/node_modules/pi-lens/dist/clients/word-index.js +680 -81
  242. package/node_modules/pi-lens/dist/clients/workspace-topology.js +89 -6
  243. package/node_modules/pi-lens/dist/index.js +51785 -36118
  244. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +182 -29
  245. package/node_modules/pi-lens/dist/mcp/server.js +219 -46
  246. package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
  247. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
  248. package/node_modules/pi-lens/dist/tools/ast-grep-search.js +93 -27
  249. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
  250. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +203 -38
  251. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +210 -42
  252. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -3
  253. package/node_modules/pi-lens/dist/tools/module-report.js +9 -0
  254. package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
  255. package/node_modules/pi-lens/docs/agent-guide.md +40 -15
  256. package/node_modules/pi-lens/docs/agent-tools.md +21 -10
  257. package/node_modules/pi-lens/docs/analysisall.md +192 -0
  258. package/node_modules/pi-lens/docs/api-ports-inventory.md +165 -0
  259. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +14 -3
  260. package/node_modules/pi-lens/docs/durable-store-audit-1202.md +32 -0
  261. package/node_modules/pi-lens/docs/environment-variables.md +44 -0
  262. package/node_modules/pi-lens/docs/fable.md +27 -0
  263. package/node_modules/pi-lens/docs/features.md +45 -3
  264. package/node_modules/pi-lens/docs/globalconfig.md +56 -1
  265. package/node_modules/pi-lens/docs/language-coverage.md +4 -3
  266. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +2 -2
  267. package/node_modules/pi-lens/docs/mcp.md +37 -4
  268. package/node_modules/pi-lens/docs/servercapabilities.md +3 -3
  269. package/node_modules/pi-lens/docs/settings.md +18 -1
  270. package/node_modules/pi-lens/docs/tools.md +4 -1
  271. package/node_modules/pi-lens/docs/usage.md +41 -5
  272. package/node_modules/pi-lens/docs/word-index.md +35 -0
  273. package/node_modules/pi-lens/package.json +10 -10
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +8 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +7 -0
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +7 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +8 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +8 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +21 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +9 -0
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +9 -0
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +13 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +16 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +27 -0
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
  300. package/node_modules/pi-lens/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
  301. package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +2 -2
  302. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +134 -2
  303. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +10 -11
  304. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
  305. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +8 -114
  306. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +129 -0
  307. package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
  308. package/node_modules/pi-mcp-adapter/CHANGELOG.md +38 -0
  309. package/node_modules/pi-mcp-adapter/README.md +38 -4
  310. package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
  311. package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +1 -1
  312. package/node_modules/pi-mcp-adapter/cli.js +25 -4
  313. package/node_modules/pi-mcp-adapter/commands.ts +15 -7
  314. package/node_modules/pi-mcp-adapter/config.ts +4 -4
  315. package/node_modules/pi-mcp-adapter/direct-tools.ts +58 -8
  316. package/node_modules/pi-mcp-adapter/host-html-template.ts +0 -8
  317. package/node_modules/pi-mcp-adapter/index.ts +8 -3
  318. package/node_modules/pi-mcp-adapter/init.ts +29 -5
  319. package/node_modules/pi-mcp-adapter/lifecycle.ts +0 -4
  320. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +1 -2
  321. package/node_modules/pi-mcp-adapter/mcp-auth.ts +60 -2
  322. package/node_modules/pi-mcp-adapter/mcp-panel.ts +45 -9
  323. package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +22 -20
  324. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
  325. package/node_modules/pi-mcp-adapter/metadata-cache.ts +37 -4
  326. package/node_modules/pi-mcp-adapter/npx-resolver.ts +81 -26
  327. package/node_modules/pi-mcp-adapter/package.json +4 -3
  328. package/node_modules/pi-mcp-adapter/proxy-modes.ts +108 -26
  329. package/node_modules/pi-mcp-adapter/request-headers-command.ts +336 -0
  330. package/node_modules/pi-mcp-adapter/search-ranking.ts +86 -7
  331. package/node_modules/pi-mcp-adapter/server-manager.ts +41 -7
  332. package/node_modules/pi-mcp-adapter/session-recovery.ts +8 -2
  333. package/node_modules/pi-mcp-adapter/tool-approval.ts +48 -7
  334. package/node_modules/pi-mcp-adapter/tool-metadata.ts +62 -4
  335. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +138 -12
  336. package/node_modules/pi-mcp-adapter/types.ts +176 -27
  337. package/node_modules/pi-mcp-adapter/ui-server.ts +20 -1
  338. package/node_modules/pi-web-access/CHANGELOG.md +14 -0
  339. package/node_modules/pi-web-access/README.md +18 -12
  340. package/node_modules/pi-web-access/auth-fetch.ts +148 -0
  341. package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
  342. package/node_modules/pi-web-access/curator-page.ts +5 -3
  343. package/node_modules/pi-web-access/curator-server.ts +2 -1
  344. package/node_modules/pi-web-access/extract.ts +106 -34
  345. package/node_modules/pi-web-access/fetch-params.ts +17 -3
  346. package/node_modules/pi-web-access/firecrawl.ts +172 -12
  347. package/node_modules/pi-web-access/gemini-search.ts +18 -4
  348. package/node_modules/pi-web-access/index.ts +120 -48
  349. package/node_modules/pi-web-access/package.json +2 -2
  350. package/node_modules/pi-web-access/summary-review.ts +11 -5
  351. package/node_modules/pi-web-access/youtube-extract.ts +2 -2
  352. package/package.json +12 -13
  353. package/node_modules/pi-lens/dist/clients/ast-grep-parser.js +0 -86
  354. package/node_modules/pi-lens/dist/clients/lsp/interactive-install.js +0 -367
  355. package/node_modules/pi-lens/dist/clients/lsp/lsp-index.js +0 -10
  356. package/node_modules/pi-lens/dist/clients/lsp/server-strategies.js +0 -5
  357. package/node_modules/pi-lens/dist/clients/project-metadata.js +0 -690
  358. package/node_modules/pi-lens/dist/clients/source-groups.js +0 -96
  359. package/node_modules/pi-lens/dist/clients/startup-marker.js +0 -6
  360. package/node_modules/pi-lens/dist/clients/types.js +0 -11
@@ -9,20 +9,277 @@
9
9
  * session's actual cacheRead/cacheWrite behavior is queryable after the
10
10
  * fact. This is what the provider DID, not what we hoped it would do.
11
11
  *
12
- * 2. Request-side prefix stability (`cache_prefix_break`) — a content hash of
12
+ * 2. Request-side context observations (`cache_context`) — one bounded record
13
+ * for every `context` call, describing pi-lens's own before/after message
14
+ * transformation, placement, injection sources, and privacy-preserving
15
+ * hashes. This is what pi-lens observed locally, not the final provider
16
+ * request after other context handlers have run and not a provider cache
17
+ * result.
18
+ *
19
+ * 3. Request-side prefix stability (`cache_prefix_break`) — a content hash of
13
20
  * `messages[0]` observed on every `context` call. After #1016 the first
14
21
  * message must stay byte-stable across a whole session; a logged CHANGE
15
- * flags that something (pi-lens or otherwise) broke the cache prefix, which
16
- * would silently invalidate the entire prompt-cache on every prefix-caching
17
- * provider. Pure observation — it never alters context injection.
22
+ * flags that something (pi-lens or otherwise) broke the cache prefix. This
23
+ * remains a local observation, never a provider cache-miss claim.
18
24
  *
19
- * Both paths are defensive: `usage` (and its fields) may be absent on older
25
+ * All paths are defensive: `usage` (and its fields) may be absent on older
20
26
  * hosts or non-assistant messages, so every access is guarded and the handlers
21
27
  * never throw — on error they `dbg(...)` and no-op, like the other index.ts
22
28
  * event handlers.
23
29
  */
24
30
  import { createHash } from "node:crypto";
25
31
  import { logLatency } from "./latency-logger.js";
32
+ /** RuntimeCoordinator's counter is process-global when sessions share a host. */
33
+ const PROCESS_TURN_SCOPE = "process-global-runtime";
34
+ const SECONDARY_TURN_SCOPE = "unavailable-concurrent-secondary";
35
+ /**
36
+ * Keep context telemetry bounded even when a tool result or user message is
37
+ * unexpectedly large. This is a hash input only; none of the sampled text is
38
+ * written to the log.
39
+ */
40
+ const MAX_HASHED_MESSAGES = 64;
41
+ const MAX_HASHED_CONTENT_CHARS = 2048;
42
+ const MAX_REPORTED_MESSAGES = 10_000;
43
+ const MAX_INJECTED_CHARS = 16_384;
44
+ const MAX_INJECTED_BYTES = 65_536;
45
+ let contextObservationCounter = 0;
46
+ function boundedHashScalar(value, state) {
47
+ if (value === null)
48
+ return "null";
49
+ if (value === undefined)
50
+ return "undefined";
51
+ if (typeof value === "string") {
52
+ const truncated = value.length > MAX_HASHED_CONTENT_CHARS;
53
+ if (truncated)
54
+ state.contentTruncated = true;
55
+ const suffix = truncated ? ":content-truncated" : "";
56
+ return `string:${value.length}:${value.slice(0, MAX_HASHED_CONTENT_CHARS)}${suffix}`;
57
+ }
58
+ if (typeof value === "number" ||
59
+ typeof value === "boolean" ||
60
+ typeof value === "bigint") {
61
+ return `${typeof value}:${String(value)}`;
62
+ }
63
+ return typeof value;
64
+ }
65
+ function boundedHashArray(value, depth, seen, state) {
66
+ if (value.length > 12)
67
+ state.contentTruncated = true;
68
+ const items = value
69
+ .slice(0, 12)
70
+ .map((item) => boundedHashValue(item, depth + 1, seen, state));
71
+ return `array:${value.length}:[${items.join(",")}]`;
72
+ }
73
+ function boundedHashObject(value, depth, seen, state) {
74
+ const keys = Object.keys(value).sort((left, right) => left.localeCompare(right));
75
+ if (keys.length > 24)
76
+ state.contentTruncated = true;
77
+ const fields = keys
78
+ .slice(0, 24)
79
+ .map((key) => `${key}:${boundedHashValue(value[key], depth + 1, seen, state)}`);
80
+ return `object:${keys.length}:{${fields.join(",")}}`;
81
+ }
82
+ function boundedHashValue(value, depth = 0, seen = new Set(), state) {
83
+ const hashState = state ?? { contentTruncated: false };
84
+ if (value === null || typeof value !== "object") {
85
+ return boundedHashScalar(value, hashState);
86
+ }
87
+ if (depth >= 3) {
88
+ hashState.contentTruncated = true;
89
+ return Object.prototype.toString.call(value);
90
+ }
91
+ if (seen.has(value))
92
+ return "[cycle]";
93
+ seen.add(value);
94
+ try {
95
+ if (Array.isArray(value)) {
96
+ return boundedHashArray(value, depth, seen, hashState);
97
+ }
98
+ return boundedHashObject(value, depth, seen, hashState);
99
+ }
100
+ catch {
101
+ return "[unreadable]";
102
+ }
103
+ finally {
104
+ seen.delete(value);
105
+ }
106
+ }
107
+ function hashMessageSequence(messages) {
108
+ const hash = createHash("sha256");
109
+ const state = { contentTruncated: false };
110
+ hash.update(`message-count:${messages.length};`);
111
+ const sampled = Math.min(messages.length, MAX_HASHED_MESSAGES);
112
+ for (let i = 0; i < sampled; i++) {
113
+ const message = messages[i];
114
+ const seen = new Set();
115
+ hash.update(`${i}|role:${boundedHashValue(message?.role, 0, seen, state)}|content:${boundedHashValue(message?.content, 0, seen, state)};`);
116
+ }
117
+ const truncated = messages.length > sampled;
118
+ if (truncated)
119
+ hash.update(`truncated-after:${sampled};`);
120
+ return {
121
+ hash: hash.digest("hex"),
122
+ truncated,
123
+ contentTruncated: state.contentTruncated,
124
+ };
125
+ }
126
+ function messageTextSize(content) {
127
+ if (typeof content === "string") {
128
+ const chars = Math.min(content.length, MAX_INJECTED_CHARS);
129
+ return {
130
+ chars,
131
+ bytes: Math.min(Buffer.byteLength(content.slice(0, chars), "utf8"), MAX_INJECTED_BYTES),
132
+ };
133
+ }
134
+ const bounded = boundedHashValue(content);
135
+ return {
136
+ chars: Math.min(bounded.length, MAX_INJECTED_CHARS),
137
+ bytes: Math.min(Buffer.byteLength(bounded.slice(0, MAX_INJECTED_CHARS), "utf8"), MAX_INJECTED_BYTES),
138
+ };
139
+ }
140
+ function measureInjectedMessages(messages) {
141
+ let chars = 0;
142
+ let bytes = 0;
143
+ const measuredCount = Math.min(messages.length, MAX_REPORTED_MESSAGES);
144
+ for (let i = 0; i < measuredCount; i++) {
145
+ const message = messages[i];
146
+ const size = messageTextSize(message?.content);
147
+ chars = Math.min(MAX_INJECTED_CHARS, chars + size.chars);
148
+ bytes = Math.min(MAX_INJECTED_BYTES, bytes + size.bytes);
149
+ if (chars === MAX_INJECTED_CHARS || bytes === MAX_INJECTED_BYTES)
150
+ break;
151
+ }
152
+ return {
153
+ chars,
154
+ bytes,
155
+ capped: chars >= MAX_INJECTED_CHARS || bytes >= MAX_INJECTED_BYTES,
156
+ };
157
+ }
158
+ function sessionKey(sessionId) {
159
+ return sessionId?.trim() ? sessionId.trim() : NO_SESSION_KEY;
160
+ }
161
+ function prefixLengthForPlacement(placement, messageCount) {
162
+ if (placement === "insert-before-final") {
163
+ return Math.max(0, messageCount - 1);
164
+ }
165
+ if (placement === "prepend")
166
+ return 0;
167
+ return messageCount;
168
+ }
169
+ function resolvePrefixObservation(truncated, observation) {
170
+ if (truncated)
171
+ return "unknown";
172
+ return observation ?? "empty";
173
+ }
174
+ function prefixBaselineForObservation(observation) {
175
+ if (observation === "baseline")
176
+ return true;
177
+ if (observation === "empty" || observation === "unknown")
178
+ return null;
179
+ return false;
180
+ }
181
+ function firstMessageChangeFor(truncated, before, after) {
182
+ if (truncated)
183
+ return "unknown";
184
+ return before !== after ? "changed" : "unchanged";
185
+ }
186
+ /**
187
+ * Log one bounded request-side observation for every `context` call. This is
188
+ * deliberately a separate phase from `cache_prefix_break`: it describes the
189
+ * messages pi-lens saw and returned from its own context handler, not the final
190
+ * provider request after other context handlers and not whether a provider
191
+ * reused or missed a prompt cache. `observationId` is local to this process; the
192
+ * host currently exposes no request id on ContextEvent/MessageEndEvent. A
193
+ * RuntimeCoordinator turn is process-global, so it is explicitly scoped as
194
+ * such and is omitted for concurrent secondary sessions.
195
+ */
196
+ export function observeCacheContext(args) {
197
+ try {
198
+ const existingMessages = args.existingMessages ?? [];
199
+ const resultMessages = args.resultMessages ?? existingMessages;
200
+ const injectedMessages = args.injectedMessages ?? [];
201
+ const placement = args.placement ?? "none";
202
+ const beforeSequence = hashMessageSequence(existingMessages);
203
+ const afterSequence = hashMessageSequence(resultMessages);
204
+ const beforePrefixLength = prefixLengthForPlacement(placement, existingMessages.length);
205
+ const afterPrefixLength = Math.min(beforePrefixLength, resultMessages.length);
206
+ const beforePrefix = hashMessageSequence(existingMessages.slice(0, beforePrefixLength));
207
+ const afterPrefix = hashMessageSequence(resultMessages.slice(0, afterPrefixLength));
208
+ const beforeFirstSequence = existingMessages.length
209
+ ? hashMessageSequence([existingMessages[0]])
210
+ : undefined;
211
+ const afterFirstSequence = resultMessages.length
212
+ ? hashMessageSequence([resultMessages[0]])
213
+ : undefined;
214
+ const beforeFirst = beforeFirstSequence?.hash ?? null;
215
+ const afterFirst = afterFirstSequence?.hash ?? null;
216
+ const firstMessageHashTruncated = beforeFirstSequence?.truncated === true ||
217
+ beforeFirstSequence?.contentTruncated === true ||
218
+ afterFirstSequence?.truncated === true ||
219
+ afterFirstSequence?.contentTruncated === true;
220
+ const prefixHashTruncated = beforePrefix.truncated ||
221
+ afterPrefix.truncated ||
222
+ beforePrefix.contentTruncated ||
223
+ afterPrefix.contentTruncated;
224
+ const prefixObservation = resolvePrefixObservation(prefixHashTruncated, args.prefixObservation);
225
+ const sizes = measureInjectedMessages(injectedMessages);
226
+ const messageCountCapped = existingMessages.length > MAX_REPORTED_MESSAGES ||
227
+ resultMessages.length > MAX_REPORTED_MESSAGES;
228
+ logLatency({
229
+ type: "phase",
230
+ filePath: "<pi-lens>",
231
+ phase: "cache_context",
232
+ durationMs: 0,
233
+ metadata: {
234
+ version: 1,
235
+ observedStage: "pi-lens-context-handler",
236
+ observationId: `ctx-${(++contextObservationCounter).toString(36)}`,
237
+ sessionId: sessionKey(args.sessionId),
238
+ sessionRole: args.sessionRole,
239
+ ...(args.sessionRole === "concurrent-secondary"
240
+ ? { turnScope: SECONDARY_TURN_SCOPE }
241
+ : { turnIndex: args.turnIndex, turnScope: PROCESS_TURN_SCOPE }),
242
+ injectionEnabled: args.injectionEnabled,
243
+ injectionSources: Array.from(args.injectionSources ?? []),
244
+ injectedMessageCount: Math.min(injectedMessages.length, MAX_REPORTED_MESSAGES),
245
+ injectedMessageCountCapped: injectedMessages.length > MAX_REPORTED_MESSAGES,
246
+ injectedChars: sizes.chars,
247
+ injectedBytes: sizes.bytes,
248
+ injectedCountsCapped: sizes.capped,
249
+ existingMessageCount: Math.min(existingMessages.length, MAX_REPORTED_MESSAGES),
250
+ resultMessageCount: Math.min(resultMessages.length, MAX_REPORTED_MESSAGES),
251
+ messageCountCapped,
252
+ placement,
253
+ prefixObservation,
254
+ prefixObservationUnknown: prefixObservation === "unknown",
255
+ prefixBaseline: prefixBaselineForObservation(prefixObservation),
256
+ firstMessageChanged: firstMessageHashTruncated
257
+ ? null
258
+ : beforeFirst !== afterFirst,
259
+ firstMessageChange: firstMessageChangeFor(firstMessageHashTruncated, beforeFirst, afterFirst),
260
+ firstMessageHashTruncated,
261
+ beforeFirstMessageHash: beforeFirst,
262
+ afterFirstMessageHash: afterFirst,
263
+ beforeSequenceHash: beforeSequence.hash,
264
+ afterSequenceHash: afterSequence.hash,
265
+ beforePrefixHash: beforePrefix.hash,
266
+ afterPrefixHash: afterPrefix.hash,
267
+ sequenceHashTruncated: beforeSequence.truncated ||
268
+ afterSequence.truncated ||
269
+ beforeSequence.contentTruncated ||
270
+ afterSequence.contentTruncated,
271
+ sequenceMessageCountTruncated: beforeSequence.truncated || afterSequence.truncated,
272
+ sequenceContentHashTruncated: beforeSequence.contentTruncated || afterSequence.contentTruncated,
273
+ prefixHashTruncated,
274
+ prefixMessageCountTruncated: beforePrefix.truncated || afterPrefix.truncated,
275
+ prefixContentHashTruncated: beforePrefix.contentTruncated || afterPrefix.contentTruncated,
276
+ },
277
+ });
278
+ }
279
+ catch (err) {
280
+ args.dbg?.(`cache-context: failed to log context observation: ${err}`);
281
+ }
282
+ }
26
283
  /**
27
284
  * Part 1 — log one `cache_usage` record for an assistant `message_end` that
28
285
  * carries a `usage`. Provider/model come straight off the message itself
@@ -31,7 +288,7 @@ import { logLatency } from "./latency-logger.js";
31
288
  * not an assistant message or has no usage; never logs a zeros-only record for
32
289
  * a message that simply lacks usage.
33
290
  */
34
- export function logCacheUsage(message, dbg) {
291
+ export function logCacheUsage(message, dbg, context) {
35
292
  try {
36
293
  if (!message || typeof message !== "object")
37
294
  return;
@@ -58,6 +315,24 @@ export function logCacheUsage(message, dbg) {
58
315
  output: u.output,
59
316
  // `Usage.cost` is a breakdown object; the total is the headline number.
60
317
  cost: u.cost?.total,
318
+ ...(context
319
+ ? {
320
+ // MessageEndEvent has no request/context id in the host API. These
321
+ // fields permit session correlation without inventing an exact
322
+ // provider-request linkage. The process-global turn is omitted
323
+ // for a concurrent secondary session.
324
+ sessionId: sessionKey(context.sessionId),
325
+ ...(context.sessionRole === "concurrent-secondary"
326
+ ? { turnScope: SECONDARY_TURN_SCOPE }
327
+ : {
328
+ turnIndex: context.turnIndex,
329
+ turnScope: PROCESS_TURN_SCOPE,
330
+ }),
331
+ contextCorrelation: context.sessionId
332
+ ? "session-only-no-request-id"
333
+ : "no-stable-session-id",
334
+ }
335
+ : {}),
61
336
  },
62
337
  });
63
338
  }
@@ -71,7 +346,13 @@ export function logCacheUsage(message, dbg) {
71
346
  * `messages[0]` actually changed byte-for-byte.
72
347
  */
73
348
  function hashFirstMessage(first) {
74
- const serialized = JSON.stringify({ role: first.role, content: first.content });
349
+ // Keep the original full local hash semantics for the existing
350
+ // `cache_prefix_break` signal. The new `cache_context` hashes above are the
351
+ // bounded hashes; this signal must not silently miss a suffix-only change.
352
+ const serialized = JSON.stringify({
353
+ role: first.role,
354
+ content: first.content,
355
+ });
75
356
  return createHash("sha256").update(serialized).digest("hex");
76
357
  }
77
358
  /**
@@ -144,11 +425,11 @@ function recordSessionHash(key, hash) {
144
425
  export function observeCachePrefix(messages, turnIndex, sessionId, sessionRole, dbg) {
145
426
  try {
146
427
  if (!messages || messages.length === 0)
147
- return;
428
+ return "empty";
148
429
  const first = messages[0];
149
430
  if (!first || typeof first !== "object")
150
- return;
151
- const key = sessionId?.trim() ? sessionId.trim() : NO_SESSION_KEY;
431
+ return "empty";
432
+ const key = sessionKey(sessionId);
152
433
  const currentHash = hashFirstMessage(first);
153
434
  const previousHash = prefixHashBySession.get(key);
154
435
  if (previousHash === undefined) {
@@ -161,7 +442,9 @@ export function observeCachePrefix(messages, turnIndex, sessionId, sessionRole,
161
442
  phase: "cache_prefix_break",
162
443
  durationMs: 0,
163
444
  metadata: {
164
- turnIndex,
445
+ ...(sessionRole === "concurrent-secondary"
446
+ ? { turnScope: SECONDARY_TURN_SCOPE }
447
+ : { turnIndex, turnScope: PROCESS_TURN_SCOPE }),
165
448
  previousHash: null,
166
449
  currentHash,
167
450
  baseline: true,
@@ -169,7 +452,7 @@ export function observeCachePrefix(messages, turnIndex, sessionId, sessionRole,
169
452
  sessionRole,
170
453
  },
171
454
  });
172
- return;
455
+ return "baseline";
173
456
  }
174
457
  if (currentHash !== previousHash) {
175
458
  logLatency({
@@ -178,7 +461,9 @@ export function observeCachePrefix(messages, turnIndex, sessionId, sessionRole,
178
461
  phase: "cache_prefix_break",
179
462
  durationMs: 0,
180
463
  metadata: {
181
- turnIndex,
464
+ ...(sessionRole === "concurrent-secondary"
465
+ ? { turnScope: SECONDARY_TURN_SCOPE }
466
+ : { turnIndex, turnScope: PROCESS_TURN_SCOPE }),
182
467
  previousHash,
183
468
  currentHash,
184
469
  sessionId: key,
@@ -189,9 +474,11 @@ export function observeCachePrefix(messages, turnIndex, sessionId, sessionRole,
189
474
  // Refresh recency (and update the stored hash after a break) so an active
190
475
  // session stays warm in the LRU and isn't evicted while still in use.
191
476
  recordSessionHash(key, currentHash);
477
+ return currentHash === previousHash ? "unchanged" : "changed";
192
478
  }
193
479
  catch (err) {
194
480
  dbg?.(`cache-prefix: failed to observe messages[0]: ${err}`);
481
+ return "empty";
195
482
  }
196
483
  }
197
484
  /**
@@ -202,7 +489,7 @@ export function observeCachePrefix(messages, turnIndex, sessionId, sessionRole,
202
489
  * evicts it. Idempotent and never throws.
203
490
  */
204
491
  export function clearCachePrefixSession(sessionId) {
205
- const key = sessionId?.trim() ? sessionId.trim() : NO_SESSION_KEY;
492
+ const key = sessionKey(sessionId);
206
493
  prefixHashBySession.delete(key);
207
494
  }
208
495
  /** Clear all per-session prefix hashes. For tests / session boundaries. */