@herbertgao/pi-extensions 2026.10.2 → 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 (359) hide show
  1. package/README.md +5 -12
  2. package/THIRD_PARTY_NOTICES.md +1 -1
  3. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +8 -0
  4. package/node_modules/@herbertgao/pi-subagents/README.md +1 -1
  5. package/node_modules/@herbertgao/pi-subagents/package.json +4 -5
  6. package/node_modules/@herbertgao/pi-subagents/src/index.ts +1 -1
  7. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +1 -1
  8. package/node_modules/@herbertgao/pi-subagents/src/mention-clone.ts +48 -72
  9. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +1 -1
  10. package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +15 -6
  11. package/node_modules/@herbertgao/pi-subagents/src/workflow/collisions.ts +4 -1
  12. package/node_modules/@herbertgao/pi-subagents/src/workflow/json-schema.ts +9 -8
  13. package/node_modules/@narumitw/pi-caffeinate/dist/index.ts +6 -1
  14. package/node_modules/@narumitw/pi-caffeinate/dist/index.ts.map +2 -2
  15. package/node_modules/@narumitw/pi-caffeinate/package.json +1 -1
  16. package/node_modules/@narumitw/pi-caffeinate/src/dbus-inhibit.ts +13 -1
  17. package/node_modules/pi-lens/CHANGELOG.md +95 -0
  18. package/node_modules/pi-lens/README.md +1 -0
  19. package/node_modules/pi-lens/dist/chunk-2FQ62SPM.js +83 -0
  20. package/node_modules/pi-lens/dist/{chunk-6P62QVEA.js → chunk-2UMYMRI3.js} +2 -2
  21. package/node_modules/pi-lens/dist/{chunk-O6TQT6RI.js → chunk-34OCDWOS.js} +1 -1
  22. package/node_modules/pi-lens/dist/{chunk-XK4CIILI.js → chunk-3FERWMCH.js} +26 -26
  23. package/node_modules/pi-lens/dist/{chunk-ZAWW3AF6.js → chunk-3JL6IFD7.js} +8 -8
  24. package/node_modules/pi-lens/dist/{chunk-OAXNDZJ7.js → chunk-3JSEWUWI.js} +8 -4
  25. package/node_modules/pi-lens/dist/{chunk-RPD5K5NC.js → chunk-3MRXIJ6I.js} +2 -2
  26. package/node_modules/pi-lens/dist/{chunk-IVP5KAB7.js → chunk-3P4PF2BX.js} +14 -14
  27. package/node_modules/pi-lens/dist/{chunk-5THXD6X5.js → chunk-3PNFWVHT.js} +2 -2
  28. package/node_modules/pi-lens/dist/{chunk-VD4JQCQC.js → chunk-3TTPKMEF.js} +37 -37
  29. package/node_modules/pi-lens/dist/{chunk-7F72PXY2.js → chunk-5JHXUETW.js} +3 -3
  30. package/node_modules/pi-lens/dist/{chunk-5ZEJHV35.js → chunk-64LU2OVH.js} +2 -2
  31. package/node_modules/pi-lens/dist/{chunk-ODEEOXPH.js → chunk-6B5I6ICN.js} +11 -11
  32. package/node_modules/pi-lens/dist/{chunk-5RXBNSW2.js → chunk-6P54TMWL.js} +16 -8
  33. package/node_modules/pi-lens/dist/{chunk-K6VYLB2U.js → chunk-6UYA642J.js} +12 -12
  34. package/node_modules/pi-lens/dist/{chunk-V7R425XG.js → chunk-6XX5HCLK.js} +4 -4
  35. package/node_modules/pi-lens/dist/{chunk-D6QS3U6M.js → chunk-7AXO3ZDF.js} +15 -15
  36. package/node_modules/pi-lens/dist/{chunk-YGVYKNSN.js → chunk-7BX5DWSA.js} +59 -12
  37. package/node_modules/pi-lens/dist/{chunk-N3YQJI6O.js → chunk-7ELY2CSM.js} +7 -4
  38. package/node_modules/pi-lens/dist/{chunk-AWE5TMPO.js → chunk-7M4T5N3V.js} +13 -13
  39. package/node_modules/pi-lens/dist/{chunk-AJ3QXTRQ.js → chunk-AFZYMAIV.js} +33 -33
  40. package/node_modules/pi-lens/dist/{chunk-AJXODZY6.js → chunk-ALQRYH6O.js} +12 -12
  41. package/node_modules/pi-lens/dist/{chunk-AJVLWNFN.js → chunk-AXKPQ6ZI.js} +5 -5
  42. package/node_modules/pi-lens/dist/{chunk-HWBZIGMJ.js → chunk-BHWHXMQ2.js} +8 -8
  43. package/node_modules/pi-lens/dist/{chunk-IXEBJSUE.js → chunk-BJF25SUG.js} +10 -10
  44. package/node_modules/pi-lens/dist/{chunk-GVXA6CCE.js → chunk-BNKLYOOO.js} +1 -1
  45. package/node_modules/pi-lens/dist/{chunk-T4ERT73Y.js → chunk-CBD2B5CP.js} +20 -20
  46. package/node_modules/pi-lens/dist/{chunk-O5WILGMI.js → chunk-CFYISHUW.js} +5 -5
  47. package/node_modules/pi-lens/dist/{chunk-EOKRQ3SA.js → chunk-CMU4RMJB.js} +1 -1
  48. package/node_modules/pi-lens/dist/{chunk-YSNFMJKN.js → chunk-CULQYFLU.js} +2 -2
  49. package/node_modules/pi-lens/dist/{chunk-ORTMY4Q4.js → chunk-D65GOCJB.js} +21 -14
  50. package/node_modules/pi-lens/dist/{chunk-NTUJEGDZ.js → chunk-D6YF5HKX.js} +2 -2
  51. package/node_modules/pi-lens/dist/{chunk-BRJKOBZP.js → chunk-DMS52BEP.js} +17 -6
  52. package/node_modules/pi-lens/dist/{chunk-NL5GLMFI.js → chunk-ELVWJYRV.js} +11 -11
  53. package/node_modules/pi-lens/dist/{chunk-4W2PLC4J.js → chunk-EUT3B22Y.js} +12 -12
  54. package/node_modules/pi-lens/dist/{chunk-GUOGVTCQ.js → chunk-F5VLZHUW.js} +1 -1
  55. package/node_modules/pi-lens/dist/{chunk-I7SPGOWI.js → chunk-FGALODG3.js} +1 -1
  56. package/node_modules/pi-lens/dist/{chunk-Q5U6FMDI.js → chunk-G3RLJYA2.js} +12 -6
  57. package/node_modules/pi-lens/dist/{chunk-FJNKANOS.js → chunk-GAYGGICG.js} +9 -9
  58. package/node_modules/pi-lens/dist/{chunk-HVUZXSWK.js → chunk-H2W2AXD3.js} +38 -40
  59. package/node_modules/pi-lens/dist/{chunk-VHQLPIRJ.js → chunk-H5EV3VMF.js} +2 -2
  60. package/node_modules/pi-lens/dist/{chunk-27BBHSZC.js → chunk-HREZQCT3.js} +3 -3
  61. package/node_modules/pi-lens/dist/{chunk-TT22N74J.js → chunk-HSIRMK2D.js} +12 -12
  62. package/node_modules/pi-lens/dist/{chunk-3I5QFT3N.js → chunk-IMSQR4WO.js} +6 -3
  63. package/node_modules/pi-lens/dist/{chunk-B3I2UUT4.js → chunk-IQZXOZ7S.js} +156 -32
  64. package/node_modules/pi-lens/dist/{chunk-RIISMHKN.js → chunk-IR6XFA5P.js} +14 -13
  65. package/node_modules/pi-lens/dist/{chunk-LNAKEO63.js → chunk-IUKPRZER.js} +4 -4
  66. package/node_modules/pi-lens/dist/{chunk-RUOG5D2C.js → chunk-IUR76K6A.js} +5 -5
  67. package/node_modules/pi-lens/dist/{chunk-TO6WL3GM.js → chunk-J3FZR3YY.js} +12 -2
  68. package/node_modules/pi-lens/dist/{chunk-Y4T27COV.js → chunk-J6DFZDFA.js} +4 -4
  69. package/node_modules/pi-lens/dist/{chunk-E4MAEDPM.js → chunk-J7WJZ6W7.js} +11 -11
  70. package/node_modules/pi-lens/dist/{chunk-52H5B5TC.js → chunk-JONGANYH.js} +30 -30
  71. package/node_modules/pi-lens/dist/{chunk-TGPXLY2J.js → chunk-KJCWXUUS.js} +35 -1
  72. package/node_modules/pi-lens/dist/{chunk-I3R7GKDR.js → chunk-KJN4IOEJ.js} +1 -1
  73. package/node_modules/pi-lens/dist/{chunk-RPD4ZSB7.js → chunk-KMGPUKFD.js} +12 -12
  74. package/node_modules/pi-lens/dist/{chunk-OQGFCZJ4.js → chunk-LD7WFTRX.js} +13 -13
  75. package/node_modules/pi-lens/dist/{chunk-DIULIWKU.js → chunk-LNOMA3MP.js} +3 -3
  76. package/node_modules/pi-lens/dist/{chunk-HHYAELLJ.js → chunk-MGIWSROG.js} +5 -5
  77. package/node_modules/pi-lens/dist/{chunk-YLF3PPT5.js → chunk-MQA2YBAH.js} +3 -3
  78. package/node_modules/pi-lens/dist/{chunk-TVN5N6GY.js → chunk-MTDRLSI7.js} +690 -186
  79. package/node_modules/pi-lens/dist/{chunk-DQ2NZ7C4.js → chunk-N2IRDBZI.js} +44 -6
  80. package/node_modules/pi-lens/dist/{chunk-AHDW3GPE.js → chunk-N77WFUL7.js} +22 -22
  81. package/node_modules/pi-lens/dist/{chunk-NXPS2L6U.js → chunk-N7YNJE5R.js} +7 -7
  82. package/node_modules/pi-lens/dist/{chunk-7FBF7NPR.js → chunk-NFXP47RV.js} +13 -12
  83. package/node_modules/pi-lens/dist/{chunk-UY752QSN.js → chunk-NJF2X5XF.js} +17 -17
  84. package/node_modules/pi-lens/dist/{chunk-WONERJTP.js → chunk-O7HZX7CA.js} +1 -1
  85. package/node_modules/pi-lens/dist/{chunk-AS44CFQ4.js → chunk-OC7LYAU5.js} +7 -7
  86. package/node_modules/pi-lens/dist/{chunk-4RERDEXR.js → chunk-OEX24ECI.js} +30 -30
  87. package/node_modules/pi-lens/dist/{chunk-6VCD4VKZ.js → chunk-OYOB3ZFG.js} +2 -2
  88. package/node_modules/pi-lens/dist/{chunk-IIXOAGHR.js → chunk-OYTMZXRF.js} +18 -15
  89. package/node_modules/pi-lens/dist/{chunk-ARFCXG7S.js → chunk-OZJINN4O.js} +2 -2
  90. package/node_modules/pi-lens/dist/{chunk-34U7PLGE.js → chunk-PCHDGUKT.js} +11 -11
  91. package/node_modules/pi-lens/dist/{chunk-VCSRSYLF.js → chunk-RJFED73B.js} +34 -36
  92. package/node_modules/pi-lens/dist/chunk-RTQNRM5X.js +27 -0
  93. package/node_modules/pi-lens/dist/{chunk-PAK6IDME.js → chunk-S6AMWKPC.js} +2 -2
  94. package/node_modules/pi-lens/dist/{chunk-U3VGR4VU.js → chunk-T4QZ6FEH.js} +4 -4
  95. package/node_modules/pi-lens/dist/{chunk-VN2AXLEX.js → chunk-T54VJRAD.js} +54 -12
  96. package/node_modules/pi-lens/dist/{chunk-ZMOL6RQC.js → chunk-TA5L5KWQ.js} +2 -2
  97. package/node_modules/pi-lens/dist/{chunk-JOLITGCC.js → chunk-TQFBZVCG.js} +1 -1
  98. package/node_modules/pi-lens/dist/{chunk-VDBPVIKD.js → chunk-U52SAGRM.js} +1 -1
  99. package/node_modules/pi-lens/dist/{chunk-LW6XKDZQ.js → chunk-UEAHAMEI.js} +284 -35
  100. package/node_modules/pi-lens/dist/{chunk-NMABWBZN.js → chunk-UGSMCQ4D.js} +1 -1
  101. package/node_modules/pi-lens/dist/{chunk-6QIUX4EP.js → chunk-USHG4DLH.js} +7 -7
  102. package/node_modules/pi-lens/dist/{chunk-LSMJ6KBE.js → chunk-V5MEBST3.js} +16 -16
  103. package/node_modules/pi-lens/dist/chunk-VGEVL5EQ.js +29 -0
  104. package/node_modules/pi-lens/dist/{chunk-47D2BO4S.js → chunk-VPWKF3GN.js} +10 -10
  105. package/node_modules/pi-lens/dist/{chunk-UK3CMEAL.js → chunk-VSMJK3ZJ.js} +1 -1
  106. package/node_modules/pi-lens/dist/{chunk-22AVIGXS.js → chunk-VYKKMYVT.js} +7 -7
  107. package/node_modules/pi-lens/dist/{chunk-OS32RNR2.js → chunk-VZHJS6DR.js} +15 -15
  108. package/node_modules/pi-lens/dist/{chunk-E2WFBRHM.js → chunk-WATC3IQR.js} +3 -3
  109. package/node_modules/pi-lens/dist/chunk-WPE5FPGR.js +54 -0
  110. package/node_modules/pi-lens/dist/{chunk-V5PXFSZK.js → chunk-WSEWKCXU.js} +4 -4
  111. package/node_modules/pi-lens/dist/{chunk-NKOUFPQF.js → chunk-WW5BLB74.js} +2 -2
  112. package/node_modules/pi-lens/dist/{chunk-PWFHBTSF.js → chunk-XGUL4SLQ.js} +2 -2
  113. package/node_modules/pi-lens/dist/{chunk-F3P7VQ7H.js → chunk-XHSCVSOL.js} +3 -3
  114. package/node_modules/pi-lens/dist/{chunk-DFVMIKMQ.js → chunk-YLN4K6WD.js} +45 -9
  115. package/node_modules/pi-lens/dist/{chunk-VNMT7C64.js → chunk-Z6BB4JLQ.js} +2 -2
  116. package/node_modules/pi-lens/dist/{chunk-NTHH2J74.js → chunk-Z7QQA2KT.js} +12 -12
  117. package/node_modules/pi-lens/dist/{chunk-F3AU2TMM.js → chunk-ZETYB3XG.js} +15 -15
  118. package/node_modules/pi-lens/dist/{chunk-XECLWQZN.js → chunk-ZMOMPX2Z.js} +12 -10
  119. package/node_modules/pi-lens/dist/{chunk-C2AUYXQH.js → chunk-ZOEYVXSV.js} +4 -4
  120. package/node_modules/pi-lens/dist/{chunk-TLGFBM3F.js → chunk-ZZUFZUQZ.js} +68 -64
  121. package/node_modules/pi-lens/dist/index.js +30113 -28398
  122. package/node_modules/pi-lens/dist/mcp/analyze-cli.js +8 -8
  123. package/node_modules/pi-lens/dist/mcp/cli.js +37 -37
  124. package/node_modules/pi-lens/dist/mcp/server.js +257 -158
  125. package/node_modules/pi-lens/dist/mcp/worker.js +55 -55
  126. package/node_modules/pi-lens/dist/probes/installer.js +19 -19
  127. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +2 -2
  128. package/node_modules/pi-lens/docs/configuration.md +9 -2
  129. package/node_modules/pi-lens/docs/custom-rules.md +25 -2
  130. package/node_modules/pi-lens/docs/environment-variables.md +34 -6
  131. package/node_modules/pi-lens/docs/features.md +37 -1
  132. package/node_modules/pi-lens/docs/globalconfig.md +1 -1
  133. package/node_modules/pi-lens/docs/lsp-capability-matrix.md +2 -1
  134. package/node_modules/pi-lens/docs/lsp-idle-eviction.md +1 -1
  135. package/node_modules/pi-lens/docs/pi-lens-fixer.md +9 -5
  136. package/node_modules/pi-lens/docs/pi-lens-merge-policy.md +43 -1
  137. package/node_modules/pi-lens/docs/pi-lens-monitor.md +3 -1
  138. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +4 -3
  139. package/node_modules/pi-lens/docs/schema/pi-lens-config-v1.json +530 -0
  140. package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
  141. package/node_modules/pi-lens/docs/word-index.md +6 -0
  142. package/node_modules/pi-lens/package.json +1 -1
  143. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-identity-operator-on-literals-test.yml +12 -2
  144. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/throw-new-error-js-test.yml +2 -0
  145. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/throw-new-error-test.yml +2 -0
  146. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-identity-operator-on-literals.yml +20 -12
  147. package/node_modules/pi-lens/rules/ast-grep-rules/rules/throw-new-error-js.yml +2 -2
  148. package/node_modules/pi-lens/rules/ast-grep-rules/rules/throw-new-error.yml +2 -2
  149. package/node_modules/pi-web-access/CHANGELOG.md +28 -0
  150. package/node_modules/pi-web-access/README.md +10 -6
  151. package/node_modules/pi-web-access/ceramic.ts +219 -0
  152. package/node_modules/pi-web-access/curator-page.ts +3 -1
  153. package/node_modules/pi-web-access/curator-server.ts +1 -0
  154. package/node_modules/pi-web-access/dist/index.js +846 -588
  155. package/node_modules/pi-web-access/dist/mcp-cli.js +725 -516
  156. package/node_modules/pi-web-access/gemini-search.ts +8 -4
  157. package/node_modules/pi-web-access/github-extract.ts +34 -2
  158. package/node_modules/pi-web-access/index.ts +72 -30
  159. package/node_modules/pi-web-access/openai-search.ts +3 -3
  160. package/node_modules/pi-web-access/package.json +2 -2
  161. package/node_modules/pi-web-access/page-query.ts +6 -9
  162. package/node_modules/pi-web-access/parallel-mcp.ts +2 -0
  163. package/node_modules/pi-web-access/query-rewrite.ts +2 -2
  164. package/node_modules/pi-web-access/storage.ts +49 -0
  165. package/node_modules/pi-web-access/summary-review.ts +7 -6
  166. package/node_modules/pi-web-access/utils.ts +21 -6
  167. package/node_modules/pi-web-access/web-tool-core.ts +1 -0
  168. package/package.json +7 -12
  169. package/node_modules/pi-lens/dist/chunk-54B42YRX.js +0 -27
  170. package/node_modules/pi-lens/dist/chunk-CGGAIP7J.js +0 -29
  171. package/node_modules/pi-lens/dist/chunk-HMFXILPV.js +0 -83
  172. package/node_modules/pi-lens/dist/chunk-T6D4Q4AQ.js +0 -54
  173. package/node_modules/pi-web-access/opencode-session-headers.ts +0 -24
  174. package/node_modules/remote-pi/LICENSE +0 -21
  175. package/node_modules/remote-pi/README.md +0 -675
  176. package/node_modules/remote-pi/dist/actions/handlers.d.ts +0 -122
  177. package/node_modules/remote-pi/dist/actions/handlers.js +0 -160
  178. package/node_modules/remote-pi/dist/actions/handlers.js.map +0 -1
  179. package/node_modules/remote-pi/dist/actions/registry.d.ts +0 -39
  180. package/node_modules/remote-pi/dist/actions/registry.js +0 -58
  181. package/node_modules/remote-pi/dist/actions/registry.js.map +0 -1
  182. package/node_modules/remote-pi/dist/bin/supervisord.d.ts +0 -2
  183. package/node_modules/remote-pi/dist/bin/supervisord.js +0 -86
  184. package/node_modules/remote-pi/dist/bin/supervisord.js.map +0 -1
  185. package/node_modules/remote-pi/dist/commands/builtin_mirror.d.ts +0 -58
  186. package/node_modules/remote-pi/dist/commands/builtin_mirror.js +0 -71
  187. package/node_modules/remote-pi/dist/commands/builtin_mirror.js.map +0 -1
  188. package/node_modules/remote-pi/dist/commands/list_commands.d.ts +0 -60
  189. package/node_modules/remote-pi/dist/commands/list_commands.js +0 -73
  190. package/node_modules/remote-pi/dist/commands/list_commands.js.map +0 -1
  191. package/node_modules/remote-pi/dist/config.d.ts +0 -62
  192. package/node_modules/remote-pi/dist/config.js +0 -115
  193. package/node_modules/remote-pi/dist/config.js.map +0 -1
  194. package/node_modules/remote-pi/dist/daemon/client.d.ts +0 -20
  195. package/node_modules/remote-pi/dist/daemon/client.js +0 -131
  196. package/node_modules/remote-pi/dist/daemon/client.js.map +0 -1
  197. package/node_modules/remote-pi/dist/daemon/control_protocol.d.ts +0 -176
  198. package/node_modules/remote-pi/dist/daemon/control_protocol.js +0 -63
  199. package/node_modules/remote-pi/dist/daemon/control_protocol.js.map +0 -1
  200. package/node_modules/remote-pi/dist/daemon/cron_log.d.ts +0 -45
  201. package/node_modules/remote-pi/dist/daemon/cron_log.js +0 -72
  202. package/node_modules/remote-pi/dist/daemon/cron_log.js.map +0 -1
  203. package/node_modules/remote-pi/dist/daemon/cron_registry.d.ts +0 -80
  204. package/node_modules/remote-pi/dist/daemon/cron_registry.js +0 -194
  205. package/node_modules/remote-pi/dist/daemon/cron_registry.js.map +0 -1
  206. package/node_modules/remote-pi/dist/daemon/id.d.ts +0 -24
  207. package/node_modules/remote-pi/dist/daemon/id.js +0 -36
  208. package/node_modules/remote-pi/dist/daemon/id.js.map +0 -1
  209. package/node_modules/remote-pi/dist/daemon/install.d.ts +0 -170
  210. package/node_modules/remote-pi/dist/daemon/install.js +0 -547
  211. package/node_modules/remote-pi/dist/daemon/install.js.map +0 -1
  212. package/node_modules/remote-pi/dist/daemon/registry.d.ts +0 -63
  213. package/node_modules/remote-pi/dist/daemon/registry.js +0 -153
  214. package/node_modules/remote-pi/dist/daemon/registry.js.map +0 -1
  215. package/node_modules/remote-pi/dist/daemon/rpc_child.d.ts +0 -194
  216. package/node_modules/remote-pi/dist/daemon/rpc_child.js +0 -389
  217. package/node_modules/remote-pi/dist/daemon/rpc_child.js.map +0 -1
  218. package/node_modules/remote-pi/dist/daemon/supervisor.d.ts +0 -93
  219. package/node_modules/remote-pi/dist/daemon/supervisor.js +0 -596
  220. package/node_modules/remote-pi/dist/daemon/supervisor.js.map +0 -1
  221. package/node_modules/remote-pi/dist/extension_ui_bridge.d.ts +0 -26
  222. package/node_modules/remote-pi/dist/extension_ui_bridge.js +0 -341
  223. package/node_modules/remote-pi/dist/extension_ui_bridge.js.map +0 -1
  224. package/node_modules/remote-pi/dist/index.d.ts +0 -213
  225. package/node_modules/remote-pi/dist/index.js +0 -4996
  226. package/node_modules/remote-pi/dist/index.js.map +0 -1
  227. package/node_modules/remote-pi/dist/mcp/mesh_result.d.ts +0 -10
  228. package/node_modules/remote-pi/dist/mcp/mesh_result.js +0 -42
  229. package/node_modules/remote-pi/dist/mcp/mesh_result.js.map +0 -1
  230. package/node_modules/remote-pi/dist/mcp/mesh_server.d.ts +0 -16
  231. package/node_modules/remote-pi/dist/mcp/mesh_server.js +0 -327
  232. package/node_modules/remote-pi/dist/mcp/mesh_server.js.map +0 -1
  233. package/node_modules/remote-pi/dist/mesh/canonical.d.ts +0 -30
  234. package/node_modules/remote-pi/dist/mesh/canonical.js +0 -61
  235. package/node_modules/remote-pi/dist/mesh/canonical.js.map +0 -1
  236. package/node_modules/remote-pi/dist/mesh/client.d.ts +0 -18
  237. package/node_modules/remote-pi/dist/mesh/client.js +0 -125
  238. package/node_modules/remote-pi/dist/mesh/client.js.map +0 -1
  239. package/node_modules/remote-pi/dist/mesh/encoding.d.ts +0 -38
  240. package/node_modules/remote-pi/dist/mesh/encoding.js +0 -216
  241. package/node_modules/remote-pi/dist/mesh/encoding.js.map +0 -1
  242. package/node_modules/remote-pi/dist/mesh/self_revoke.d.ts +0 -73
  243. package/node_modules/remote-pi/dist/mesh/self_revoke.js +0 -401
  244. package/node_modules/remote-pi/dist/mesh/self_revoke.js.map +0 -1
  245. package/node_modules/remote-pi/dist/mesh/siblings.d.ts +0 -49
  246. package/node_modules/remote-pi/dist/mesh/siblings.js +0 -159
  247. package/node_modules/remote-pi/dist/mesh/siblings.js.map +0 -1
  248. package/node_modules/remote-pi/dist/mesh/types.d.ts +0 -34
  249. package/node_modules/remote-pi/dist/mesh/types.js +0 -11
  250. package/node_modules/remote-pi/dist/mesh/types.js.map +0 -1
  251. package/node_modules/remote-pi/dist/mesh/verify.d.ts +0 -17
  252. package/node_modules/remote-pi/dist/mesh/verify.js +0 -86
  253. package/node_modules/remote-pi/dist/mesh/verify.js.map +0 -1
  254. package/node_modules/remote-pi/dist/pairing/crypto.d.ts +0 -8
  255. package/node_modules/remote-pi/dist/pairing/crypto.js +0 -22
  256. package/node_modules/remote-pi/dist/pairing/crypto.js.map +0 -1
  257. package/node_modules/remote-pi/dist/pairing/handshake.d.ts +0 -28
  258. package/node_modules/remote-pi/dist/pairing/handshake.js +0 -113
  259. package/node_modules/remote-pi/dist/pairing/handshake.js.map +0 -1
  260. package/node_modules/remote-pi/dist/pairing/noise-sha256.d.ts +0 -16
  261. package/node_modules/remote-pi/dist/pairing/noise-sha256.js +0 -103
  262. package/node_modules/remote-pi/dist/pairing/noise-sha256.js.map +0 -1
  263. package/node_modules/remote-pi/dist/pairing/qr.d.ts +0 -59
  264. package/node_modules/remote-pi/dist/pairing/qr.js +0 -125
  265. package/node_modules/remote-pi/dist/pairing/qr.js.map +0 -1
  266. package/node_modules/remote-pi/dist/pairing/storage.d.ts +0 -120
  267. package/node_modules/remote-pi/dist/pairing/storage.js +0 -604
  268. package/node_modules/remote-pi/dist/pairing/storage.js.map +0 -1
  269. package/node_modules/remote-pi/dist/protocol/codec.d.ts +0 -7
  270. package/node_modules/remote-pi/dist/protocol/codec.js +0 -49
  271. package/node_modules/remote-pi/dist/protocol/codec.js.map +0 -1
  272. package/node_modules/remote-pi/dist/protocol/types.d.ts +0 -393
  273. package/node_modules/remote-pi/dist/protocol/types.js +0 -2
  274. package/node_modules/remote-pi/dist/protocol/types.js.map +0 -1
  275. package/node_modules/remote-pi/dist/rooms.d.ts +0 -29
  276. package/node_modules/remote-pi/dist/rooms.js +0 -57
  277. package/node_modules/remote-pi/dist/rooms.js.map +0 -1
  278. package/node_modules/remote-pi/dist/session/address.d.ts +0 -49
  279. package/node_modules/remote-pi/dist/session/address.js +0 -58
  280. package/node_modules/remote-pi/dist/session/address.js.map +0 -1
  281. package/node_modules/remote-pi/dist/session/agent_bridge.d.ts +0 -55
  282. package/node_modules/remote-pi/dist/session/agent_bridge.js +0 -146
  283. package/node_modules/remote-pi/dist/session/agent_bridge.js.map +0 -1
  284. package/node_modules/remote-pi/dist/session/bridge.d.ts +0 -37
  285. package/node_modules/remote-pi/dist/session/bridge.js +0 -130
  286. package/node_modules/remote-pi/dist/session/bridge.js.map +0 -1
  287. package/node_modules/remote-pi/dist/session/broker.d.ts +0 -189
  288. package/node_modules/remote-pi/dist/session/broker.js +0 -507
  289. package/node_modules/remote-pi/dist/session/broker.js.map +0 -1
  290. package/node_modules/remote-pi/dist/session/broker_remote.d.ts +0 -74
  291. package/node_modules/remote-pi/dist/session/broker_remote.js +0 -583
  292. package/node_modules/remote-pi/dist/session/broker_remote.js.map +0 -1
  293. package/node_modules/remote-pi/dist/session/cwd_lock.d.ts +0 -33
  294. package/node_modules/remote-pi/dist/session/cwd_lock.js +0 -126
  295. package/node_modules/remote-pi/dist/session/cwd_lock.js.map +0 -1
  296. package/node_modules/remote-pi/dist/session/envelope.d.ts +0 -33
  297. package/node_modules/remote-pi/dist/session/envelope.js +0 -117
  298. package/node_modules/remote-pi/dist/session/envelope.js.map +0 -1
  299. package/node_modules/remote-pi/dist/session/global_config.d.ts +0 -33
  300. package/node_modules/remote-pi/dist/session/global_config.js +0 -73
  301. package/node_modules/remote-pi/dist/session/global_config.js.map +0 -1
  302. package/node_modules/remote-pi/dist/session/ipc.d.ts +0 -27
  303. package/node_modules/remote-pi/dist/session/ipc.js +0 -22
  304. package/node_modules/remote-pi/dist/session/ipc.js.map +0 -1
  305. package/node_modules/remote-pi/dist/session/leader_election.d.ts +0 -32
  306. package/node_modules/remote-pi/dist/session/leader_election.js +0 -106
  307. package/node_modules/remote-pi/dist/session/leader_election.js.map +0 -1
  308. package/node_modules/remote-pi/dist/session/local_config.d.ts +0 -55
  309. package/node_modules/remote-pi/dist/session/local_config.js +0 -157
  310. package/node_modules/remote-pi/dist/session/local_config.js.map +0 -1
  311. package/node_modules/remote-pi/dist/session/mesh_message.d.ts +0 -20
  312. package/node_modules/remote-pi/dist/session/mesh_message.js +0 -28
  313. package/node_modules/remote-pi/dist/session/mesh_message.js.map +0 -1
  314. package/node_modules/remote-pi/dist/session/mesh_node.d.ts +0 -116
  315. package/node_modules/remote-pi/dist/session/mesh_node.js +0 -573
  316. package/node_modules/remote-pi/dist/session/mesh_node.js.map +0 -1
  317. package/node_modules/remote-pi/dist/session/peer.d.ts +0 -139
  318. package/node_modules/remote-pi/dist/session/peer.js +0 -451
  319. package/node_modules/remote-pi/dist/session/peer.js.map +0 -1
  320. package/node_modules/remote-pi/dist/session/peer_inventory.d.ts +0 -13
  321. package/node_modules/remote-pi/dist/session/peer_inventory.js +0 -48
  322. package/node_modules/remote-pi/dist/session/peer_inventory.js.map +0 -1
  323. package/node_modules/remote-pi/dist/session/peer_limits.d.ts +0 -9
  324. package/node_modules/remote-pi/dist/session/peer_limits.js +0 -21
  325. package/node_modules/remote-pi/dist/session/peer_limits.js.map +0 -1
  326. package/node_modules/remote-pi/dist/session/setup_wizard.d.ts +0 -39
  327. package/node_modules/remote-pi/dist/session/setup_wizard.js +0 -63
  328. package/node_modules/remote-pi/dist/session/setup_wizard.js.map +0 -1
  329. package/node_modules/remote-pi/dist/session/tool_gate.d.ts +0 -5
  330. package/node_modules/remote-pi/dist/session/tool_gate.js +0 -11
  331. package/node_modules/remote-pi/dist/session/tool_gate.js.map +0 -1
  332. package/node_modules/remote-pi/dist/session/tools.d.ts +0 -24
  333. package/node_modules/remote-pi/dist/session/tools.js +0 -252
  334. package/node_modules/remote-pi/dist/session/tools.js.map +0 -1
  335. package/node_modules/remote-pi/dist/session/wizard.d.ts +0 -13
  336. package/node_modules/remote-pi/dist/session/wizard.js +0 -20
  337. package/node_modules/remote-pi/dist/session/wizard.js.map +0 -1
  338. package/node_modules/remote-pi/dist/settings.d.ts +0 -15
  339. package/node_modules/remote-pi/dist/settings.js +0 -52
  340. package/node_modules/remote-pi/dist/settings.js.map +0 -1
  341. package/node_modules/remote-pi/dist/transport/peer_channel.d.ts +0 -37
  342. package/node_modules/remote-pi/dist/transport/peer_channel.js +0 -98
  343. package/node_modules/remote-pi/dist/transport/peer_channel.js.map +0 -1
  344. package/node_modules/remote-pi/dist/transport/pi_forward_client.d.ts +0 -29
  345. package/node_modules/remote-pi/dist/transport/pi_forward_client.js +0 -62
  346. package/node_modules/remote-pi/dist/transport/pi_forward_client.js.map +0 -1
  347. package/node_modules/remote-pi/dist/transport/relay_client.d.ts +0 -91
  348. package/node_modules/remote-pi/dist/transport/relay_client.js +0 -206
  349. package/node_modules/remote-pi/dist/transport/relay_client.js.map +0 -1
  350. package/node_modules/remote-pi/dist/ui/footer.d.ts +0 -32
  351. package/node_modules/remote-pi/dist/ui/footer.js +0 -34
  352. package/node_modules/remote-pi/dist/ui/footer.js.map +0 -1
  353. package/node_modules/remote-pi/docs/daemon.md +0 -289
  354. package/node_modules/remote-pi/package.json +0 -92
  355. package/node_modules/remote-pi/service-templates/launchd.plist.template +0 -35
  356. package/node_modules/remote-pi/service-templates/systemd.service.template +0 -19
  357. package/node_modules/remote-pi/service-templates/task-launcher.vbs.template +0 -10
  358. package/node_modules/remote-pi/service-templates/task-scheduler.xml.template +0 -38
  359. package/node_modules/remote-pi/skills/agent-network/SKILL.md +0 -310
