@herbertgao/pi-extensions 2026.10.1 → 2026.10.3

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 (934) hide show
  1. package/README.md +14 -17
  2. package/THIRD_PARTY_NOTICES.md +2 -2
  3. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-audit.md +2 -2
  4. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-debt.md +2 -2
  5. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-gain.md +1 -1
  6. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-help.md +1 -1
  7. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-review.md +2 -2
  8. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail.md +1 -1
  9. package/node_modules/@dietrichgebert/ponytail/.qoder/rules/ponytail.md +21 -21
  10. package/node_modules/@dietrichgebert/ponytail/.qoder-plugin/plugin.json +1 -1
  11. package/node_modules/@dietrichgebert/ponytail/AGENTS.md +21 -21
  12. package/node_modules/@dietrichgebert/ponytail/README.md +68 -72
  13. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-activate.js +7 -0
  14. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-instructions.js +30 -30
  15. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-map.js +103 -0
  16. package/node_modules/@dietrichgebert/ponytail/package.json +2 -2
  17. package/node_modules/@dietrichgebert/ponytail/skills/ponytail/SKILL.md +33 -101
  18. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-audit/SKILL.md +89 -32
  19. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-debt/SKILL.md +13 -12
  20. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-gain/SKILL.md +17 -16
  21. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-help/SKILL.md +9 -10
  22. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-review/SKILL.md +96 -57
  23. package/node_modules/{remote-pi → @herbertgao/pi-agency}/LICENSE +1 -1
  24. package/node_modules/@herbertgao/pi-agency/README.md +40 -0
  25. package/node_modules/@herbertgao/pi-agency/package.json +53 -0
  26. package/node_modules/@herbertgao/pi-agency/src/index.ts +441 -0
  27. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +12 -12
  28. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +30 -6
  29. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +15 -9
  30. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +27 -9
  31. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/codemode.ts +4 -1
  32. package/node_modules/@herbertgao/pi-cc-extensions/package.json +2 -2
  33. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +8 -0
  34. package/node_modules/@herbertgao/pi-subagents/README.md +1 -1
  35. package/node_modules/@herbertgao/pi-subagents/package.json +4 -5
  36. package/node_modules/@herbertgao/pi-subagents/src/index.ts +1 -1
  37. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +1 -1
  38. package/node_modules/@herbertgao/pi-subagents/src/mention-clone.ts +48 -72
  39. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +1 -1
  40. package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +15 -6
  41. package/node_modules/@herbertgao/pi-subagents/src/workflow/collisions.ts +4 -1
  42. package/node_modules/@herbertgao/pi-subagents/src/workflow/json-schema.ts +9 -8
  43. package/node_modules/@narumitw/pi-caffeinate/dist/index.ts +6 -1
  44. package/node_modules/@narumitw/pi-caffeinate/dist/index.ts.map +2 -2
  45. package/node_modules/@narumitw/pi-caffeinate/package.json +3 -3
  46. package/node_modules/@narumitw/pi-caffeinate/src/dbus-inhibit.ts +13 -1
  47. package/node_modules/@tifan/pi-mermaid-open/package.json +5 -1
  48. package/node_modules/@tifan/pi-mermaid-open/src/index.ts +26 -9
  49. package/node_modules/@tifan/pi-recap/package.json +1 -1
  50. package/node_modules/@tifan/pi-recap/src/index.ts +104 -20
  51. package/node_modules/@tifan/pi-recap/src/models.ts +46 -4
  52. package/node_modules/pi-antigravity/README.md +23 -2
  53. package/node_modules/pi-antigravity/package.json +5 -3
  54. package/node_modules/pi-antigravity/src/diagnostics/diagnostics.ts +3 -2
  55. package/node_modules/pi-antigravity/src/features/generate-image.ts +72 -0
  56. package/node_modules/pi-antigravity/src/features/google-search.ts +71 -0
  57. package/node_modules/pi-antigravity/src/image/image.ts +86 -47
  58. package/node_modules/pi-antigravity/src/image/native.ts +69 -0
  59. package/node_modules/pi-antigravity/src/index.ts +52 -137
  60. package/node_modules/pi-antigravity/src/search/search.ts +130 -31
  61. package/node_modules/pi-antigravity/src/stream/stream.ts +162 -113
  62. package/node_modules/pi-antigravity/src/types/enums.ts +1 -0
  63. package/node_modules/pi-antigravity/src/types/types.ts +1 -1
  64. package/node_modules/pi-antigravity/src/utils/security.ts +47 -1
  65. package/node_modules/pi-antigravity/src/utils/util.ts +38 -6
  66. package/node_modules/pi-lens/CHANGELOG.md +1159 -0
  67. package/node_modules/pi-lens/README.md +116 -25
  68. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +6 -2
  69. package/node_modules/pi-lens/dist/chunk-2BEMIWEW.js +51 -0
  70. package/node_modules/pi-lens/dist/chunk-2EECBNC5.js +89 -0
  71. package/node_modules/pi-lens/dist/chunk-2FQ62SPM.js +83 -0
  72. package/node_modules/pi-lens/dist/chunk-2UMYMRI3.js +118 -0
  73. package/node_modules/pi-lens/dist/chunk-34OCDWOS.js +48 -0
  74. package/node_modules/pi-lens/dist/chunk-3FERWMCH.js +144 -0
  75. package/node_modules/pi-lens/dist/chunk-3JL6IFD7.js +97 -0
  76. package/node_modules/pi-lens/dist/chunk-3JSEWUWI.js +327 -0
  77. package/node_modules/pi-lens/dist/chunk-3MED75HT.js +37 -0
  78. package/node_modules/pi-lens/dist/chunk-3MRXIJ6I.js +79 -0
  79. package/node_modules/pi-lens/dist/chunk-3P4PF2BX.js +2250 -0
  80. package/node_modules/pi-lens/dist/chunk-3PNFWVHT.js +201 -0
  81. package/node_modules/pi-lens/dist/chunk-3TTPKMEF.js +162 -0
  82. package/node_modules/pi-lens/dist/chunk-5JHXUETW.js +326 -0
  83. package/node_modules/pi-lens/dist/chunk-5W3R6LFT.js +24 -0
  84. package/node_modules/pi-lens/dist/chunk-64LU2OVH.js +341 -0
  85. package/node_modules/pi-lens/dist/chunk-6B5I6ICN.js +44 -0
  86. package/node_modules/pi-lens/dist/chunk-6P54TMWL.js +274 -0
  87. package/node_modules/pi-lens/dist/chunk-6UYA642J.js +145 -0
  88. package/node_modules/pi-lens/dist/chunk-6XX5HCLK.js +398 -0
  89. package/node_modules/pi-lens/dist/chunk-7AXO3ZDF.js +30 -0
  90. package/node_modules/pi-lens/dist/chunk-7BX5DWSA.js +1907 -0
  91. package/node_modules/pi-lens/dist/chunk-7ELY2CSM.js +902 -0
  92. package/node_modules/pi-lens/dist/chunk-7M4T5N3V.js +38 -0
  93. package/node_modules/pi-lens/dist/chunk-7U2IDNXN.js +37 -0
  94. package/node_modules/pi-lens/dist/chunk-ABBOA7UD.js +157 -0
  95. package/node_modules/pi-lens/dist/chunk-AFZYMAIV.js +756 -0
  96. package/node_modules/pi-lens/dist/chunk-ALQRYH6O.js +112 -0
  97. package/node_modules/pi-lens/dist/chunk-AXKPQ6ZI.js +1610 -0
  98. package/node_modules/pi-lens/dist/chunk-BHWHXMQ2.js +62 -0
  99. package/node_modules/pi-lens/dist/chunk-BJF25SUG.js +22 -0
  100. package/node_modules/pi-lens/dist/chunk-BNKLYOOO.js +18 -0
  101. package/node_modules/pi-lens/dist/chunk-BW5KFIQC.js +59 -0
  102. package/node_modules/pi-lens/dist/chunk-CBD2B5CP.js +599 -0
  103. package/node_modules/pi-lens/dist/chunk-CFYISHUW.js +42 -0
  104. package/node_modules/pi-lens/dist/chunk-CMU4RMJB.js +30 -0
  105. package/node_modules/pi-lens/dist/chunk-CULQYFLU.js +14 -0
  106. package/node_modules/pi-lens/dist/chunk-D65GOCJB.js +289 -0
  107. package/node_modules/pi-lens/dist/chunk-D6YF5HKX.js +59 -0
  108. package/node_modules/pi-lens/dist/chunk-DMS52BEP.js +342 -0
  109. package/node_modules/pi-lens/dist/chunk-ELVWJYRV.js +211 -0
  110. package/node_modules/pi-lens/dist/chunk-EUT3B22Y.js +27 -0
  111. package/node_modules/pi-lens/dist/chunk-F5VLZHUW.js +146 -0
  112. package/node_modules/pi-lens/dist/chunk-FC4RP4I2.js +12 -0
  113. package/node_modules/pi-lens/dist/chunk-FGALODG3.js +55 -0
  114. package/node_modules/pi-lens/dist/chunk-G3RLJYA2.js +2358 -0
  115. package/node_modules/pi-lens/dist/chunk-GAYGGICG.js +32 -0
  116. package/node_modules/pi-lens/dist/chunk-H2W2AXD3.js +419 -0
  117. package/node_modules/pi-lens/dist/chunk-H5EV3VMF.js +114 -0
  118. package/node_modules/pi-lens/dist/chunk-HREZQCT3.js +106 -0
  119. package/node_modules/pi-lens/dist/chunk-HSIRMK2D.js +517 -0
  120. package/node_modules/pi-lens/dist/chunk-IMSQR4WO.js +184 -0
  121. package/node_modules/pi-lens/dist/chunk-IQZXOZ7S.js +4816 -0
  122. package/node_modules/pi-lens/dist/chunk-IR6XFA5P.js +37 -0
  123. package/node_modules/pi-lens/dist/chunk-IUKPRZER.js +78 -0
  124. package/node_modules/pi-lens/dist/chunk-IUR76K6A.js +262 -0
  125. package/node_modules/pi-lens/dist/chunk-J3FZR3YY.js +98 -0
  126. package/node_modules/pi-lens/dist/chunk-J6DFZDFA.js +562 -0
  127. package/node_modules/pi-lens/dist/chunk-J7WJZ6W7.js +50 -0
  128. package/node_modules/pi-lens/dist/chunk-JONGANYH.js +202 -0
  129. package/node_modules/pi-lens/dist/chunk-KJCWXUUS.js +425 -0
  130. package/node_modules/pi-lens/dist/chunk-KJN4IOEJ.js +17 -0
  131. package/node_modules/pi-lens/dist/chunk-KMGPUKFD.js +25 -0
  132. package/node_modules/pi-lens/dist/chunk-LD7WFTRX.js +4533 -0
  133. package/node_modules/pi-lens/dist/chunk-LJDODBBZ.js +78 -0
  134. package/node_modules/pi-lens/dist/chunk-LNOMA3MP.js +169 -0
  135. package/node_modules/pi-lens/dist/chunk-MGIWSROG.js +2334 -0
  136. package/node_modules/pi-lens/dist/chunk-MQA2YBAH.js +308 -0
  137. package/node_modules/pi-lens/dist/chunk-MTDRLSI7.js +21720 -0
  138. package/node_modules/pi-lens/dist/chunk-N2IRDBZI.js +2355 -0
  139. package/node_modules/pi-lens/dist/chunk-N77WFUL7.js +50 -0
  140. package/node_modules/pi-lens/dist/chunk-N7YNJE5R.js +466 -0
  141. package/node_modules/pi-lens/dist/chunk-NABLC7W3.js +148 -0
  142. package/node_modules/pi-lens/dist/chunk-NFXP47RV.js +999 -0
  143. package/node_modules/pi-lens/dist/chunk-NJF2X5XF.js +45 -0
  144. package/node_modules/pi-lens/dist/chunk-NXL4FFQQ.js +47 -0
  145. package/node_modules/pi-lens/dist/chunk-O7HZX7CA.js +309 -0
  146. package/node_modules/pi-lens/dist/chunk-OC7LYAU5.js +708 -0
  147. package/node_modules/pi-lens/dist/chunk-OEX24ECI.js +348 -0
  148. package/node_modules/pi-lens/dist/chunk-OYOB3ZFG.js +78 -0
  149. package/node_modules/pi-lens/dist/chunk-OYTMZXRF.js +41 -0
  150. package/node_modules/pi-lens/dist/chunk-OZJINN4O.js +84 -0
  151. package/node_modules/pi-lens/dist/chunk-PCHDGUKT.js +23 -0
  152. package/node_modules/pi-lens/dist/chunk-QOZXJ4NA.js +3364 -0
  153. package/node_modules/pi-lens/dist/chunk-QYF5S6P6.js +45 -0
  154. package/node_modules/pi-lens/dist/chunk-RJFED73B.js +79 -0
  155. package/node_modules/pi-lens/dist/chunk-RTQNRM5X.js +27 -0
  156. package/node_modules/pi-lens/dist/chunk-S6AMWKPC.js +189 -0
  157. package/node_modules/pi-lens/dist/chunk-T4QZ6FEH.js +308 -0
  158. package/node_modules/pi-lens/dist/chunk-T54VJRAD.js +5456 -0
  159. package/node_modules/pi-lens/dist/chunk-TA5L5KWQ.js +167 -0
  160. package/node_modules/pi-lens/dist/chunk-TQFBZVCG.js +27 -0
  161. package/node_modules/pi-lens/dist/chunk-U52SAGRM.js +62 -0
  162. package/node_modules/pi-lens/dist/chunk-UEAHAMEI.js +5814 -0
  163. package/node_modules/pi-lens/dist/chunk-UGSMCQ4D.js +327 -0
  164. package/node_modules/pi-lens/dist/chunk-UOPVJQ4N.js +103 -0
  165. package/node_modules/pi-lens/dist/chunk-USHG4DLH.js +459 -0
  166. package/node_modules/pi-lens/dist/chunk-UXVNL3D7.js +23 -0
  167. package/node_modules/pi-lens/dist/chunk-V5MEBST3.js +4139 -0
  168. package/node_modules/pi-lens/dist/chunk-VGEVL5EQ.js +29 -0
  169. package/node_modules/pi-lens/dist/chunk-VPWKF3GN.js +87 -0
  170. package/node_modules/pi-lens/dist/chunk-VSMJK3ZJ.js +1250 -0
  171. package/node_modules/pi-lens/dist/chunk-VYKKMYVT.js +560 -0
  172. package/node_modules/pi-lens/dist/chunk-VZHJS6DR.js +442 -0
  173. package/node_modules/pi-lens/dist/chunk-WATC3IQR.js +213 -0
  174. package/node_modules/pi-lens/dist/chunk-WPE5FPGR.js +54 -0
  175. package/node_modules/pi-lens/dist/chunk-WSEWKCXU.js +188 -0
  176. package/node_modules/pi-lens/dist/chunk-WW5BLB74.js +659 -0
  177. package/node_modules/pi-lens/dist/chunk-XGUL4SLQ.js +406 -0
  178. package/node_modules/pi-lens/dist/chunk-XHSCVSOL.js +170 -0
  179. package/node_modules/pi-lens/dist/chunk-XIMYZ3JA.js +27 -0
  180. package/node_modules/pi-lens/dist/chunk-YLN4K6WD.js +385 -0
  181. package/node_modules/pi-lens/dist/chunk-Z3CCVDUB.js +76 -0
  182. package/node_modules/pi-lens/dist/chunk-Z54ZSOBZ.js +61 -0
  183. package/node_modules/pi-lens/dist/chunk-Z6BB4JLQ.js +478 -0
  184. package/node_modules/pi-lens/dist/chunk-Z7QQA2KT.js +27 -0
  185. package/node_modules/pi-lens/dist/chunk-ZETYB3XG.js +2626 -0
  186. package/node_modules/pi-lens/dist/chunk-ZMOMPX2Z.js +87 -0
  187. package/node_modules/pi-lens/dist/chunk-ZOEYVXSV.js +149 -0
  188. package/node_modules/pi-lens/dist/chunk-ZZUFZUQZ.js +12120 -0
  189. package/node_modules/pi-lens/dist/index.js +39712 -29876
  190. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +186 -232
  191. package/node_modules/pi-lens/dist/mcp/cli.js +137 -98
  192. package/node_modules/pi-lens/dist/mcp/server.js +22688 -1573
  193. package/node_modules/pi-lens/dist/mcp/worker.js +97 -38
  194. package/node_modules/pi-lens/dist/probes/installer.js +135 -0
  195. package/node_modules/pi-lens/dist/workers/project-snapshot-persist-worker.js +22 -0
  196. package/node_modules/pi-lens/dist/workers/review-graph-persist-worker.js +15 -0
  197. package/node_modules/pi-lens/docs/agent-guide.md +13 -4
  198. package/node_modules/pi-lens/docs/agent-tools.md +42 -0
  199. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +5 -4
  200. package/node_modules/pi-lens/docs/configuration.md +28 -2
  201. package/node_modules/pi-lens/docs/custom-rules.md +25 -2
  202. package/node_modules/pi-lens/docs/environment-variables.md +86 -8
  203. package/node_modules/pi-lens/docs/features.md +45 -5
  204. package/node_modules/pi-lens/docs/globalconfig.md +2 -1
  205. package/node_modules/pi-lens/docs/io-bridge-v2-migration.md +94 -0
  206. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +37 -4
  207. package/node_modules/pi-lens/docs/lsp-idle-eviction.md +124 -0
  208. package/node_modules/pi-lens/docs/mcp.md +21 -5
  209. package/node_modules/pi-lens/docs/merge-queue-rollout.md +77 -0
  210. package/node_modules/pi-lens/docs/pi-lens-fixer.md +160 -88
  211. package/node_modules/pi-lens/docs/pi-lens-investigator.md +74 -26
  212. package/node_modules/pi-lens/docs/pi-lens-merge-policy.md +736 -0
  213. package/node_modules/pi-lens/docs/pi-lens-monitor.md +75 -7
  214. package/node_modules/pi-lens/docs/pi-lens-retro.md +6 -4
  215. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +189 -48
  216. package/node_modules/pi-lens/docs/pi-lens-subagent.md +246 -45
  217. package/node_modules/pi-lens/docs/pi-lens-warden.md +18 -7
  218. package/node_modules/pi-lens/docs/public-api-stability.md +55 -0
  219. package/node_modules/pi-lens/docs/release-qa-baseline.md +84 -17
  220. package/node_modules/pi-lens/docs/rfcs/3654-unified-io-bridge-v2.md +316 -0
  221. package/node_modules/pi-lens/docs/schema/pi-lens-config-v1.json +530 -0
  222. package/node_modules/pi-lens/docs/servercapabilities.md +56 -49
  223. package/node_modules/pi-lens/docs/settings.md +1 -0
  224. package/node_modules/pi-lens/docs/usage.md +5 -1
  225. package/node_modules/pi-lens/docs/word-index.md +6 -0
  226. package/node_modules/pi-lens/grammars/tree-sitter-bash.wasm +0 -0
  227. package/node_modules/pi-lens/grammars/tree-sitter-bash.wasm.json +3 -3
  228. package/node_modules/pi-lens/package.json +18 -6
  229. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/advisory-registry-subset-test.yml +41 -0
  230. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-identity-operator-on-literals-test.yml +12 -2
  231. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/throw-new-error-js-test.yml +2 -0
  232. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/throw-new-error-test.yml +2 -0
  233. package/node_modules/pi-lens/rules/ast-grep-rules/rules/advisory-registry-subset.yml +50 -0
  234. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-identity-operator-on-literals.yml +20 -12
  235. package/node_modules/pi-lens/rules/ast-grep-rules/rules/throw-new-error-js.yml +2 -2
  236. package/node_modules/pi-lens/rules/ast-grep-rules/rules/throw-new-error.yml +2 -2
  237. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +1275 -62
  238. package/node_modules/pi-lens/scripts/download-grammars.js +5 -0
  239. package/node_modules/pi-lens/scripts/grammars.lock.json +6 -1
  240. package/node_modules/pi-lens/scripts/install-selftest.mjs +33 -34
  241. package/node_modules/pi-lens/scripts/lib/host-provided-deps.mjs +13 -4
  242. package/node_modules/pi-lens/scripts/lib/web-tree-sitter-dir.mjs +18 -0
  243. package/node_modules/pi-web-access/CHANGELOG.md +28 -0
  244. package/node_modules/pi-web-access/README.md +10 -6
  245. package/node_modules/pi-web-access/ceramic.ts +219 -0
  246. package/node_modules/pi-web-access/curator-page.ts +3 -1
  247. package/node_modules/pi-web-access/curator-server.ts +1 -0
  248. package/node_modules/pi-web-access/dist/index.js +846 -588
  249. package/node_modules/pi-web-access/dist/mcp-cli.js +725 -516
  250. package/node_modules/pi-web-access/gemini-search.ts +8 -4
  251. package/node_modules/pi-web-access/github-extract.ts +34 -2
  252. package/node_modules/pi-web-access/index.ts +72 -30
  253. package/node_modules/pi-web-access/openai-search.ts +3 -3
  254. package/node_modules/pi-web-access/package.json +2 -2
  255. package/node_modules/pi-web-access/page-query.ts +6 -9
  256. package/node_modules/pi-web-access/parallel-mcp.ts +2 -0
  257. package/node_modules/pi-web-access/query-rewrite.ts +2 -2
  258. package/node_modules/pi-web-access/storage.ts +49 -0
  259. package/node_modules/pi-web-access/summary-review.ts +7 -6
  260. package/node_modules/pi-web-access/utils.ts +21 -6
  261. package/node_modules/pi-web-access/web-tool-core.ts +1 -0
  262. package/package.json +19 -19
  263. package/node_modules/@dietrichgebert/ponytail/README.es.md +0 -212
  264. package/node_modules/@dietrichgebert/ponytail/README.ja.md +0 -212
  265. package/node_modules/@dietrichgebert/ponytail/README.ko.md +0 -212
  266. package/node_modules/@dietrichgebert/ponytail/README.zh-CN.md +0 -212
  267. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +0 -35
  268. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +0 -1610
  269. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +0 -593
  270. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +0 -147
  271. package/node_modules/pi-lens/dist/clients/agent-nudge.js +0 -325
  272. package/node_modules/pi-lens/dist/clients/analysed-root.js +0 -1
  273. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +0 -731
  274. package/node_modules/pi-lens/dist/clients/ast-grep-rule-manager.js +0 -146
  275. package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +0 -118
  276. package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -10
  277. package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +0 -154
  278. package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +0 -75
  279. package/node_modules/pi-lens/dist/clients/atomic-write.js +0 -189
  280. package/node_modules/pi-lens/dist/clients/availability-probe-flight.js +0 -12
  281. package/node_modules/pi-lens/dist/clients/bash-file-access.js +0 -1056
  282. package/node_modules/pi-lens/dist/clients/biome-client.js +0 -357
  283. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +0 -837
  284. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +0 -129
  285. package/node_modules/pi-lens/dist/clients/bootstrap.js +0 -569
  286. package/node_modules/pi-lens/dist/clients/bounded-cache.js +0 -190
  287. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +0 -202
  288. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +0 -295
  289. package/node_modules/pi-lens/dist/clients/build-identity.js +0 -95
  290. package/node_modules/pi-lens/dist/clients/bundled-resource-health.js +0 -113
  291. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +0 -105
  292. package/node_modules/pi-lens/dist/clients/bus-publish.js +0 -171
  293. package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +0 -190
  294. package/node_modules/pi-lens/dist/clients/cache-manager.js +0 -524
  295. package/node_modules/pi-lens/dist/clients/cache-observability.js +0 -1313
  296. package/node_modules/pi-lens/dist/clients/call-graph.js +0 -852
  297. package/node_modules/pi-lens/dist/clients/cargo-manifest.js +0 -422
  298. package/node_modules/pi-lens/dist/clients/cascade-budget.js +0 -196
  299. package/node_modules/pi-lens/dist/clients/cascade-format.js +0 -315
  300. package/node_modules/pi-lens/dist/clients/cascade-logger.js +0 -31
  301. package/node_modules/pi-lens/dist/clients/cascade-types.js +0 -9
  302. package/node_modules/pi-lens/dist/clients/child-unref.js +0 -124
  303. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +0 -286
  304. package/node_modules/pi-lens/dist/clients/codebase-model.js +0 -173
  305. package/node_modules/pi-lens/dist/clients/collateral-test-role.js +0 -40
  306. package/node_modules/pi-lens/dist/clients/complexity-client.js +0 -462
  307. package/node_modules/pi-lens/dist/clients/config-core/deny.js +0 -221
  308. package/node_modules/pi-lens/dist/clients/config-core/index.js +0 -50
  309. package/node_modules/pi-lens/dist/clients/config-core/merge.js +0 -357
  310. package/node_modules/pi-lens/dist/clients/config-core/normalize.js +0 -340
  311. package/node_modules/pi-lens/dist/clients/config-core/process-spec.js +0 -248
  312. package/node_modules/pi-lens/dist/clients/config-core/provenance.js +0 -164
  313. package/node_modules/pi-lens/dist/clients/config-core/records.js +0 -218
  314. package/node_modules/pi-lens/dist/clients/config-core/resolve.js +0 -125
  315. package/node_modules/pi-lens/dist/clients/config-core/safe-object.js +0 -78
  316. package/node_modules/pi-lens/dist/clients/config-core/schema.js +0 -165
  317. package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +0 -285
  318. package/node_modules/pi-lens/dist/clients/config-locations.js +0 -388
  319. package/node_modules/pi-lens/dist/clients/config-resolve.js +0 -798
  320. package/node_modules/pi-lens/dist/clients/config-schema.js +0 -251
  321. package/node_modules/pi-lens/dist/clients/config-warn.js +0 -407
  322. package/node_modules/pi-lens/dist/clients/console-guard-install.js +0 -26
  323. package/node_modules/pi-lens/dist/clients/cooperative-budget.js +0 -42
  324. package/node_modules/pi-lens/dist/clients/dead-code-client.js +0 -510
  325. package/node_modules/pi-lens/dist/clients/dead-code-logger.js +0 -30
  326. package/node_modules/pi-lens/dist/clients/deadline-utils.js +0 -280
  327. package/node_modules/pi-lens/dist/clients/debug-handles.js +0 -211
  328. package/node_modules/pi-lens/dist/clients/debug-heap.js +0 -174
  329. package/node_modules/pi-lens/dist/clients/deferred-lsp-work.js +0 -106
  330. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +0 -426
  331. package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +0 -169
  332. package/node_modules/pi-lens/dist/clients/dependency-checker.js +0 -885
  333. package/node_modules/pi-lens/dist/clients/deps/ast-grep-napi.js +0 -22
  334. package/node_modules/pi-lens/dist/clients/deps/js-yaml.js +0 -9
  335. package/node_modules/pi-lens/dist/clients/deps/minimatch.js +0 -4
  336. package/node_modules/pi-lens/dist/clients/deps/pi-tui.js +0 -14
  337. package/node_modules/pi-lens/dist/clients/deps/typebox.js +0 -14
  338. package/node_modules/pi-lens/dist/clients/deps/vscode-jsonrpc.js +0 -1
  339. package/node_modules/pi-lens/dist/clients/deps/web-tree-sitter.js +0 -24
  340. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +0 -630
  341. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +0 -305
  342. package/node_modules/pi-lens/dist/clients/diagnostic-logger.js +0 -73
  343. package/node_modules/pi-lens/dist/clients/diagnostic-tracker.js +0 -105
  344. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +0 -230
  345. package/node_modules/pi-lens/dist/clients/dispatch/ast-grep-catalog.js +0 -65
  346. package/node_modules/pi-lens/dist/clients/dispatch/auxiliary-lsp.js +0 -343
  347. package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +0 -35
  348. package/node_modules/pi-lens/dist/clients/dispatch/diagnostic-taxonomy.js +0 -66
  349. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +0 -1269
  350. package/node_modules/pi-lens/dist/clients/dispatch/fact-provider-types.js +0 -1
  351. package/node_modules/pi-lens/dist/clients/dispatch/fact-rule-runner.js +0 -17
  352. package/node_modules/pi-lens/dist/clients/dispatch/fact-runner.js +0 -30
  353. package/node_modules/pi-lens/dist/clients/dispatch/fact-scheduler.js +0 -68
  354. package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +0 -377
  355. package/node_modules/pi-lens/dist/clients/dispatch/facts/comment-facts.js +0 -22
  356. package/node_modules/pi-lens/dist/clients/dispatch/facts/file-content.js +0 -19
  357. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +0 -348
  358. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +0 -204
  359. package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +0 -34
  360. package/node_modules/pi-lens/dist/clients/dispatch/facts/try-catch-facts.js +0 -119
  361. package/node_modules/pi-lens/dist/clients/dispatch/finding-policy.js +0 -339
  362. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +0 -417
  363. package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +0 -89
  364. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +0 -2377
  365. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +0 -11
  366. package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +0 -87
  367. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +0 -278
  368. package/node_modules/pi-lens/dist/clients/dispatch/priorities.js +0 -20
  369. package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +0 -50
  370. package/node_modules/pi-lens/dist/clients/dispatch/rule-ignores.js +0 -69
  371. package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +0 -135
  372. package/node_modules/pi-lens/dist/clients/dispatch/rules/async-noise.js +0 -40
  373. package/node_modules/pi-lens/dist/clients/dispatch/rules/async-unnecessary-wrapper.js +0 -27
  374. package/node_modules/pi-lens/dist/clients/dispatch/rules/cors-wildcard.js +0 -54
  375. package/node_modules/pi-lens/dist/clients/dispatch/rules/error-obscuring.js +0 -32
  376. package/node_modules/pi-lens/dist/clients/dispatch/rules/error-swallowing.js +0 -31
  377. package/node_modules/pi-lens/dist/clients/dispatch/rules/framework-call-noise.js +0 -67
  378. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-complexity.js +0 -72
  379. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-fan-out.js +0 -74
  380. package/node_modules/pi-lens/dist/clients/dispatch/rules/high-import-coupling.js +0 -37
  381. package/node_modules/pi-lens/dist/clients/dispatch/rules/missing-error-propagation.js +0 -62
  382. package/node_modules/pi-lens/dist/clients/dispatch/rules/no-commented-credentials.js +0 -52
  383. package/node_modules/pi-lens/dist/clients/dispatch/rules/pass-through-wrappers.js +0 -42
  384. package/node_modules/pi-lens/dist/clients/dispatch/rules/placeholder-comments.js +0 -40
  385. package/node_modules/pi-lens/dist/clients/dispatch/rules/unsafe-boundary.js +0 -126
  386. package/node_modules/pi-lens/dist/clients/dispatch/runner-context.js +0 -52
  387. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +0 -111
  388. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +0 -1196
  389. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +0 -296
  390. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +0 -219
  391. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +0 -82
  392. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +0 -416
  393. package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -183
  394. package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +0 -176
  395. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -164
  396. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +0 -195
  397. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +0 -137
  398. package/node_modules/pi-lens/dist/clients/dispatch/runners/fact-rules.js +0 -29
  399. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +0 -69
  400. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +0 -167
  401. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +0 -104
  402. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -135
  403. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +0 -91
  404. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +0 -171
  405. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +0 -940
  406. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +0 -117
  407. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +0 -108
  408. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +0 -99
  409. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +0 -110
  410. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +0 -379
  411. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +0 -167
  412. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +0 -102
  413. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +0 -414
  414. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +0 -85
  415. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +0 -120
  416. package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -62
  417. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -445
  418. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +0 -169
  419. package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +0 -99
  420. package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +0 -88
  421. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +0 -256
  422. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +0 -173
  423. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +0 -102
  424. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +0 -128
  425. package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +0 -259
  426. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -183
  427. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +0 -166
  428. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +0 -163
  429. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +0 -174
  430. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +0 -168
  431. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +0 -125
  432. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -643
  433. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +0 -253
  434. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +0 -496
  435. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +0 -90
  436. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -95
  437. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +0 -274
  438. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +0 -1701
  439. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +0 -101
  440. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +0 -235
  441. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +0 -169
  442. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils.js +0 -15
  443. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +0 -128
  444. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +0 -242
  445. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +0 -95
  446. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +0 -79
  447. package/node_modules/pi-lens/dist/clients/dispatch/suppress-writer.js +0 -79
  448. package/node_modules/pi-lens/dist/clients/dispatch/tool-profile.js +0 -30
  449. package/node_modules/pi-lens/dist/clients/dispatch/types.js +0 -21
  450. package/node_modules/pi-lens/dist/clients/dispatch/utils/format-utils.js +0 -45
  451. package/node_modules/pi-lens/dist/clients/dispatch/utils/lsp-diagnostics.js +0 -35
  452. package/node_modules/pi-lens/dist/clients/disposition-logger.js +0 -32
  453. package/node_modules/pi-lens/dist/clients/disposition-publish.js +0 -124
  454. package/node_modules/pi-lens/dist/clients/durable-store.js +0 -97
  455. package/node_modules/pi-lens/dist/clients/effective-config.js +0 -415
  456. package/node_modules/pi-lens/dist/clients/env-utils.js +0 -83
  457. package/node_modules/pi-lens/dist/clients/error-class.js +0 -23
  458. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +0 -17
  459. package/node_modules/pi-lens/dist/clients/event-loop-hold.js +0 -274
  460. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +0 -250
  461. package/node_modules/pi-lens/dist/clients/extension-log.js +0 -504
  462. package/node_modules/pi-lens/dist/clients/extension-mode.js +0 -73
  463. package/node_modules/pi-lens/dist/clients/feature-hints.js +0 -103
  464. package/node_modules/pi-lens/dist/clients/file-kinds.js +0 -412
  465. package/node_modules/pi-lens/dist/clients/file-role.js +0 -134
  466. package/node_modules/pi-lens/dist/clients/file-time.js +0 -160
  467. package/node_modules/pi-lens/dist/clients/file-utils.js +0 -1032
  468. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +0 -494
  469. package/node_modules/pi-lens/dist/clients/finding-identity.js +0 -88
  470. package/node_modules/pi-lens/dist/clients/fix-worklog.js +0 -103
  471. package/node_modules/pi-lens/dist/clients/format-events-publish.js +0 -343
  472. package/node_modules/pi-lens/dist/clients/format-service.js +0 -222
  473. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +0 -9
  474. package/node_modules/pi-lens/dist/clients/formatters.js +0 -2032
  475. package/node_modules/pi-lens/dist/clients/freshness-cadence.js +0 -17
  476. package/node_modules/pi-lens/dist/clients/freshness.js +0 -38
  477. package/node_modules/pi-lens/dist/clients/generated-artifacts.js +0 -394
  478. package/node_modules/pi-lens/dist/clients/generation-guard.js +0 -215
  479. package/node_modules/pi-lens/dist/clients/git-guard.js +0 -1136
  480. package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +0 -243
  481. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +0 -509
  482. package/node_modules/pi-lens/dist/clients/go-client.js +0 -104
  483. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +0 -605
  484. package/node_modules/pi-lens/dist/clients/gradle-ktfmt-style.js +0 -348
  485. package/node_modules/pi-lens/dist/clients/grammar-source.js +0 -465
  486. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +0 -135
  487. package/node_modules/pi-lens/dist/clients/hashline-anchor.js +0 -424
  488. package/node_modules/pi-lens/dist/clients/hook-budgets.js +0 -76
  489. package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +0 -140
  490. package/node_modules/pi-lens/dist/clients/host-ports.js +0 -39
  491. package/node_modules/pi-lens/dist/clients/indent-retarget.js +0 -154
  492. package/node_modules/pi-lens/dist/clients/inline-blocker-dispositions.js +0 -186
  493. package/node_modules/pi-lens/dist/clients/install-diagnostics.js +0 -156
  494. package/node_modules/pi-lens/dist/clients/installer/index.js +0 -5416
  495. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +0 -58
  496. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +0 -783
  497. package/node_modules/pi-lens/dist/clients/instance-reaper-state.js +0 -7
  498. package/node_modules/pi-lens/dist/clients/instance-reaper.js +0 -1137
  499. package/node_modules/pi-lens/dist/clients/instance-registry-lock.js +0 -145
  500. package/node_modules/pi-lens/dist/clients/instance-registry.js +0 -773
  501. package/node_modules/pi-lens/dist/clients/jscpd-client.js +0 -378
  502. package/node_modules/pi-lens/dist/clients/json-cache-read.js +0 -65
  503. package/node_modules/pi-lens/dist/clients/knip-client.js +0 -788
  504. package/node_modules/pi-lens/dist/clients/language-policy.js +0 -252
  505. package/node_modules/pi-lens/dist/clients/language-profile.js +0 -361
  506. package/node_modules/pi-lens/dist/clients/language-registry.js +0 -603
  507. package/node_modules/pi-lens/dist/clients/latency-logger.js +0 -727
  508. package/node_modules/pi-lens/dist/clients/lazy-import.js +0 -21
  509. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +0 -54
  510. package/node_modules/pi-lens/dist/clients/lens-config.js +0 -404
  511. package/node_modules/pi-lens/dist/clients/lens-engine.js +0 -358
  512. package/node_modules/pi-lens/dist/clients/lens-events.js +0 -125
  513. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +0 -467
  514. package/node_modules/pi-lens/dist/clients/lens-map.js +0 -1156
  515. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +0 -74
  516. package/node_modules/pi-lens/dist/clients/log-cleanup.js +0 -222
  517. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +0 -178
  518. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +0 -421
  519. package/node_modules/pi-lens/dist/clients/lsp/client.js +0 -4593
  520. package/node_modules/pi-lens/dist/clients/lsp/config.js +0 -691
  521. package/node_modules/pi-lens/dist/clients/lsp/content-limits.js +0 -41
  522. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +0 -225
  523. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +0 -467
  524. package/node_modules/pi-lens/dist/clients/lsp/edits.js +0 -1219
  525. package/node_modules/pi-lens/dist/clients/lsp/index.js +0 -8305
  526. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +0 -216
  527. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +0 -298
  528. package/node_modules/pi-lens/dist/clients/lsp/language.js +0 -63
  529. package/node_modules/pi-lens/dist/clients/lsp/launch.js +0 -746
  530. package/node_modules/pi-lens/dist/clients/lsp/lombok.js +0 -158
  531. package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +0 -5
  532. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +0 -251
  533. package/node_modules/pi-lens/dist/clients/lsp/position-encoding.js +0 -61
  534. package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +0 -83
  535. package/node_modules/pi-lens/dist/clients/lsp/server.js +0 -3290
  536. package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +0 -192
  537. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +0 -17
  538. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +0 -56
  539. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +0 -332
  540. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/capability-snapshot.js +0 -1
  541. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +0 -85
  542. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/index.js +0 -10
  543. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +0 -392
  544. package/node_modules/pi-lens/dist/clients/lsp/watch-queue.js +0 -61
  545. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +0 -639
  546. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +0 -30
  547. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +0 -184
  548. package/node_modules/pi-lens/dist/clients/lsp-budget.js +0 -186
  549. package/node_modules/pi-lens/dist/clients/lsp-document-symbols.js +0 -110
  550. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +0 -9
  551. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +0 -396
  552. package/node_modules/pi-lens/dist/clients/lsp-status.js +0 -59
  553. package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +0 -40
  554. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +0 -420
  555. package/node_modules/pi-lens/dist/clients/mcp/host-shim.js +0 -31
  556. package/node_modules/pi-lens/dist/clients/mcp/ipc.js +0 -523
  557. package/node_modules/pi-lens/dist/clients/mcp/review.js +0 -197
  558. package/node_modules/pi-lens/dist/clients/mcp/session.js +0 -412
  559. package/node_modules/pi-lens/dist/clients/memory-sampler.js +0 -208
  560. package/node_modules/pi-lens/dist/clients/message-end-attribution.js +0 -30
  561. package/node_modules/pi-lens/dist/clients/metrics-client.js +0 -107
  562. package/node_modules/pi-lens/dist/clients/metrics-history.js +0 -327
  563. package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +0 -263
  564. package/node_modules/pi-lens/dist/clients/model-provider.js +0 -53
  565. package/node_modules/pi-lens/dist/clients/module-report-lsp.js +0 -189
  566. package/node_modules/pi-lens/dist/clients/module-report.js +0 -2150
  567. package/node_modules/pi-lens/dist/clients/mutating-tool.js +0 -651
  568. package/node_modules/pi-lens/dist/clients/mutation-attribution.js +0 -368
  569. package/node_modules/pi-lens/dist/clients/mutation-bridge.js +0 -240
  570. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +0 -734
  571. package/node_modules/pi-lens/dist/clients/observed-mutation-sources.js +0 -101
  572. package/node_modules/pi-lens/dist/clients/observed-mutation.js +0 -1253
  573. package/node_modules/pi-lens/dist/clients/oldtext-autopatch.js +0 -128
  574. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +0 -496
  575. package/node_modules/pi-lens/dist/clients/opengrep-client.js +0 -333
  576. package/node_modules/pi-lens/dist/clients/opengrep-config.js +0 -76
  577. package/node_modules/pi-lens/dist/clients/package-manager.js +0 -607
  578. package/node_modules/pi-lens/dist/clients/package-root.js +0 -38
  579. package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +0 -439
  580. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +0 -43
  581. package/node_modules/pi-lens/dist/clients/path-keyed-map.js +0 -108
  582. package/node_modules/pi-lens/dist/clients/path-utils.js +0 -1244
  583. package/node_modules/pi-lens/dist/clients/performance-report.js +0 -331
  584. package/node_modules/pi-lens/dist/clients/persist-debounce.js +0 -70
  585. package/node_modules/pi-lens/dist/clients/persistent-reverify.js +0 -271
  586. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +0 -119
  587. package/node_modules/pi-lens/dist/clients/pipeline.js +0 -1480
  588. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +0 -37
  589. package/node_modules/pi-lens/dist/clients/print-mode.js +0 -21
  590. package/node_modules/pi-lens/dist/clients/probe-home-state.js +0 -228
  591. package/node_modules/pi-lens/dist/clients/process-bridge.js +0 -66
  592. package/node_modules/pi-lens/dist/clients/process-singletons.js +0 -210
  593. package/node_modules/pi-lens/dist/clients/process-snapshot.js +0 -68
  594. package/node_modules/pi-lens/dist/clients/project-changes.js +0 -181
  595. package/node_modules/pi-lens/dist/clients/project-conventions.js +0 -171
  596. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +0 -137
  597. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +0 -109
  598. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +0 -668
  599. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +0 -118
  600. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/dead-code.js +0 -47
  601. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +0 -40
  602. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/govulncheck.js +0 -34
  603. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +0 -38
  604. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +0 -47
  605. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +0 -48
  606. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +0 -40
  607. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +0 -127
  608. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +0 -81
  609. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +0 -520
  610. package/node_modules/pi-lens/dist/clients/project-diagnostics/types.js +0 -1
  611. package/node_modules/pi-lens/dist/clients/project-lens-config.js +0 -807
  612. package/node_modules/pi-lens/dist/clients/project-report.js +0 -701
  613. package/node_modules/pi-lens/dist/clients/project-scale.js +0 -237
  614. package/node_modules/pi-lens/dist/clients/project-scan-policy.js +0 -11
  615. package/node_modules/pi-lens/dist/clients/project-snapshot-fingerprint.js +0 -48
  616. package/node_modules/pi-lens/dist/clients/project-snapshot-persist-worker.js +0 -20
  617. package/node_modules/pi-lens/dist/clients/project-snapshot.js +0 -1653
  618. package/node_modules/pi-lens/dist/clients/project-trust.js +0 -181
  619. package/node_modules/pi-lens/dist/clients/python-environment.js +0 -217
  620. package/node_modules/pi-lens/dist/clients/python-provenance.js +0 -639
  621. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +0 -47
  622. package/node_modules/pi-lens/dist/clients/quiet-window.js +0 -236
  623. package/node_modules/pi-lens/dist/clients/read-bridge.js +0 -139
  624. package/node_modules/pi-lens/dist/clients/read-expansion.js +0 -266
  625. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +0 -251
  626. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +0 -1306
  627. package/node_modules/pi-lens/dist/clients/read-guard.js +0 -1561
  628. package/node_modules/pi-lens/dist/clients/recent-touches.js +0 -240
  629. package/node_modules/pi-lens/dist/clients/redact/secrets.js +0 -449
  630. package/node_modules/pi-lens/dist/clients/resource-sampler.js +0 -628
  631. package/node_modules/pi-lens/dist/clients/reverse-deps.js +0 -270
  632. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +0 -5126
  633. package/node_modules/pi-lens/dist/clients/review-graph/format.js +0 -33
  634. package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +0 -163
  635. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +0 -481
  636. package/node_modules/pi-lens/dist/clients/review-graph/persist-worker.js +0 -10
  637. package/node_modules/pi-lens/dist/clients/review-graph/query.js +0 -244
  638. package/node_modules/pi-lens/dist/clients/review-graph/revision-drift.js +0 -21
  639. package/node_modules/pi-lens/dist/clients/review-graph/service.js +0 -96
  640. package/node_modules/pi-lens/dist/clients/review-graph/shared-extraction-ir.js +0 -82
  641. package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +0 -103
  642. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +0 -299
  643. package/node_modules/pi-lens/dist/clients/review-graph/types.js +0 -1
  644. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +0 -449
  645. package/node_modules/pi-lens/dist/clients/review-graph-logger.js +0 -81
  646. package/node_modules/pi-lens/dist/clients/ruff-client.js +0 -179
  647. package/node_modules/pi-lens/dist/clients/rules-scanner.js +0 -131
  648. package/node_modules/pi-lens/dist/clients/run-duration.js +0 -55
  649. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +0 -746
  650. package/node_modules/pi-lens/dist/clients/runtime-config.js +0 -77
  651. package/node_modules/pi-lens/dist/clients/runtime-context.js +0 -211
  652. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +0 -1324
  653. package/node_modules/pi-lens/dist/clients/runtime-session.js +0 -2485
  654. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +0 -1295
  655. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +0 -2049
  656. package/node_modules/pi-lens/dist/clients/runtime-turn.js +0 -3767
  657. package/node_modules/pi-lens/dist/clients/rust-client.js +0 -97
  658. package/node_modules/pi-lens/dist/clients/safe-spawn.js +0 -2357
  659. package/node_modules/pi-lens/dist/clients/sanitize.js +0 -286
  660. package/node_modules/pi-lens/dist/clients/scan-utils.js +0 -25
  661. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +0 -165
  662. package/node_modules/pi-lens/dist/clients/search-read-registration.js +0 -94
  663. package/node_modules/pi-lens/dist/clients/secret-findings.js +0 -114
  664. package/node_modules/pi-lens/dist/clients/security-scan-client.js +0 -352
  665. package/node_modules/pi-lens/dist/clients/session-event-guard.js +0 -297
  666. package/node_modules/pi-lens/dist/clients/session-lifecycle.js +0 -484
  667. package/node_modules/pi-lens/dist/clients/session-start-observability.js +0 -94
  668. package/node_modules/pi-lens/dist/clients/session-state-store.js +0 -101
  669. package/node_modules/pi-lens/dist/clients/session-summary.js +0 -15
  670. package/node_modules/pi-lens/dist/clients/sessionstart-logger.js +0 -27
  671. package/node_modules/pi-lens/dist/clients/sg-runner.js +0 -876
  672. package/node_modules/pi-lens/dist/clients/sgconfig.js +0 -347
  673. package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +0 -338
  674. package/node_modules/pi-lens/dist/clients/single-flight.js +0 -158
  675. package/node_modules/pi-lens/dist/clients/situational-tool-telemetry.js +0 -108
  676. package/node_modules/pi-lens/dist/clients/skills-resolver.js +0 -141
  677. package/node_modules/pi-lens/dist/clients/slow-fs.js +0 -138
  678. package/node_modules/pi-lens/dist/clients/smells-rollup.js +0 -231
  679. package/node_modules/pi-lens/dist/clients/source-filter.js +0 -596
  680. package/node_modules/pi-lens/dist/clients/source-walker.js +0 -211
  681. package/node_modules/pi-lens/dist/clients/spawn-output-cap.js +0 -120
  682. package/node_modules/pi-lens/dist/clients/spawn-timeout-cooldown.js +0 -110
  683. package/node_modules/pi-lens/dist/clients/stale-marker.js +0 -17
  684. package/node_modules/pi-lens/dist/clients/startup-scan.js +0 -400
  685. package/node_modules/pi-lens/dist/clients/startup-timing.js +0 -130
  686. package/node_modules/pi-lens/dist/clients/string-utils.js +0 -33
  687. package/node_modules/pi-lens/dist/clients/subagent-mode.js +0 -111
  688. package/node_modules/pi-lens/dist/clients/symbol-containment.js +0 -24
  689. package/node_modules/pi-lens/dist/clients/symbol-types.js +0 -5
  690. package/node_modules/pi-lens/dist/clients/test-runner-client.js +0 -2507
  691. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +0 -287
  692. package/node_modules/pi-lens/dist/clients/todo-scanner.js +0 -229
  693. package/node_modules/pi-lens/dist/clients/tool-agreement.js +0 -423
  694. package/node_modules/pi-lens/dist/clients/tool-config.js +0 -249
  695. package/node_modules/pi-lens/dist/clients/tool-cwd.js +0 -270
  696. package/node_modules/pi-lens/dist/clients/tool-definition.js +0 -47
  697. package/node_modules/pi-lens/dist/clients/tool-event.js +0 -105
  698. package/node_modules/pi-lens/dist/clients/tool-policy.js +0 -2755
  699. package/node_modules/pi-lens/dist/clients/tool-probe.js +0 -53
  700. package/node_modules/pi-lens/dist/clients/tool-render.js +0 -156
  701. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +0 -118
  702. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +0 -364
  703. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +0 -4195
  704. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +0 -100
  705. package/node_modules/pi-lens/dist/clients/tree-sitter-navigator.js +0 -269
  706. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +0 -569
  707. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +0 -158
  708. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +0 -1265
  709. package/node_modules/pi-lens/dist/clients/trivy-client.js +0 -467
  710. package/node_modules/pi-lens/dist/clients/tui-fit.js +0 -50
  711. package/node_modules/pi-lens/dist/clients/turn-context.js +0 -52
  712. package/node_modules/pi-lens/dist/clients/turn-end/lane.js +0 -51
  713. package/node_modules/pi-lens/dist/clients/turn-end/lanes/govulncheck.js +0 -112
  714. package/node_modules/pi-lens/dist/clients/turn-end/lanes/secrets.js +0 -204
  715. package/node_modules/pi-lens/dist/clients/turn-summary-render.js +0 -72
  716. package/node_modules/pi-lens/dist/clients/turn-summary.js +0 -132
  717. package/node_modules/pi-lens/dist/clients/typos-config.js +0 -29
  718. package/node_modules/pi-lens/dist/clients/user-notify.js +0 -51
  719. package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +0 -57
  720. package/node_modules/pi-lens/dist/clients/warm-attach.js +0 -305
  721. package/node_modules/pi-lens/dist/clients/widget-state.js +0 -1808
  722. package/node_modules/pi-lens/dist/clients/word-index-logger.js +0 -49
  723. package/node_modules/pi-lens/dist/clients/word-index-store.js +0 -459
  724. package/node_modules/pi-lens/dist/clients/word-index.js +0 -2080
  725. package/node_modules/pi-lens/dist/clients/workspace-topology.js +0 -443
  726. package/node_modules/pi-lens/dist/clients/write-ordering-guard.js +0 -65
  727. package/node_modules/pi-lens/dist/clients/zizmor-config.js +0 -340
  728. package/node_modules/pi-lens/dist/i18n.js +0 -69
  729. package/node_modules/pi-lens/dist/mcp/build-staleness.js +0 -124
  730. package/node_modules/pi-lens/dist/scripts/lib/process-scan.mjs +0 -583
  731. package/node_modules/pi-lens/dist/scripts/lib/skills-predicate.mjs +0 -129
  732. package/node_modules/pi-lens/dist/scripts/lib/web-tree-sitter-dir.mjs +0 -123
  733. package/node_modules/pi-lens/dist/tools/activate-tools.js +0 -87
  734. package/node_modules/pi-lens/dist/tools/ast-grep-outline.js +0 -137
  735. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +0 -244
  736. package/node_modules/pi-lens/dist/tools/ast-grep-search.js +0 -762
  737. package/node_modules/pi-lens/dist/tools/effective-config.js +0 -90
  738. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +0 -375
  739. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +0 -2571
  740. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +0 -1745
  741. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +0 -1367
  742. package/node_modules/pi-lens/dist/tools/lsp-structured-output.js +0 -294
  743. package/node_modules/pi-lens/dist/tools/module-report.js +0 -382
  744. package/node_modules/pi-lens/dist/tools/project-report.js +0 -86
  745. package/node_modules/pi-lens/dist/tools/render-compact.js +0 -374
  746. package/node_modules/pi-lens/dist/tools/scan-progress.js +0 -53
  747. package/node_modules/pi-lens/dist/tools/shared.js +0 -29
  748. package/node_modules/pi-lens/dist/tools/symbol-search.js +0 -155
  749. package/node_modules/pi-web-access/opencode-session-headers.ts +0 -24
  750. package/node_modules/remote-pi/README.md +0 -675
  751. package/node_modules/remote-pi/dist/actions/handlers.d.ts +0 -122
  752. package/node_modules/remote-pi/dist/actions/handlers.js +0 -160
  753. package/node_modules/remote-pi/dist/actions/handlers.js.map +0 -1
  754. package/node_modules/remote-pi/dist/actions/registry.d.ts +0 -39
  755. package/node_modules/remote-pi/dist/actions/registry.js +0 -58
  756. package/node_modules/remote-pi/dist/actions/registry.js.map +0 -1
  757. package/node_modules/remote-pi/dist/bin/supervisord.d.ts +0 -2
  758. package/node_modules/remote-pi/dist/bin/supervisord.js +0 -86
  759. package/node_modules/remote-pi/dist/bin/supervisord.js.map +0 -1
  760. package/node_modules/remote-pi/dist/commands/builtin_mirror.d.ts +0 -58
  761. package/node_modules/remote-pi/dist/commands/builtin_mirror.js +0 -71
  762. package/node_modules/remote-pi/dist/commands/builtin_mirror.js.map +0 -1
  763. package/node_modules/remote-pi/dist/commands/list_commands.d.ts +0 -60
  764. package/node_modules/remote-pi/dist/commands/list_commands.js +0 -73
  765. package/node_modules/remote-pi/dist/commands/list_commands.js.map +0 -1
  766. package/node_modules/remote-pi/dist/config.d.ts +0 -62
  767. package/node_modules/remote-pi/dist/config.js +0 -115
  768. package/node_modules/remote-pi/dist/config.js.map +0 -1
  769. package/node_modules/remote-pi/dist/daemon/client.d.ts +0 -20
  770. package/node_modules/remote-pi/dist/daemon/client.js +0 -131
  771. package/node_modules/remote-pi/dist/daemon/client.js.map +0 -1
  772. package/node_modules/remote-pi/dist/daemon/control_protocol.d.ts +0 -176
  773. package/node_modules/remote-pi/dist/daemon/control_protocol.js +0 -63
  774. package/node_modules/remote-pi/dist/daemon/control_protocol.js.map +0 -1
  775. package/node_modules/remote-pi/dist/daemon/cron_log.d.ts +0 -45
  776. package/node_modules/remote-pi/dist/daemon/cron_log.js +0 -72
  777. package/node_modules/remote-pi/dist/daemon/cron_log.js.map +0 -1
  778. package/node_modules/remote-pi/dist/daemon/cron_registry.d.ts +0 -80
  779. package/node_modules/remote-pi/dist/daemon/cron_registry.js +0 -194
  780. package/node_modules/remote-pi/dist/daemon/cron_registry.js.map +0 -1
  781. package/node_modules/remote-pi/dist/daemon/id.d.ts +0 -24
  782. package/node_modules/remote-pi/dist/daemon/id.js +0 -36
  783. package/node_modules/remote-pi/dist/daemon/id.js.map +0 -1
  784. package/node_modules/remote-pi/dist/daemon/install.d.ts +0 -170
  785. package/node_modules/remote-pi/dist/daemon/install.js +0 -547
  786. package/node_modules/remote-pi/dist/daemon/install.js.map +0 -1
  787. package/node_modules/remote-pi/dist/daemon/registry.d.ts +0 -63
  788. package/node_modules/remote-pi/dist/daemon/registry.js +0 -153
  789. package/node_modules/remote-pi/dist/daemon/registry.js.map +0 -1
  790. package/node_modules/remote-pi/dist/daemon/rpc_child.d.ts +0 -194
  791. package/node_modules/remote-pi/dist/daemon/rpc_child.js +0 -389
  792. package/node_modules/remote-pi/dist/daemon/rpc_child.js.map +0 -1
  793. package/node_modules/remote-pi/dist/daemon/supervisor.d.ts +0 -93
  794. package/node_modules/remote-pi/dist/daemon/supervisor.js +0 -596
  795. package/node_modules/remote-pi/dist/daemon/supervisor.js.map +0 -1
  796. package/node_modules/remote-pi/dist/extension_ui_bridge.d.ts +0 -26
  797. package/node_modules/remote-pi/dist/extension_ui_bridge.js +0 -341
  798. package/node_modules/remote-pi/dist/extension_ui_bridge.js.map +0 -1
  799. package/node_modules/remote-pi/dist/index.d.ts +0 -213
  800. package/node_modules/remote-pi/dist/index.js +0 -4996
  801. package/node_modules/remote-pi/dist/index.js.map +0 -1
  802. package/node_modules/remote-pi/dist/mcp/mesh_result.d.ts +0 -10
  803. package/node_modules/remote-pi/dist/mcp/mesh_result.js +0 -42
  804. package/node_modules/remote-pi/dist/mcp/mesh_result.js.map +0 -1
  805. package/node_modules/remote-pi/dist/mcp/mesh_server.d.ts +0 -16
  806. package/node_modules/remote-pi/dist/mcp/mesh_server.js +0 -327
  807. package/node_modules/remote-pi/dist/mcp/mesh_server.js.map +0 -1
  808. package/node_modules/remote-pi/dist/mesh/canonical.d.ts +0 -30
  809. package/node_modules/remote-pi/dist/mesh/canonical.js +0 -61
  810. package/node_modules/remote-pi/dist/mesh/canonical.js.map +0 -1
  811. package/node_modules/remote-pi/dist/mesh/client.d.ts +0 -18
  812. package/node_modules/remote-pi/dist/mesh/client.js +0 -125
  813. package/node_modules/remote-pi/dist/mesh/client.js.map +0 -1
  814. package/node_modules/remote-pi/dist/mesh/encoding.d.ts +0 -38
  815. package/node_modules/remote-pi/dist/mesh/encoding.js +0 -216
  816. package/node_modules/remote-pi/dist/mesh/encoding.js.map +0 -1
  817. package/node_modules/remote-pi/dist/mesh/self_revoke.d.ts +0 -73
  818. package/node_modules/remote-pi/dist/mesh/self_revoke.js +0 -401
  819. package/node_modules/remote-pi/dist/mesh/self_revoke.js.map +0 -1
  820. package/node_modules/remote-pi/dist/mesh/siblings.d.ts +0 -49
  821. package/node_modules/remote-pi/dist/mesh/siblings.js +0 -159
  822. package/node_modules/remote-pi/dist/mesh/siblings.js.map +0 -1
  823. package/node_modules/remote-pi/dist/mesh/types.d.ts +0 -34
  824. package/node_modules/remote-pi/dist/mesh/types.js +0 -11
  825. package/node_modules/remote-pi/dist/mesh/types.js.map +0 -1
  826. package/node_modules/remote-pi/dist/mesh/verify.d.ts +0 -17
  827. package/node_modules/remote-pi/dist/mesh/verify.js +0 -86
  828. package/node_modules/remote-pi/dist/mesh/verify.js.map +0 -1
  829. package/node_modules/remote-pi/dist/pairing/crypto.d.ts +0 -8
  830. package/node_modules/remote-pi/dist/pairing/crypto.js +0 -22
  831. package/node_modules/remote-pi/dist/pairing/crypto.js.map +0 -1
  832. package/node_modules/remote-pi/dist/pairing/handshake.d.ts +0 -28
  833. package/node_modules/remote-pi/dist/pairing/handshake.js +0 -113
  834. package/node_modules/remote-pi/dist/pairing/handshake.js.map +0 -1
  835. package/node_modules/remote-pi/dist/pairing/noise-sha256.d.ts +0 -16
  836. package/node_modules/remote-pi/dist/pairing/noise-sha256.js +0 -103
  837. package/node_modules/remote-pi/dist/pairing/noise-sha256.js.map +0 -1
  838. package/node_modules/remote-pi/dist/pairing/qr.d.ts +0 -59
  839. package/node_modules/remote-pi/dist/pairing/qr.js +0 -125
  840. package/node_modules/remote-pi/dist/pairing/qr.js.map +0 -1
  841. package/node_modules/remote-pi/dist/pairing/storage.d.ts +0 -120
  842. package/node_modules/remote-pi/dist/pairing/storage.js +0 -604
  843. package/node_modules/remote-pi/dist/pairing/storage.js.map +0 -1
  844. package/node_modules/remote-pi/dist/protocol/codec.d.ts +0 -7
  845. package/node_modules/remote-pi/dist/protocol/codec.js +0 -49
  846. package/node_modules/remote-pi/dist/protocol/codec.js.map +0 -1
  847. package/node_modules/remote-pi/dist/protocol/types.d.ts +0 -393
  848. package/node_modules/remote-pi/dist/protocol/types.js +0 -2
  849. package/node_modules/remote-pi/dist/protocol/types.js.map +0 -1
  850. package/node_modules/remote-pi/dist/rooms.d.ts +0 -29
  851. package/node_modules/remote-pi/dist/rooms.js +0 -57
  852. package/node_modules/remote-pi/dist/rooms.js.map +0 -1
  853. package/node_modules/remote-pi/dist/session/address.d.ts +0 -49
  854. package/node_modules/remote-pi/dist/session/address.js +0 -58
  855. package/node_modules/remote-pi/dist/session/address.js.map +0 -1
  856. package/node_modules/remote-pi/dist/session/agent_bridge.d.ts +0 -55
  857. package/node_modules/remote-pi/dist/session/agent_bridge.js +0 -146
  858. package/node_modules/remote-pi/dist/session/agent_bridge.js.map +0 -1
  859. package/node_modules/remote-pi/dist/session/bridge.d.ts +0 -37
  860. package/node_modules/remote-pi/dist/session/bridge.js +0 -130
  861. package/node_modules/remote-pi/dist/session/bridge.js.map +0 -1
  862. package/node_modules/remote-pi/dist/session/broker.d.ts +0 -189
  863. package/node_modules/remote-pi/dist/session/broker.js +0 -507
  864. package/node_modules/remote-pi/dist/session/broker.js.map +0 -1
  865. package/node_modules/remote-pi/dist/session/broker_remote.d.ts +0 -74
  866. package/node_modules/remote-pi/dist/session/broker_remote.js +0 -583
  867. package/node_modules/remote-pi/dist/session/broker_remote.js.map +0 -1
  868. package/node_modules/remote-pi/dist/session/cwd_lock.d.ts +0 -33
  869. package/node_modules/remote-pi/dist/session/cwd_lock.js +0 -126
  870. package/node_modules/remote-pi/dist/session/cwd_lock.js.map +0 -1
  871. package/node_modules/remote-pi/dist/session/envelope.d.ts +0 -33
  872. package/node_modules/remote-pi/dist/session/envelope.js +0 -117
  873. package/node_modules/remote-pi/dist/session/envelope.js.map +0 -1
  874. package/node_modules/remote-pi/dist/session/global_config.d.ts +0 -33
  875. package/node_modules/remote-pi/dist/session/global_config.js +0 -73
  876. package/node_modules/remote-pi/dist/session/global_config.js.map +0 -1
  877. package/node_modules/remote-pi/dist/session/ipc.d.ts +0 -27
  878. package/node_modules/remote-pi/dist/session/ipc.js +0 -22
  879. package/node_modules/remote-pi/dist/session/ipc.js.map +0 -1
  880. package/node_modules/remote-pi/dist/session/leader_election.d.ts +0 -32
  881. package/node_modules/remote-pi/dist/session/leader_election.js +0 -106
  882. package/node_modules/remote-pi/dist/session/leader_election.js.map +0 -1
  883. package/node_modules/remote-pi/dist/session/local_config.d.ts +0 -55
  884. package/node_modules/remote-pi/dist/session/local_config.js +0 -157
  885. package/node_modules/remote-pi/dist/session/local_config.js.map +0 -1
  886. package/node_modules/remote-pi/dist/session/mesh_message.d.ts +0 -20
  887. package/node_modules/remote-pi/dist/session/mesh_message.js +0 -28
  888. package/node_modules/remote-pi/dist/session/mesh_message.js.map +0 -1
  889. package/node_modules/remote-pi/dist/session/mesh_node.d.ts +0 -116
  890. package/node_modules/remote-pi/dist/session/mesh_node.js +0 -573
  891. package/node_modules/remote-pi/dist/session/mesh_node.js.map +0 -1
  892. package/node_modules/remote-pi/dist/session/peer.d.ts +0 -139
  893. package/node_modules/remote-pi/dist/session/peer.js +0 -451
  894. package/node_modules/remote-pi/dist/session/peer.js.map +0 -1
  895. package/node_modules/remote-pi/dist/session/peer_inventory.d.ts +0 -13
  896. package/node_modules/remote-pi/dist/session/peer_inventory.js +0 -48
  897. package/node_modules/remote-pi/dist/session/peer_inventory.js.map +0 -1
  898. package/node_modules/remote-pi/dist/session/peer_limits.d.ts +0 -9
  899. package/node_modules/remote-pi/dist/session/peer_limits.js +0 -21
  900. package/node_modules/remote-pi/dist/session/peer_limits.js.map +0 -1
  901. package/node_modules/remote-pi/dist/session/setup_wizard.d.ts +0 -39
  902. package/node_modules/remote-pi/dist/session/setup_wizard.js +0 -63
  903. package/node_modules/remote-pi/dist/session/setup_wizard.js.map +0 -1
  904. package/node_modules/remote-pi/dist/session/tool_gate.d.ts +0 -5
  905. package/node_modules/remote-pi/dist/session/tool_gate.js +0 -11
  906. package/node_modules/remote-pi/dist/session/tool_gate.js.map +0 -1
  907. package/node_modules/remote-pi/dist/session/tools.d.ts +0 -24
  908. package/node_modules/remote-pi/dist/session/tools.js +0 -252
  909. package/node_modules/remote-pi/dist/session/tools.js.map +0 -1
  910. package/node_modules/remote-pi/dist/session/wizard.d.ts +0 -13
  911. package/node_modules/remote-pi/dist/session/wizard.js +0 -20
  912. package/node_modules/remote-pi/dist/session/wizard.js.map +0 -1
  913. package/node_modules/remote-pi/dist/settings.d.ts +0 -15
  914. package/node_modules/remote-pi/dist/settings.js +0 -52
  915. package/node_modules/remote-pi/dist/settings.js.map +0 -1
  916. package/node_modules/remote-pi/dist/transport/peer_channel.d.ts +0 -37
  917. package/node_modules/remote-pi/dist/transport/peer_channel.js +0 -98
  918. package/node_modules/remote-pi/dist/transport/peer_channel.js.map +0 -1
  919. package/node_modules/remote-pi/dist/transport/pi_forward_client.d.ts +0 -29
  920. package/node_modules/remote-pi/dist/transport/pi_forward_client.js +0 -62
  921. package/node_modules/remote-pi/dist/transport/pi_forward_client.js.map +0 -1
  922. package/node_modules/remote-pi/dist/transport/relay_client.d.ts +0 -91
  923. package/node_modules/remote-pi/dist/transport/relay_client.js +0 -206
  924. package/node_modules/remote-pi/dist/transport/relay_client.js.map +0 -1
  925. package/node_modules/remote-pi/dist/ui/footer.d.ts +0 -32
  926. package/node_modules/remote-pi/dist/ui/footer.js +0 -34
  927. package/node_modules/remote-pi/dist/ui/footer.js.map +0 -1
  928. package/node_modules/remote-pi/docs/daemon.md +0 -289
  929. package/node_modules/remote-pi/package.json +0 -92
  930. package/node_modules/remote-pi/service-templates/launchd.plist.template +0 -35
  931. package/node_modules/remote-pi/service-templates/systemd.service.template +0 -19
  932. package/node_modules/remote-pi/service-templates/task-launcher.vbs.template +0 -10
  933. package/node_modules/remote-pi/service-templates/task-scheduler.xml.template +0 -38
  934. package/node_modules/remote-pi/skills/agent-network/SKILL.md +0 -310