@@ -1,66 +1,69 @@
1
1
  import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
2
2
  import {
3
3
  emitBounded
4
- } from "./chunk-ARFCXG7S.js";
4
+ } from "./chunk-OZJINN4O.js";
5
5
  import {
6
6
  createReadGuardEditBatchSummary,
7
7
  getReadGuardCorrelationId,
8
8
  logReadGuardEvent,
9
9
  normalizeMessage,
10
+ sanitizeCorrelationId,
10
11
  stableFindingId
11
- } from "./chunk-OAXNDZJ7.js";
12
+ } from "./chunk-3JSEWUWI.js";
12
13
  import {
13
14
  mapWithConcurrency
14
15
  } from "./chunk-XIMYZ3JA.js";
15
16
  import {
16
- LSP_SERVERS,
17
17
  classifyCascadeWaitTier,
18
18
  classifyServerWaitTier,
19
19
  enforceLspRootCeiling,
20
20
  findLocalTyposConfig,
21
21
  getAstGrepRuleSources,
22
+ getServerById,
22
23
  getServerInitOverride,
23
24
  getServersForFileWithConfig,
24
25
  getSessionRootsForTelemetry,
25
26
  getStrategy,
26
27
  hasProjectBoundaryMarker,
28
+ isAuxiliary,
27
29
  isDirectLspCommandTemporarilyUnavailable,
28
30
  isOutsideAllSessionRoots,
29
31
  resetClassicTsRepairGuard,
30
32
  resetLspLaunchAvailabilityGeneration,
31
- resolveLspServerCwd
32
- } from "./chunk-B3I2UUT4.js";
33
+ resolveLspServerCwd,
34
+ serverTraits
35
+ } from "./chunk-IQZXOZ7S.js";
33
36
  import {
34
37
  findLocalOpengrepConfig
35
- } from "./chunk-VDBPVIKD.js";
38
+ } from "./chunk-U52SAGRM.js";
36
39
  import {
37
40
  getSharedTreeSitterClient,
38
41
  loadWebTreeSitter,
39
42
  logTreeSitterDiagnostic,
40
43
  resolveTreeSitterLanguage,
41
44
  wasmQueryInput
42
- } from "./chunk-LW6XKDZQ.js";
45
+ } from "./chunk-UEAHAMEI.js";
43
46
  import {
44
47
  EXTENSION_TO_GRAMMAR,
45
48
  EXTENSION_TO_LSP_ID,
46
49
  KIND_TO_GRAMMAR
47
- } from "./chunk-TGPXLY2J.js";
50
+ } from "./chunk-KJCWXUUS.js";
48
51
  import {
49
52
  js_yaml_default
50
53
  } from "./chunk-QOZXJ4NA.js";