@@ -1,1561 +0,0 @@
1
- /**
2
- * Read-Before-Edit Guard for pi-lens
3
- *
4
- * Blocks edits that lack adequate prior reading:
5
- * 1. Zero-read edit: never read this file in this branch
6
- * 2. File modified since read: disk content changed (FileTime)
7
- * 3. Out-of-range edit: edit target not covered by any previous read
8
- * 4. LSP expansion exemption: single-line read expanded to full symbol counts
9
- *
10
- * Falls back safely when LSP is unavailable.
11
- */
12
- import * as fs from "node:fs";
13
- import { incrementDegradationCount } from "./degradation-ledger.js";
14
- import { createFileTime } from "./file-time.js";
15
- import { hashDiagnosticContent } from "./lsp/diagnostic-binding.js";
16
- import { normalizeEphemeralMapKey, normalizeFilePath } from "./path-utils.js";
17
- import { logReadGuardEvent } from "./read-guard-logger.js";
18
- export const READ_GUARD_STATE_VERSION = 1;
19
- // --- Constants ---
20
- const DEFAULT_CONFIG = {
21
- enabled: true,
22
- mode: "block",
23
- contextLines: 3,
24
- exemptions: [
25
- { pattern: "*.md", mode: "warn" },
26
- { pattern: "*.txt", mode: "allow" },
27
- { pattern: "*.log", mode: "allow" },
28
- ],
29
- };
30
- const OWN_EDIT_STALE_GRACE_MS = Math.max(0, Number.parseInt(process.env.PI_LENS_READ_GUARD_OWN_EDIT_GRACE_MS ?? "120000", 10) || 120000);
31
- /** Avoid hashing very large reads in the hot path. */
32
- const READ_HASH_MAX_LINES = Math.max(0, Number.parseInt(process.env.PI_LENS_READ_GUARD_HASH_MAX_LINES ?? "3000", 10) || 3000);
33
- /**
34
- * Content bindings are defense in depth on a read-adjacent hot path. Cap the
35
- * synchronous disk read so bridge registration never hashes an unbounded file.
36
- */
37
- const READ_BINDING_MAX_BYTES = 4 * 1024 * 1024;
38
- const READ_GUARD_MAX_FILES = 256;
39
- /**
40
- * #1904 item 3: `enforceFileCap` bounds how many FILES the store holds, not how
41
- * many records each file holds. One hot file reached 268 hash-bearing records in
42
- * 75 minutes, and every record carries a `lineHashes` map, so a long session on
43
- * a few files grows without bound. Cap records per file and drop oldest-first.
44
- *
45
- * Eviction is safe in the blocking direction: it narrows the staleness-rescue
46
- * path — a stale edit rescued by an older snapshot that still matches — and can
47
- * turn an allow into a "re-read and retry" block. It never turns a block into
48
- * an allow. See `enforceRecordCapForFile` for which records go first, and why
49
- * age alone is the wrong order.
50
- */
51
- const READ_GUARD_MAX_RECORDS_PER_FILE = 128;
52
- const NO_TRIM = {
53
- evictedCount: 0,
54
- evictedCreditCount: 0,
55
- evictedGenuineCount: 0,
56
- };
57
- function enforceRecordCapForFile(records) {
58
- if (records.length <= READ_GUARD_MAX_RECORDS_PER_FILE)
59
- return NO_TRIM;
60
- const excess = records.length - READ_GUARD_MAX_RECORDS_PER_FILE;
61
- const evicted = new Set();
62
- let evictedCreditCount = 0;
63
- for (let i = 0; i < records.length && evicted.size < excess; i++) {
64
- if (records[i].searchCredit !== undefined) {
65
- evicted.add(i);
66
- evictedCreditCount++;
67
- }
68
- }
69
- for (let i = 0; i < records.length && evicted.size < excess; i++) {
70
- evicted.add(i);
71
- }
72
- const kept = records.filter((_, i) => !evicted.has(i));
73
- records.length = 0;
74
- for (const record of kept)
75
- records.push(record);
76
- return {
77
- evictedCount: excess,
78
- evictedCreditCount,
79
- evictedGenuineCount: excess - evictedCreditCount,
80
- };
81
- }
82
- /**
83
- * #1904 class sweep: `this.edits` is the same shape as `this.reads` — a
84
- * per-file array that only ever grows. Its cap sits far above every consumer's
85
- * reach, so trimming is inert: `canTreatStalenessAsOwnPriorEdit` reads only the
86
- * last record, and `findRelocation`'s window saturates at
87
- * RELOCATION_WINDOW_MAX / RELOCATION_WINDOW_PER_EDIT (20) applied edits. Only
88
- * `getStats`, a debug surface, sees the older records at all.
89
- */
90
- const READ_GUARD_MAX_EDITS_PER_FILE = 256;
91
- // Unconsumed reads remain valid until edit or session end, but this high
92
- // sanity cap prevents a read-only session from growing without bound.
93
- const READ_GUARD_MAX_UNCONSUMED_FILES = 4096;
94
- const READ_GUARD_IDLE_EVICT_MS_DEFAULT = 30 * 60_000;
95
- export function captureReadContentBinding(filePath, offset, limit) {
96
- try {
97
- if (fs.statSync(filePath).size > READ_BINDING_MAX_BYTES)
98
- return undefined;
99
- const content = fs.readFileSync(filePath, "utf-8");
100
- // Allocate at most the classification prefix first. For range bindings,
101
- // splitting then stops at the requested range end rather than the EOF.
102
- const lines = splitLinesThrough(content, READ_HASH_MAX_LINES + 1);
103
- if (lines.length <= READ_HASH_MAX_LINES) {
104
- return {
105
- hash: hashDiagnosticContent(content),
106
- fullFile: true,
107
- offset: 1,
108
- limit: lines.length,
109
- };
110
- }
111
- const scopedOffset = Math.max(1, offset);
112
- const scopedLimit = Math.min(Math.max(0, limit), READ_HASH_MAX_LINES);
113
- if (scopedLimit === 0)
114
- return undefined;
115
- const rangeLines = splitLinesThrough(content, scopedOffset - 1 + scopedLimit);
116
- const scopedContent = rangeLines
117
- .slice(scopedOffset - 1, scopedOffset - 1 + scopedLimit)
118
- .join("\n");
119
- return {
120
- hash: hashDiagnosticContent(scopedContent),
121
- fullFile: false,
122
- offset: scopedOffset,
123
- limit: scopedLimit,
124
- };
125
- }
126
- catch {
127
- return undefined;
128
- }
129
- }
130
- export function _currentContentMatchesBindingForTests(filePath, binding) {
131
- try {
132
- const content = fs.readFileSync(filePath, "utf-8");
133
- const boundContent = binding.fullFile
134
- ? content
135
- : splitLines(content)
136
- .slice(binding.offset - 1, binding.offset - 1 + binding.limit)
137
- .join("\n");
138
- return hashDiagnosticContent(boundContent) === binding.hash;
139
- }
140
- catch {
141
- return false;
142
- }
143
- }
144
- const currentContentMatchesBinding = _currentContentMatchesBindingForTests;
145
- // Adaptive relocation window (findRelocation). A globally-unique hash-sequence
146
- // match always wins; when the content is duplicated elsewhere, we fall back to
147
- // a match that is unique WITHIN this window of the original position. The window
148
- // widens with edits already applied to the file (accumulated line drift) —
149
- // floor + per-edit growth, capped — the analog of pi-hashline-readmap's
150
- // edits-scaled relocation window.
151
- const RELOCATION_WINDOW_MIN = Math.max(1, Number.parseInt(process.env.PI_LENS_READ_GUARD_RELOCATION_WINDOW_MIN ?? "40", 10) || 40);
152
- const RELOCATION_WINDOW_PER_EDIT = Math.max(0, Number.parseInt(process.env.PI_LENS_READ_GUARD_RELOCATION_WINDOW_PER_EDIT ?? "20", 10) || 20);
153
- const RELOCATION_WINDOW_MAX = Math.max(RELOCATION_WINDOW_MIN, Number.parseInt(process.env.PI_LENS_READ_GUARD_RELOCATION_WINDOW_MAX ?? "400", 10) || 400);
154
- function splitLines(text) {
155
- return text.split(/\r?\n/);
156
- }
157
- /** `splitLines` semantics, but without scanning/allocating beyond `maxLines`. */
158
- function splitLinesThrough(text, maxLines) {
159
- if (maxLines <= 0)
160
- return [];
161
- const lines = [];
162
- let start = 0;
163
- while (lines.length < maxLines) {
164
- const newline = text.indexOf("\n", start);
165
- if (newline === -1) {
166
- lines.push(text.slice(start));
167
- break;
168
- }
169
- const end = newline > start && text[newline - 1] === "\r" ? newline - 1 : newline;
170
- lines.push(text.slice(start, end));
171
- start = newline + 1;
172
- if (start === text.length && lines.length < maxLines) {
173
- lines.push("");
174
- break;
175
- }
176
- }
177
- return lines;
178
- }
179
- export function lineContentHash(line) {
180
- // FNV-1a over whitespace-stripped content. This treats no-op formatter/touch
181
- // changes as still-valid context while detecting semantic line changes.
182
- const normalized = line.replace(/\s+/g, "");
183
- let hash = 2166136261;
184
- for (let i = 0; i < normalized.length; i++) {
185
- hash = Math.imul(hash ^ normalized.charCodeAt(i), 16777619);
186
- }
187
- return (hash >>> 0).toString(16).padStart(8, "0");
188
- }
189
- function readRangeCoversLine(read, lineNo) {
190
- return (lineNo >= read.effectiveOffset &&
191
- lineNo <= read.effectiveOffset + read.effectiveLimit - 1);
192
- }
193
- function readEffectiveRangeCoversRange(read, [startLine, endLine]) {
194
- return (readRangeCoversLine(read, startLine) && readRangeCoversLine(read, endLine));
195
- }
196
- function captureLineHashes(filePath, offset, limit) {
197
- if (limit <= 0 || limit > READ_HASH_MAX_LINES)
198
- return undefined;
199
- try {
200
- const lines = splitLines(fs.readFileSync(filePath, "utf-8"));
201
- const hashes = {};
202
- const end = Math.min(lines.length, offset + limit - 1);
203
- for (let lineNo = Math.max(1, offset); lineNo <= end; lineNo++) {
204
- hashes[lineNo] = lineContentHash(lines[lineNo - 1] ?? "");
205
- }
206
- return Object.keys(hashes).length > 0 ? hashes : undefined;
207
- }
208
- catch {
209
- return undefined;
210
- }
211
- }
212
- export function currentLinesMatchReadSnapshot(filePath, read, [startLine, endLine]) {
213
- const hashes = read.lineHashes ?? {};
214
- const missingLines = [];
215
- const mismatchedLines = [];
216
- for (let lineNo = startLine; lineNo <= endLine; lineNo += 1) {
217
- if (!readRangeCoversLine(read, lineNo) || hashes[lineNo] === undefined) {
218
- missingLines.push(lineNo);
219
- }
220
- }
221
- if (missingLines.length > 0) {
222
- return { checked: false, matches: false, missingLines, mismatchedLines };
223
- }
224
- let lines;
225
- try {
226
- lines = splitLines(fs.readFileSync(filePath, "utf-8"));
227
- }
228
- catch {
229
- return {
230
- checked: true,
231
- matches: false,
232
- missingLines,
233
- mismatchedLines: [...Array(endLine - startLine + 1)].map((_, index) => startLine + index),
234
- };
235
- }
236
- for (let lineNo = startLine; lineNo <= endLine; lineNo += 1) {
237
- if (lineNo < 1 || lineNo > lines.length) {
238
- mismatchedLines.push(lineNo);
239
- continue;
240
- }
241
- if (lineContentHash(lines[lineNo - 1] ?? "") !== hashes[lineNo]) {
242
- mismatchedLines.push(lineNo);
243
- }
244
- }
245
- return {
246
- checked: true,
247
- matches: mismatchedLines.length === 0,
248
- missingLines,
249
- mismatchedLines,
250
- };
251
- }
252
- // --- ReadGuard Class ---
253
- export class ReadGuard {
254
- config;
255
- reads = new Map();
256
- edits = new Map();
257
- fileLastUsed = new Map();
258
- fileIdleTimers = new Map();
259
- /** Reads remain behavior-gating until the corresponding edit is published. */
260
- consumedReadFiles = new Set();
261
- fileTime;
262
- exemptions = new Set(); // One-time exemptions via /lens-allow-edit
263
- pendingCreations = new Map();
264
- // Files that recordWritten() has fired on this session. Lets
265
- // wasWrittenThisSession() return a deterministic answer for files the
266
- // pi Write tool authored, independent of filesystem mtime granularity
267
- // or clock skew (NFS, FAT32, etc.).
268
- writtenThisSession = new Set();
269
- unchangedThisSession = new Set();
270
- // Existence-independent index for hasKnownPath/forgetPath (#1668 review
271
- // F1). `this.key()` (normalizeFilePath) branches on whether `filePath`
272
- // currently exists on disk, on EVERY platform since #3098 — an existing
273
- // file resolves to its on-disk casing (win32: `realpathSync.native`;
274
- // POSIX: the on-disk casing of the trailing segments), a missing one to a
275
- // lowercased tail on win32 and to the caller's own spelling on POSIX.
276
- // recordRead/recordWritten always key while the file is still on disk
277
- // (real casing); hasKnownPath/forgetPath are queried AFTER an external
278
- // delete already landed, when the path no longer exists — recomputing
279
- // `this.key()` at that point returns a DIFFERENT string for any
280
- // mixed-case basename (win32 `MyModule.ts` → `mymodule.ts`; POSIX
281
- // `Components/x.ts` held against an on-disk `components/`), so a lookup
282
- // against `reads`/`writtenThisSession` silently misses. This index maps
283
- // a purely syntactic key (`normalizeEphemeralMapKey` — slash-fold +
284
- // lowercase, no filesystem access, so it never depends on current disk
285
- // state) to the REAL key `reads`/`writtenThisSession` used at record
286
- // time, so a post-delete lookup finds the same entry regardless of what
287
- // happened to the file since. Pruned inside `evictFile` so it never
288
- // outlives the record it points at.
289
- //
290
- // ONLY hasKnownPath/forgetPath route through this index, because only they
291
- // need the real key BACK. `noteCreatedFile` (keys while the file is still
292
- // absent) and `recordWritten` (keys after it exists) straddle the same
293
- // state change, and used to do it through `this.key()` directly — a
294
- // spelling whose key moved orphaned the `pendingCreations` entry and the
295
- // creation read was never injected (#3163, pre-existing on win32 and
296
- // reachable on POSIX since #3098). They now key `pendingCreations` by the
297
- // same existence-independent `normalizeEphemeralMapKey` spelling this index
298
- // is keyed by; no index is needed there, since the real key is derived by
299
- // `recordWritten` itself at the moment it is used.
300
- knownPathIndex = new Map();
301
- /** Running per-file record-cap trim totals for this session (#1913 F1). */
302
- trimAccumulators = new Map();
303
- sessionId;
304
- sessionStartMs;
305
- constructor(sessionId, config = {}) {
306
- this.sessionId = sessionId;
307
- this.sessionStartMs = Date.now();
308
- this.config = { ...DEFAULT_CONFIG, ...config };
309
- this.fileTime = createFileTime(sessionId);
310
- }
311
- /**
312
- * Canonical Map key for a file path. Read sources arrive with mixed
313
- * separators/casing — the Read tool gives OS-native backslashes on Windows,
314
- * while LSP-expanded and search-tool reads arrive slash-normalized from URIs.
315
- * Keying the reads/edits/exemptions maps on the raw path made a read recorded
316
- * under one form invisible to an edit checked under another, producing a false
317
- * `zero_read` block despite the file having been read. `normalizeFilePath`
318
- * folds separators and Windows casing to one key, so record and lookup always
319
- * agree. Every map access in this class MUST key through here.
320
- */
321
- key(filePath) {
322
- return normalizeFilePath(filePath);
323
- }
324
- idleEvictMs() {
325
- const value = Number.parseInt(process.env.PI_LENS_READ_GUARD_IDLE_EVICT_MS ?? "", 10);
326
- return Number.isSafeInteger(value) && value > 0
327
- ? value
328
- : READ_GUARD_IDLE_EVICT_MS_DEFAULT;
329
- }
330
- clearFileTimer(filePath) {
331
- const timer = this.fileIdleTimers.get(filePath);
332
- if (timer)
333
- clearTimeout(timer);
334
- this.fileIdleTimers.delete(filePath);
335
- }
336
- /**
337
- * #1918: `enforceRecordCapForFile` (the per-file record cap) is the only
338
- * `read-guard.ts` eviction path #1913 gave a bounded record. This whole-file
339
- * evictor has four call sites — `enforceFileCap`'s consumed-file cap, its
340
- * unconsumed-file cap, `touchFile`'s idle timer, and `forgetPath`'s
341
- * external-delete cleanup — and none of them left any trace. A live
342
- * regression in any of the four (e.g. a cap set too low) dropped a file's
343
- * read/edit history with nothing in `~/.pi-lens/read-guard.log` to show it
344
- * happened.
345
- *
346
- * Follows #1913's aggregate-after-first pattern for the three PRESSURE
347
- * reasons (cap exhaustion, external delete): `incrementDegradationCount`
348
- * keeps the exact per-file eviction tally (and its own power-of-two
349
- * milestone rows in latency.log) while the read-guard.log line fires only
350
- * on the rising edge, so a file evicted repeatedly across a session can't
351
- * flood the log.
352
- *
353
- * `idle-timeout` is deliberately excluded from both the ledger tally and
354
- * the log line (#1918 review F2): idle eviction is designed housekeeping,
355
- * not a fault signal — a read-only session that idles out N files is
356
- * healthy, ordinary behavior, and N is unbounded (every distinct file path
357
- * touched that session is its own ledger subject). Recording it here would
358
- * grow the ledger's tally map without bound and spam `pilens_health` in
359
- * every healthy session, unlike the other three reasons, which are rare by
360
- * construction (only real pressure or an explicit delete reaches them).
361
- */
362
- evictFile(filePath, reason) {
363
- this.clearFileTimer(filePath);
364
- this.reads.delete(filePath);
365
- this.edits.delete(filePath);
366
- this.fileLastUsed.delete(filePath);
367
- this.consumedReadFiles.delete(filePath);
368
- this.writtenThisSession.delete(filePath);
369
- // #1668 review F1: prune the reverse-pointing knownPathIndex entries
370
- // too, so it never outlives the record it points at.
371
- for (const [syntacticKey, stored] of this.knownPathIndex) {
372
- if (stored === filePath)
373
- this.knownPathIndex.delete(syntacticKey);
374
- }
375
- if (reason === "idle-timeout")
376
- return;
377
- const isRisingEdge = incrementDegradationCount({
378
- kind: "read-guard-file-evicted",
379
- subject: filePath,
380
- reason: `evicted (${reason})`,
381
- });
382
- if (isRisingEdge) {
383
- logReadGuardEvent({
384
- event: "read_file_evicted",
385
- sessionId: this.sessionId,
386
- filePath,
387
- metadata: { reason },
388
- });
389
- }
390
- }
391
- touchFile(filePath) {
392
- const now = Date.now();
393
- this.fileLastUsed.set(filePath, now);
394
- this.clearFileTimer(filePath);
395
- // An outstanding read is enforcement state, not a rebuildable cache entry.
396
- // It must survive idle time and file-cap pressure until the edit consumes it.
397
- if (this.reads.has(filePath) && !this.consumedReadFiles.has(filePath))
398
- return;
399
- const stamp = now;
400
- const timer = setTimeout(() => {
401
- if (this.fileLastUsed.get(filePath) !== stamp)
402
- return;
403
- // Never turn an outstanding read into a zero-read block through idle
404
- // eviction. Only consumed reads and rebuildable edit history may expire.
405
- if (this.reads.has(filePath) && !this.consumedReadFiles.has(filePath))
406
- return;
407
- // #1918 review F2: this reason is intentionally silent inside
408
- // evictFile — idle eviction is routine housekeeping, not a fault, and
409
- // every distinct file idling out this session is its own ledger
410
- // subject, so recording it would grow unbounded and flood
411
- // pilens_health on every healthy session. See evictFile's doc comment.
412
- this.evictFile(filePath, "idle-timeout");
413
- }, this.idleEvictMs());
414
- timer.unref?.();
415
- this.fileIdleTimers.set(filePath, timer);
416
- }
417
- enforceFileCap() {
418
- while (this.reads.size > READ_GUARD_MAX_FILES) {
419
- const victim = [...this.reads.keys()]
420
- .filter((filePath) => this.consumedReadFiles.has(filePath))
421
- .sort((a, b) => (this.fileLastUsed.get(a) ?? 0) - (this.fileLastUsed.get(b) ?? 0))[0];
422
- if (!victim)
423
- break;
424
- this.evictFile(victim, "file-cap-consumed");
425
- }
426
- while (this.reads.size > READ_GUARD_MAX_UNCONSUMED_FILES) {
427
- const victim = [...this.reads.keys()]
428
- .filter((filePath) => !this.consumedReadFiles.has(filePath))
429
- .sort((a, b) => (this.fileLastUsed.get(a) ?? 0) - (this.fileLastUsed.get(b) ?? 0))[0];
430
- if (!victim)
431
- break;
432
- // This is a normal read miss: a later edit must require a fresh read,
433
- // never silently allow and never become a permanent hard-block.
434
- this.evictFile(victim, "file-cap-unconsumed");
435
- }
436
- }
437
- // --- Public API ---
438
- /**
439
- * Record that a file was read.
440
- * Call this from the tool_call handler after any LSP expansion.
441
- */
442
- recordRead(record, opts) {
443
- const filePath = this.key(record.filePath);
444
- if (opts?.supersedes) {
445
- const records = this.reads.get(filePath);
446
- const provisionalSource = `native-read:${opts.supersedes.toolCallId}:provisional`;
447
- const provisionalIndex = records?.findIndex((candidate) => candidate.source === provisionalSource);
448
- if (provisionalIndex !== undefined && provisionalIndex >= 0)
449
- records.splice(provisionalIndex, 1);
450
- }
451
- // #1668 review F1: index by the existence-independent syntactic key
452
- // while the file is (presumably) still on disk, so a later
453
- // hasKnownPath/forgetPath lookup after an external delete can still
454
- // find this entry's real key.
455
- this.knownPathIndex.set(normalizeEphemeralMapKey(record.filePath), filePath);
456
- const storedRecord = {
457
- ...record,
458
- filePath,
459
- lineHashes: record.lineHashes ??
460
- captureLineHashes(filePath, record.effectiveOffset, record.effectiveLimit),
461
- };
462
- const arr = this.reads.get(storedRecord.filePath) ?? [];
463
- this.consumedReadFiles.delete(storedRecord.filePath);
464
- arr.push(storedRecord);
465
- this.reads.set(storedRecord.filePath, arr);
466
- // Capture BEFORE the trim: `readCountForFile` saturates at the cap once
467
- // eviction starts, so it stops showing growth. `rawReadCountForFile` keeps
468
- // the real arrival count observable alongside `evictedRecordCount`.
469
- const rawReadCountForFile = arr.length;
470
- const { evictedCount: evictedRecordCount, evictedCreditCount, evictedGenuineCount, } = enforceRecordCapForFile(arr);
471
- this.touchFile(storedRecord.filePath);
472
- this.enforceFileCap();
473
- logReadGuardEvent({
474
- event: "read_recorded",
475
- sessionId: this.sessionId,
476
- filePath: storedRecord.filePath,
477
- requestedOffset: storedRecord.requestedOffset,
478
- requestedLimit: storedRecord.requestedLimit,
479
- effectiveOffset: storedRecord.effectiveOffset,
480
- effectiveLimit: storedRecord.effectiveLimit,
481
- symbol: storedRecord.enclosingSymbol?.name,
482
- symbolKind: storedRecord.enclosingSymbol?.kind,
483
- symbolStartLine: storedRecord.enclosingSymbol?.startLine,
484
- symbolEndLine: storedRecord.enclosingSymbol?.endLine,
485
- metadata: {
486
- expandedByLsp: storedRecord.expandedByLsp,
487
- turnIndex: storedRecord.turnIndex,
488
- writeIndex: storedRecord.writeIndex,
489
- readCountForFile: arr.length,
490
- hashLineCount: Object.keys(storedRecord.lineHashes ?? {}).length,
491
- ...(storedRecord.source !== undefined && {
492
- source: storedRecord.source,
493
- }),
494
- ...(storedRecord.searchCredit !== undefined && {
495
- searchCreditReason: storedRecord.searchCredit.reason,
496
- searchCreditMarginBefore: storedRecord.searchCredit.marginBefore,
497
- searchCreditMarginAfter: storedRecord.searchCredit.marginAfter,
498
- }),
499
- ...(evictedRecordCount > 0 && {
500
- evictedRecordCount,
501
- rawReadCountForFile,
502
- }),
503
- },
504
- });
505
- // #1913: the eviction counters above ride `read_recorded`, which is
506
- // gated behind PI_LENS_READ_GUARD_VERBOSE — off by default. That left a
507
- // live eviction regression with no trace at default verbosity.
508
- //
509
- // review F1: a naive "emit every trim" fix floods read-guard.log once a
510
- // hot file sits past the cap — every later push trims exactly 1 record
511
- // (the array is always AT the cap before the push, so `excess` is
512
- // always exactly 1), so 300 recordRead calls on one file is ~172
513
- // identical always-on lines, and can rotate `edit_blocked` records out
514
- // of the 1MB cap. `rawReadCountForFile` also freezes at cap+1 forever,
515
- // so a raw per-trim record can't even discriminate thrash severity.
516
- //
517
- // Fix: emit the read-guard.log line ONCE per file per session, on the
518
- // FIRST trim only. `trimAccumulators` still updates on every trim,
519
- // queryable via `getTrimStats` — the running totals a health surface or
520
- // a future emission point would need are never lost, even though
521
- // read-guard.log stops re-announcing them. "Have we already logged
522
- // this file's first trim" routes through the degradation ledger's own
523
- // rising-edge tally (`incrementDegradationCount` — the pattern
524
- // CLAUDE.md names for repeated degradations) instead of a hand-rolled
525
- // per-file Set; subsequent trims still call it, so the ledger's own
526
- // entry for this (kind, subject) keeps its reason text and count
527
- // current for the health/degradation summary even after read-guard.log
528
- // goes quiet.
529
- if (evictedRecordCount > 0) {
530
- const key = storedRecord.filePath;
531
- const acc = this.trimAccumulators.get(key) ?? {
532
- totalEvicted: 0,
533
- evictedCreditCount: 0,
534
- evictedGenuineCount: 0,
535
- trimEventCount: 0,
536
- };
537
- acc.totalEvicted += evictedRecordCount;
538
- acc.evictedCreditCount += evictedCreditCount;
539
- acc.evictedGenuineCount += evictedGenuineCount;
540
- acc.trimEventCount += 1;
541
- this.trimAccumulators.set(key, acc);
542
- const isRisingEdge = incrementDegradationCount({
543
- kind: "read-guard-record-cap-trim",
544
- subject: key,
545
- reason: `trim #${acc.trimEventCount}: evicted ${evictedRecordCount} (credit ${evictedCreditCount}, genuine ${evictedGenuineCount})`,
546
- });
547
- if (isRisingEdge) {
548
- logReadGuardEvent({
549
- event: "read_cap_trimmed",
550
- sessionId: this.sessionId,
551
- filePath: storedRecord.filePath,
552
- metadata: {
553
- trimEventCount: acc.trimEventCount,
554
- evictedRecordCount: acc.totalEvicted,
555
- evictedCreditCount: acc.evictedCreditCount,
556
- evictedGenuineCount: acc.evictedGenuineCount,
557
- rawReadCountForFile,
558
- },
559
- });
560
- }
561
- }
562
- // Also update FileTime stamp for this file
563
- this.fileTime.read(storedRecord.filePath);
564
- }
565
- /**
566
- * Record a structured symbol read (the `readSymbol` engine capability / its
567
- * MCP mirror) as a genuine read of that symbol's line range — the
568
- * read-substitute tie-in for #245. `readSymbol` returns the verbatim body, so
569
- * an edit within [startLine, endLine] is legitimately covered, exactly like a
570
- * TS/LSP-expanded read that delivered the whole enclosing symbol. Line hashes
571
- * for the range are captured by recordRead, so the edit is also
572
- * snapshot-verified (drift since the symbol read still blocks).
573
- *
574
- * Intentionally NOT offered for module *outlines*: an outline shows a symbol's
575
- * shape (name/signature/range), not its body, so granting edit coverage from
576
- * it would let the agent edit lines it never saw. Only a body-delivering read
577
- * (readSymbol / raw Read) records coverage.
578
- */
579
- recordSymbolRead(filePath, symbol, turnIndex, writeIndex) {
580
- const span = Math.max(1, symbol.endLine - symbol.startLine + 1);
581
- this.recordRead({
582
- filePath,
583
- requestedOffset: symbol.startLine,
584
- requestedLimit: span,
585
- effectiveOffset: symbol.startLine,
586
- effectiveLimit: span,
587
- expandedByLsp: false,
588
- enclosingSymbol: {
589
- name: symbol.name,
590
- kind: symbol.kind,
591
- startLine: symbol.startLine,
592
- endLine: symbol.endLine,
593
- },
594
- turnIndex,
595
- writeIndex,
596
- timestamp: Date.now(),
597
- });
598
- }
599
- /**
600
- * Check if an edit should be allowed.
601
- * Returns verdict with action and optional reason for blocking.
602
- */
603
- checkEdit(filePath, touchedLines, editRanges, options) {
604
- // Canonicalize once: every map lookup below (and every private helper this
605
- // passes filePath to) must agree with how recordRead keyed the read.
606
- filePath = this.key(filePath);
607
- if (this.reads.has(filePath) || this.edits.has(filePath))
608
- this.touchFile(filePath);
609
- // Check exemptions
610
- if (this.exemptions.has(filePath)) {
611
- this.exemptions.delete(filePath); // One-time use
612
- const verdict = this.allow();
613
- this.recordVerdict(filePath, "edit", touchedLines, verdict, {
614
- reasonKind: "manual_exemption",
615
- });
616
- return verdict;
617
- }
618
- // Check config exemptions by pattern
619
- const exemptionMode = this.getExemptionMode(filePath);
620
- if (exemptionMode === "allow") {
621
- const verdict = this.allow();
622
- this.recordVerdict(filePath, "edit", touchedLines, verdict, {
623
- reasonKind: "pattern_exemption",
624
- exemptionMode,
625
- });
626
- return verdict;
627
- }
628
- // "warn" pattern exemptions downgrade all blocking verdicts to warnings.
629
- const effectiveMode = exemptionMode === "warn" ? "warn" : undefined;
630
- // 1. Zero-read check
631
- const fileReads = this.reads.get(filePath);
632
- if (!fileReads || fileReads.length === 0) {
633
- // If the file was written after this session started, the agent authored
634
- // it in this session (via Write or any other mechanism). Allow the edit —
635
- // a synthetic read would have been injected for Write tool calls, but
636
- // this catches cases where the write bypassed the hook or the session
637
- // restarted between write and edit.
638
- if (this.wasWrittenThisSession(filePath)) {
639
- this.injectCreationRead(filePath, 0, 0);
640
- const verdict = this.allow();
641
- this.recordVerdict(filePath, "edit", touchedLines, verdict, {
642
- reasonKind: "session_authored",
643
- });
644
- return verdict;
645
- }
646
- const verdict = this.blockOrWarn("zero-read", `🔄 RETRYABLE — Edit without read: you have not read \`${filePath}\` in this conversation. Read it first, then retry: \`read path="${filePath}"\`.`, undefined, effectiveMode);
647
- this.recordVerdict(filePath, "edit", touchedLines, verdict, {
648
- reasonKind: "zero_read",
649
- });
650
- return verdict;
651
- }
652
- const lastBoundRead = [...fileReads]
653
- .reverse()
654
- .find((read) => read.contentBinding !== undefined);
655
- if (lastBoundRead?.contentBinding &&
656
- !currentContentMatchesBinding(filePath, lastBoundRead.contentBinding)) {
657
- const verdict = this.blockOrWarn("file-modified", `🔄 RETRYABLE — File modified since read\n\nYou last read \`${filePath}\` at ${new Date(lastBoundRead.timestamp).toISOString()}.\nThe file content no longer matches the bridge-recorded read.\n\nYour mental model is out of sync with the actual file content.\nTo proceed:\n 1. Re-read the file: \`read path="${filePath}"\``, undefined, effectiveMode);
658
- this.recordVerdict(filePath, "edit", touchedLines, verdict, {
659
- reasonKind: "file_modified",
660
- lastReadTimestamp: lastBoundRead.timestamp,
661
- contentBindingMismatch: true,
662
- });
663
- return verdict;
664
- }
665
- // 2. FileTime check (actual staleness)
666
- let ignoredOwnEditStaleness = false;
667
- let ignoredHashStaleness = false;
668
- let ignoredOldTextResolvedStaleness = false;
669
- if (this.fileTime.hasChanged(filePath)) {
670
- const lastRead = fileReads[fileReads.length - 1];
671
- if (options?.oldTextResolved === true) {
672
- // The host-facing preflight sets this only after oldText resolves to
673
- // exactly one span in the live bytes. That content evidence is newer
674
- // and stronger than FileTime's coarse external-write signal, matching
675
- // the skipSnapshotCheck exception at the later range-stale gate.
676
- ignoredOldTextResolvedStaleness = true;
677
- }
678
- else if (this.canTreatStalenessAsOwnPriorEdit(filePath, lastRead.timestamp)) {
679
- ignoredOwnEditStaleness = true;
680
- }
681
- else if (this.canIgnoreStalenessByHashes(filePath, fileReads, touchedLines, editRanges)) {
682
- ignoredHashStaleness = true;
683
- }
684
- else {
685
- const verdict = this.blockOrWarn("file-modified", `🔄 RETRYABLE — File modified since read\n\nYou last read \`${filePath}\` at ${new Date(lastRead.timestamp).toISOString()}.\nThe file has been modified on disk since then (auto-format, external tool, or previous edit).\n\nYour mental model is out of sync with the actual file content.\nTo proceed:\n 1. Re-read the file: \`read path="${filePath}"\``, undefined, effectiveMode);
686
- this.recordVerdict(filePath, "edit", touchedLines, verdict, {
687
- reasonKind: "file_modified",
688
- lastReadTimestamp: lastRead.timestamp,
689
- });
690
- return verdict;
691
- }
692
- }
693
- // If no line range specified, we can only check zero-read and FileTime
694
- if (!touchedLines) {
695
- const verdict = this.allow();
696
- this.recordVerdict(filePath, "edit", touchedLines, verdict, {
697
- reasonKind: "no_line_info",
698
- });
699
- return verdict;
700
- }
701
- // 3. Range coverage check
702
- // When the edit touches multiple disjoint spots (e.g. rename across 4 tool
703
- // registrations), check each spot independently. Collapsing to a bounding
704
- // box would falsely flag reads that cover exactly the right lines.
705
- const rangesToCheck = editRanges && editRanges.length > 1 ? editRanges : [touchedLines];
706
- let viaSymbol = false;
707
- for (const range of rangesToCheck) {
708
- const snapshotValidation = this.validateRangeSnapshot(filePath, range, !!options?.skipSnapshotCheck);
709
- const coverage = this.checkCoverage(filePath, range);
710
- if (!coverage.covered) {
711
- const lastRead = fileReads[fileReads.length - 1];
712
- const [editStart, editEnd] = range;
713
- const lastReadEnd = lastRead.effectiveOffset + lastRead.effectiveLimit - 1;
714
- // If oldText was resolved (content-verified), the model demonstrably
715
- // knew the content it's replacing — line drift from prior edits in
716
- // the session is the likely cause. Downgrade to warn rather than block.
717
- const outOfRangeMode = options?.oldTextResolved
718
- ? "warn"
719
- : effectiveMode;
720
- const verdict = this.blockOrWarn("out-of-range", `🔄 RETRYABLE — Edit outside read range\n\nYou read \`${filePath}\` lines ${lastRead.effectiveOffset}-${lastReadEnd}${lastRead.enclosingSymbol ? ` (${lastRead.enclosingSymbol.kind} \`${lastRead.enclosingSymbol.name}\`)` : ""}, but your edit touches lines ${editStart}-${editEnd}.\n\nRead the relevant section first, then retry the edit:\n \`read path="${filePath}" offset=${Math.max(1, editStart - 5)} limit=${Math.min(30, editEnd - editStart + 10)}\``, {
721
- editRange: range,
722
- readRanges: fileReads.map((r) => ({
723
- start: r.effectiveOffset,
724
- end: r.effectiveOffset + r.effectiveLimit - 1,
725
- })),
726
- symbolRanges: fileReads
727
- .filter((r) => r.enclosingSymbol)
728
- .map((r) => ({
729
- name: r.enclosingSymbol.name,
730
- start: r.enclosingSymbol.startLine,
731
- end: r.enclosingSymbol.endLine,
732
- })),
733
- }, outOfRangeMode);
734
- this.recordVerdict(filePath, "edit", touchedLines, verdict, {
735
- reasonKind: "out_of_range",
736
- oldTextResolved: options?.oldTextResolved ?? false,
737
- });
738
- return verdict;
739
- }
740
- if (snapshotValidation.shouldBlock && !options?.skipSnapshotCheck) {
741
- const [editStart, editEnd] = range;
742
- // Grace period: when the snapshot is stale because THIS session's own
743
- // earlier edit shifted line numbers (ignoredOwnEditStaleness), and
744
- // the agent read the file recently, downgrade to a warning rather
745
- // than blocking. The agent has fresh context — they just don't
746
- // know the exact new line numbers after the shift.
747
- const RANGE_STALE_GRACE_MS = 60_000;
748
- const lastRead = fileReads[fileReads.length - 1];
749
- const graceActive = ignoredOwnEditStaleness &&
750
- Date.now() - lastRead.timestamp < RANGE_STALE_GRACE_MS;
751
- // Content-verified relocation: if the lines the agent read have
752
- // merely shifted (same content, new offset), tell them exactly where
753
- // so they re-target in one turn. We hint rather than silently
754
- // re-apply: the host applies native range edits positionally and
755
- // can't re-verify, so an unverified auto-relocation could corrupt.
756
- const relocation = this.findRelocation(filePath, fileReads, range);
757
- const relocationNote = relocation
758
- ? `\n\n📍 The content you read at lines ${relocation.from[0]}-${relocation.from[1]} now appears unchanged at lines ${relocation.to[0]}-${relocation.to[1]} — it shifted position. Re-target your edit to lines ${relocation.to[0]}-${relocation.to[1]}.`
759
- : "";
760
- const verdict = this.blockOrWarn("range-stale", `🔄 RETRYABLE — Edit range changed since read\n\nYou are editing \`${filePath}\` lines ${editStart}-${editEnd}, but those lines no longer match the content you read earlier.${relocationNote}\n\nRe-read the relevant section, then retry the edit using the current line range/content:\n \`read path="${filePath}" offset=${Math.max(1, editStart - 5)} limit=${Math.min(30, editEnd - editStart + 10)}\``, {
761
- editRange: range,
762
- readRanges: fileReads.map((r) => ({
763
- start: r.effectiveOffset,
764
- end: r.effectiveOffset + r.effectiveLimit - 1,
765
- })),
766
- symbolRanges: fileReads
767
- .filter((r) => r.enclosingSymbol)
768
- .map((r) => ({
769
- name: r.enclosingSymbol.name,
770
- start: r.enclosingSymbol.startLine,
771
- end: r.enclosingSymbol.endLine,
772
- })),
773
- snapshot: {
774
- status: snapshotValidation.status,
775
- mismatchedLines: snapshotValidation.mismatchedLines,
776
- missingLines: snapshotValidation.missingLines,
777
- },
778
- ...(relocation ? { relocation } : {}),
779
- }, graceActive ? "warn" : effectiveMode);
780
- // Offer auto-apply only for a single-range edit: we relocated exactly
781
- // one range, so shifting it is the whole edit. A multi-range edit
782
- // could have other drifted spots we returned before checking, so it
783
- // stays a hint.
784
- if (relocation && rangesToCheck.length === 1) {
785
- verdict.relocation = relocation;
786
- }
787
- this.recordVerdict(filePath, "edit", touchedLines, verdict, {
788
- reasonKind: "range_stale",
789
- range,
790
- mismatchedLines: snapshotValidation.mismatchedLines.slice(0, 20),
791
- graceActive,
792
- relocatedTo: relocation?.to ?? null,
793
- relocationAutoApplyOffered: !!verdict.relocation,
794
- });
795
- return verdict;
796
- }
797
- if (coverage.viaSymbol)
798
- viaSymbol = true;
799
- }
800
- const verdict = this.allow();
801
- this.recordVerdict(filePath, "edit", touchedLines, verdict, {
802
- reasonKind: ignoredOldTextResolvedStaleness
803
- ? "file_modified_oldtext_unique"
804
- : viaSymbol
805
- ? "symbol_coverage"
806
- : "range_coverage",
807
- viaSymbol,
808
- ignoredOwnEditStaleness,
809
- ignoredHashStaleness,
810
- oldTextResolved: ignoredOldTextResolvedStaleness,
811
- });
812
- return verdict;
813
- }
814
- /**
815
- * Check if this is a new file (no existing file on disk).
816
- * New file writes are exempt from the guard.
817
- */
818
- isNewFile(filePath) {
819
- try {
820
- return !fs.existsSync(filePath);
821
- }
822
- catch {
823
- return true; // Assume new if we can't stat
824
- }
825
- }
826
- /**
827
- * Whether pi-lens has any record of this path from a read or write this
828
- * session (#1668). Gates external-delete detection: a path pi-lens never
829
- * read or wrote is one no LSP server's cache was ever told about through
830
- * us, so an `rm` naming it carries no signal worth checking disk for —
831
- * this is a lookup against state already being tracked, never a fresh
832
- * filesystem stat over an unbounded path set.
833
- *
834
- * MUST go through `knownPathIndex`, not `this.key(filePath)` directly
835
- * (#1668 review F1): this is called AFTER a bash delete has already
836
- * landed, when `filePath` no longer exists — `this.key()` at that point
837
- * returns a lowercased-tail key, not the real-casing key `recordRead`/
838
- * `recordWritten` stored while the file was still on disk. For a
839
- * mixed-case basename (`MyModule.ts`) the two keys differ and a direct
840
- * `this.key()` lookup always misses.
841
- */
842
- hasKnownPath(filePath) {
843
- const stored = this.knownPathIndex.get(normalizeEphemeralMapKey(filePath));
844
- return (stored !== undefined &&
845
- (this.reads.has(stored) || this.writtenThisSession.has(stored)));
846
- }
847
- /**
848
- * Every path this guard has a read or a write record for, as normalized keys
849
- * (#2430).
850
- *
851
- * This is the read-guard half of the observational net's TRACKED-FILE SET:
852
- * the files pi-lens has actually seen this session, which the `agent_settled`
853
- * sweep hash-checks instead of walking the workspace. It reads state already
854
- * held in memory and stats nothing; both backing containers are capped
855
- * (`READ_GUARD_MAX_UNCONSUMED_FILES`), so the result is bounded by
856
- * construction.
857
- *
858
- * `touchFile` is deliberately NOT called: enumerating is not using, and
859
- * refreshing every entry's idle clock on each sweep would make the idle
860
- * eviction unreachable.
861
- */
862
- getTrackedPaths() {
863
- const tracked = new Set(this.reads.keys());
864
- for (const written of this.writtenThisSession)
865
- tracked.add(written);
866
- return [...tracked];
867
- }
868
- /**
869
- * Drop all record of a path pi-lens confirmed no longer exists on disk
870
- * (#1668, external delete). Without this a later write reusing the same
871
- * path would inherit a stale writtenThisSession/reads entry from before
872
- * the delete, and a repeat `rm` of the same already-gone path would keep
873
- * matching {@link hasKnownPath} and re-emitting a type-3 notification.
874
- *
875
- * MUST evict through `knownPathIndex`, not `this.key(filePath)` directly
876
- * (#1668 review F1) — same reasoning as {@link hasKnownPath}: `filePath`
877
- * is already gone from disk by the time this runs, so recomputing
878
- * `this.key()` targets the wrong (lowercased-tail) key for a mixed-case
879
- * basename and leaves the real entry behind un-evicted.
880
- */
881
- forgetPath(filePath) {
882
- const stored = this.knownPathIndex.get(normalizeEphemeralMapKey(filePath));
883
- this.evictFile(stored ?? this.key(filePath), "external-delete");
884
- }
885
- /**
886
- * Mark a file as pending creation (Write tool to a non-existing file).
887
- * Must be called from the tool_call handler before the write lands so
888
- * isNewFile() still returns true. recordWritten will inject a synthetic
889
- * read so immediate follow-up edits are not blocked by zero_read.
890
- *
891
- * Keyed by the existence-INDEPENDENT syntactic spelling
892
- * (`normalizeEphemeralMapKey`, the same key `knownPathIndex` uses), NOT by
893
- * `this.key()` (#3163). This entry is written while the file is still
894
- * ABSENT and read back by `recordWritten` once it EXISTS — the one state
895
- * change `normalizeFilePath` itself branches on — so a `this.key()` on each
896
- * side answered with two different strings for any spelling whose key moves
897
- * when the file appears (win32: a mixed-case basename, lower-cased in the
898
- * absent branch by `resolveNonExisting`; POSIX since #3098: a parent whose
899
- * canonical casing is adopted once `realpathSync.native` succeeds). The
900
- * entry was then orphaned — nothing prunes `pendingCreations` — and the
901
- * creation read never injected. The syntactic key never touches the
902
- * filesystem, so it cannot move; the REAL key the injected read needs is
903
- * derived by `recordWritten` itself and is not stored here.
904
- */
905
- noteCreatedFile(filePath, turnIndex, writeIndex) {
906
- this.pendingCreations.set(normalizeEphemeralMapKey(filePath), {
907
- turnIndex,
908
- writeIndex,
909
- });
910
- }
911
- /**
912
- * Refresh the FileTime stamp after the model's own write lands on disk.
913
- * Call this from the tool_result handler so the next checkEdit on the same
914
- * file doesn't see "file_modified" caused by our own previous edit.
915
- */
916
- recordWritten(rawFilePath) {
917
- const filePath = this.key(rawFilePath);
918
- this.unchangedThisSession.delete(filePath);
919
- // #1668 review F1: index by the existence-independent syntactic key
920
- // (see `knownPathIndex`) so a later hasKnownPath/forgetPath lookup
921
- // after an external delete can still find this entry's real key.
922
- this.knownPathIndex.set(normalizeEphemeralMapKey(rawFilePath), filePath);
923
- this.fileTime.read(filePath);
924
- this.writtenThisSession.add(filePath);
925
- if (this.reads.has(filePath))
926
- this.consumedReadFiles.add(filePath);
927
- this.touchFile(filePath);
928
- this.enforceFileCap();
929
- // #3163: `noteCreatedFile` announced this creation while the file was
930
- // still absent, so the announcement is keyed by the syntactic spelling —
931
- // look it up the same way, never through the existence-dependent
932
- // `filePath` above. `injectCreationRead` still receives the real key.
933
- const creationKey = normalizeEphemeralMapKey(rawFilePath);
934
- const creation = this.pendingCreations.get(creationKey);
935
- if (creation) {
936
- this.pendingCreations.delete(creationKey);
937
- this.injectCreationRead(filePath, creation.turnIndex, creation.writeIndex);
938
- }
939
- }
940
- /** Record that a recognized mutation had complete evidence but changed no bytes. */
941
- recordUnchanged(rawFilePath) {
942
- const filePath = this.key(rawFilePath);
943
- // A no-op command is scoped to this command. It must not erase a
944
- // confirmed Write from earlier in the session.
945
- if (!this.writtenThisSession.has(filePath))
946
- this.unchangedThisSession.add(filePath);
947
- }
948
- /**
949
- * Add a one-time exemption for a file.
950
- * Called via /lens-allow-edit command.
951
- */
952
- addExemption(filePath) {
953
- this.exemptions.add(this.key(filePath));
954
- logReadGuardEvent({
955
- event: "exemption_added",
956
- sessionId: this.sessionId,
957
- filePath,
958
- metadata: {
959
- source: "lens-allow-edit",
960
- },
961
- });
962
- }
963
- /**
964
- * Get summary statistics for /lens-health.
965
- */
966
- getSummary() {
967
- let totalEdits = 0;
968
- let totalBlocks = 0;
969
- let lspExpansionsHelped = 0;
970
- const byReason = {};
971
- const byFile = {};
972
- for (const [filePath, records] of this.edits) {
973
- for (const record of records) {
974
- totalEdits++;
975
- byFile[filePath] = byFile[filePath] ?? { edits: 0, blocks: 0 };
976
- byFile[filePath].edits++;
977
- if (record.verdict === "blocked") {
978
- totalBlocks++;
979
- byFile[filePath].blocks++;
980
- }
981
- if (record.reason) {
982
- byReason[record.reason] = (byReason[record.reason] ?? 0) + 1;
983
- }
984
- // Count LSP expansions that allowed an edit
985
- if (record.precedingReads.some((r) => r.expandedByLsp) &&
986
- record.verdict === "allowed") {
987
- lspExpansionsHelped++;
988
- }
989
- }
990
- }
991
- return {
992
- totalEdits,
993
- totalBlocks,
994
- byReason,
995
- byFile,
996
- lspExpansionsHelped,
997
- };
998
- }
999
- /**
1000
- * Get all read records for a file (for debugging).
1001
- */
1002
- getReadHistory(filePath) {
1003
- const key = this.key(filePath);
1004
- if (this.reads.has(key))
1005
- this.touchFile(key);
1006
- return this.reads.get(key) ?? [];
1007
- }
1008
- /**
1009
- * Session-lifetime record-cap trim totals for a file (#1913 review F1).
1010
- * `recordRead` only writes ONE `read_cap_trimmed` read-guard.log line per
1011
- * file per session (on the first trim), so this is the running-totals
1012
- * surface for everything after that — the exact split this class already
1013
- * computes on every trim, not re-derivable from `getReadHistory` alone
1014
- * (which only shows the SURVIVING records, not what was evicted).
1015
- */
1016
- getTrimStats(filePath) {
1017
- const stats = this.trimAccumulators.get(this.key(filePath));
1018
- return stats ? { ...stats } : undefined;
1019
- }
1020
- /**
1021
- * Snapshot the read-set for persistence across a session resume (#1041).
1022
- * Mirrors widget-state's `exportWidgetState`: the Map is emitted as
1023
- * `[key, records]` tuples, keys already in `normalizeFilePath` form. Only
1024
- * `reads` is serialized — it is the payload `checkEdit`'s zero-read/coverage
1025
- * checks consult. Safe to call even when the guard is disabled (an empty or
1026
- * never-populated `reads` simply exports zero entries).
1027
- */
1028
- exportState() {
1029
- return {
1030
- version: READ_GUARD_STATE_VERSION,
1031
- reads: [...this.reads.entries()].map(([key, records]) => [
1032
- key,
1033
- records.map((record) => ({ ...record })),
1034
- ]),
1035
- };
1036
- }
1037
- /**
1038
- * Rehydrate a persisted read-set (#1041) into this (fresh, post-resume)
1039
- * guard, with mandatory staleness reconciliation: each read is re-verified
1040
- * against the CURRENT on-disk content via its recorded `lineHashes`, and any
1041
- * read whose file changed (or no longer exists, or that carries no verifiable
1042
- * hashes) is DROPPED. A rehydrated read must never mask a real staleness — a
1043
- * resume must not let the agent edit a file that changed on disk while it
1044
- * believed it held a fresh read. Kept reads are replayed through
1045
- * {@link recordRead}, which re-keys through {@link key} (idempotent — the
1046
- * exported keys are already normalized) and re-stamps FileTime so the next
1047
- * `checkEdit` sees a consistent baseline. Version-guarded and null-safe:
1048
- * `undefined` / a mismatched version / a missing field loads as "no prior
1049
- * reads". Returns a count of imported vs dropped reads for logging.
1050
- */
1051
- importState(state) {
1052
- const result = { imported: 0, dropped: 0 };
1053
- if (!state || state.version !== READ_GUARD_STATE_VERSION)
1054
- return result;
1055
- // A corrupt/hand-edited sidecar must degrade to "no prior reads", never
1056
- // throw: loadSessionState validates only version/widget, so a malformed
1057
- // `reads` reaches here. If importState threw, the session_start try/catch
1058
- // would abort the ENTIRE rehydration (incl. widget + mountLensWidget)
1059
- // rather than just skipping the read-set.
1060
- if (!Array.isArray(state.reads))
1061
- return result;
1062
- for (const entry of state.reads) {
1063
- // Skip anything that isn't a well-formed [key, records] tuple.
1064
- if (!Array.isArray(entry) || entry.length !== 2)
1065
- continue;
1066
- const [rawPath, records] = entry;
1067
- if (typeof rawPath !== "string")
1068
- continue;
1069
- if (!Array.isArray(records) || records.length === 0)
1070
- continue;
1071
- const filePath = this.key(rawPath);
1072
- let lines;
1073
- try {
1074
- lines = splitLines(fs.readFileSync(filePath, "utf-8"));
1075
- }
1076
- catch {
1077
- // File gone since it was read → drop every read for it.
1078
- result.dropped += records.length;
1079
- continue;
1080
- }
1081
- for (const record of records) {
1082
- const rehydrated = { ...record, filePath };
1083
- // readHashesStillMatch returns false when the recorded hashes no
1084
- // longer match disk OR when the read captured no hashes — both
1085
- // unverifiable, so both drop (safety over convenience).
1086
- if (this.readHashesStillMatch(rehydrated, lines)) {
1087
- this.recordRead(rehydrated);
1088
- result.imported += 1;
1089
- }
1090
- else {
1091
- result.dropped += 1;
1092
- }
1093
- }
1094
- }
1095
- return result;
1096
- }
1097
- /**
1098
- * Get all edit records for a file (for debugging).
1099
- */
1100
- getEditHistory(filePath) {
1101
- const key = this.key(filePath);
1102
- if (this.edits.has(key))
1103
- this.touchFile(key);
1104
- return this.edits.get(key) ?? [];
1105
- }
1106
- // --- Private helpers ---
1107
- injectCreationRead(filePath, turnIndex, writeIndex) {
1108
- let lineCount = 0;
1109
- try {
1110
- lineCount = splitLines(fs.readFileSync(filePath, "utf-8")).length;
1111
- }
1112
- catch {
1113
- return;
1114
- }
1115
- if (lineCount === 0)
1116
- return;
1117
- this.recordRead({
1118
- filePath,
1119
- requestedOffset: 1,
1120
- requestedLimit: lineCount,
1121
- effectiveOffset: 1,
1122
- effectiveLimit: lineCount,
1123
- expandedByLsp: false,
1124
- turnIndex,
1125
- writeIndex,
1126
- timestamp: Date.now(),
1127
- });
1128
- }
1129
- wasWrittenThisSession(filePath) {
1130
- if (this.unchangedThisSession.has(filePath))
1131
- return false;
1132
- // Authoritative path: we observed a write of this file via recordWritten.
1133
- // Survives mtime granularity (FAT32 ~2s), clock skew (NFS), and external
1134
- // tools that touch mtime backward.
1135
- if (this.writtenThisSession.has(filePath))
1136
- return true;
1137
- try {
1138
- return fs.statSync(filePath).mtimeMs >= this.sessionStartMs;
1139
- }
1140
- catch {
1141
- return false;
1142
- }
1143
- }
1144
- canTreatStalenessAsOwnPriorEdit(filePath, lastReadTimestamp) {
1145
- const edits = this.edits.get(filePath) ?? [];
1146
- const latest = edits.at(-1);
1147
- if (!latest)
1148
- return false;
1149
- if (latest.verdict !== "allowed" && latest.verdict !== "warned")
1150
- return false;
1151
- if (latest.timestamp < lastReadTimestamp)
1152
- return false;
1153
- return Date.now() - latest.timestamp <= OWN_EDIT_STALE_GRACE_MS;
1154
- }
1155
- canIgnoreStalenessByHashes(filePath, reads, touchedLines, editRanges) {
1156
- let lines;
1157
- try {
1158
- lines = splitLines(fs.readFileSync(filePath, "utf-8"));
1159
- }
1160
- catch {
1161
- return false;
1162
- }
1163
- const rangesToCheck = touchedLines
1164
- ? editRanges && editRanges.length > 1
1165
- ? editRanges
1166
- : [touchedLines]
1167
- : undefined;
1168
- if (!rangesToCheck) {
1169
- const lastRead = reads.at(-1);
1170
- return !!lastRead && this.readHashesStillMatch(lastRead, lines);
1171
- }
1172
- return rangesToCheck.every((range) => reads.some((read) => this.readCoversRange(read, range) &&
1173
- this.readRangeHashesStillMatch(read, lines, range)));
1174
- }
1175
- readCoversRange(read, [editStart, editEnd]) {
1176
- const readStart = Math.max(1, read.effectiveOffset - this.config.contextLines);
1177
- const readEnd = read.effectiveOffset + read.effectiveLimit - 1 + this.config.contextLines;
1178
- if (editStart >= readStart && editEnd <= readEnd)
1179
- return true;
1180
- if (!read.enclosingSymbol)
1181
- return false;
1182
- return (read.enclosingSymbol.startLine <= editStart &&
1183
- read.enclosingSymbol.endLine >= editEnd);
1184
- }
1185
- validateRangeSnapshot(filePath, range,
1186
- /**
1187
- * What the CALLER will do with the verdict. `checkEdit` honours
1188
- * `skipSnapshotCheck` for content-validated (oldText) edits, so a
1189
- * `shouldBlock` verdict may never reach the gate (#1904 item 1).
1190
- */
1191
- snapshotCheckSkipped) {
1192
- const reads = this.reads.get(filePath) ?? [];
1193
- const candidates = reads.filter((read) => this.readCoversRange(read, range));
1194
- let status = "unavailable";
1195
- let matchingReadIndex = -1;
1196
- let missingLines = [];
1197
- let mismatchedLines = [];
1198
- let checkedCandidateCount = 0;
1199
- let unavailableCandidateCount = 0;
1200
- let hashUnavailableCandidateCount = 0;
1201
- let lastMismatchTimestamp = -Infinity;
1202
- let lastUnavailableTimestamp = -Infinity;
1203
- for (let i = 0; i < candidates.length; i += 1) {
1204
- const validation = currentLinesMatchReadSnapshot(filePath, candidates[i], range);
1205
- if (!validation.checked) {
1206
- unavailableCandidateCount += 1;
1207
- if (readEffectiveRangeCoversRange(candidates[i], range)) {
1208
- hashUnavailableCandidateCount += 1;
1209
- }
1210
- if (status === "unavailable") {
1211
- missingLines = validation.missingLines;
1212
- }
1213
- lastUnavailableTimestamp = Math.max(lastUnavailableTimestamp, candidates[i].timestamp);
1214
- continue;
1215
- }
1216
- checkedCandidateCount += 1;
1217
- if (validation.matches) {
1218
- status = "match";
1219
- matchingReadIndex = i;
1220
- missingLines = [];
1221
- mismatchedLines = [];
1222
- break;
1223
- }
1224
- status = "mismatch";
1225
- missingLines = [];
1226
- mismatchedLines = validation.mismatchedLines;
1227
- lastMismatchTimestamp = Math.max(lastMismatchTimestamp, candidates[i].timestamp);
1228
- }
1229
- // Enforce only when no candidate that actually delivered the target range
1230
- // lacks hashes. Context-only/symbol-only coverage may be unavailable without
1231
- // weakening enforcement from another hash-checkable read of the same range.
1232
- // Also suppress when a re-read (unavailable only due to context-zone boundary)
1233
- // is more recent than the stale read that triggered the mismatch — the agent
1234
- // refreshed their view, and the re-read's edge lines fall within contextLines.
1235
- const shouldBlock = status === "mismatch" &&
1236
- lastUnavailableTimestamp <= lastMismatchTimestamp &&
1237
- checkedCandidateCount > 0 &&
1238
- hashUnavailableCandidateCount === 0;
1239
- // `enforced` below states INTENT — whether this validation reached a
1240
- // decidable verdict. It says nothing about what the caller did with it.
1241
- // `outcome` states the caller's actual behaviour, which is what an
1242
- // enforcement rate must be computed from (#1904 item 1).
1243
- const outcome = shouldBlock
1244
- ? snapshotCheckSkipped
1245
- ? "bypassed-content-match"
1246
- : "enforced-block"
1247
- : status === "match"
1248
- ? "enforced-pass"
1249
- : "not-decidable";
1250
- logReadGuardEvent({
1251
- event: "range_snapshot_validation",
1252
- sessionId: this.sessionId,
1253
- filePath,
1254
- metadata: {
1255
- range,
1256
- status,
1257
- candidateReadCount: candidates.length,
1258
- checkedCandidateCount,
1259
- unavailableCandidateCount,
1260
- hashUnavailableCandidateCount,
1261
- matchingReadIndex,
1262
- missingLineCount: missingLines.length,
1263
- mismatchedLineCount: mismatchedLines.length,
1264
- missingLines: missingLines.slice(0, 20),
1265
- mismatchedLines: mismatchedLines.slice(0, 20),
1266
- enforced: shouldBlock || status === "match",
1267
- outcome,
1268
- },
1269
- });
1270
- return {
1271
- status,
1272
- matchingReadIndex,
1273
- missingLines,
1274
- mismatchedLines,
1275
- candidateReadCount: candidates.length,
1276
- checkedCandidateCount,
1277
- unavailableCandidateCount,
1278
- shouldBlock,
1279
- };
1280
- }
1281
- readRangeHashesStillMatch(read, lines, [startLine, endLine]) {
1282
- const hashes = read.lineHashes ?? {};
1283
- for (let lineNo = startLine; lineNo <= endLine; lineNo += 1) {
1284
- if (!readRangeCoversLine(read, lineNo) || hashes[lineNo] === undefined) {
1285
- return false;
1286
- }
1287
- if (lineNo < 1 || lineNo > lines.length)
1288
- return false;
1289
- if (lineContentHash(lines[lineNo - 1] ?? "") !== hashes[lineNo]) {
1290
- return false;
1291
- }
1292
- }
1293
- return true;
1294
- }
1295
- /**
1296
- * Content-verified relocation. When a range the agent read has drifted, find
1297
- * where the read-time line-hash sequence for [startLine,endLine] now appears
1298
- * in the current file. Returns the unique new location, or undefined when:
1299
- * no recorded read captured hashes for the whole range; the sequence is too
1300
- * short to be collision-resistant (<2 lines); or it now matches zero or
1301
- * multiple spots. Powers a *hint* only — never a silent positional re-apply.
1302
- */
1303
- findRelocation(filePath, reads, [startLine, endLine]) {
1304
- const span = endLine - startLine + 1;
1305
- // A single line's hash collides too easily to relocate on confidently.
1306
- if (span < 2)
1307
- return undefined;
1308
- // Newest read that captured hashes for the entire target range wins.
1309
- let wanted;
1310
- for (let i = reads.length - 1; i >= 0; i -= 1) {
1311
- const hashes = reads[i].lineHashes;
1312
- if (!hashes)
1313
- continue;
1314
- const seq = [];
1315
- let complete = true;
1316
- for (let lineNo = startLine; lineNo <= endLine; lineNo += 1) {
1317
- const h = hashes[lineNo];
1318
- if (h === undefined) {
1319
- complete = false;
1320
- break;
1321
- }
1322
- seq.push(h);
1323
- }
1324
- if (complete) {
1325
- wanted = seq;
1326
- break;
1327
- }
1328
- }
1329
- if (!wanted)
1330
- return undefined;
1331
- let lines;
1332
- try {
1333
- lines = splitLines(fs.readFileSync(filePath, "utf-8"));
1334
- }
1335
- catch {
1336
- return undefined;
1337
- }
1338
- const currentHashes = lines.map((line) => lineContentHash(line));
1339
- const lastStart = currentHashes.length - span; // last valid 0-based start
1340
- const matchStarts = [];
1341
- for (let i = 0; i <= lastStart; i += 1) {
1342
- let ok = true;
1343
- for (let j = 0; j < span; j += 1) {
1344
- if (currentHashes[i + j] !== wanted[j]) {
1345
- ok = false;
1346
- break;
1347
- }
1348
- }
1349
- if (ok)
1350
- matchStarts.push(i + 1); // 1-indexed
1351
- }
1352
- let newStart;
1353
- if (matchStarts.length === 1) {
1354
- // Unique across the whole file → certainly the relocated span,
1355
- // regardless of how far it drifted (e.g. a large refactor moved it).
1356
- newStart = matchStarts[0];
1357
- }
1358
- else if (matchStarts.length > 1) {
1359
- // Duplicated elsewhere: fall back to locality. Lines rarely teleport,
1360
- // so accept a match unique WITHIN an adaptive window of the original
1361
- // position — out-of-window duplicates don't poison a locally
1362
- // unambiguous relocation. The window widens with the edits already
1363
- // applied to this file this session (each prior edit shifts line
1364
- // numbers, so accumulated drift grows).
1365
- const appliedEdits = (this.edits.get(filePath) ?? []).filter((record) => record.verdict !== "blocked").length;
1366
- const window = Math.min(RELOCATION_WINDOW_MAX, Math.max(RELOCATION_WINDOW_MIN, appliedEdits * RELOCATION_WINDOW_PER_EDIT));
1367
- const lo = startLine - window;
1368
- const hi = endLine + window;
1369
- const local = matchStarts.filter((start) => start >= lo && start <= hi);
1370
- if (local.length === 1)
1371
- newStart = local[0];
1372
- }
1373
- if (newStart === undefined || newStart === startLine)
1374
- return undefined;
1375
- return { from: [startLine, endLine], to: [newStart, newStart + span - 1] };
1376
- }
1377
- readHashesStillMatch(read, lines) {
1378
- const entries = Object.entries(read.lineHashes ?? {});
1379
- if (entries.length === 0)
1380
- return false;
1381
- for (const [lineText, expected] of entries) {
1382
- const lineNo = Number(lineText);
1383
- if (!Number.isInteger(lineNo) || lineNo < 1 || lineNo > lines.length) {
1384
- return false;
1385
- }
1386
- if (lineContentHash(lines[lineNo - 1] ?? "") !== expected)
1387
- return false;
1388
- }
1389
- return true;
1390
- }
1391
- checkCoverage(filePath, touchedLines) {
1392
- const [editStart, editEnd] = touchedLines;
1393
- const reads = this.reads.get(filePath) ?? [];
1394
- // First pass: check symbol coverage and any single read that covers the edit.
1395
- for (const read of reads) {
1396
- if (read.provisional === true)
1397
- continue;
1398
- const readStart = Math.max(1, read.effectiveOffset - this.config.contextLines);
1399
- const readEnd = read.effectiveOffset +
1400
- read.effectiveLimit -
1401
- 1 +
1402
- this.config.contextLines;
1403
- if (editStart >= readStart && editEnd <= readEnd) {
1404
- return { covered: true, viaSymbol: false };
1405
- }
1406
- if (read.enclosingSymbol) {
1407
- const symStart = read.enclosingSymbol.startLine;
1408
- const symEnd = read.enclosingSymbol.endLine;
1409
- if (symStart <= editStart && symEnd >= editEnd) {
1410
- return { covered: true, viaSymbol: true };
1411
- }
1412
- }
1413
- }
1414
- // Second pass: merge all read intervals and check if their union covers
1415
- // [editStart, editEnd]. Handles multi-chunk reads (e.g. 1-100 + 101-200).
1416
- const intervals = reads
1417
- .filter((read) => read.provisional !== true)
1418
- .map((read) => [
1419
- Math.max(1, read.effectiveOffset - this.config.contextLines),
1420
- read.effectiveOffset +
1421
- read.effectiveLimit -
1422
- 1 +
1423
- this.config.contextLines,
1424
- ]);
1425
- intervals.sort((a, b) => a[0] - b[0]);
1426
- // Merge overlapping/adjacent intervals
1427
- const merged = [];
1428
- for (const [s, e] of intervals) {
1429
- if (merged.length > 0 && s <= merged[merged.length - 1][1] + 1) {
1430
- merged[merged.length - 1][1] = Math.max(merged[merged.length - 1][1], e);
1431
- }
1432
- else {
1433
- merged.push([s, e]);
1434
- }
1435
- }
1436
- for (const [s, e] of merged) {
1437
- if (editStart >= s && editEnd <= e) {
1438
- return { covered: true, viaSymbol: false };
1439
- }
1440
- }
1441
- return { covered: false, viaSymbol: false };
1442
- }
1443
- getExemptionMode(filePath) {
1444
- for (const exemption of this.config.exemptions) {
1445
- if (this.matchesPattern(filePath, exemption.pattern)) {
1446
- return exemption.mode;
1447
- }
1448
- }
1449
- return null;
1450
- }
1451
- matchesPattern(filePath, pattern) {
1452
- // Simple glob matching — can be expanded
1453
- if (pattern.startsWith("*")) {
1454
- const suffix = pattern.slice(1);
1455
- return filePath.endsWith(suffix);
1456
- }
1457
- if (pattern.includes("*")) {
1458
- // Adjacent stars are equivalent to one star in this dialect. Collapse
1459
- // them before compiling so a non-match cannot backtrack over every
1460
- // nullable `.*` group (#2622).
1461
- const collapsedPattern = pattern.replace(/\*+/g, "*");
1462
- const regex = new RegExp(`^${collapsedPattern.replace(/\\/g, "\\\\").replace(/\./g, "\\.").replace(/\*/g, ".*")}$`);
1463
- return regex.test(filePath);
1464
- }
1465
- return filePath === pattern;
1466
- }
1467
- blockOrWarn(_reason, message, details, overrideMode) {
1468
- const mode = overrideMode ?? this.config.mode;
1469
- if (mode === "warn") {
1470
- return { action: "warn", reason: message, details };
1471
- }
1472
- return { action: "block", reason: message, details };
1473
- }
1474
- allow() {
1475
- return { action: "allow" };
1476
- }
1477
- recordEdit(filePath, tool, touchedLines, verdict) {
1478
- this.touchFile(filePath);
1479
- const arr = this.edits.get(filePath) ?? [];
1480
- arr.push({
1481
- filePath,
1482
- tool,
1483
- touchedLines,
1484
- precedingReads: this.reads.get(filePath) ?? [],
1485
- verdict: mapVerdictAction(verdict.action),
1486
- reason: verdict.reason,
1487
- timestamp: Date.now(),
1488
- });
1489
- if (arr.length > READ_GUARD_MAX_EDITS_PER_FILE) {
1490
- const trimmedCount = arr.length - READ_GUARD_MAX_EDITS_PER_FILE;
1491
- arr.splice(0, trimmedCount);
1492
- // #1918: the #282-289 doc comment argues this trim is inert in
1493
- // practice (every consumer reads only the last record or a
1494
- // bounded relocation window), but an argument is not a record —
1495
- // if that assumption ever breaks, this is the only signal. Same
1496
- // aggregate-after-first shape as `read_cap_trimmed` (#1913):
1497
- // `incrementDegradationCount` keeps the exact per-file tally,
1498
- // the read-guard.log line fires once on the rising edge.
1499
- const isRisingEdge = incrementDegradationCount({
1500
- kind: "read-guard-edits-cap-trim",
1501
- subject: filePath,
1502
- reason: `trimmed ${trimmedCount} edit record(s) past cap`,
1503
- });
1504
- if (isRisingEdge) {
1505
- logReadGuardEvent({
1506
- event: "edits_cap_trimmed",
1507
- sessionId: this.sessionId,
1508
- filePath,
1509
- metadata: { trimmedCount, cappedLength: arr.length },
1510
- });
1511
- }
1512
- }
1513
- this.edits.set(filePath, arr);
1514
- }
1515
- recordVerdict(filePath, tool, touchedLines, verdict, metadata = {}) {
1516
- const normalizedTouchedLines = touchedLines ?? [1, 1];
1517
- this.recordEdit(filePath, tool, normalizedTouchedLines, verdict);
1518
- const reads = this.reads.get(filePath) ?? [];
1519
- logReadGuardEvent({
1520
- event: verdict.action === "allow"
1521
- ? "edit_allowed"
1522
- : verdict.action === "warn"
1523
- ? "edit_warned"
1524
- : "edit_blocked",
1525
- sessionId: this.sessionId,
1526
- filePath,
1527
- metadata: {
1528
- tool,
1529
- touchedLines: touchedLines ?? null,
1530
- normalizedTouchedLines,
1531
- readCount: reads.length,
1532
- reads: reads.map((read) => ({
1533
- requestedOffset: read.requestedOffset,
1534
- requestedLimit: read.requestedLimit,
1535
- effectiveOffset: read.effectiveOffset,
1536
- effectiveLimit: read.effectiveLimit,
1537
- expandedByLsp: read.expandedByLsp,
1538
- enclosingSymbol: read.enclosingSymbol ?? null,
1539
- timestamp: read.timestamp,
1540
- })),
1541
- verdictAction: verdict.action,
1542
- details: verdict.details,
1543
- ...metadata,
1544
- },
1545
- });
1546
- }
1547
- }
1548
- // --- Factory ---
1549
- function mapVerdictAction(action) {
1550
- switch (action) {
1551
- case "allow":
1552
- return "allowed";
1553
- case "block":
1554
- return "blocked";
1555
- case "warn":
1556
- return "warned";
1557
- }
1558
- }
1559
- export function createReadGuard(sessionId, config) {
1560
- return new ReadGuard(sessionId, config);
1561
- }