51
54
  import {
52
55
  buildModuleGraph
53
- } from "./chunk-V7R425XG.js";
56
+ } from "./chunk-6XX5HCLK.js";
54
57
  import {
55
58
  HOOK_WALL_BUDGET_MS,
56
59
  bounded,
57
60
  withDeadline,
58
61
  withTimeout
59
- } from "./chunk-GUOGVTCQ.js";
62
+ } from "./chunk-F5VLZHUW.js";
60
63
  import {
61
64
  loadProjectSnapshot,
62
65
  readJsonCache
63
- } from "./chunk-AJVLWNFN.js";
66
+ } from "./chunk-AXKPQ6ZI.js";
64
67
  import {
65
68
  PathKeyedMap
66
69
  } from "./chunk-Z3CCVDUB.js";
@@ -70,19 +73,19 @@ import {
70
73
  requestWarmCodeActions,
71
74
  requestWarmDiagnostics,
72
75
  toRunnerDisplayPath
73
- } from "./chunk-PWFHBTSF.js";
76
+ } from "./chunk-XGUL4SLQ.js";
74
77
  import {
75
78
  findLocalZizmorConfig,
76
79
  logSessionStart
77
- } from "./chunk-OQGFCZJ4.js";
80
+ } from "./chunk-LD7WFTRX.js";
78
81
  import {
79
82
  assertInstallAllowed,
80
83
  isLspSpawnAllowedByTrust,
81
84
  projectTrustDenialReason
82
- } from "./chunk-NTUJEGDZ.js";
85
+ } from "./chunk-D6YF5HKX.js";
83
86
  import {
84
87
  getLspCapableKinds
85
- } from "./chunk-22AVIGXS.js";
88
+ } from "./chunk-VYKKMYVT.js";
86
89
  import {
87
90
  createGenerationMap,
88
91
  createGenerationSource,
@@ -101,22 +104,22 @@ import {
101
104
  removeLspChild,
102
105
  sampleProcessTreeCpuPercent,
103
106
  selectLivePeerInstances
104
- } from "./chunk-VN2AXLEX.js";
107
+ } from "./chunk-T54VJRAD.js";
105
108
  import {
106
109
  createSingleFlight
107
110
  } from "./chunk-BW5KFIQC.js";
108
111
  import {
109
112
  SPECIAL_FILENAMES,
110
113
  getLanguageId
111
- } from "./chunk-5ZEJHV35.js";
114
+ } from "./chunk-64LU2OVH.js";
112
115
  import {
113
116
  detectFileRole
114
- } from "./chunk-DQ2NZ7C4.js";
117
+ } from "./chunk-N2IRDBZI.js";
115
118
  import {
116
119
  findGoverningTsconfigDir,
117
120
  getDirectoryMarkers,
118
121
  registerWorkspaceTopologyReset
119
- } from "./chunk-VNMT7C64.js";
122
+ } from "./chunk-Z6BB4JLQ.js";
120
123
  import {
121
124
  writeFileAtomic
122
125
  } from "./chunk-2EECBNC5.js";
@@ -125,15 +128,15 @@ import {
125
128
  incrementDegradationCount,
126
129
  recordDegradation,
127
130
  recordDegradationOnce
128
- } from "./chunk-N3YQJI6O.js";
131
+ } from "./chunk-7ELY2CSM.js";
129
132
  import {
130
133
  logExtension
131
- } from "./chunk-O6TQT6RI.js";
134
+ } from "./chunk-34OCDWOS.js";
132
135
  import {
133
136
  logLatency,
134
137
  phaseFinished,
135
138
  phaseStarted
136
- } from "./chunk-5THXD6X5.js";
139
+ } from "./chunk-3PNFWVHT.js";
137
140
  import {
138
141
  createNdjsonLogger,
139
142
  getGlobalPiLensLogDir,
@@ -142,7 +145,7 @@ import {
142
145
  isTestMode,
143
146
  nextModuleEvaluationOrdinal,
144
147
  toPositiveFinite
145
- } from "./chunk-UK3CMEAL.js";
148
+ } from "./chunk-VSMJK3ZJ.js";
146
149
  import {
147
150
  BoundedFifoMap,
148
151
  BoundedLruCache,
@@ -157,11 +160,12 @@ import {
157
160
  normalizeFilePath,
158
161
  normalizeMapKey,
159
162
  pathsEqual,
163
+ realpathOrResolve,
160
164
  toPosix,
161
165
  toProjectRelativePath,
162
166
  uriToDiskPath,
163
167
  uriToPath
164
- } from "./chunk-Q5U6FMDI.js";
168
+ } from "./chunk-G3RLJYA2.js";
165
169
  import {
166
170
  __commonJS,
167
171
  __require,
@@ -3966,7 +3970,7 @@ function resyncDocumentOnPastEof(filePath) {
3966
3970
  void (async () => {
3967
3971
  try {
3968
3972
  const [{ getLSPService: getLSPService2 }, content] = await Promise.all([
3969
- import("./chunk-VCSRSYLF.js"),
3973
+ import("./chunk-RJFED73B.js"),
3970
3974
  fs.promises.readFile(filePath, "utf-8")
3971
3975
  ]);
3972
3976
  await getLSPService2().openFile(filePath, content, {
@@ -4510,7 +4514,7 @@ function findGoModule(cwd, filePath) {
4510
4514
  while (true) {
4511
4515
  try {
4512
4516
  const content = fs3.readFileSync(path2.join(dir, "go.mod"), "utf-8");
4513
- const m = content.match(/^\s*module\s+(\S+)/m);
4517
+ const m = content.match(/^[^\S\n\r\u2028\u2029]*module\s+(\S+)/m);
4514
4518
  if (m)
4515
4519
  return { moduleDir: dir, modulePath: m[1] };
4516
4520
  } catch {
@@ -6193,6 +6197,7 @@ var liveEmitter = createLiveBusEmitter();
6193
6197
  var liveEmitter2 = createLiveBusEmitter();
6194
6198
 
6195
6199
  // dist/clients/read-guard.js
6200
+ import { createHash as createHash3 } from "node:crypto";
6196
6201
  import * as fs7 from "node:fs";
6197
6202
 
6198
6203
  // dist/clients/file-time.js
@@ -6558,6 +6563,71 @@ var DEFAULT_CONFIG = {
6558
6563
  };
6559
6564
  var READ_HASH_MAX_LINES = Math.max(0, Number.parseInt(process.env.PI_LENS_READ_GUARD_HASH_MAX_LINES ?? "3000", 10) || 3e3);
6560
6565
  var READ_BINDING_MAX_BYTES = 4 * 1024 * 1024;
6566
+ function hashFileBytes(filePath) {
6567
+ return createHash3("sha256").update(fs7.readFileSync(filePath)).digest("hex");
6568
+ }
6569
+ var ABSENT_SIZE = -1;
6570
+ function observeAuthoredBytes(filePath, knownHash, authoredRanges) {
6571
+ try {
6572
+ const stat2 = fs7.statSync(filePath);
6573
+ const hash = knownHash !== void 0 ? knownHash : stat2.size <= READ_BINDING_MAX_BYTES ? hashFileBytes(filePath) : void 0;
6574
+ return {
6575
+ size: stat2.size,
6576
+ mtimeMs: stat2.mtimeMs,
6577
+ ctimeMs: stat2.ctimeMs,
6578
+ ...hash !== void 0 && { hash },
6579
+ ...authoredRanges !== void 0 && { authoredRanges },
6580
+ ...authoredRanges !== void 0 && {
6581
+ authoredLineCount: splitLines(fs7.readFileSync(filePath, "utf-8")).length
6582
+ }
6583
+ };
6584
+ } catch {
6585
+ return { size: ABSENT_SIZE, mtimeMs: 0, ctimeMs: 0 };
6586
+ }
6587
+ }
6588
+ function composeAuthoredRanges(previous, next, nextLineCount) {
6589
+ const ranges = previous.authoredLineCount !== void 0 && nextLineCount === previous.authoredLineCount ? [...previous.authoredRanges ?? [], ...next] : next;
6590
+ const sorted = [...ranges].sort((a, b) => a[0] - b[0]);
6591
+ const merged = [];
6592
+ for (const [start, end] of sorted) {
6593
+ const last = merged.at(-1);
6594
+ if (last && start <= last[1] + 1)
6595
+ last[1] = Math.max(last[1], end);
6596
+ else
6597
+ merged.push([start, end]);
6598
+ }
6599
+ return merged;
6600
+ }
6601
+ function authoredRangeCovers(authored, touchedLines, editRanges) {
6602
+ if (authored.authoredRanges === void 0)
6603
+ return true;
6604
+ const requested = editRanges ?? (touchedLines ? [touchedLines] : void 0);
6605
+ return requested !== void 0 && requested.every(([start, end]) => authored.authoredRanges.some(([authoredStart, authoredEnd]) => start >= authoredStart && end <= authoredEnd));
6606
+ }
6607
+ function isValidAuthoredRanges(value) {
6608
+ return Array.isArray(value) && value.every((range) => Array.isArray(range) && range.length === 2 && Number.isFinite(range[0]) && Number.isFinite(range[1]));
6609
+ }
6610
+ function authoredBytesIntact(filePath, authored) {
6611
+ let stat2;
6612
+ try {
6613
+ stat2 = fs7.statSync(filePath);
6614
+ } catch {
6615
+ return authored.size === ABSENT_SIZE;
6616
+ }
6617
+ if (stat2.size === authored.size && stat2.mtimeMs === authored.mtimeMs && stat2.ctimeMs === authored.ctimeMs)
6618
+ return true;
6619
+ if (authored.hash === void 0 || stat2.size !== authored.size)
6620
+ return false;
6621
+ try {
6622
+ if (hashFileBytes(filePath) !== authored.hash)
6623
+ return false;
6624
+ } catch {
6625
+ return false;
6626
+ }
6627
+ authored.mtimeMs = stat2.mtimeMs;
6628
+ authored.ctimeMs = stat2.ctimeMs;
6629
+ return true;
6630
+ }
6561
6631
  var READ_GUARD_MAX_FILES = 256;
6562
6632
  var READ_GUARD_MAX_RECORDS_PER_FILE = 128;
6563
6633
  var NO_TRIM = {
@@ -6592,6 +6662,9 @@ function enforceRecordCapForFile(records) {
6592
6662
  }
6593
6663
  var READ_GUARD_MAX_EDITS_PER_FILE = 256;
6594
6664
  var READ_GUARD_MAX_UNCONSUMED_FILES = 4096;
6665
+ var READ_GUARD_MAX_AUTHORED_FILES = READ_GUARD_MAX_UNCONSUMED_FILES;
6666
+ var READ_GUARD_MAX_CHECKED_CALLS = 16;
6667
+ var READ_GUARD_MAX_CHECKED_PATHS_PER_CALL = 128;
6595
6668
  var READ_GUARD_IDLE_EVICT_MS_DEFAULT = 30 * 6e4;
6596
6669
  function _currentContentMatchesBindingForTests(filePath, binding) {
6597
6670
  try {
@@ -6708,7 +6781,19 @@ var ReadGuard = class {
6708
6781
  // Files that recordWritten() has fired on this session: the only evidence
6709
6782
  // of session authorship, independent of filesystem mtime granularity or
6710
6783
  // clock skew (NFS, FAT32, etc.) and of other writers' mtimes (#3520).
6711
- writtenThisSession = /* @__PURE__ */ new Set();
6784
+ // Each holds the content identity of the bytes the conversation last
6785
+ // wrote; authorship ends (`retired`) when the disk's bytes differ (#4131).
6786
+ writtenThisSession = /* @__PURE__ */ new Map();
6787
+ // #4187 R4-1: the paths each in-flight call's own `tool_call` pre-write
6788
+ // check ran on, keyed by the call id (`noteCheckedPaths`). A write that
6789
+ // carries no bytes of its own may re-baseline an authorship ONLY for a path
6790
+ // in its own call's set, because the set a tool NAMES at `tool_call` can be
6791
+ // narrower than the set it WRITES: a rename's importers, an ast-grep folder
6792
+ // or its project default, a server-initiated `workspace/applyEdit` that has
6793
+ // no call at all. Bounded by READ_GUARD_MAX_CHECKED_CALLS; session state,
6794
+ // so it dies with the guard every session start replaces, and a branch move
6795
+ // clears it with the authorship it licenses.
6796
+ checkedPathsByCall = /* @__PURE__ */ new Map();
6712
6797
  // Files whose write record idle-expired (#3520): `evictFile` drops the
6713
6798
  // authorship with the rest of the file's state, and a later zero-read edit
6714
6799
  // must say so instead of "you have not read" (false for a file the agent
@@ -6777,6 +6862,15 @@ var ReadGuard = class {
6777
6862
  key(filePath) {
6778
6863
  return normalizeFilePath(filePath);
6779
6864
  }
6865
+ /**
6866
+ * License keys compare paths independently resolved by the tool_call and
6867
+ * the writer. Unlike the general map key, this identity must collapse a
6868
+ * symlink spelling to the file it names; missing files retain an absolute
6869
+ * resolved fallback so the comparison still fails closed.
6870
+ */
6871
+ licenseKey(filePath) {
6872
+ return normalizeFilePath(realpathOrResolve(filePath));
6873
+ }
6780
6874
  idleEvictMs() {
6781
6875
  const value = Number.parseInt(process.env.PI_LENS_READ_GUARD_IDLE_EVICT_MS ?? "", 10);
6782
6876
  return Number.isSafeInteger(value) && value > 0 ? value : READ_GUARD_IDLE_EVICT_MS_DEFAULT;
@@ -6850,17 +6944,58 @@ var ReadGuard = class {
6850
6944
  * and log it once per file (the set is the once-only gate). The set holds
6851
6945
  * at most `READ_GUARD_MAX_FILES` files, oldest dropped first.
6852
6946
  */
6853
- noteExpiredWrite(filePath) {
6947
+ noteExpiredWrite(filePath, reason = "idle-timeout") {
6854
6948
  if (this.expiredWrites.has(filePath))
6855
6949
  return;
6856
6950
  this.expiredWrites.add(filePath);
6951
+ if (reason === "authorship-cap") {
6952
+ const isRisingEdge = incrementDegradationCount({
6953
+ kind: "read-guard-authorship-cap",
6954
+ subject: this.sessionId,
6955
+ reason: "authorship cap evicted a live write record"
6956
+ });
6957
+ if (isRisingEdge) {
6958
+ logReadGuardEvent({
6959
+ event: "read_file_evicted",
6960
+ sessionId: this.sessionId,
6961
+ filePath,
6962
+ metadata: { reason, authorshipDropped: true }
6963
+ });
6964
+ }
6965
+ return;
6966
+ }
6857
6967
  logReadGuardEvent({
6858
6968
  event: "read_file_evicted",
6859
6969
  sessionId: this.sessionId,
6860
6970
  filePath,
6861
- metadata: { reason: "idle-timeout", authorshipDropped: true }
6971
+ metadata: { reason, authorshipDropped: true }
6862
6972
  });
6863
6973
  }
6974
+ /**
6975
+ * #4187 R2-6: hold `writtenThisSession` at READ_GUARD_MAX_AUTHORED_FILES.
6976
+ * A retired entry blocks like no entry, so it goes first; a live one is
6977
+ * dropped least recently credited first, as an expired write (#3520).
6978
+ */
6979
+ enforceAuthorshipCap() {
6980
+ let excess = this.writtenThisSession.size - READ_GUARD_MAX_AUTHORED_FILES;
6981
+ if (excess <= 0)
6982
+ return;
6983
+ for (const [filePath, authored] of this.writtenThisSession) {
6984
+ if (excess <= 0)
6985
+ return;
6986
+ if (!authored.retired)
6987
+ continue;
6988
+ this.writtenThisSession.delete(filePath);
6989
+ excess -= 1;
6990
+ }
6991
+ for (const filePath of this.writtenThisSession.keys()) {
6992
+ if (excess <= 0)
6993
+ return;
6994
+ this.writtenThisSession.delete(filePath);
6995
+ this.noteExpiredWrite(filePath, "authorship-cap");
6996
+ excess -= 1;
6997
+ }
6998
+ }
6864
6999
  touchFile(filePath) {
6865
7000
  const now = Date.now();
6866
7001
  this.fileLastUsed.set(filePath, now);
@@ -6893,19 +7028,63 @@ var ReadGuard = class {
6893
7028
  }
6894
7029
  }
6895
7030
  // --- Public API ---
7031
+ /**
7032
+ * Drop every provisional native-read capture still held. The run boundary's
7033
+ * backstop (`handleAgentEnd`): a call's capture is released when the call
7034
+ * ends ({@link dropProvisionalReadByCall}), and only a run that dies
7035
+ * between a read's tool_call and its tool_execution_end leaves one here
7036
+ * (#4185 R2-3, round 4). Returns how many it dropped.
7037
+ */
7038
+ dropProvisionalReads() {
7039
+ let dropped = 0;
7040
+ for (const [filePath, records] of this.reads) {
7041
+ const kept = records.filter((record2) => {
7042
+ if (!record2.provisional)
7043
+ return true;
7044
+ dropped += 1;
7045
+ return false;
7046
+ });
7047
+ if (kept.length === 0)
7048
+ this.reads.delete(filePath);
7049
+ else if (kept.length !== records.length)
7050
+ this.reads.set(filePath, kept);
7051
+ }
7052
+ return dropped;
7053
+ }
7054
+ /**
7055
+ * Drop the tool_call capture of the read call `toolCallId` (the record
7056
+ * whose `source` is `native-read:<call>:provisional`), whatever path
7057
+ * spelling the call's result carries. The delivered record supersedes it
7058
+ * ({@link recordRead}); a call that ended in error, failed by the host or
7059
+ * blocked by a later extension, releases it at tool_execution_end
7060
+ * (`handleToolExecutionEnd`), because alone it satisfied the zero-read
7061
+ * check for an oldText edit of lines the agent never saw (#4185 rounds 2
7062
+ * to 4; `formal/read-guard` FailedReadLive, BlockedReadLive). Keyed by
7063
+ * the call's own identity: a nested call's is unique where its parent's,
7064
+ * the transcript identity a record carries, is shared by every read a
7065
+ * script makes. Returns whether a capture was found.
7066
+ */
7067
+ dropProvisionalReadByCall(toolCallId) {
7068
+ for (const [filePath, records] of this.reads) {
7069
+ const source = `native-read:${toolCallId}:provisional`;
7070
+ const index = records.findIndex((record2) => record2.source === source);
7071
+ if (index < 0)
7072
+ continue;
7073
+ records.splice(index, 1);
7074
+ if (records.length === 0)
7075
+ this.reads.delete(filePath);
7076
+ return true;
7077
+ }
7078
+ return false;
7079
+ }
6896
7080
  /**
6897
7081
  * Record that a file was read.
6898
7082
  * Call this from the tool_call handler after any LSP expansion.
6899
7083
  */
6900
7084
  recordRead(record2, opts) {
6901
7085
  const filePath = this.key(record2.filePath);
6902
- if (opts?.supersedes) {
6903
- const records = this.reads.get(filePath);
6904
- const provisionalSource = `native-read:${opts.supersedes.toolCallId}:provisional`;
6905
- const provisionalIndex = records?.findIndex((candidate) => candidate.source === provisionalSource);
6906
- if (provisionalIndex !== void 0 && provisionalIndex >= 0)
6907
- records.splice(provisionalIndex, 1);
6908
- }
7086
+ if (opts?.supersedes)
7087
+ this.dropProvisionalReadByCall(opts.supersedes.toolCallId);
6909
7088
  this.knownPathIndex.set(normalizeEphemeralMapKey(record2.filePath), filePath);
6910
7089
  const storedRecord = {
6911
7090
  ...record2,
@@ -6988,6 +7167,101 @@ var ReadGuard = class {
6988
7167
  if (opts?.stampFileTime !== false)
6989
7168
  this.fileTime.read(storedRecord.filePath);
6990
7169
  }
7170
+ /**
7171
+ * #4131: end the file's authorship when its bytes are no longer the ones the
7172
+ * conversation last wrote. Called by every guard decision that relies on
7173
+ * authorship: the zero-read edit check, and the start of a write that
7174
+ * carries no bytes of its own (a recognized bash write's tool_call, the
7175
+ * agent_end drain), which would otherwise re-baseline over the other
7176
+ * writer's bytes. Returns true when it ended the authorship here.
7177
+ */
7178
+ retireChangedAuthorship(rawFilePath) {
7179
+ return this.retireIfChanged(this.key(rawFilePath));
7180
+ }
7181
+ /**
7182
+ * #4187 R4-1: record the paths THIS call's `tool_call` pre-write check ran
7183
+ * on, so the call's own bytes-less write may advance exactly those and no
7184
+ * other path it changes. The `tool_call` handler calls it beside
7185
+ * {@link retireChangedAuthorship}: the licensed set IS the checked set.
7186
+ *
7187
+ * A call id both sides normalize with `sanitizeCorrelationId`, so a producer
7188
+ * passes the host's raw id. No id (a server-initiated `workspace/applyEdit`,
7189
+ * a drain) licenses nothing. Both caps fail closed: a path past
7190
+ * READ_GUARD_MAX_CHECKED_PATHS_PER_CALL, or one named by a call evicted
7191
+ * past READ_GUARD_MAX_CHECKED_CALLS, ends an authorship instead of
7192
+ * re-baselining it, which costs a read and never vouches for a byte the
7193
+ * conversation did not see.
7194
+ */
7195
+ noteCheckedPaths(rawToolCallId, rawPaths) {
7196
+ const toolCallId = sanitizeCorrelationId(rawToolCallId);
7197
+ if (toolCallId === void 0 || rawPaths.length === 0)
7198
+ return;
7199
+ let checked = this.checkedPathsByCall.get(toolCallId);
7200
+ if (!checked) {
7201
+ checked = /* @__PURE__ */ new Set();
7202
+ this.checkedPathsByCall.set(toolCallId, checked);
7203
+ while (this.checkedPathsByCall.size > READ_GUARD_MAX_CHECKED_CALLS) {
7204
+ const oldest = this.checkedPathsByCall.keys().next();
7205
+ if (oldest.done === true)
7206
+ break;
7207
+ this.checkedPathsByCall.delete(oldest.value);
7208
+ }
7209
+ }
7210
+ for (const rawPath of rawPaths) {
7211
+ if (checked.size >= READ_GUARD_MAX_CHECKED_PATHS_PER_CALL)
7212
+ return;
7213
+ checked.add(this.licenseKey(rawPath));
7214
+ }
7215
+ }
7216
+ /**
7217
+ * Consume whether `toolCallId`'s own `tool_call` licensed this path
7218
+ * ({@link noteCheckedPaths}). A `(call,path)` license is single-use: a
7219
+ * settled call id must not re-baseline a later writer's bytes.
7220
+ */
7221
+ consumeCheckedAtCall(toolCallId, filePath) {
7222
+ const callId = sanitizeCorrelationId(toolCallId);
7223
+ if (callId === void 0)
7224
+ return false;
7225
+ const checked = this.checkedPathsByCall.get(callId);
7226
+ const licensedPath = this.licenseKey(filePath);
7227
+ if (checked?.has(licensedPath) !== true)
7228
+ return false;
7229
+ checked.delete(licensedPath);
7230
+ if (checked.size === 0)
7231
+ this.checkedPathsByCall.delete(callId);
7232
+ return true;
7233
+ }
7234
+ /**
7235
+ * {@link retireChangedAuthorship} on a key. `writer` names a write that
7236
+ * ended it after the fact (a bridge write, which no pre-write check
7237
+ * guarded), so the record tells the two triggers apart.
7238
+ */
7239
+ retireIfChanged(filePath, writer3, force = false) {
7240
+ const authored = this.writtenThisSession.get(filePath);
7241
+ if (!authored || authored.retired || !force && authoredBytesIntact(filePath, authored))
7242
+ return false;
7243
+ authored.retired = true;
7244
+ const isRisingEdge = incrementDegradationCount({
7245
+ kind: "read-guard-authorship-retired",
7246
+ subject: filePath,
7247
+ reason: force ? "authored range was unknown; the next edit needs a read" : "another writer changed the bytes the conversation wrote; the next edit needs a read"
7248
+ });
7249
+ if (isRisingEdge)
7250
+ logReadGuardEvent({
7251
+ event: "authorship_retired",
7252
+ sessionId: this.sessionId,
7253
+ filePath,
7254
+ metadata: {
7255
+ authoredSize: authored.size,
7256
+ hashed: authored.hash !== void 0,
7257
+ ...writer3 !== void 0 && { writer: writer3 },
7258
+ ...authored.toolCallId !== void 0 && {
7259
+ toolCallId: authored.toolCallId
7260
+ }
7261
+ }
7262
+ });
7263
+ return true;
7264
+ }
6991
7265
  /**
6992
7266
  * #3525: whether the disk may hold bytes no read or own write accounts
6993
7267
  * for: FileTime moved since its stamp, or there is none. An edit that
@@ -7069,14 +7343,29 @@ var ReadGuard = class {
7069
7343
  const effectiveMode = exemptionMode === "warn" ? "warn" : void 0;
7070
7344
  const fileReads = this.reads.get(filePath);
7071
7345
  if (!fileReads || fileReads.length === 0) {
7072
- if (this.writtenThisSession.has(filePath)) {
7073
- this.injectCreationRead(filePath, 0, 0);
7346
+ this.retireChangedAuthorship(filePath);
7347
+ if (this.writtenThisSession.get(filePath)?.retired) {
7348
+ const verdict3 = this.blockOrWarn("authorship-retired", `\u{1F504} RETRYABLE \u2014 File modified since your write: \`${filePath}\` no longer holds the bytes this conversation wrote. Read it first, then retry: \`read path="${filePath}"\`.`, void 0, effectiveMode);
7349
+ this.recordVerdict(filePath, "edit", touchedLines, verdict3, {
7350
+ reasonKind: "authorship_retired"
7351
+ });
7352
+ return verdict3;
7353
+ }
7354
+ const authored = this.writtenThisSession.get(filePath);
7355
+ if (authored && authoredRangeCovers(authored, touchedLines, editRanges)) {
7074
7356
  const verdict3 = this.allow();
7075
7357
  this.recordVerdict(filePath, "edit", touchedLines, verdict3, {
7076
7358
  reasonKind: "session_authored"
7077
7359
  });
7078
7360
  return verdict3;
7079
7361
  }
7362
+ if (authored) {
7363
+ const verdict3 = this.blockOrWarn("authorship-range", `\u{1F504} RETRYABLE \u2014 Edit without read: this write only covered part of \`${filePath}\`; read the requested lines first, then retry.`, void 0, effectiveMode);
7364
+ this.recordVerdict(filePath, "edit", touchedLines, verdict3, {
7365
+ reasonKind: "authorship_range"
7366
+ });
7367
+ return verdict3;
7368
+ }
7080
7369
  const writeRecordExpired = this.expiredWrites.has(filePath);
7081
7370
  const verdict2 = this.blockOrWarn("zero-read", writeRecordExpired ? `\u{1F504} RETRYABLE \u2014 Edit without read: the earlier write record for \`${filePath}\` expired, so it needs a read again. Read it first, then retry: \`read path="${filePath}"\`.` : `\u{1F504} RETRYABLE \u2014 Edit without read: you have not read \`${filePath}\` in this conversation. Read it first, then retry: \`read path="${filePath}"\`.`, void 0, effectiveMode);
7082
7371
  this.recordVerdict(filePath, "edit", touchedLines, verdict2, {
@@ -7267,7 +7556,7 @@ Re-read the relevant section, then retry the edit using the current line range/c
7267
7556
  */
7268
7557
  getTrackedPaths() {
7269
7558
  const tracked = new Set(this.reads.keys());
7270
- for (const written of this.writtenThisSession)
7559
+ for (const written of this.writtenThisSession.keys())
7271
7560
  tracked.add(written);
7272
7561
  return [...tracked];
7273
7562
  }
@@ -7335,7 +7624,7 @@ Re-read the relevant section, then retry the edit using the current line range/c
7335
7624
  this.knownPathIndex.set(normalizeEphemeralMapKey(rawFilePath), filePath);
7336
7625
  if (opts?.stampFileTime !== false)
7337
7626
  this.fileTime.read(filePath);
7338
- this.writtenThisSession.add(filePath);
7627
+ this.creditAuthorship(filePath, opts);
7339
7628
  if (this.reads.has(filePath))
7340
7629
  this.consumedReadFiles.add(filePath);
7341
7630
  this.touchFile(filePath);
@@ -7436,24 +7725,68 @@ Re-read the relevant section, then retry the edit using the current line range/c
7436
7725
  }
7437
7726
  /**
7438
7727
  * The files this session authored (#3612, D5): `writtenThisSession`, which
7439
- * the zero-read check reads. A `/reload` keeps the conversation and its branch, so the
7440
- * reloaded guard keeps them; every other start resets them.
7728
+ * the zero-read check reads, each with the content identity and transcript
7729
+ * id of its write (#4131, #3603).
7441
7730
  */
7442
7731
  exportAuthorship() {
7443
- return { written: [...this.writtenThisSession] };
7732
+ return {
7733
+ // A released reader treats every `written` path as authored, so a
7734
+ // retired one is carried in `entries` only.
7735
+ written: [...this.writtenThisSession].flatMap(([filePath, authored]) => authored.retired ? [] : [filePath]),
7736
+ entries: [...this.writtenThisSession].map(([filePath, authored]) => ({
7737
+ filePath,
7738
+ ...authored
7739
+ }))
7740
+ };
7444
7741
  }
7445
7742
  /**
7446
- * Restore {@link exportAuthorship}'s output. Null-safe on a malformed
7447
- * payload; a row from a released writer may carry a `sessionStartMs`, which
7448
- * is ignored.
7743
+ * Restore {@link exportAuthorship}'s output for the branch this session
7744
+ * starts on (#3603): the {@link importBranch} rule, an entry is imported
7745
+ * iff its write's `toolCallId` is in `onBranch`. Its content identity
7746
+ * comes with it, so bytes another writer changed meanwhile end it at the
7747
+ * next check. A released writer's row (#3612: `written` paths only, maybe a
7748
+ * `sessionStartMs`) names no write, so it imports nothing. Null-safe on a
7749
+ * malformed payload; never throws.
7449
7750
  */
7450
- importAuthorship(state5) {
7751
+ importAuthorship(state5, onBranch) {
7752
+ const result = { imported: 0, dropped: 0 };
7451
7753
  const authorship = state5;
7754
+ const entries = Array.isArray(authorship?.entries) ? authorship.entries : [];
7755
+ const withEntry = /* @__PURE__ */ new Set();
7756
+ for (const entry of entries) {
7757
+ const candidate = entry;
7758
+ if (typeof candidate?.filePath !== "string")
7759
+ continue;
7760
+ withEntry.add(candidate.filePath);
7761
+ if (typeof candidate.toolCallId !== "string" || !onBranch.has(candidate.toolCallId) || typeof candidate.size !== "number" || typeof candidate.mtimeMs !== "number" || typeof candidate.ctimeMs !== "number" || candidate.hash !== void 0 && typeof candidate.hash !== "string" || candidate.retired !== void 0 && candidate.retired !== true || candidate.authoredRanges !== void 0 && !isValidAuthoredRanges(candidate.authoredRanges) || candidate.authoredLineCount !== void 0 && typeof candidate.authoredLineCount !== "number") {
7762
+ result.dropped += 1;
7763
+ continue;
7764
+ }
7765
+ const filePath = this.key(candidate.filePath);
7766
+ this.knownPathIndex.set(normalizeEphemeralMapKey(candidate.filePath), filePath);
7767
+ this.writtenThisSession.set(filePath, {
7768
+ size: candidate.size,
7769
+ mtimeMs: candidate.mtimeMs,
7770
+ ctimeMs: candidate.ctimeMs,
7771
+ ...candidate.hash !== void 0 && { hash: candidate.hash },
7772
+ toolCallId: candidate.toolCallId,
7773
+ ...candidate.retired === true && { retired: true },
7774
+ ...candidate.authoredRanges !== void 0 && {
7775
+ authoredRanges: candidate.authoredRanges
7776
+ },
7777
+ ...candidate.authoredLineCount !== void 0 && {
7778
+ authoredLineCount: candidate.authoredLineCount
7779
+ }
7780
+ });
7781
+ result.imported += 1;
7782
+ }
7783
+ this.enforceAuthorshipCap();
7452
7784
  if (Array.isArray(authorship?.written)) {
7453
7785
  for (const filePath of authorship.written)
7454
- if (typeof filePath === "string")
7455
- this.writtenThisSession.add(filePath);
7786
+ if (typeof filePath === "string" && !withEntry.has(filePath))
7787
+ result.dropped += 1;
7456
7788
  }
7789
+ return result;
7457
7790
  }
7458
7791
  /**
7459
7792
  * Keep exactly the reads the conversation still shows the agent, after it
@@ -7464,8 +7797,10 @@ Re-read the relevant section, then retry the edit using the current line range/c
7464
7797
  * is re-verified or re-stamped here: the FileTime stamps are cleared, so
7465
7798
  * each kept record must pass the per-line hash check against disk at the
7466
7799
  * next edit. A stamp taken on the abandoned branch would otherwise vouch
7467
- * for bytes this branch never showed. Edits, authored-write and
7468
- * pending-creation state came from the old branch too, so they go.
7800
+ * for bytes this branch never showed. Authorship follows the same rule
7801
+ * (#3603): it stays, retired or not, iff the write it came from is on the
7802
+ * branch, and its content identity still decides. Edits, idle-expiry
7803
+ * marks and pending creations came from the old branch, so they go.
7469
7804
  */
7470
7805
  retainBranch(onBranch) {
7471
7806
  const result = { kept: 0, dropped: 0 };
@@ -7482,10 +7817,13 @@ Re-read the relevant section, then retry the edit using the current line range/c
7482
7817
  }
7483
7818
  this.reads.delete(filePath);
7484
7819
  }
7820
+ for (const [filePath, authored] of this.writtenThisSession)
7821
+ if (authored.toolCallId === void 0 || !onBranch.has(authored.toolCallId))
7822
+ this.writtenThisSession.delete(filePath);
7485
7823
  this.edits.clear();
7486
- this.writtenThisSession.clear();
7487
7824
  this.expiredWrites.clear();
7488
7825
  this.pendingCreations.clear();
7826
+ this.checkedPathsByCall.clear();
7489
7827
  this.fileTime.clear();
7490
7828
  moveBranch(this.scope);
7491
7829
  return result;
@@ -7555,6 +7893,70 @@ Re-read the relevant section, then retry the edit using the current line range/c
7555
7893
  return this.edits.get(key) ?? [];
7556
7894
  }
7557
7895
  // --- Private helpers ---
7896
+ /**
7897
+ * #4187 R4-1: whether a write may re-baseline an EXISTING authorship over
7898
+ * the bytes it just landed. One rule, three caller classes:
7899
+ *
7900
+ * - `advanceAuthorship` omitted — the caller checked this exact path
7901
+ * immediately before writing (a native edit's preflight, a drain's
7902
+ * `retireChangedAuthorshipBeforeDrain`), so the only unaccounted bytes
7903
+ * are its own. Advance.
7904
+ * - `true` — a bytes-less in-process tool write (an LSP edit, an ast-grep
7905
+ * apply, a diagnostic-mark suppress, an observed replay). Advance only a
7906
+ * path its OWN `tool_call` checked ({@link noteCheckedPaths}): the set a
7907
+ * tool names can be narrower than the set it writes, and a path outside
7908
+ * it ends the authorship instead.
7909
+ * - `false` — a record with no pre-write check at all (a co-process
7910
+ * producer, the settled sweep). Never advance.
7911
+ *
7912
+ * A path with no entry is a first credit only when the writer supplies
7913
+ * evidence for it. Partial bridge credits retain their reported range;
7914
+ * settled-sweep drift supplies no such evidence (#4210).
7915
+ */
7916
+ mayAdvanceAuthorship(filePath, opts) {
7917
+ if (opts?.advanceAuthorship === void 0)
7918
+ return true;
7919
+ if (opts.advanceAuthorship !== true)
7920
+ return false;
7921
+ return this.consumeCheckedAtCall(opts.toolCallId, filePath);
7922
+ }
7923
+ /**
7924
+ * #4131: record the content identity of the bytes `recordWritten` just
7925
+ * credited. A retired authorship is never resumed (see
7926
+ * `AuthoredBytes.retired`).
7927
+ */
7928
+ creditAuthorship(filePath, opts) {
7929
+ const existing = this.writtenThisSession.get(filePath);
7930
+ if (!existing && opts?.allowFirstAuthorship === false)
7931
+ return;
7932
+ if (existing?.retired)
7933
+ return;
7934
+ if (opts?.authorship === "unknown") {
7935
+ if (existing)
7936
+ this.retireIfChanged(filePath, "bridge", true);
7937
+ return;
7938
+ }
7939
+ if (existing && !this.mayAdvanceAuthorship(filePath, opts)) {
7940
+ this.retireIfChanged(filePath, "bridge");
7941
+ return;
7942
+ }
7943
+ const observed = observeAuthoredBytes(filePath, opts?.contentHash, opts?.authoredRanges);
7944
+ const { authoredRanges: _observedRanges, ...observedWithoutRanges } = observed;
7945
+ const composedRanges = existing?.authoredRanges === void 0 ? existing ? void 0 : observed.authoredRanges : opts?.authoredRanges === void 0 ? void 0 : composeAuthoredRanges(existing, opts.authoredRanges, observed.authoredLineCount);
7946
+ const toolCallId = opts?.toolCallId ?? existing?.toolCallId;
7947
+ this.writtenThisSession.delete(filePath);
7948
+ this.writtenThisSession.set(filePath, {
7949
+ ...observedWithoutRanges,
7950
+ ...composedRanges !== void 0 ? {
7951
+ authoredRanges: composedRanges,
7952
+ ...observed.authoredLineCount !== void 0 && {
7953
+ authoredLineCount: observed.authoredLineCount
7954
+ }
7955
+ } : {},
7956
+ ...toolCallId !== void 0 && { toolCallId }
7957
+ });
7958
+ this.enforceAuthorshipCap();
7959
+ }
7558
7960
  /**
7559
7961
  * `writtenContent`, when the write's bytes are known, is the agent's view
7560
7962
  * of the file: hashed from it, not from a disk another writer may have
@@ -8841,7 +9243,7 @@ function classifyDiagnostic(d) {
8841
9243
  import * as path9 from "node:path";
8842
9244
 
8843
9245
  // dist/clients/dispatch/runners/yaml-rule-parser.js
8844
- import { createHash as createHash3 } from "node:crypto";
9246
+ import { createHash as createHash4 } from "node:crypto";
8845
9247
  import * as fs10 from "node:fs";
8846
9248
  import * as path8 from "node:path";
8847
9249
  var OVERLY_BROAD_PATTERNS = ["$NAME", "$FIELD", "$_", "$X", "$VAR", "$EXPR"];
@@ -8884,8 +9286,14 @@ function loadYamlRuleFiles(files2, severityFilter) {
8884
9286
  const documents = content.split(/^---\s*$/m).filter((doc) => doc.trim());
8885
9287
  for (const document of documents) {
8886
9288
  const rule = parseSimpleYaml(document.trim());
8887
- if (!rule?.id)
9289
+ if (!rule?.id) {
9290
+ recordDegradationOnce({
9291
+ kind: "ast-grep-rule-invalid",
9292
+ subject: file,
9293
+ reason: "ast-grep rule could not be parsed"
9294
+ });
8888
9295
  continue;
9296
+ }
8889
9297
  if (severityFilter && rule.severity !== severityFilter)
8890
9298
  continue;
8891
9299
  rules.push(rule);
@@ -8895,7 +9303,7 @@ function loadYamlRuleFiles(files2, severityFilter) {
8895
9303
  }
8896
9304
  function loadYamlRulesFresh(ruleDir, severityFilter) {
8897
9305
  const files2 = findYamlRuleFiles(ruleDir);
8898
- const hash = createHash3("sha256");
9306
+ const hash = createHash4("sha256");
8899
9307
  for (const file of files2) {
8900
9308
  hash.update(path8.relative(ruleDir, file));
8901
9309
  hash.update("\0");
@@ -9343,12 +9751,12 @@ function getFullScanWallClockMs() {
9343
9751
  }
9344
9752
 
9345
9753
  // dist/clients/lsp/document-drift.js
9346
- import { createHash as createHash4 } from "node:crypto";
9754
+ import { createHash as createHash5 } from "node:crypto";
9347
9755
  import nodeFs from "node:fs";
9348
9756
  function fingerprintDocumentContent(content) {
9349
9757
  if (content.length <= 96)
9350
9758
  return `${content.length}:${content}`;
9351
- return `${content.length}:${createHash4("sha256").update(content).digest("hex")}`;
9759
+ return `${content.length}:${createHash5("sha256").update(content).digest("hex")}`;
9352
9760
  }
9353
9761
  var SKIPPED = {
9354
9762
  checked: 0,
@@ -10100,7 +10508,10 @@ function uniqueDetails(files2, fileDetails) {
10100
10508
  const key = keyFor(detail.filePath);
10101
10509
  const previous = byPath.get(key);
10102
10510
  if (!previous) {
10103
- byPath.set(key, detail);
10511
+ byPath.set(key, {
10512
+ ...detail,
10513
+ ranges: detail.ranges ?? (detail.range ? [detail.range] : void 0)
10514
+ });
10104
10515
  continue;
10105
10516
  }
10106
10517
  byPath.set(key, {
@@ -10109,6 +10520,11 @@ function uniqueDetails(files2, fileDetails) {
10109
10520
  start: Math.min(previous.range.start, detail.range.start),
10110
10521
  end: Math.max(previous.range.end, detail.range.end)
10111
10522
  } : previous.range ?? detail.range,
10523
+ ranges: [
10524
+ ...previous.ranges ?? (previous.range ? [previous.range] : []),
10525
+ ...detail.ranges ?? (detail.range ? [detail.range] : [])
10526
+ ],
10527
+ authorshipUnknown: previous.authorshipUnknown === true || detail.authorshipUnknown === true,
10112
10528
  importsChanged: previous.importsChanged || detail.importsChanged
10113
10529
  });
10114
10530
  }
@@ -10118,6 +10534,7 @@ function uniqueDetails(files2, fileDetails) {
10118
10534
  // range is still enough to invalidate the touched-file turn state;
10119
10535
  // never synchronously re-read the whole file here.
10120
10536
  range: { start: 1, end: 1 },
10537
+ authorshipUnknown: true,
10121
10538
  importsChanged: true
10122
10539
  });
10123
10540
  }
@@ -10133,7 +10550,18 @@ function bookkeepLspMutation(context, files2, fileDetails) {
10133
10550
  }
10134
10551
  if (sessionLive && context.readGuard) {
10135
10552
  try {
10136
- context.readGuard.recordWritten(filePath, { stampFileTime: false });
10553
+ context.readGuard.recordWritten(filePath, {
10554
+ stampFileTime: false,
10555
+ advanceAuthorship: true,
10556
+ ...detail.authorshipUnknown !== true && detail.ranges !== void 0 && {
10557
+ authorship: "partial",
10558
+ authoredRanges: detail.ranges.map(({ start, end }) => [start, end])
10559
+ },
10560
+ ...detail.authorshipUnknown === true && { authorship: "unknown" },
10561
+ ...context.toolCallId !== void 0 && {
10562
+ toolCallId: context.toolCallId
10563
+ }
10564
+ });
10137
10565
  } catch (err) {
10138
10566
  context.dbg?.(`lsp mutation read-guard stamp failed for ${filePath}: ${err}`);
10139
10567
  }
@@ -10152,11 +10580,19 @@ function bookkeepLspMutation(context, files2, fileDetails) {
10152
10580
  }
10153
10581
  } else {
10154
10582
  try {
10583
+ const editRanges = detail.ranges && detail.authorshipUnknown !== true ? detail.ranges.map(({ start, end }) => [start, end]) : detail.range && detail.authorshipUnknown !== true ? [[detail.range.start, detail.range.end]] : void 0;
10155
10584
  const recorded = bridge.recordMutation({
10156
10585
  filePath,
10157
10586
  kind: "edit",
10158
- editRanges: detail.range ? [[detail.range.start, detail.range.end]] : void 0,
10587
+ ...detail.range !== void 0 && {
10588
+ touchedLines: [detail.range.start, detail.range.end]
10589
+ },
10590
+ ...detail.authorshipUnknown === true && {
10591
+ authorshipUnknown: true
10592
+ },
10593
+ ...editRanges !== void 0 ? { editRanges } : {},
10159
10594
  consumer: context.tool,
10595
+ provenance: "observed",
10160
10596
  // Real value threaded through, not the bridge's own
10161
10597
  // historical `false` default (#2450 review round 2, F1) —
10162
10598
  // the tsserver organize-imports/add-import case is exactly
@@ -10166,7 +10602,12 @@ function bookkeepLspMutation(context, files2, fileDetails) {
10166
10602
  // enqueues a deferred autofix/format pass for an LSP-applied
10167
10603
  // edit (#2450 review round 2, F3).
10168
10604
  deferAutofix: false,
10169
- ...context.session && { lineage: context.session }
10605
+ ...context.session && { lineage: context.session },
10606
+ // #4187 R4-1: the same license the direct branch threads, so
10607
+ // the two branches stay equivalent for the same write.
10608
+ ...context.toolCallId !== void 0 && {
10609
+ toolCallId: context.toolCallId
10610
+ }
10170
10611
  });
10171
10612
  if (!recorded) {
10172
10613
  context.dbg?.(`lsp mutation bridge dropped ${filePath} (recordMutation() returned false \u2014 see the mutation_bridge debug output for the reason: malformed entry, out-of-scope path, or a bookkeeping error)`);
@@ -10349,10 +10790,10 @@ async function raceToCompletion(promises4, shouldComplete, options = { timeoutMs
10349
10790
  let completed = false;
10350
10791
  let remaining = promises4.length;
10351
10792
  const descriptors = options.descriptors ?? [];
10352
- const hasAuxiliaries = descriptors.some((d) => d.role === "auxiliary");
10353
- const auxIndices = hasAuxiliaries ? new Set(descriptors.map((d, i) => d.role === "auxiliary" ? i : -1).filter((i) => i >= 0)) : /* @__PURE__ */ new Set();
10354
- const primaryCount = promises4.length - auxIndices.size;
10355
- let primaryRemaining = primaryCount;
10793
+ const hasAuxiliaries = descriptors.some((d) => isAuxiliary(d));
10794
+ const auxIndices = hasAuxiliaries ? new Set(descriptors.map((d, i) => isAuxiliary(d) ? i : -1).filter((i) => i >= 0)) : /* @__PURE__ */ new Set();
10795
+ const languageCount = promises4.length - auxIndices.size;
10796
+ let languageRemaining = languageCount;
10356
10797
  let auxGraceStarted = false;
10357
10798
  return new Promise((resolve9) => {
10358
10799
  const timeout = setTimeout(() => {
@@ -10394,7 +10835,7 @@ async function raceToCompletion(promises4, shouldComplete, options = { timeoutMs
10394
10835
  finalize();
10395
10836
  return;
10396
10837
  }
10397
- if (hasAuxiliaries && primaryRemaining === 0 && !auxGraceStarted) {
10838
+ if (hasAuxiliaries && languageRemaining === 0 && !auxGraceStarted) {
10398
10839
  startAuxGrace();
10399
10840
  }
10400
10841
  const collected = results.filter((r) => r !== void 0);
@@ -10402,7 +10843,7 @@ async function raceToCompletion(promises4, shouldComplete, options = { timeoutMs
10402
10843
  if (options.graceMs !== void 0 && options.graceMs > 0 && !graceTimer) {
10403
10844
  graceTimer = setTimeout(() => finalize(), options.graceMs);
10404
10845
  } else if (auxGraceStarted) {
10405
- } else if (!hasAuxiliaries || primaryRemaining === 0) {
10846
+ } else if (!hasAuxiliaries || languageRemaining === 0) {
10406
10847
  finalize();
10407
10848
  }
10408
10849
  }
@@ -10415,14 +10856,14 @@ async function raceToCompletion(promises4, shouldComplete, options = { timeoutMs
10415
10856
  results[index] = result;
10416
10857
  remaining--;
10417
10858
  if (!isAux)
10418
- primaryRemaining--;
10859
+ languageRemaining--;
10419
10860
  check();
10420
10861
  }
10421
10862
  }).catch(() => {
10422
10863
  if (!completed) {
10423
10864
  remaining--;
10424
10865
  if (!isAux)
10425
- primaryRemaining--;
10866
+ languageRemaining--;
10426
10867
  check();
10427
10868
  }
10428
10869
  });
@@ -11364,13 +11805,23 @@ async function applyWorkspaceEdit(edit, cwd, options = {}) {
11364
11805
  }
11365
11806
  const content = await fs11.readFile(diskPath, "utf-8");
11366
11807
  const updated = applyTextEditsToString(content, edits, "utf-16");
11808
+ const changesLineCount = edits.some((edit2) => {
11809
+ const removedLines = edit2.range.end.line - edit2.range.start.line;
11810
+ const insertedLines = (edit2.newText.match(/\r\n|\r|\n/g) ?? []).length;
11811
+ return removedLines !== insertedLines;
11812
+ });
11367
11813
  await fs11.writeFile(diskPath, updated, "utf-8");
11368
11814
  const start = Math.min(...edits.map((item) => item.range.start.line + 1));
11369
- const end = Math.max(...edits.map((item) => item.range.end.line + 1));
11815
+ const end = Math.max(...edits.map((item) => item.range.end.line + (item.range.end.character === 0 && item.range.end.line > item.range.start.line ? 0 : 1)));
11370
11816
  touchedFiles.add(filePath);
11371
11817
  fileDetails.push({
11372
11818
  filePath,
11373
11819
  range: { start, end },
11820
+ ranges: edits.map((item) => ({
11821
+ start: item.range.start.line + 1,
11822
+ end: item.range.end.line + (item.range.end.character === 0 && item.range.end.line > item.range.start.line ? 0 : 1)
11823
+ })),
11824
+ authorshipUnknown: changesLineCount,
11374
11825
  importsChanged: importsSignature(content) !== importsSignature(updated)
11375
11826
  });
11376
11827
  markApplied(op);
@@ -11387,6 +11838,7 @@ async function applyWorkspaceEdit(edit, cwd, options = {}) {
11387
11838
  fileDetails.push({
11388
11839
  filePath,
11389
11840
  range: { start: 1, end: 1 },
11841
+ authorshipUnknown: true,
11390
11842
  importsChanged: false
11391
11843
  });
11392
11844
  markApplied(op);
@@ -11405,10 +11857,12 @@ async function applyWorkspaceEdit(edit, cwd, options = {}) {
11405
11857
  fileDetails.push({
11406
11858
  filePath: oldPath,
11407
11859
  range: { start: 1, end: 1 },
11860
+ authorshipUnknown: true,
11408
11861
  importsChanged: true
11409
11862
  }, {
11410
11863
  filePath: newPath,
11411
11864
  range: { start: 1, end: 1 },
11865
+ authorshipUnknown: true,
11412
11866
  importsChanged: true
11413
11867
  });
11414
11868
  markApplied(op);
@@ -11424,6 +11878,7 @@ async function applyWorkspaceEdit(edit, cwd, options = {}) {
11424
11878
  fileDetails.push({
11425
11879
  filePath,
11426
11880
  range: { start: 1, end: 1 },
11881
+ authorshipUnknown: true,
11427
11882
  importsChanged: true
11428
11883
  });
11429
11884
  markApplied(op);
@@ -15166,6 +15621,43 @@ function getLanguageId2(filePath) {
15166
15621
  return LANGUAGE_EXTENSIONS[base] ?? LANGUAGE_EXTENSIONS[base.toLowerCase()] ?? getBasenameLanguageId(base);
15167
15622
  }
15168
15623
 
15624
+ // dist/clients/lsp/spawn-history.js
15625
+ var MAX_SERVER_HISTORIES = 64;
15626
+ var successfulSpawnDurationMs = new BoundedFifoMap(MAX_SERVER_HISTORIES);
15627
+ function recordSuccessfulLspSpawn(serverId, durationMs) {
15628
+ if (!Number.isFinite(durationMs) || durationMs < 0)
15629
+ return;
15630
+ successfulSpawnDurationMs.delete(serverId);
15631
+ successfulSpawnDurationMs.set(serverId, durationMs);
15632
+ }
15633
+ function getSuccessfulLspSpawnDurationMs(serverId) {
15634
+ return successfulSpawnDurationMs.get(serverId);
15635
+ }
15636
+
15637
+ // dist/clients/lsp/auxiliary-lifecycle.js
15638
+ function readEnvAuxGraceMs() {
15639
+ const raw = process.env.PI_LENS_AUX_GRACE_MS;
15640
+ if (raw === void 0)
15641
+ return void 0;
15642
+ const parsed = Number.parseInt(raw, 10);
15643
+ if (!Number.isFinite(parsed) || parsed < 0)
15644
+ return void 0;
15645
+ return parsed;
15646
+ }
15647
+ var DEFAULT_AUX_GRACE_CEILING_MS = 2e3;
15648
+ var MAX_ADAPTIVE_AUX_GRACE_CEILING_MS = 8e3;
15649
+ var ADAPTIVE_AUX_GRACE_MARGIN_MS = 500;
15650
+ function auxWaitBudgetMs(serverId, isCold, configuredCeilingMs, declaredWaitMs) {
15651
+ if (configuredCeilingMs !== void 0 || !isCold) {
15652
+ return Math.min(declaredWaitMs, configuredCeilingMs ?? DEFAULT_AUX_GRACE_CEILING_MS);
15653
+ }
15654
+ const observedSpawnMs = getSuccessfulLspSpawnDurationMs(serverId);
15655
+ if (observedSpawnMs === void 0 || observedSpawnMs <= 0) {
15656
+ return Math.min(declaredWaitMs, DEFAULT_AUX_GRACE_CEILING_MS);
15657
+ }
15658
+ return Math.min(MAX_ADAPTIVE_AUX_GRACE_CEILING_MS, Math.max(declaredWaitMs, observedSpawnMs + ADAPTIVE_AUX_GRACE_MARGIN_MS));
15659
+ }
15660
+
15169
15661
  // dist/clients/lazy-import.js
15170
15662
  function createLazyImport(load) {
15171
15663
  let cached;
@@ -15183,7 +15675,7 @@ function createLazyImport(load) {
15183
15675
  }
15184
15676
 
15185
15677
  // dist/clients/lsp-lazy.js
15186
- var lazyLsp = createLazyImport(() => import("./chunk-VCSRSYLF.js"));
15678
+ var lazyLsp = createLazyImport(() => import("./chunk-RJFED73B.js"));
15187
15679
  function warmLspService() {
15188
15680
  return lazyLsp.get();
15189
15681
  }
@@ -15252,19 +15744,6 @@ function promoteToLocal(reason) {
15252
15744
  record("takeover", state4.cwd ?? process.cwd(), reason, pid);
15253
15745
  }
15254
15746
 
15255
- // dist/clients/lsp/spawn-history.js
15256
- var MAX_SERVER_HISTORIES = 64;
15257
- var successfulSpawnDurationMs = new BoundedFifoMap(MAX_SERVER_HISTORIES);
15258
- function recordSuccessfulLspSpawn(serverId, durationMs) {
15259
- if (!Number.isFinite(durationMs) || durationMs < 0)
15260
- return;
15261
- successfulSpawnDurationMs.delete(serverId);
15262
- successfulSpawnDurationMs.set(serverId, durationMs);
15263
- }
15264
- function getSuccessfulLspSpawnDurationMs(serverId) {
15265
- return successfulSpawnDurationMs.get(serverId);
15266
- }
15267
-
15268
15747
  // dist/clients/lsp/index.js
15269
15748
  function textDocumentSaveOf(client) {
15270
15749
  if (typeof client.getSaveOptions !== "function")
@@ -15425,29 +15904,7 @@ function readTsserverSyncGraceMs() {
15425
15904
  return 300;
15426
15905
  return parsed;
15427
15906
  }
15428
- function readEnvAuxGraceMs() {
15429
- const raw = process.env.PI_LENS_AUX_GRACE_MS;
15430
- if (raw === void 0)
15431
- return void 0;
15432
- const parsed = Number.parseInt(raw, 10);
15433
- if (!Number.isFinite(parsed) || parsed < 0)
15434
- return void 0;
15435
- return parsed;
15436
- }
15437
- var DEFAULT_AUX_GRACE_CEILING_MS = 2e3;
15438
- var MAX_ADAPTIVE_AUX_GRACE_CEILING_MS = 8e3;
15439
15907
  var MAX_TOUCH_CANDIDATES = 8;
15440
- var ADAPTIVE_AUX_GRACE_MARGIN_MS = 500;
15441
- function auxWaitBudgetMs(serverId, isCold, configuredCeilingMs, declaredWaitMs) {
15442
- if (configuredCeilingMs !== void 0 || !isCold) {
15443
- return Math.min(declaredWaitMs, configuredCeilingMs ?? DEFAULT_AUX_GRACE_CEILING_MS);
15444
- }
15445
- const observedSpawnMs = getSuccessfulLspSpawnDurationMs(serverId);
15446
- if (observedSpawnMs === void 0 || observedSpawnMs <= 0) {
15447
- return Math.min(declaredWaitMs, DEFAULT_AUX_GRACE_CEILING_MS);
15448
- }
15449
- return Math.min(MAX_ADAPTIVE_AUX_GRACE_CEILING_MS, Math.max(declaredWaitMs, observedSpawnMs + ADAPTIVE_AUX_GRACE_MARGIN_MS));
15450
- }
15451
15908
  var DIAGNOSTICS_SEMANTIC_SETTLE_THRESHOLD_MS = Math.max(0, Number.parseInt(process.env.PI_LENS_LSP_DIAGNOSTICS_SEMANTIC_THRESHOLD_MS ?? "250", 10) || 250);
15452
15909
  var DIAGNOSTICS_SEMANTIC_SETTLE_WAIT_MS = Math.max(0, Number.parseInt(process.env.PI_LENS_LSP_DIAGNOSTICS_SEMANTIC_SETTLE_MS ?? "400", 10) || 400);
15453
15910
  var EARLY_UNBLOCK_GRACE_MS = Math.max(0, Number.parseInt(process.env.PI_LENS_LSP_EARLY_UNBLOCK_GRACE_MS ?? "400", 10) || 400);
@@ -15476,14 +15933,21 @@ function groupFilesByPrimaryServer(files2) {
15476
15933
  const servers = getServersForFileWithConfig(filePath);
15477
15934
  const primary = servers[0]?.id ?? "none";
15478
15935
  const group = byServer.get(primary);
15936
+ const fallbackFamilyHeads = /* @__PURE__ */ new Map();
15937
+ const familyHeads = servers.map((server) => {
15938
+ const head = server.fallbackFor ? fallbackFamilyHeads.get(server.fallbackFor) ?? server.fallbackFor : server.id;
15939
+ fallbackFamilyHeads.set(server.id, head);
15940
+ return head;
15941
+ });
15942
+ const multiServer = new Set(familyHeads).size > 1;
15479
15943
  if (group) {
15480
15944
  group.files.push(filePath);
15481
- if (servers.length > 1)
15945
+ if (multiServer)
15482
15946
  group.multiServer = true;
15483
15947
  } else {
15484
15948
  byServer.set(primary, {
15485
15949
  files: [filePath],
15486
- multiServer: servers.length > 1
15950
+ multiServer
15487
15951
  });
15488
15952
  }
15489
15953
  }
@@ -15527,7 +15991,7 @@ function notifyWriteBudgetMs() {
15527
15991
  return Number.isFinite(raw) && raw > 0 ? raw : 2e3;
15528
15992
  }
15529
15993
  function primaryServerWaitFloorMs(filePath, maxWaitMs) {
15530
- const serverWaitOverrideMs = getServersForFileWithConfig(filePath).filter((s) => s.role !== "auxiliary").reduce((max, server) => Math.max(max, server.clientWaitTimeoutMs ?? 0), 0);
15994
+ const serverWaitOverrideMs = getServersForFileWithConfig(filePath).filter((s) => !isAuxiliary(s)).reduce((max, server) => Math.max(max, server.clientWaitTimeoutMs ?? 0), 0);
15531
15995
  return Math.max(maxWaitMs ?? 0, serverWaitOverrideMs);
15532
15996
  }
15533
15997
  var NOTIFY_WEDGED_BUDGET_MULTIPLIER = 5;
@@ -15545,15 +16009,6 @@ function notifyStallCpuSampleMs() {
15545
16009
  return Number.isFinite(raw) && raw > 0 ? raw : 1200;
15546
16010
  }
15547
16011
  var NOTIFY_STALL_CPU_BUSY_FLOOR_PERCENT = 10;
15548
- var AUX_NOTIFY_INFLIGHT_DEFAULT = 8;
15549
- function auxNotifyInflightLimit(info) {
15550
- const perServer = info.notifyInflightLimit;
15551
- if (typeof perServer === "number" && Number.isFinite(perServer) && perServer > 0) {
15552
- return Math.floor(perServer);
15553
- }
15554
- const raw = Number(process.env.PI_LENS_LSP_AUX_NOTIFY_INFLIGHT);
15555
- return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : AUX_NOTIFY_INFLIGHT_DEFAULT;
15556
- }
15557
16012
  function workspacePullBudgetMs() {
15558
16013
  const raw = Number(process.env.PI_LENS_LSP_WORKSPACE_PULL_MS);
15559
16014
  return Number.isFinite(raw) && raw > 0 ? raw : 3e4;
@@ -15608,8 +16063,11 @@ async function collectWorkspaceDiagnosticFiles(root, maxFiles = getMaxWorkspaceD
15608
16063
  return files2;
15609
16064
  }
15610
16065
  var LSPService = class _LSPService {
16066
+ static MAX_SIBLING_ANNOUNCEMENTS = 1024;
15611
16067
  sessionCwd;
15612
16068
  state;
16069
+ /** Content hashes already announced to a sibling root in this service. */
16070
+ siblingAnnouncementHashes = new BoundedFifoMap(_LSPService.MAX_SIBLING_ANNOUNCEMENTS);
15613
16071
  workspaceProbeLogged = /* @__PURE__ */ new Set();
15614
16072
  /** Per-service immutable root-boundary verdicts; root detectors cache hits too. */
15615
16073
  projectBoundaryCache = /* @__PURE__ */ new Map();
@@ -15945,6 +16403,38 @@ var LSPService = class _LSPService {
15945
16403
  checkDestroyed() {
15946
16404
  return this.isDestroyed;
15947
16405
  }
16406
+ /**
16407
+ * Keep live same-server clients in sibling package roots current. A
16408
+ * didOpen/didChange only updates the client that owns the touched root, but
16409
+ * TypeScript importers in another package can retain the old module graph.
16410
+ * The notification uses each client's existing #271 watch queue.
16411
+ */
16412
+ announceToSiblings(filePath, content, contentHash2 = this.hashContent(content)) {
16413
+ const normalizedPath = normalizeMapKey(filePath);
16414
+ if (this.siblingAnnouncementHashes.get(normalizedPath) === contentHash2) {
16415
+ return Promise.resolve();
16416
+ }
16417
+ return Promise.all(getServersForFileWithConfig(filePath).map((server) => this.resolveServerRoot(server, filePath).then((root) => {
16418
+ if (!root)
16419
+ return false;
16420
+ const ownKey = `${server.id}:${normalizeMapKey(root)}`;
16421
+ return this.notifyLiveClientsForServer(server.id, filePath, 2, ownKey, false);
16422
+ }))).then(() => {
16423
+ this.siblingAnnouncementHashes.set(normalizedPath, contentHash2);
16424
+ });
16425
+ }
16426
+ notifyLiveClientsForServer(serverId, filePath, type, ownKey, includeOwn) {
16427
+ let notified = false;
16428
+ const prefix = `${serverId}:`;
16429
+ for (const [key, client] of this.state.clients) {
16430
+ if (!key.startsWith(prefix) || !includeOwn && key === ownKey || !client.isAlive()) {
16431
+ continue;
16432
+ }
16433
+ client.notify.watchedFileChange(filePath, type);
16434
+ notified = true;
16435
+ }
16436
+ return notified;
16437
+ }
15948
16438
  async withClientSpawnGate(operation) {
15949
16439
  const previous = this.clientSpawnGate;
15950
16440
  let release;
@@ -16484,7 +16974,7 @@ var LSPService = class _LSPService {
16484
16974
  return false;
16485
16975
  if (record2.gateOpen)
16486
16976
  return false;
16487
- return record2.unacked >= auxNotifyInflightLimit(entry.info);
16977
+ return record2.unacked >= serverTraits(entry.info).notifyInflightLimit;
16488
16978
  }
16489
16979
  /**
16490
16980
  * #1714: hold the next notify until this auxiliary has proven it PROCESSED the
@@ -16495,10 +16985,12 @@ var LSPService = class _LSPService {
16495
16985
  * sends ONE request round-trip (`pingLiveness`, #1277,
16496
16986
  * clients/lsp/client.ts:2577).
16497
16987
  *
16498
- * WHY A REPLY PROVES PROCESSING, measured rather than assumed. ast-grep-lsp is
16499
- * tower-lsp-server and drains its message stream in order on one task, so a
16500
- * request written after N `didOpen`s is answered after those N are scanned.
16501
- * Live probe against the real binary, 30 real repository files:
16988
+ * WHY A REPLY PROVES PROCESSING, measured rather than assumed, and declared
16989
+ * as the `replyOrdering: "single-task-ordered"` trait on the row that was
16990
+ * measured (#1722, #1756 stage 1). ast-grep-lsp is tower-lsp-server and
16991
+ * drains its message stream in order on one task, so a request written after
16992
+ * N `didOpen`s is answered after those N are scanned. Live probe against the
16993
+ * real binary, 30 real repository files:
16502
16994
  *
16503
16995
  * idle `workspace/symbol` reply 0 ms
16504
16996
  * after 30 didOpens 2263 ms, 29 of 30 publishes already in
@@ -16512,13 +17004,19 @@ var LSPService = class _LSPService {
16512
17004
  * immaterial against a ceiling of 4 to 8.
16513
17005
  *
16514
17006
  * A server that answered requests off a SEPARATE task would not give this
16515
- * proof. What keeps that server safe is NOT the fail-open latch below: the
16516
- * latch never arms for it. Such a server answers the barrier instantly,
16517
- * `unacked` resets, and the gate stays inert for the whole sweep. Safety comes
16518
- * from the outcome that inertness produces — the notify sequence is exactly
16519
- * the pre-#1714 one, and #743's write deadline, backpressure streak and wedge
16520
- * timer own the stall the same way they did before this change. The throttle
16521
- * buys such a server nothing; it also costs it nothing.
17007
+ * proof, and one that declares `replyOrdering: "threaded"` is therefore
17008
+ * stepped over below rather than round-tripped. What kept such a server safe
17009
+ * BEFORE the trait existed is NOT the fail-open latch: the latch never arms
17010
+ * for it. It answered the barrier instantly, `unacked` reset, and the gate
17011
+ * stayed inert for the whole sweep. Safety comes from the outcome that
17012
+ * inertness produces — the notify sequence is exactly the pre-#1714 one, and
17013
+ * #743's write deadline, backpressure streak and wedge timer own the stall
17014
+ * the same way they did before this change. The throttle buys such a server
17015
+ * nothing; it also costs it nothing. Declaring the trait reaches that same
17016
+ * end state without spending the round-trip, and `unmeasured` — the default,
17017
+ * and every server that has not been probed — keeps the barrier armed
17018
+ * exactly as before, so an unmeasured server is never granted a pacing
17019
+ * change by omission.
16522
17020
  *
16523
17021
  * The wait is bounded by the CALLER's remaining budget, never by a schedule of
16524
17022
  * its own: every waiter gives the shared round-trip only `waitMs`, so a caller
@@ -16547,10 +17045,11 @@ var LSPService = class _LSPService {
16547
17045
  }
16548
17046
  if (record2.gateOpen)
16549
17047
  return;
16550
- const limit = auxNotifyInflightLimit(entry.info);
17048
+ const traits = serverTraits(entry.info);
17049
+ const limit = traits.notifyInflightLimit;
16551
17050
  if (record2.unacked < limit)
16552
17051
  return;
16553
- const ping = entry.client.pingLiveness;
17052
+ const ping = traits.replyOrdering === "threaded" ? void 0 : entry.client.pingLiveness;
16554
17053
  if (!ping) {
16555
17054
  record2.unacked = 0;
16556
17055
  return;
@@ -16806,7 +17305,7 @@ var LSPService = class _LSPService {
16806
17305
  const attached = new Set(spawned.map((entry) => entry.info.id));
16807
17306
  const skipped = [];
16808
17307
  for (const server of getServersForFileWithConfig(filePath)) {
16809
- if (server.role !== "auxiliary")
17308
+ if (!isAuxiliary(server))
16810
17309
  continue;
16811
17310
  if (attached.has(server.id))
16812
17311
  continue;
@@ -16884,7 +17383,7 @@ var LSPService = class _LSPService {
16884
17383
  async getClientForFile(filePath, maxWaitMs, hardCapMs, resolvedRoots, waitSkipReasons) {
16885
17384
  if (this.checkDestroyed())
16886
17385
  return void 0;
16887
- const servers = getServersForFileWithConfig(filePath).filter((s) => s.role !== "auxiliary");
17386
+ const servers = getServersForFileWithConfig(filePath).filter((s) => !isAuxiliary(s));
16888
17387
  const rootMemo = /* @__PURE__ */ new Map();
16889
17388
  const partitionCandidates = async () => {
16890
17389
  const resolved2 = await Promise.all(servers.map(async (server) => {
@@ -17107,7 +17606,7 @@ var LSPService = class _LSPService {
17107
17606
  root: await this.resolveServerRoot(server, filePath)
17108
17607
  })))).filter((entry) => entry.root !== void 0);
17109
17608
  for (const { server, root } of rootedServers)
17110
- if (server.role === "auxiliary")
17609
+ if (isAuxiliary(server))
17111
17610
  auxiliaryRoots?.set(server.id, normalizeMapKey(root));
17112
17611
  let serverCountAttempted = 0;
17113
17612
  const acquisitions = /* @__PURE__ */ new Map();
@@ -17132,7 +17631,7 @@ var LSPService = class _LSPService {
17132
17631
  async getAuxiliaryClientsForFile(filePath, enabledIds, onOutcome, maxWaitMs, signal, hook) {
17133
17632
  if (this.checkDestroyed() || enabledIds.size === 0)
17134
17633
  return [];
17135
- const servers = getServersForFileWithConfig(filePath).filter((s) => s.role === "auxiliary" && enabledIds.has(s.id));
17634
+ const servers = getServersForFileWithConfig(filePath).filter((s) => isAuxiliary(s) && enabledIds.has(s.id));
17136
17635
  if (servers.length === 0)
17137
17636
  return [];
17138
17637
  const rootMemo = /* @__PURE__ */ new Map();
@@ -17285,11 +17784,7 @@ var LSPService = class _LSPService {
17285
17784
  const root = await this.resolveServerRoot(server, filePath);
17286
17785
  if (!root)
17287
17786
  continue;
17288
- const key = `${server.id}:${normalizeMapKey(root)}`;
17289
- const existing = this.state.clients.get(key);
17290
- if (existing?.isAlive()) {
17291
- existing.notify.watchedFileChange(filePath, type);
17292
- }
17787
+ this.notifyLiveClientsForServer(server.id, filePath, type, `${server.id}:${normalizeMapKey(root)}`, true);
17293
17788
  }
17294
17789
  }
17295
17790
  /**
@@ -17524,7 +18019,7 @@ var LSPService = class _LSPService {
17524
18019
  const root = await this.resolveServerRootOnce(server, filePath, rootMemo);
17525
18020
  if (!root || this.checkDestroyed())
17526
18021
  return void 0;
17527
- if (server.role !== "auxiliary") {
18022
+ if (!isAuxiliary(server)) {
17528
18023
  resolvedRoots?.set(server.id, normalizeMapKey(root));
17529
18024
  }
17530
18025
  const allowInstall = this.shouldAllowInstall(server.id);
@@ -17875,6 +18370,16 @@ var LSPService = class _LSPService {
17875
18370
  });
17876
18371
  return;
17877
18372
  }
18373
+ const contentHash2 = this.hashContent(content);
18374
+ if (typeof content === "string") {
18375
+ void this.announceToSiblings(filePath, content, contentHash2).catch((cause) => {
18376
+ incrementDegradationCount({
18377
+ kind: "lsp-sibling-announcement",
18378
+ subject: normalizeMapKey(filePath),
18379
+ reason: `sibling notification failed: ${String(cause)}`
18380
+ });
18381
+ });
18382
+ }
17878
18383
  const startedAt = Date.now();
17879
18384
  let fingerprintMemo;
17880
18385
  const contentFingerprint = () => fingerprintMemo ??= fingerprintDocumentContent(content);
@@ -17960,7 +18465,7 @@ var LSPService = class _LSPService {
17960
18465
  const leaseKeys = await this.acquireClientLeases(spawned, filePath);
17961
18466
  if (!leaseKeys) {
17962
18467
  if (this.checkDestroyed()) {
17963
- const primaryServerIds = spawned.filter((entry) => entry.info.role !== "auxiliary").map((entry) => entry.info.id);
18468
+ const primaryServerIds = spawned.filter((entry) => !isAuxiliary(entry.info)).map((entry) => entry.info.id);
17964
18469
  return {
17965
18470
  diags: [],
17966
18471
  inconclusive: true,
@@ -17998,10 +18503,10 @@ var LSPService = class _LSPService {
17998
18503
  const readPathVersion = (client) => client.getDiagnosticsVersionForPath?.(filePath);
17999
18504
  const diagnosticBaselines = new Map(spawned.map((entry) => [entry.client, readPathVersion(entry.client)]));
18000
18505
  const markedAtMs = Date.now();
18001
- const touchContentHash = this.hashContent(content);
18506
+ const touchContentHash = contentHash2;
18002
18507
  const bindingMatchesTouchContent = (binding2) => binding2?.contentHash === touchContentHash;
18003
18508
  const carriedAuxiliary = options.collectDiagnostics ? spawned.flatMap((entry) => {
18004
- if (entry.info.role !== "auxiliary")
18509
+ if (!isAuxiliary(entry.info))
18005
18510
  return [];
18006
18511
  const binding2 = entry.client.getDiagnosticBinding?.(filePath);
18007
18512
  if (!bindingMatchesTouchContent(binding2))
@@ -18009,11 +18514,11 @@ var LSPService = class _LSPService {
18009
18514
  const diags = entry.client.getDiagnostics(filePath);
18010
18515
  return diags.length > 0 ? [{ serverId: entry.info.id, diags, binding: binding2 }] : [];
18011
18516
  }) : [];
18012
- const auxPublishedThisContent = new Set(spawned.flatMap((entry) => entry.info.role === "auxiliary" && bindingMatchesTouchContent(entry.client.getDiagnosticBinding?.(filePath)) ? [entry.info.id] : []));
18517
+ const auxPublishedThisContent = new Set(spawned.flatMap((entry) => isAuxiliary(entry.info) && bindingMatchesTouchContent(entry.client.getDiagnosticBinding?.(filePath)) ? [entry.info.id] : []));
18013
18518
  const spawnedByServerId = new Map(spawned.map((entry) => [entry.info.id, entry]));
18014
18519
  const auxCoversThisContent = (serverId) => {
18015
18520
  const entry = spawnedByServerId.get(serverId);
18016
- if (entry?.info.role !== "auxiliary")
18521
+ if (!entry || !isAuxiliary(entry.info))
18017
18522
  return false;
18018
18523
  return auxPublishedThisContent.has(serverId) || bindingMatchesTouchContent(entry.client.getDiagnosticBinding?.(filePath));
18019
18524
  };
@@ -18027,7 +18532,7 @@ var LSPService = class _LSPService {
18027
18532
  if (notifySkippedServerIds.has(entry.info.id))
18028
18533
  return;
18029
18534
  const clientKey = await this.demonstratedReadyKeyFor(entry.info, filePath);
18030
- const gated = entry.info.role === "auxiliary" && clientKey !== void 0;
18535
+ const gated = isAuxiliary(entry.info) && clientKey !== void 0;
18031
18536
  let slot;
18032
18537
  if (gated && clientKey) {
18033
18538
  const barrierStartedAt = Date.now();
@@ -18115,7 +18620,7 @@ var LSPService = class _LSPService {
18115
18620
  }
18116
18621
  }
18117
18622
  const notifyWriteTimedOut = notifyWriteTimedOutServerIds.length > 0;
18118
- const primaryEntries = spawned.filter((entry) => entry.info.role !== "auxiliary");
18623
+ const primaryEntries = spawned.filter((entry) => !isAuxiliary(entry.info));
18119
18624
  const primaryServerIds = new Set(primaryEntries.map((e) => e.info.id));
18120
18625
  const primaryNotifyWriteTimedOutServerIds = notifyWriteTimedOutServerIds.filter((id) => primaryServerIds.has(id));
18121
18626
  const primaryNotifyWriteTimedOut = primaryNotifyWriteTimedOutServerIds.length > 0;
@@ -18212,7 +18717,7 @@ var LSPService = class _LSPService {
18212
18717
  }
18213
18718
  }
18214
18719
  const waitStartedAt = Date.now();
18215
- const hasTouchAuxiliaries = clientScope === "with-auxiliary" && options.collectDiagnostics === true && spawned.some((e) => e.info.role === "auxiliary");
18720
+ const hasTouchAuxiliaries = clientScope === "with-auxiliary" && options.collectDiagnostics === true && spawned.some((e) => isAuxiliary(e.info));
18216
18721
  if (spawned.some((entry) => entry.info.custom === true)) {
18217
18722
  try {
18218
18723
  const snapshotHook = options.hook ?? "tool_result_edit";
@@ -18242,7 +18747,7 @@ var LSPService = class _LSPService {
18242
18747
  const perServerWaitTimeouts = spawned.map((entry, entryIndex) => {
18243
18748
  const declaredServerTimeout = perServerDeclaredTimeouts[entryIndex];
18244
18749
  const observedSpawnMs = getSuccessfulLspSpawnDurationMs(entry.client.serverId);
18245
- return hasTouchAuxiliaries && entry.info.role === "auxiliary" && coldAuxiliaryServerIds.has(entry.client.serverId) && (configuredAuxCeilingMs !== void 0 || observedSpawnMs !== void 0 && observedSpawnMs > 0) ? auxWaitBudgetMs(entry.client.serverId, true, configuredAuxCeilingMs, declaredServerTimeout) : perServerDeclaredTimeouts[entryIndex];
18750
+ return hasTouchAuxiliaries && isAuxiliary(entry.info) && coldAuxiliaryServerIds.has(entry.client.serverId) && (configuredAuxCeilingMs !== void 0 || observedSpawnMs !== void 0 && observedSpawnMs > 0) ? auxWaitBudgetMs(entry.client.serverId, true, configuredAuxCeilingMs, declaredServerTimeout) : perServerDeclaredTimeouts[entryIndex];
18246
18751
  });
18247
18752
  const firstContactCutOffServerIds = /* @__PURE__ */ new Set();
18248
18753
  const firstContactErroredServerIds = /* @__PURE__ */ new Set();
@@ -18263,10 +18768,10 @@ var LSPService = class _LSPService {
18263
18768
  }
18264
18769
  };
18265
18770
  const perServerRaceBudgets = spawned.map((entry, entryIndex) => {
18266
- return hasTouchAuxiliaries && entry.info.role === "auxiliary" ? auxWaitBudgetMs(entry.client.serverId, coldAuxiliaryServerIds.has(entry.client.serverId), configuredAuxCeilingMs, perServerDeclaredTimeouts[entryIndex]) : perServerDeclaredTimeouts[entryIndex];
18771
+ return hasTouchAuxiliaries && isAuxiliary(entry.info) ? auxWaitBudgetMs(entry.client.serverId, coldAuxiliaryServerIds.has(entry.client.serverId), configuredAuxCeilingMs, perServerDeclaredTimeouts[entryIndex]) : perServerDeclaredTimeouts[entryIndex];
18267
18772
  });
18268
18773
  const perServerWaits = spawned.map((entry, entryIndex) => {
18269
- if (hasTouchAuxiliaries && entry.info.role === "auxiliary" && this.isAuxiliaryWaitDemoted(entry.info.id, entry.client.root ?? filePath)) {
18774
+ if (hasTouchAuxiliaries && isAuxiliary(entry.info) && this.isAuxiliaryWaitDemoted(entry.info.id, entry.client.root ?? filePath)) {
18270
18775
  return Promise.resolve(void 0);
18271
18776
  }
18272
18777
  if (this.state.diagnosticsUnsupported.has(entry.info.id) || diagnosticsUnsupportedServerIds.includes(entry.info.id)) {
@@ -18349,8 +18854,8 @@ var LSPService = class _LSPService {
18349
18854
  });
18350
18855
  let pushWaitSettled = false;
18351
18856
  const pushWait = hasTouchAuxiliaries ? (() => {
18352
- const primaryWaits = perServerWaits.filter((_, i) => spawned[i].info.role !== "auxiliary");
18353
- const auxWaits = perServerWaits.map((p, i) => spawned[i].info.role === "auxiliary" ? {
18857
+ const primaryWaits = perServerWaits.filter((_, i) => !isAuxiliary(spawned[i].info));
18858
+ const auxWaits = perServerWaits.map((p, i) => isAuxiliary(spawned[i].info) ? {
18354
18859
  promise: p,
18355
18860
  serverId: spawned[i].info.id,
18356
18861
  client: spawned[i].client,
@@ -18532,7 +19037,7 @@ var LSPService = class _LSPService {
18532
19037
  ...new Set(diagnosticsUnsupportedServerIds)
18533
19038
  ];
18534
19039
  if (!hasTouchAuxiliaries && options.collectDiagnostics === true) {
18535
- const auxEntries = spawned.filter((entry) => entry.info.role === "auxiliary");
19040
+ const auxEntries = spawned.filter((entry) => isAuxiliary(entry.info));
18536
19041
  if (auxEntries.length > 0) {
18537
19042
  const outcomes = auxEntries.map((entry) => {
18538
19043
  const baseline = diagnosticBaselines.get(entry.client);
@@ -18587,9 +19092,9 @@ var LSPService = class _LSPService {
18587
19092
  const waited = spawned.filter((entry) => !deferredResyncServerIds.has(entry.info.id) && !diagnosticsUnsupportedServerIds.includes(entry.info.id));
18588
19093
  const unanswered = waited.filter((entry) => !answeredForThisTouch(entry));
18589
19094
  diagnosticsUnansweredServerIds = unanswered.map((e) => e.info.id);
18590
- diagnosticsUnansweredPrimaryServerIds = unanswered.filter((entry) => entry.info.role !== "auxiliary").map((e) => e.info.id);
18591
- const hasWaitedPrimary = waited.some((entry) => entry.info.role !== "auxiliary");
18592
- const hasUnsupportedPrimary = diagnosticsUnsupportedServerIds.some((serverId) => spawned.some((entry) => entry.info.id === serverId && entry.info.role !== "auxiliary"));
19095
+ diagnosticsUnansweredPrimaryServerIds = unanswered.filter((entry) => !isAuxiliary(entry.info)).map((e) => e.info.id);
19096
+ const hasWaitedPrimary = waited.some((entry) => !isAuxiliary(entry.info));
19097
+ const hasUnsupportedPrimary = diagnosticsUnsupportedServerIds.some((serverId) => spawned.some((entry) => entry.info.id === serverId && !isAuxiliary(entry.info)));
18593
19098
  diagnosticsTimedOut = !hasWaitedPrimary && !hasUnsupportedPrimary || diagnosticsUnansweredPrimaryServerIds.length > 0;
18594
19099
  for (const entry of unanswered) {
18595
19100
  incrementDegradationCount({
@@ -18662,7 +19167,7 @@ var LSPService = class _LSPService {
18662
19167
  }
18663
19168
  const auxCoveredAtMerge = new Set(spawned.filter((entry) => auxCoversThisContent(entry.info.id)).map((entry) => entry.info.id));
18664
19169
  const uncoveredDeferredServerIds = notifyDeferredServerIds.filter((serverId) => !auxCoveredAtMerge.has(serverId));
18665
- const staleWriteAuxiliaryServerIds = spawned.filter((entry) => entry.info.role === "auxiliary" && notifyWriteTimedOutServerIds.includes(entry.info.id) && !auxCoveredAtMerge.has(entry.info.id)).map((entry) => entry.info.id);
19170
+ const staleWriteAuxiliaryServerIds = spawned.filter((entry) => isAuxiliary(entry.info) && notifyWriteTimedOutServerIds.includes(entry.info.id) && !auxCoveredAtMerge.has(entry.info.id)).map((entry) => entry.info.id);
18666
19171
  const droppedAuxiliaryServerIds = /* @__PURE__ */ new Set([
18667
19172
  ...uncoveredDeferredServerIds,
18668
19173
  ...staleWriteAuxiliaryServerIds
@@ -18753,7 +19258,7 @@ var LSPService = class _LSPService {
18753
19258
  });
18754
19259
  const inconclusive = verdict.inconclusive;
18755
19260
  const brokenSkippedServerIds = collected !== void 0 ? await this.brokenSkippedAuxiliaryServerIds(filePath, clientScope, options, spawned) : [];
18756
- const auxNoAnswerServerIds = spawned.filter((entry) => entry.info.role === "auxiliary" && (diagnosticsUnansweredServerIds.includes(entry.info.id) || notifyWriteTimedOutServerIds.includes(entry.info.id)) && !auxCoveredAtMerge.has(entry.info.id)).map((entry) => entry.info.id);
19261
+ const auxNoAnswerServerIds = spawned.filter((entry) => isAuxiliary(entry.info) && (diagnosticsUnansweredServerIds.includes(entry.info.id) || notifyWriteTimedOutServerIds.includes(entry.info.id)) && !auxCoveredAtMerge.has(entry.info.id)).map((entry) => entry.info.id);
18757
19262
  const unconfirmedServerIds = [
18758
19263
  .../* @__PURE__ */ new Set([
18759
19264
  ...auxUnconfirmedServerIds ?? [],
@@ -18823,7 +19328,7 @@ var LSPService = class _LSPService {
18823
19328
  }
18824
19329
  }
18825
19330
  const result = { diags: collected ?? [] };
18826
- const primaryDiagnosticsUnsupported = diagnosticsUnsupportedServerIds.some((serverId) => spawned.some((entry) => entry.info.id === serverId && entry.info.role !== "auxiliary"));
19331
+ const primaryDiagnosticsUnsupported = diagnosticsUnsupportedServerIds.some((serverId) => spawned.some((entry) => entry.info.id === serverId && !isAuxiliary(entry.info)));
18827
19332
  if (diagnosticsUnsupportedServerIds.length > 0) {
18828
19333
  result.diagnosticsUnsupportedServerIds = diagnosticsUnsupportedServerIds;
18829
19334
  }
@@ -19122,10 +19627,10 @@ var LSPService = class _LSPService {
19122
19627
  });
19123
19628
  const graceMs = diagnosticsMode === "document" ? 0 : EARLY_UNBLOCK_GRACE_MS;
19124
19629
  const diagDescriptors = spawned.map((entry) => {
19125
- if (entry.info.role !== "auxiliary")
19126
- return { role: "primary" };
19630
+ if (!isAuxiliary(entry.info))
19631
+ return { role: entry.info.role };
19127
19632
  const strategy = getStrategy(entry.info.id, entry.client.getLaunchVariant?.());
19128
- return { role: "auxiliary", budgetMs: strategy.aggregateWaitMs };
19633
+ return { role: entry.info.role, budgetMs: strategy.aggregateWaitMs };
19129
19634
  });
19130
19635
  const perServer = await raceToCompletion(clientWaits, (results) => results.some((r) => r.diagnosticCount > 0 || getStrategy(r.serverId, r.launchVariant).seedFirstPush), {
19131
19636
  timeoutMs: Math.max(...spawned.map((entry) => getStrategy(entry.info.id, entry.client.getLaunchVariant?.()).aggregateWaitMs)),
@@ -19133,8 +19638,9 @@ var LSPService = class _LSPService {
19133
19638
  descriptors: diagDescriptors,
19134
19639
  // #1458 S2: ceiling, not a flat wait — each auxiliary's own
19135
19640
  // budgetMs (above) determines the actual per-touch grace up to
19136
- // this cap. Matches touchFile's `readEnvAuxGraceMs() ?? 2000`.
19137
- auxGraceMs: readEnvAuxGraceMs() ?? 2e3
19641
+ // this cap. Matches touchFile's ceiling, and names the shared
19642
+ // constant instead of repeating its value (#1488).
19643
+ auxGraceMs: readEnvAuxGraceMs() ?? DEFAULT_AUX_GRACE_CEILING_MS
19138
19644
  });
19139
19645
  const earlyUnblockedCount = spawned.length - perServer.length;
19140
19646
  const perServerFull = spawned.map((entry) => {
@@ -19341,21 +19847,22 @@ var LSPService = class _LSPService {
19341
19847
  * a polyglot workspace used to win every no-filePath query outright (#1812
19342
19848
  * — a supporting primary server spawned later never got a look-in). Scans
19343
19849
  * for the first LIVE client matching `predicate` (when given), preferring
19344
- * any primary (non-`"auxiliary"` role) match over an auxiliary one — the
19345
- * same primary-over-auxiliary preference `getClientForFile` encodes via
19346
- * its `role !== "auxiliary"` filter (this file, `getClientForFile`) and
19850
+ * any primary (non-auxiliary) match over an auxiliary one — the same
19851
+ * primary-over-auxiliary preference `getClientForFile` encodes via its
19852
+ * `!isAuxiliary` filter (this file, `getClientForFile`) and
19347
19853
  * `getAliveServerIds` groups by. `isAlive()` is required for BOTH the
19348
19854
  * preferred and fallback candidate — mirrors `getCapabilitySnapshots`'s
19349
19855
  * own no-filePath branch (this file, ~line 5868), whose liveness filter
19350
19856
  * this helper otherwise duplicates; without it a dead primary would win
19351
19857
  * over a live, answering auxiliary. Role is read from the map key's
19352
- * `serverId` prefix against `LSP_SERVERS`, the same single source of
19353
- * truth `getCapabilitySnapshots` already parses that key from — never a
19354
- * second, hand-rolled role table. A `serverId` prefix absent from
19355
- * `LSP_SERVERS` (should not happen in practice — every spawned client's
19356
- * key is built from a known server's `id`) resolves to `role === undefined`,
19357
- * which falls through to the primary branch: unknown treated as primary,
19358
- * never silently dropped. Returns undefined only when NO live client
19858
+ * `serverId` prefix through `getServerById` against `LSP_SERVERS`, the
19859
+ * same single source of truth `getCapabilitySnapshots` already parses that
19860
+ * key from — never a second, hand-rolled role table. A `serverId` prefix
19861
+ * absent from `LSP_SERVERS` (should not happen in practice — every spawned
19862
+ * client's key is built from a known server's `id`) resolves to no server,
19863
+ * which `isAuxiliary` reads as `DEFAULT_LSP_SERVER_ROLE` and so falls
19864
+ * through to the primary branch: unknown treated as primary, never
19865
+ * silently dropped. Returns undefined only when NO live client
19359
19866
  * (primary or auxiliary) matches.
19360
19867
  */
19361
19868
  selectWorkspaceScopeClient(predicate) {
@@ -19367,8 +19874,7 @@ var LSPService = class _LSPService {
19367
19874
  continue;
19368
19875
  const separator = key.indexOf(":");
19369
19876
  const serverId = separator >= 0 ? key.slice(0, separator) : key;
19370
- const role = LSP_SERVERS.find((s) => s.id === serverId)?.role;
19371
- if (role === "auxiliary") {
19877
+ if (isAuxiliary(getServerById(serverId))) {
19372
19878
  if (!auxFallback)
19373
19879
  auxFallback = { client, serverId };
19374
19880
  continue;
@@ -19995,7 +20501,7 @@ var LSPService = class _LSPService {
19995
20501
  if (this.checkDestroyed() || options.signal?.aborted) {
19996
20502
  return { performedWarmup: false, failedServerIds: [] };
19997
20503
  }
19998
- const servers = getServersForFileWithConfig(representativeFile).filter((s) => s.role !== "auxiliary");
20504
+ const servers = getServersForFileWithConfig(representativeFile).filter((s) => !isAuxiliary(s));
19999
20505
  if (servers.length === 0) {
20000
20506
  return { performedWarmup: false, failedServerIds: [] };
20001
20507
  }
@@ -20324,7 +20830,7 @@ var LSPService = class _LSPService {
20324
20830
  const { clients } = await this.getClientsForFile(filePath, WORKSPACE_SWEEP_EXCLUDED_SERVER_IDS);
20325
20831
  for (const entry of clients) {
20326
20832
  let auxKey;
20327
- if (entry.info.role === "auxiliary") {
20833
+ if (isAuxiliary(entry.info)) {
20328
20834
  auxKey = await this.demonstratedReadyKeyFor(entry.info, filePath);
20329
20835
  if (auxKey && this.auxNotifyBacklogAtCeiling(auxKey, entry)) {
20330
20836
  continue;
@@ -20760,7 +21266,7 @@ var LSPService = class _LSPService {
20760
21266
  * but slow/wedged" (#1766) — a cold spawn still in flight is not a verdict
20761
21267
  * on a server that doesn't exist yet.
20762
21268
  *
20763
- * Mirrors the `role !== "auxiliary"` filter getClientForFile applies at
21269
+ * Mirrors the `!isAuxiliary` filter getClientForFile applies at
20764
21270
  * :2146-2148 (kept coupled to that line intentionally): auxiliary servers
20765
21271
  * (opengrep, typos, …) spawn routinely and concurrently with an ALREADY
20766
21272
  * ALIVE primary (dispatch/runners/lsp.ts's with-auxiliary path fires one
@@ -20803,7 +21309,7 @@ var LSPService = class _LSPService {
20803
21309
  describeTouchCandidates(filePath, resolvedRoots, auxiliaryRoots = /* @__PURE__ */ new Map()) {
20804
21310
  const candidates = [];
20805
21311
  for (const server of getServersForFileWithConfig(filePath)) {
20806
- const auxiliary = server.role === "auxiliary";
21312
+ const auxiliary = isAuxiliary(server);
20807
21313
  if (auxiliary && !auxiliaryRoots.has(server.id))
20808
21314
  continue;
20809
21315
  if (candidates.length >= MAX_TOUCH_CANDIDATES)
@@ -21011,8 +21517,7 @@ var LSPService = class _LSPService {
21011
21517
  if (seen.has(id))
21012
21518
  continue;
21013
21519
  seen.add(id);
21014
- const role = LSP_SERVERS.find((s) => s.id === id)?.role;
21015
- (role === "auxiliary" ? aux : primary).push(id);
21520
+ (isAuxiliary(getServerById(id)) ? aux : primary).push(id);
21016
21521
  }
21017
21522
  return [...primary, ...aux];
21018
21523
  }
@@ -21201,7 +21706,6 @@ export {
21201
21706
  getLspIdleEvictMs,
21202
21707
  getLspIdleEvictMsForRoot,
21203
21708
  getLspClientCeiling,
21204
- auxWaitBudgetMs,
21205
21709
  groupFilesByPrimaryServer,
21206
21710
  createSweepIndexGate,
21207
21711
  runPerServerGroups,