token-goat 2.9.30 → 2.9.31

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 (172) hide show
  1. package/README.md +24 -7
  2. package/SECURITY.md +10 -2
  3. package/THIRD_PARTY_NOTICES.md +1 -1
  4. package/dist/native/linux-arm64/tg-hook +0 -0
  5. package/dist/native/linux-x64/tg-hook +0 -0
  6. package/dist/native/win32-arm64/tg-hook.exe +0 -0
  7. package/dist/native/win32-x64/tg-hook.exe +0 -0
  8. package/dist/{token-goat-chunk-QSBRTGM2.mjs → token-goat-chunk-25TRAWET.mjs} +17 -7
  9. package/dist/token-goat-chunk-2KXO6V2B.mjs +203 -0
  10. package/dist/{token-goat-chunk-IVKIZWAB.mjs → token-goat-chunk-2M7E4PF7.mjs} +41 -12
  11. package/dist/{token-goat-chunk-SMVRN2HU.mjs → token-goat-chunk-2UPZGKKY.mjs} +2 -2
  12. package/dist/token-goat-chunk-2WLPWKHV.mjs +135 -0
  13. package/dist/token-goat-chunk-2XBMMDBD.mjs +126 -0
  14. package/dist/token-goat-chunk-32ZOY4N5.mjs +23 -0
  15. package/dist/token-goat-chunk-3EZRNKTK.mjs +188 -0
  16. package/dist/token-goat-chunk-4ADZHI7J.mjs +63 -0
  17. package/dist/token-goat-chunk-4GBVPH3D.mjs +242 -0
  18. package/dist/{token-goat-chunk-KWPNJBDH.mjs → token-goat-chunk-4GILLLWY.mjs} +7 -4
  19. package/dist/{token-goat-chunk-67TQJ76B.mjs → token-goat-chunk-4NAPX7BA.mjs} +36 -3
  20. package/dist/{token-goat-chunk-PU7V3FD6.mjs → token-goat-chunk-4SC7UJYV.mjs} +3354 -2553
  21. package/dist/token-goat-chunk-4SMRO4TO.mjs +366 -0
  22. package/dist/{token-goat-chunk-AFLYLHDH.mjs → token-goat-chunk-4ZBFWLXF.mjs} +283 -427
  23. package/dist/{token-goat-chunk-JDWCRD6M.mjs → token-goat-chunk-57BIAXJL.mjs} +73 -40
  24. package/dist/token-goat-chunk-5AC66HNP.mjs +115 -0
  25. package/dist/token-goat-chunk-5IMLKSOP.mjs +145 -0
  26. package/dist/token-goat-chunk-646E6U44.mjs +398 -0
  27. package/dist/token-goat-chunk-65N4RHP4.mjs +33 -0
  28. package/dist/{token-goat-chunk-JN4XMLSD.mjs → token-goat-chunk-6DG6HWP3.mjs} +8 -2
  29. package/dist/token-goat-chunk-6ENQSNRZ.mjs +573 -0
  30. package/dist/token-goat-chunk-6ZY4GNF2.mjs +91 -0
  31. package/dist/{token-goat-chunk-VPBBTSJR.mjs → token-goat-chunk-76FGDLRO.mjs} +262 -255
  32. package/dist/{token-goat-chunk-7IN2MKDS.mjs → token-goat-chunk-7EZTZOMC.mjs} +121 -289
  33. package/dist/{token-goat-chunk-OJBNYTJP.mjs → token-goat-chunk-7MAOA6X4.mjs} +640 -80
  34. package/dist/token-goat-chunk-7R2ZVVAX.mjs +218 -0
  35. package/dist/{token-goat-chunk-PD5VKH46.mjs → token-goat-chunk-AUNGYYQX.mjs} +2 -2
  36. package/dist/{token-goat-chunk-HG45AF2B.mjs → token-goat-chunk-AUQYRMMH.mjs} +794 -37
  37. package/dist/token-goat-chunk-B3PEKBYG.mjs +128 -0
  38. package/dist/{token-goat-chunk-A3CK6N5Z.mjs → token-goat-chunk-BA4HI6BW.mjs} +1309 -110
  39. package/dist/{token-goat-chunk-JB6QN6VF.mjs → token-goat-chunk-BFCWY73P.mjs} +59 -23
  40. package/dist/{token-goat-chunk-B45VMIIH.mjs → token-goat-chunk-BWGFUZZE.mjs} +7 -5
  41. package/dist/{token-goat-chunk-P4QMM4WK.mjs → token-goat-chunk-C75NA7PL.mjs} +50 -23
  42. package/dist/token-goat-chunk-CBIQ7WTD.mjs +87 -0
  43. package/dist/token-goat-chunk-CETM74KC.mjs +307 -0
  44. package/dist/{token-goat-chunk-H2PHHGM5.mjs → token-goat-chunk-CIMQ2BB3.mjs} +57 -12
  45. package/dist/{token-goat-chunk-N2NMER56.mjs → token-goat-chunk-CJLS3CDL.mjs} +46 -4
  46. package/dist/{token-goat-chunk-VES7EL7O.mjs → token-goat-chunk-CO3UYX6G.mjs} +1 -1
  47. package/dist/token-goat-chunk-COPNXRJM.mjs +154 -0
  48. package/dist/token-goat-chunk-CSJ5L3NL.mjs +479 -0
  49. package/dist/{token-goat-chunk-4LCCNRTG.mjs → token-goat-chunk-CVCEVUGK.mjs} +8 -6
  50. package/dist/{token-goat-chunk-B2RZZCSS.mjs → token-goat-chunk-CXHOFN7R.mjs} +37 -20
  51. package/dist/token-goat-chunk-D735C4QM.mjs +603 -0
  52. package/dist/{token-goat-chunk-APGZXSPS.mjs → token-goat-chunk-DGS2LVNA.mjs} +47 -30
  53. package/dist/{token-goat-chunk-54K3EUV3.mjs → token-goat-chunk-DOWRSMWO.mjs} +1 -1
  54. package/dist/token-goat-chunk-E45INMY3.mjs +71 -0
  55. package/dist/token-goat-chunk-EC5F37F4.mjs +244 -0
  56. package/dist/{token-goat-chunk-XTQAOTSO.mjs → token-goat-chunk-EDBDRVH5.mjs} +4 -1
  57. package/dist/{token-goat-chunk-5BCIJXMO.mjs → token-goat-chunk-EELTBKDM.mjs} +14 -10
  58. package/dist/{token-goat-chunk-W3QCJS6G.mjs → token-goat-chunk-F4HYL64R.mjs} +8 -6
  59. package/dist/token-goat-chunk-F6PAUIEK.mjs +624 -0
  60. package/dist/token-goat-chunk-FDSREWLX.mjs +692 -0
  61. package/dist/token-goat-chunk-GGGG7RWB.mjs +236 -0
  62. package/dist/{token-goat-chunk-C63JQV5K.mjs → token-goat-chunk-GL3ETKSC.mjs} +233 -15
  63. package/dist/token-goat-chunk-GVB2MUST.mjs +1298 -0
  64. package/dist/token-goat-chunk-GWFLAUMQ.mjs +307 -0
  65. package/dist/{token-goat-chunk-ZEJVMUQH.mjs → token-goat-chunk-HLPW63A7.mjs} +2 -2
  66. package/dist/{token-goat-chunk-GOLEOXZP.mjs → token-goat-chunk-HU4TORC5.mjs} +44 -26
  67. package/dist/{token-goat-chunk-Z57YODA2.mjs → token-goat-chunk-I7VKZZAW.mjs} +2 -2
  68. package/dist/{token-goat-chunk-NYKZLXMK.mjs → token-goat-chunk-IA6XSPE3.mjs} +6 -6
  69. package/dist/{token-goat-chunk-E3OQX2XT.mjs → token-goat-chunk-IAGXMHIK.mjs} +81 -42
  70. package/dist/{token-goat-chunk-HYBFA5X3.mjs → token-goat-chunk-IK2DRMFX.mjs} +24 -68
  71. package/dist/{token-goat-chunk-COBQGL5S.mjs → token-goat-chunk-INNPTRUX.mjs} +249 -299
  72. package/dist/{token-goat-chunk-7NADEDKJ.mjs → token-goat-chunk-JJ5BX4F4.mjs} +6 -1
  73. package/dist/{token-goat-chunk-PEPTHRFF.mjs → token-goat-chunk-JVFSHVFJ.mjs} +6 -6
  74. package/dist/token-goat-chunk-KGEFFSHD.mjs +50 -0
  75. package/dist/token-goat-chunk-KQBLRPBN.mjs +433 -0
  76. package/dist/{token-goat-chunk-Q5VTGRH7.mjs → token-goat-chunk-LCNQ2CNN.mjs} +55 -319
  77. package/dist/token-goat-chunk-M3LFJDPU.mjs +58 -0
  78. package/dist/token-goat-chunk-M52KPT3C.mjs +87 -0
  79. package/dist/token-goat-chunk-MBEG42H7.mjs +150 -0
  80. package/dist/{token-goat-chunk-EVC4TOLE.mjs → token-goat-chunk-METECDOH.mjs} +3 -12
  81. package/dist/{token-goat-chunk-T4FK7VOV.mjs → token-goat-chunk-MHX7XCC3.mjs} +2 -1
  82. package/dist/token-goat-chunk-N5LSOVSU.mjs +137 -0
  83. package/dist/{token-goat-chunk-LOBGHXPB.mjs → token-goat-chunk-N5MMR2IW.mjs} +1 -1
  84. package/dist/token-goat-chunk-N7B3JQ2C.mjs +160 -0
  85. package/dist/{token-goat-chunk-RHXKCZX3.mjs → token-goat-chunk-NISNMD7T.mjs} +2182 -2959
  86. package/dist/token-goat-chunk-NMD42SFQ.mjs +434 -0
  87. package/dist/token-goat-chunk-NOSS5VWY.mjs +174 -0
  88. package/dist/{token-goat-chunk-KQNZDCBD.mjs → token-goat-chunk-NSNSPPBG.mjs} +25 -3
  89. package/dist/token-goat-chunk-OBOFIHKN.mjs +38 -0
  90. package/dist/token-goat-chunk-OBRFJ5O5.mjs +76 -0
  91. package/dist/{token-goat-chunk-JTQQGXZH.mjs → token-goat-chunk-ODBT2USM.mjs} +124 -57
  92. package/dist/token-goat-chunk-OS5EM5PP.mjs +107 -0
  93. package/dist/token-goat-chunk-OUKDTVNU.mjs +1178 -0
  94. package/dist/token-goat-chunk-OXFMP75N.mjs +566 -0
  95. package/dist/{token-goat-chunk-DZX2YDPT.mjs → token-goat-chunk-OYIYKDDT.mjs} +2 -2
  96. package/dist/{token-goat-chunk-QCLDAQTS.mjs → token-goat-chunk-P6HQSWQ3.mjs} +50 -53
  97. package/dist/token-goat-chunk-P6VGNTQT.mjs +71 -0
  98. package/dist/{token-goat-chunk-7BXSNBY5.mjs → token-goat-chunk-PFWIMH2E.mjs} +23 -15
  99. package/dist/token-goat-chunk-PJZXIS3G.mjs +39 -0
  100. package/dist/token-goat-chunk-PUO5INXP.mjs +606 -0
  101. package/dist/{token-goat-chunk-GFBSN7BD.mjs → token-goat-chunk-PX5PQWTY.mjs} +22 -250
  102. package/dist/token-goat-chunk-Q5QDBC6O.mjs +111 -0
  103. package/dist/token-goat-chunk-QFNQXSDT.mjs +40 -0
  104. package/dist/{token-goat-chunk-FBNQNSJK.mjs → token-goat-chunk-QH7YVYOY.mjs} +2198 -2782
  105. package/dist/{token-goat-chunk-64FBPDTK.mjs → token-goat-chunk-QLMUHEPB.mjs} +12 -9
  106. package/dist/{token-goat-chunk-RZRTDST7.mjs → token-goat-chunk-QQOJIHL3.mjs} +37 -11
  107. package/dist/{token-goat-chunk-XKI4SEMX.mjs → token-goat-chunk-QQTWJY2O.mjs} +19 -0
  108. package/dist/{token-goat-chunk-U4N3BN2W.mjs → token-goat-chunk-QYHO5CPH.mjs} +3 -2
  109. package/dist/token-goat-chunk-RELAVWAD.mjs +27 -0
  110. package/dist/{token-goat-chunk-ETBHVDB6.mjs → token-goat-chunk-RSLPOBKQ.mjs} +22 -11
  111. package/dist/token-goat-chunk-SAMFDIS2.mjs +712 -0
  112. package/dist/token-goat-chunk-SDELXLDM.mjs +48 -0
  113. package/dist/{token-goat-chunk-4JJS54TR.mjs → token-goat-chunk-SOAFZNNZ.mjs} +161 -38
  114. package/dist/{token-goat-chunk-664RTTUV.mjs → token-goat-chunk-SRTHT7KV.mjs} +21 -16
  115. package/dist/{token-goat-chunk-4FVIAHSI.mjs → token-goat-chunk-T5P5ACNQ.mjs} +7 -6
  116. package/dist/token-goat-chunk-T5YGG4V3.mjs +45 -0
  117. package/dist/token-goat-chunk-TAUZ4OFS.mjs +110 -0
  118. package/dist/{token-goat-chunk-COTXZHTS.mjs → token-goat-chunk-TM6MC754.mjs} +4 -3
  119. package/dist/token-goat-chunk-TWUKBOMP.mjs +72 -0
  120. package/dist/{token-goat-chunk-3CHUNJ2O.mjs → token-goat-chunk-U2KSB4SG.mjs} +8 -5
  121. package/dist/{token-goat-chunk-3Z5S4KGM.mjs → token-goat-chunk-U4WHMG6Y.mjs} +29 -49
  122. package/dist/{token-goat-chunk-PY533T7F.mjs → token-goat-chunk-UGJHGR5A.mjs} +21 -15
  123. package/dist/{token-goat-chunk-CYCEYAEO.mjs → token-goat-chunk-UOUDVA5U.mjs} +1 -1
  124. package/dist/{token-goat-chunk-JDHE26RP.mjs → token-goat-chunk-URRJ33GR.mjs} +16 -5
  125. package/dist/{token-goat-chunk-56EIMS3E.mjs → token-goat-chunk-UZOROKIS.mjs} +23 -17
  126. package/dist/{token-goat-chunk-CZ3LUXYZ.mjs → token-goat-chunk-VIHGGGEI.mjs} +427 -308
  127. package/dist/token-goat-chunk-VTJLAA7P.mjs +21 -0
  128. package/dist/token-goat-chunk-VWDXLT4F.mjs +678 -0
  129. package/dist/{token-goat-chunk-YARZFCZS.mjs → token-goat-chunk-VWZA2H4P.mjs} +62 -53
  130. package/dist/{token-goat-chunk-SQSSNQPE.mjs → token-goat-chunk-VXEXMJDL.mjs} +3979 -4745
  131. package/dist/token-goat-chunk-WBA5SKT4.mjs +547 -0
  132. package/dist/{token-goat-chunk-SJHWVNDQ.mjs → token-goat-chunk-WDH44VAU.mjs} +7 -4
  133. package/dist/token-goat-chunk-X2SUP3EI.mjs +107 -0
  134. package/dist/token-goat-chunk-X3BELZSJ.mjs +125 -0
  135. package/dist/{token-goat-chunk-FZ3NEW5O.mjs → token-goat-chunk-XLRBNZCC.mjs} +13 -8
  136. package/dist/{token-goat-chunk-Z44YO3QZ.mjs → token-goat-chunk-XRKUX6CM.mjs} +1 -1
  137. package/dist/token-goat-chunk-XX6O25RN.mjs +82 -0
  138. package/dist/token-goat-chunk-XZSUHBVS.mjs +65 -0
  139. package/dist/{token-goat-chunk-WY3L4DVT.mjs → token-goat-chunk-Y2ZTQOCL.mjs} +13 -8
  140. package/dist/{token-goat-chunk-UQ5UK2T7.mjs → token-goat-chunk-YB4VMGK2.mjs} +1 -1
  141. package/dist/token-goat-chunk-YBIIY3HR.mjs +51 -0
  142. package/dist/{token-goat-chunk-ZQSJMO2R.mjs → token-goat-chunk-YHVDKQBR.mjs} +105 -105
  143. package/dist/{token-goat-chunk-H2K3QP6H.mjs → token-goat-chunk-YJB5XLWI.mjs} +8 -6
  144. package/dist/{token-goat-chunk-ZCDTCVIS.mjs → token-goat-chunk-YKB6AQG3.mjs} +5 -2
  145. package/dist/token-goat-chunk-YMTG2YA5.mjs +35 -0
  146. package/dist/token-goat-chunk-YPBZYQRV.mjs +40 -0
  147. package/dist/{token-goat-chunk-HOJLCGDP.mjs → token-goat-chunk-YY6AVUFL.mjs} +2033 -2251
  148. package/dist/token-goat-chunk-Z7N5OMMP.mjs +98 -0
  149. package/dist/token-goat-chunk-ZNMNUPAZ.mjs +938 -0
  150. package/dist/token-goat-hook-client.cjs +1 -1
  151. package/dist/token-goat-hook-client.mjs +3 -3
  152. package/dist/token-goat-hook.mjs +67 -38
  153. package/dist/token-goat.core.mjs +4 -4
  154. package/docs/C4_RUNTIME_ARCHITECTURE.md +1 -1
  155. package/docs/cli.md +40 -21
  156. package/docs/install.md +19 -9
  157. package/docs/security.md +5 -3
  158. package/package.json +2 -1
  159. package/dist/token-goat-chunk-5KDMCMF2.mjs +0 -362
  160. package/dist/token-goat-chunk-6QMAS7AB.mjs +0 -42
  161. package/dist/token-goat-chunk-7F2UBPD3.mjs +0 -397
  162. package/dist/token-goat-chunk-ARDNEC2U.mjs +0 -248
  163. package/dist/token-goat-chunk-ATCPY7EQ.mjs +0 -134
  164. package/dist/token-goat-chunk-EFHMMIGP.mjs +0 -68
  165. package/dist/token-goat-chunk-IVALLT3S.mjs +0 -48
  166. package/dist/token-goat-chunk-JDU3EXSU.mjs +0 -65
  167. package/dist/token-goat-chunk-MKPF4CL5.mjs +0 -37
  168. package/dist/token-goat-chunk-RC35RBUL.mjs +0 -312
  169. package/dist/token-goat-chunk-USILZ3T3.mjs +0 -53
  170. package/dist/token-goat-chunk-VL3YIFHZ.mjs +0 -2975
  171. package/dist/token-goat-chunk-XU4P6Z24.mjs +0 -133
  172. package/dist/token-goat-chunk-Y776AENC.mjs +0 -1597
@@ -0,0 +1,236 @@
1
+ import { createRequire as __cjsRequire } from 'node:module';
2
+ const require = __cjsRequire(import.meta.url);
3
+ import {
4
+ displaySafeText
5
+ } from "./token-goat-chunk-FNFBKLRD.mjs";
6
+ import {
7
+ init_define_import_meta_env
8
+ } from "./token-goat-chunk-A37V4PBF.mjs";
9
+
10
+ // src/hint_suggestion_guard.ts
11
+ init_define_import_meta_env();
12
+ function looksLikeSuggestion(slice) {
13
+ return slice.includes('"');
14
+ }
15
+ var SAFE_OUTSIDE_QUOTES = /^[A-Za-z0-9 \t_./=:+-]*$/;
16
+ var CONTROL_OR_BIDI = new RegExp("[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F-\\u009F\\u200E\\u200F\\u202A-\\u202E\\u2066-\\u2069\\u061C\\u200B-\\u200D\\u2028\\u2029\\uFE00-\\uFE0F]|[\\u{E0000}-\\u{E007F}]|\\p{Cf}", "u");
17
+ var POWERSHELL_DOUBLE_QUOTE = /[\u201C\u201D\u201E]/;
18
+ function suggestionIsUnsafe(slice) {
19
+ if (slice.includes("\n") || slice.includes("\r") || slice.includes("`") || CONTROL_OR_BIDI.test(slice)) return true;
20
+ const scan = scanQuotes(slice);
21
+ if (scan.open !== null) return true;
22
+ if (scan.singleQuoted.some((body) => ENDS_SINGLE_QUOTES.test(body))) return true;
23
+ if ([...scan.gaps, ...scan.doubleQuoted].some((text) => text.includes("$") || POWERSHELL_DOUBLE_QUOTE.test(text))) return true;
24
+ if (scan.doubleQuoted.some((body) => body.includes('\\"'))) return true;
25
+ if ([...scan.singleQuoted, ...scan.doubleQuoted].some((body) => body.includes(UNQUOTABLE))) return true;
26
+ return scan.gaps.slice(1).some((gap) => !SAFE_OUTSIDE_QUOTES.test(gap));
27
+ }
28
+ function scanQuotes(slice, insideSingle = false, stopAtTick = false) {
29
+ const scan = { gaps: [], singleQuoted: [], doubleQuoted: [], open: null, tick: -1 };
30
+ let i = insideSingle ? slice.indexOf("'") + 1 : 0;
31
+ if (i === 0 && insideSingle) return scan;
32
+ let gapStart = i;
33
+ while (i < slice.length) {
34
+ const mark = slice[i];
35
+ if (mark === "\\") {
36
+ i += 2;
37
+ continue;
38
+ }
39
+ if (stopAtTick && mark === "`") {
40
+ scan.tick = i;
41
+ break;
42
+ }
43
+ if (mark === '"' || mark === "'" && /\s/.test(slice[i - 1] ?? " ")) {
44
+ scan.gaps.push(slice.slice(gapStart, i));
45
+ const close = closingMark(slice, mark, i + 1);
46
+ if (close === -1) {
47
+ scan.open = mark;
48
+ return scan;
49
+ }
50
+ ;
51
+ (mark === '"' ? scan.doubleQuoted : scan.singleQuoted).push(slice.slice(i + 1, close));
52
+ i = close + 1;
53
+ gapStart = i;
54
+ continue;
55
+ }
56
+ i++;
57
+ }
58
+ scan.gaps.push(slice.slice(gapStart, Math.min(i, slice.length)));
59
+ return scan;
60
+ }
61
+ function closingMark(slice, mark, from) {
62
+ if (mark === "'") return slice.indexOf("'", from);
63
+ for (let i = from; i < slice.length; i++) {
64
+ if (slice[i] === "\\") i++;
65
+ else if (slice[i] === '"') return i;
66
+ }
67
+ return -1;
68
+ }
69
+ function singleQuoteLeftOpen(slice, insideSingle) {
70
+ return scanQuotes(slice, insideSingle).open === "'";
71
+ }
72
+ function holdsCommandSubstitution(line) {
73
+ return line.includes("$(");
74
+ }
75
+ var TRAILING_FLAGS = /^(?: -{1,2}[A-Za-z][A-Za-z0-9-]*(?: [0-9]+)?)*$/;
76
+ function fencedCommandEnd(line) {
77
+ const scan = scanQuotes(line, false, true);
78
+ if (scan.tick === -1 || !TRAILING_FLAGS.test(scan.gaps[scan.gaps.length - 1] ?? "")) return -1;
79
+ const tick = scan.tick;
80
+ const region = line.slice(0, line.lastIndexOf("`"));
81
+ return region.includes('"') && (ENDS_SINGLE_QUOTES.test(region) || CONTROL_OR_BIDI.test(region)) ? -1 : tick;
82
+ }
83
+ var SUGGESTED_PROGRAMS = ["pandoc", "duckdb", "rg"];
84
+ var SUGGESTION_START = new RegExp(`token-goat |(?<![\\w./-])(?:${SUGGESTED_PROGRAMS.join("|")}) `);
85
+ function nextSuggestionStart(text, from) {
86
+ const re = new RegExp(SUGGESTION_START.source, "g");
87
+ re.lastIndex = from;
88
+ const found = re.exec(text);
89
+ return found === null ? -1 : found.index;
90
+ }
91
+ function omitted(program) {
92
+ return program + " (command omitted: the path contains shell metacharacters)";
93
+ }
94
+ function suggestionProgram(text, start) {
95
+ return SUGGESTED_PROGRAMS.find((p) => text.startsWith(p + " ", start)) ?? "token-goat";
96
+ }
97
+ function stripUnsafeSuggestions(text) {
98
+ if (!SUGGESTION_START.test(text)) return text;
99
+ let out = "";
100
+ let at = 0;
101
+ for (; ; ) {
102
+ const start = nextSuggestionStart(text, at);
103
+ if (start === -1) return out + text.slice(at);
104
+ const lineBreak = text.slice(start).search(/[\r\n]/);
105
+ const line = lineBreak === -1 ? text.slice(start) : text.slice(start, start + lineBreak);
106
+ const firstTick = line.indexOf("`");
107
+ const narrow = firstTick === -1 ? line : line.slice(0, firstTick);
108
+ out += text.slice(at, start);
109
+ const placeholder = omitted(suggestionProgram(text, start));
110
+ if (!(looksLikeSuggestion(narrow) && suggestionIsUnsafe(narrow)) && !singleQuoteLeftOpen(narrow, text[start - 1] === "'") && !holdsCommandSubstitution(line)) {
111
+ out += narrow;
112
+ at = start + narrow.length;
113
+ continue;
114
+ }
115
+ const end = text[start - 1] === "`" ? fencedCommandEnd(line) : -1;
116
+ if (end !== -1) {
117
+ out += placeholder + "`";
118
+ at = start + end + 1;
119
+ continue;
120
+ }
121
+ const lastTick = line.lastIndexOf("`");
122
+ const wide = lastTick === -1 || text[start - 1] !== "`" ? line : line.slice(0, lastTick);
123
+ const tick = line.slice(wide.length).startsWith("`") ? "`" : "";
124
+ const paired = pairRemovedParens(out.slice(Math.max(out.lastIndexOf("\n"), out.lastIndexOf("\r")) + 1), wide, line.slice(wide.length + tick.length));
125
+ out += placeholder + tick + paired.close;
126
+ at = start + wide.length + tick.length + paired.skip;
127
+ }
128
+ }
129
+ function pairRemovedParens(before, removed, after) {
130
+ let open = 0;
131
+ for (const c of before) {
132
+ if (c === "(") open++;
133
+ else if (c === ")" && open > 0) open--;
134
+ }
135
+ let inner = 0;
136
+ for (let i = 0; i < after.length; i++) {
137
+ const c = after[i];
138
+ if (c === "(") inner++;
139
+ else if (c !== ")") continue;
140
+ else if (inner > 0) inner--;
141
+ else if (open > 0) open--;
142
+ else return { skip: i + 1, close: "" };
143
+ }
144
+ return { skip: 0, close: ")".repeat(Math.min(open, removed.split(")").length - 1)) };
145
+ }
146
+ var REWRITTEN_IN_DOUBLE_QUOTES = /[$`"\u201C-\u201E]|\\(?:\\|$)/;
147
+ var ENDS_SINGLE_QUOTES = /['\u2018-\u201B\r\n]/;
148
+ function quotedArg(value) {
149
+ if (unquotable(value)) return '"' + UNQUOTABLE + '"';
150
+ if (REWRITTEN_IN_DOUBLE_QUOTES.test(value)) return "'" + value + "'";
151
+ return '"' + value + '"';
152
+ }
153
+ function singleQuotable(value) {
154
+ return !ENDS_SINGLE_QUOTES.test(value) && !CONTROL_OR_BIDI.test(value);
155
+ }
156
+ function unquotable(value) {
157
+ return LINE_BREAK.test(value) || REWRITTEN_IN_DOUBLE_QUOTES.test(value) && !singleQuotable(value);
158
+ }
159
+ var LINE_BREAK = /[\r\n]/;
160
+ function canQuoteArg(value) {
161
+ return !unquotable(value);
162
+ }
163
+ var UNQUOTABLE = "<a value no quote mark can hold>";
164
+ function quotedArgs(...values) {
165
+ const held = values.map((v) => unquotable(v) ? UNQUOTABLE : v);
166
+ const single = held.some((v) => REWRITTEN_IN_DOUBLE_QUOTES.test(v)) && held.every(singleQuotable);
167
+ return held.map((v) => single ? "'" + v + "'" : '"' + (REWRITTEN_IN_DOUBLE_QUOTES.test(v) ? UNQUOTABLE : v) + '"');
168
+ }
169
+ function fileSubject(rest, shown, raw = shown) {
170
+ return rest.includes(raw) || rest.includes(shown) ? "this file" : proseQuoted(shown) ?? "this file";
171
+ }
172
+ function nameSubject(noun, value) {
173
+ const quoted = proseQuoted(displaySafeText(value));
174
+ return quoted === null ? "this " + noun : noun + " " + quoted;
175
+ }
176
+ function echoedValue(value) {
177
+ const safe = displaySafeText(value);
178
+ const quoted = quotedArg(safe);
179
+ return quoted.includes(UNQUOTABLE) ? safe : quoted;
180
+ }
181
+ var BLANK_LOOKING = /\u00a0|\u034f|\u115f|\u1160|\u17b4|\u17b5|\u2800|\u3164|\ufe0f|\uffa0|[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/g;
182
+ function echoedCommand(value) {
183
+ return echoedValue(value.replace(BLANK_LOOKING, (ch) => String.fromCharCode(92) + "u" + ch.charCodeAt(0).toString(16).padStart(4, "0")));
184
+ }
185
+ function couldNotRead(file) {
186
+ return `Could not read: ${echoedValue(file)}`;
187
+ }
188
+ function proseQuoted(value) {
189
+ const quoted = quotedArg(value);
190
+ return quoted.includes("`") || quoted.includes(UNQUOTABLE) ? null : quoted;
191
+ }
192
+ function sentenceStart(subject) {
193
+ return subject.charAt(0).toUpperCase() + subject.slice(1);
194
+ }
195
+ function fencedCommand(command) {
196
+ return "`" + command + "`";
197
+ }
198
+ function leadWithCommand(command, purpose = "", reason = "") {
199
+ const tail = reason.trim();
200
+ return "Run " + fencedCommand(command) + (purpose === "" ? "" : " " + purpose) + "." + (tail === "" ? "" : (tail.includes("`") ? "\n" : " ") + tail);
201
+ }
202
+ function docNavigation(filePath) {
203
+ const ext = /\.(txt|rst)$/i.exec(filePath)?.[1]?.toLowerCase();
204
+ return { section: ext !== "txt", outline: ext === void 0 };
205
+ }
206
+ function grepLinesHint(pattern, shownPath, reason = "") {
207
+ const [quotedPattern, quotedPath] = quotedArgs(pattern, shownPath);
208
+ return leadWithCommand("token-goat grep " + quotedPattern + " " + quotedPath + " -C 3", "to read the matching lines", reason);
209
+ }
210
+ function configGetCommand(shownPath, key) {
211
+ return "token-goat config-get " + quotedArgs(shownPath, key).join(" ");
212
+ }
213
+ function docSectionHint(shownPath, heading, reason = "") {
214
+ const nav = docNavigation(shownPath);
215
+ if (!nav.section) return grepLinesHint("<pattern>", shownPath, reason);
216
+ return leadWithCommand("token-goat section " + quotedArg(shownPath + "::" + heading), "to read one section" + (nav.outline ? ", or " + fencedCommand("token-goat outline " + quotedArg(shownPath)) + " for every heading with line ranges" : ""), reason);
217
+ }
218
+
219
+ export {
220
+ stripUnsafeSuggestions,
221
+ quotedArg,
222
+ canQuoteArg,
223
+ quotedArgs,
224
+ fileSubject,
225
+ nameSubject,
226
+ echoedValue,
227
+ echoedCommand,
228
+ couldNotRead,
229
+ sentenceStart,
230
+ fencedCommand,
231
+ leadWithCommand,
232
+ docNavigation,
233
+ grepLinesHint,
234
+ configGetCommand,
235
+ docSectionHint
236
+ };
@@ -2,13 +2,16 @@ import { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
3
  import {
4
4
  redactSecrets
5
- } from "./token-goat-chunk-4FVIAHSI.mjs";
5
+ } from "./token-goat-chunk-T5P5ACNQ.mjs";
6
6
  import {
7
7
  shortFingerprint
8
- } from "./token-goat-chunk-7IN2MKDS.mjs";
8
+ } from "./token-goat-chunk-4GBVPH3D.mjs";
9
9
  import {
10
10
  registerReset
11
11
  } from "./token-goat-chunk-YZAAZU2S.mjs";
12
+ import {
13
+ quotedArg
14
+ } from "./token-goat-chunk-GGGG7RWB.mjs";
12
15
  import {
13
16
  foldPath
14
17
  } from "./token-goat-chunk-AM5V6GSX.mjs";
@@ -24,6 +27,127 @@ import {
24
27
  init_define_import_meta_env();
25
28
  import { randomBytes, randomUUID } from "node:crypto";
26
29
  import * as fs from "node:fs";
30
+
31
+ // src/read_range_evasion.ts
32
+ init_define_import_meta_env();
33
+ var states = /* @__PURE__ */ new Map();
34
+ var clock = 0;
35
+ function stamp() {
36
+ clock = Math.max(Date.now(), clock + 1);
37
+ return clock;
38
+ }
39
+ function stateFor(key) {
40
+ const state = states.get(key) ?? { resetAt: 0, denied: [], recent: [] };
41
+ states.set(key, state);
42
+ return state;
43
+ }
44
+ function evaluateRangeEvasion(options) {
45
+ if (options.locked) return "locked";
46
+ const [start, end] = options.span;
47
+ const denied = options.denied ?? [];
48
+ const recent = [...options.served.slice(-3), ...denied].slice(-3);
49
+ const overlap = ([s, e]) => Math.max(0, Math.min(end, e) - Math.max(start, s) + 1);
50
+ const anchored = recent.filter(([s]) => s === start);
51
+ if (anchored.length >= 3 || anchored.some(([, e]) => Math.abs(end - e) <= 20)) return "same_anchor";
52
+ const microCandidates = [...options.served.slice(-2), ...denied.slice(-1)];
53
+ if (microCandidates.some(([s, e]) => {
54
+ const startDelta = Math.abs(start - s);
55
+ const endDelta = Math.abs(end - e);
56
+ return startDelta >= 1 && startDelta <= 15 && endDelta >= 1 && endDelta <= 15 && overlap([s, e]) > 0;
57
+ })) return "micro_shift";
58
+ if (recent.some(([s, e]) => {
59
+ if (s === start && end - e > 20) return false;
60
+ return overlap([s, e]) >= Math.min(end - start + 1, e - s + 1) / 2;
61
+ })) return "overlap";
62
+ return null;
63
+ }
64
+ function getReadRangeDenials(key) {
65
+ return states.get(key)?.denied.map((v) => v.span) ?? [];
66
+ }
67
+ function lockReadRange(key, span, identity) {
68
+ const state = stateFor(key);
69
+ state.denied.push({ span: [span[0], span[1]], at: stamp() });
70
+ state.denied = state.denied.slice(-3);
71
+ if (identity) state.identity = identity;
72
+ }
73
+ function recordRecentReadRange(key, span) {
74
+ const state = stateFor(key);
75
+ const last = state.recent.at(-1)?.span;
76
+ if (last?.[0] === span[0] && last[1] === span[1]) return;
77
+ const resync = state.resetAt > 0 && state.recent.length === 0 && state.denied.length === 0;
78
+ state.recent.push({ span: [span[0], span[1]], at: stamp(), ...resync ? { resync: true } : {} });
79
+ state.recent = state.recent.slice(-3);
80
+ }
81
+ function getRecentReadRanges(key) {
82
+ return states.get(key)?.recent.filter((v) => v.resync !== true).map((v) => v.span) ?? [];
83
+ }
84
+ function clearReadRangeIfChanged(key, identity) {
85
+ const prior = states.get(key)?.identity;
86
+ if (prior && identity && (prior.size !== identity.size || prior.mtimeMs !== identity.mtimeMs)) clearReadRangeEvasion(key);
87
+ }
88
+ function clearReadRangeEvasion(key) {
89
+ states.set(key, { resetAt: stamp(), denied: [], recent: [] });
90
+ }
91
+ function clearAllReadRangeEvasion() {
92
+ states.clear();
93
+ }
94
+ function exportReadRangeEvasion() {
95
+ return structuredClone(Array.from(states));
96
+ }
97
+ function importReadRangeEvasion(entries) {
98
+ states = new Map(structuredClone(entries));
99
+ for (const [, state] of states) {
100
+ clock = Math.max(clock, state.resetAt, ...state.recent.map((v) => v.at), ...state.denied.map((v) => v.at));
101
+ }
102
+ }
103
+ function coerceReadRangeEvasion(raw) {
104
+ if (!Array.isArray(raw)) return [];
105
+ const finite = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0;
106
+ const visits = (rawVisits) => {
107
+ if (!Array.isArray(rawVisits)) return [];
108
+ return rawVisits.flatMap((v) => {
109
+ if (typeof v !== "object" || v === null || !("span" in v) || !("at" in v)) return [];
110
+ const span = v.span;
111
+ if (!Array.isArray(span) || span.length !== 2 || !finite(span[0]) || !finite(span[1]) || !Number.isInteger(span[0]) || !Number.isInteger(span[1]) || span[0] < 1 || span[1] < span[0] || !finite(v.at)) return [];
112
+ return [{ span: [span[0], span[1]], at: v.at, ..."resync" in v && v.resync === true ? { resync: true } : {} }];
113
+ }).slice(-3);
114
+ };
115
+ return raw.flatMap((pair) => {
116
+ if (!Array.isArray(pair) || typeof pair[0] !== "string") return [];
117
+ const value = pair[1];
118
+ if (typeof value !== "object" || value === null || !("resetAt" in value) || !finite(value.resetAt)) return [];
119
+ const state = {
120
+ resetAt: value.resetAt,
121
+ denied: visits("denied" in value ? value.denied : void 0),
122
+ recent: visits("recent" in value ? value.recent : void 0)
123
+ };
124
+ if ("identity" in value && typeof value.identity === "object" && value.identity !== null && "size" in value.identity && "mtimeMs" in value.identity && finite(value.identity.size) && finite(value.identity.mtimeMs)) {
125
+ state.identity = { size: value.identity.size, mtimeMs: value.identity.mtimeMs };
126
+ }
127
+ return [[pair[0], state]];
128
+ });
129
+ }
130
+ function mergeReadRangeEvasion(disk, mem) {
131
+ const merged = new Map(structuredClone(disk));
132
+ const mergeVisits = (a, b) => Array.from(new Map([...a, ...b].map((v) => [JSON.stringify(v), v])).values()).sort((a2, b2) => a2.at - b2.at).slice(-3);
133
+ for (const [key, state] of mem) {
134
+ const prior = merged.get(key);
135
+ if (!prior || state.resetAt > prior.resetAt) {
136
+ merged.set(key, state);
137
+ } else if (state.resetAt === prior.resetAt) {
138
+ const denied = mergeVisits(prior.denied, state.denied);
139
+ const recent = mergeVisits(prior.recent, state.recent);
140
+ const newestIdentity = (state.denied.at(-1)?.at ?? 0) >= (prior.denied.at(-1)?.at ?? 0) ? state.identity : prior.identity;
141
+ merged.set(key, { resetAt: state.resetAt, denied, recent, ...newestIdentity ? { identity: newestIdentity } : {} });
142
+ }
143
+ }
144
+ return Array.from(merged);
145
+ }
146
+ function rangeLockMessage(shown, span) {
147
+ return "Line-range reads of this file are locked for this compaction epoch. Run `token-goat read " + quotedArg(shown + "@" + span[0] + "-" + span[1]) + "` for this window, or `token-goat skeleton " + quotedArg(shown) + "` for structure. Do not shrink, grow, or shift the Read range.";
148
+ }
149
+
150
+ // src/session.ts
27
151
  var _files = /* @__PURE__ */ new Map();
28
152
  var _filesAtLoad = /* @__PURE__ */ new Map();
29
153
  var _filesFullReadCountAtLoad = /* @__PURE__ */ new Map();
@@ -35,6 +159,7 @@ var _grepQueries = /* @__PURE__ */ new Map();
35
159
  var _globQueries = /* @__PURE__ */ new Map();
36
160
  var _lastTabContext = null;
37
161
  var _seenImageHashes = [];
162
+ var _verificationRuns = [];
38
163
  var _outstandingAgentSpawns = [];
39
164
  var _outstandingAgentSpawnsAtLoad = [];
40
165
  var _bashReruns = /* @__PURE__ */ new Set();
@@ -44,6 +169,8 @@ var _fileLineRanges = /* @__PURE__ */ new Map();
44
169
  var _fileServedOutputs = /* @__PURE__ */ new Map();
45
170
  var _fileLineRangesAtLoad = /* @__PURE__ */ new Map();
46
171
  var _fileServedOutputsAtLoad = /* @__PURE__ */ new Map();
172
+ var _fileLineRangesReset = /* @__PURE__ */ new Set();
173
+ var _fileLineRangeIdentities = /* @__PURE__ */ new Map();
47
174
  var _compactedAt = 0;
48
175
  var _cliReads = /* @__PURE__ */ new Set();
49
176
  var _pendingLargeFileHints = /* @__PURE__ */ new Map();
@@ -68,6 +195,9 @@ function resolveFilesKey(normalized) {
68
195
  }
69
196
  return normalized;
70
197
  }
198
+ function newEntryEpochBase() {
199
+ return _compactedAt > 0 ? { epochBase: { at: _compactedAt, readCount: 0, fullReadCount: 0 } } : {};
200
+ }
71
201
  function recordFileRead(filePath, isFullRead = true) {
72
202
  const normalized = normalizePath(filePath);
73
203
  const key = resolveFilesKey(normalized);
@@ -81,16 +211,35 @@ function recordFileRead(filePath, isFullRead = true) {
81
211
  lastReadAt: now,
82
212
  wasEdited: false,
83
213
  sizeBytes: size,
84
- ...isFullRead ? { lastFullReadAt: now, fullReadCount: 1 } : {}
214
+ ...isFullRead ? { lastFullReadAt: now, fullReadCount: 1 } : {},
215
+ ...newEntryEpochBase()
85
216
  });
86
217
  return;
87
218
  }
219
+ const opensEpoch = _compactedAt > 0 && prev.lastReadAt < _compactedAt && (prev.epochBase?.at ?? 0) < _compactedAt;
88
220
  _files.set(key, {
89
221
  ...prev,
90
222
  readCount: prev.readCount + 1,
91
223
  lastReadAt: now,
92
224
  sizeBytes: size,
93
- ...isFullRead ? { lastFullReadAt: now, fullReadCount: (prev.fullReadCount ?? 0) + 1 } : {}
225
+ ...isFullRead ? { lastFullReadAt: now, fullReadCount: (prev.fullReadCount ?? 0) + 1 } : {},
226
+ ...opensEpoch ? { epochBase: { at: _compactedAt, readCount: prev.readCount, fullReadCount: prev.fullReadCount ?? 0 } } : {}
227
+ });
228
+ }
229
+ function unrecordRefusedRead(filePath, before) {
230
+ const key = resolveFilesKey(normalizePath(filePath));
231
+ const current = _files.get(key);
232
+ if (current === void 0) return;
233
+ if (before === void 0) {
234
+ _files.delete(key);
235
+ return;
236
+ }
237
+ const { fullReadCount: _fullReadCount, lastFullReadAt: _lastFullReadAt, ...rest } = current;
238
+ _files.set(key, {
239
+ ...rest,
240
+ readCount: before.readCount,
241
+ ...before.fullReadCount !== void 0 ? { fullReadCount: before.fullReadCount } : {},
242
+ ...before.lastFullReadAt !== void 0 ? { lastFullReadAt: before.lastFullReadAt } : {}
94
243
  });
95
244
  }
96
245
  var MAX_SYMBOLS_PER_FILE = 25;
@@ -105,7 +254,8 @@ function recordSymbolRead(filePath, symbol) {
105
254
  lastReadAt: Date.now(),
106
255
  wasEdited: false,
107
256
  sizeBytes: fileSize(normalized),
108
- symbols_read: [symbol]
257
+ symbols_read: [symbol],
258
+ ...newEntryEpochBase()
109
259
  });
110
260
  return;
111
261
  }
@@ -123,19 +273,21 @@ function filesFullReadCountAtLoad() {
123
273
  function recordFileEdit(filePath) {
124
274
  const normalized = normalizePath(filePath);
125
275
  const key = resolveFilesKey(normalized);
126
- _fileLineRanges.delete(foldPath(normalized));
276
+ resetFileLineRanges(normalized);
127
277
  const prev = _files.get(key);
278
+ const now = Date.now();
128
279
  if (prev === void 0) {
129
280
  _files.set(key, {
130
281
  path: key,
131
282
  readCount: 0,
132
283
  lastReadAt: 0,
133
284
  wasEdited: true,
285
+ lastEditedAt: now,
134
286
  sizeBytes: fileSize(normalized)
135
287
  });
136
288
  return;
137
289
  }
138
- _files.set(key, { ...prev, wasEdited: true });
290
+ _files.set(key, { ...prev, wasEdited: true, lastEditedAt: now });
139
291
  }
140
292
  function getSessionFiles() {
141
293
  return _files;
@@ -148,6 +300,24 @@ function wasFileFullyReadThisSession(filePath) {
148
300
  const entry = _files.get(resolveFilesKey(normalizePath(filePath)));
149
301
  return entry !== void 0 && entry.lastFullReadAt !== void 0 && entry.lastFullReadAt >= _compactedAt;
150
302
  }
303
+ function epochReadCounts(entry, compactedAt = _compactedAt) {
304
+ if (entry.lastReadAt < compactedAt) return { reads: 0, fullReads: 0 };
305
+ const base = entry.epochBase !== void 0 && entry.epochBase.at === compactedAt ? entry.epochBase : void 0;
306
+ const fullInEpoch = entry.lastFullReadAt !== void 0 && entry.lastFullReadAt >= compactedAt;
307
+ if (base === void 0 && compactedAt > 0) return { reads: Math.min(entry.readCount, 1), fullReads: fullInEpoch ? 1 : 0 };
308
+ const reads = Math.max(0, entry.readCount - (base?.readCount ?? 0));
309
+ const fullReads = fullInEpoch ? Math.max(0, (entry.fullReadCount ?? 0) - (base?.fullReadCount ?? 0)) : 0;
310
+ return { reads, fullReads };
311
+ }
312
+ function wasEditedSinceCompaction(entry, compactedAt = _compactedAt) {
313
+ return entry.wasEdited && (entry.lastEditedAt === void 0 || entry.lastEditedAt >= compactedAt);
314
+ }
315
+ function mergeEpochBase(a, b) {
316
+ if (a === void 0) return b;
317
+ if (b === void 0 || a.at > b.at) return a;
318
+ if (b.at > a.at) return b;
319
+ return { at: a.at, readCount: Math.max(a.readCount, b.readCount), fullReadCount: Math.max(a.fullReadCount, b.fullReadCount) };
320
+ }
151
321
  function getCompactedAt() {
152
322
  return _compactedAt;
153
323
  }
@@ -155,6 +325,8 @@ function markCompacted(now = Date.now()) {
155
325
  if (now <= _compactedAt) return;
156
326
  _compactedAt = now;
157
327
  _fileLineRanges = /* @__PURE__ */ new Map();
328
+ _fileLineRangeIdentities = /* @__PURE__ */ new Map();
329
+ clearAllReadRangeEvasion();
158
330
  _fileServedOutputs = /* @__PURE__ */ new Map();
159
331
  _cliReads = /* @__PURE__ */ new Set();
160
332
  }
@@ -276,6 +448,12 @@ function setLastTabContext(text) {
276
448
  function lastTabContextMatches(text) {
277
449
  return _lastTabContext !== null && _lastTabContext === shortFingerprint(text);
278
450
  }
451
+ var MAX_VERIFICATION_RUNS = 8;
452
+ function recordVerificationRun(command, exitCode, at = Date.now()) {
453
+ _verificationRuns = _verificationRuns.filter((r) => r.command !== command);
454
+ _verificationRuns.push({ command, exitCode, at });
455
+ if (_verificationRuns.length > MAX_VERIFICATION_RUNS) _verificationRuns = _verificationRuns.slice(-MAX_VERIFICATION_RUNS);
456
+ }
279
457
  var MAX_SEEN_IMAGE_HASHES = 16;
280
458
  function hasSeenImage(hash) {
281
459
  return _seenImageHashes.includes(hash);
@@ -340,11 +518,11 @@ function consumedCurlDownloadKeys() {
340
518
  return consumed;
341
519
  }
342
520
  function consumedFileLineRangeKeys() {
343
- const consumed = [];
521
+ const consumed = new Set(_fileLineRangesReset);
344
522
  for (const key of _fileLineRangesAtLoad.keys()) {
345
- if (!_fileLineRanges.has(key)) consumed.push(key);
523
+ if (!_fileLineRanges.has(key)) consumed.add(key);
346
524
  }
347
- return consumed;
525
+ return [...consumed];
348
526
  }
349
527
  function consumedFileServedOutputKeys() {
350
528
  const consumed = [];
@@ -365,22 +543,34 @@ function statIdentity(absPath) {
365
543
  function recordFileLineRange(filePath, start, end) {
366
544
  const normalized = normalizePath(filePath);
367
545
  const key = foldPath(normalized);
546
+ const entryKey = resolveFilesKey(normalized);
547
+ const entry = _files.get(entryKey);
548
+ const identity = dropFileLineRangesIfChanged(normalized);
368
549
  const ranges = _fileLineRanges.get(key) ?? [];
369
550
  if (!ranges.some(([s, e]) => s === start && e === end)) {
370
551
  ranges.push([start, end]);
371
552
  if (ranges.length > MAX_RANGES_PER_FILE) ranges.splice(0, ranges.length - MAX_RANGES_PER_FILE);
372
553
  _fileLineRanges.set(key, ranges);
373
554
  }
374
- const entryKey = resolveFilesKey(normalized);
375
- const entry = _files.get(entryKey);
376
- const identity = statIdentity(normalized);
555
+ if (identity) _fileLineRangeIdentities.set(key, identity);
377
556
  if (entry && identity) _files.set(entryKey, { ...entry, rangeFileIdentity: identity });
378
557
  }
558
+ function dropFileLineRangesIfChanged(filePath) {
559
+ const normalized = normalizePath(filePath);
560
+ const identity = statIdentity(normalized);
561
+ const prior = _fileLineRangeIdentities.get(foldPath(normalized)) ?? _files.get(resolveFilesKey(normalized))?.rangeFileIdentity;
562
+ if (prior !== void 0 && identity !== void 0 && (prior.size !== identity.size || prior.mtimeMs !== identity.mtimeMs)) resetFileLineRanges(normalized);
563
+ return identity;
564
+ }
379
565
  function getFileLineRanges(filePath) {
380
566
  return _fileLineRanges.get(foldPath(normalizePath(filePath))) ?? [];
381
567
  }
382
568
  function resetFileLineRanges(filePath) {
383
- _fileLineRanges.delete(foldPath(normalizePath(filePath)));
569
+ const key = foldPath(normalizePath(filePath));
570
+ clearReadRangeEvasion(key);
571
+ _fileLineRanges.delete(key);
572
+ _fileLineRangeIdentities.delete(key);
573
+ _fileLineRangesReset.add(key);
384
574
  }
385
575
  var MAX_SERVED_OUTPUTS_PER_FILE = 8;
386
576
  var GENERIC_SERVED_OUTPUT_KEY = "\0<bash-generic-served>";
@@ -410,7 +600,8 @@ function markFileTruncated(filePath, isFullRead = true) {
410
600
  wasEdited: false,
411
601
  sizeBytes: fileSize(normalized),
412
602
  wasTruncated: true,
413
- ...isFullRead ? { lastFullReadAt: now, fullReadCount: 1 } : {}
603
+ ...isFullRead ? { lastFullReadAt: now, fullReadCount: 1 } : {},
604
+ ...newEntryEpochBase()
414
605
  });
415
606
  return;
416
607
  }
@@ -451,6 +642,8 @@ function exportSessionState() {
451
642
  bashReruns: Array.from(_bashReruns),
452
643
  curlDownloads: Array.from(_curlDownloads.entries()),
453
644
  fileLineRanges: Array.from(_fileLineRanges.entries()),
645
+ readRangeEvasion: exportReadRangeEvasion(),
646
+ ..._fileLineRangeIdentities.size > 0 ? { fileLineRangeIdentities: Array.from(_fileLineRangeIdentities.entries()) } : {},
454
647
  ..._fileServedOutputs.size > 0 ? { fileServedOutputs: Array.from(_fileServedOutputs.entries()) } : {},
455
648
  cliReads: Array.from(_cliReads),
456
649
  pendingLargeFileHints: Array.from(_pendingLargeFileHints.entries()),
@@ -460,10 +653,12 @@ function exportSessionState() {
460
653
  outstandingAgentSpawns: _outstandingAgentSpawns.map((e) => [e.prompt, e.ts]),
461
654
  ..._lastTabContext !== null ? { lastTabContextDigest: _lastTabContext } : {},
462
655
  ..._seenImageHashes.length > 0 ? { seenImageHashes: [..._seenImageHashes] } : {},
656
+ ..._verificationRuns.length > 0 ? { verificationRuns: [..._verificationRuns] } : {},
463
657
  ..._compactedAt > 0 ? { compactedAt: _compactedAt } : {}
464
658
  };
465
659
  }
466
660
  function importSessionState(s) {
661
+ importReadRangeEvasion(s.readRangeEvasion ?? []);
467
662
  _files = /* @__PURE__ */ new Map();
468
663
  for (const e of s.files) {
469
664
  if (e && typeof e.path === "string") _files.set(e.path, e);
@@ -479,6 +674,8 @@ function importSessionState(s) {
479
674
  _curlDownloadsAtLoad = new Map(_curlDownloads);
480
675
  _fileLineRanges = new Map(s.fileLineRanges ?? []);
481
676
  _fileLineRangesAtLoad = new Map(_fileLineRanges);
677
+ _fileLineRangesReset = /* @__PURE__ */ new Set();
678
+ _fileLineRangeIdentities = new Map(s.fileLineRangeIdentities ?? []);
482
679
  _fileServedOutputs = new Map(s.fileServedOutputs ?? []);
483
680
  _fileServedOutputsAtLoad = new Map(_fileServedOutputs);
484
681
  _cliReads = new Set(s.cliReads ?? []);
@@ -492,9 +689,11 @@ function importSessionState(s) {
492
689
  _outstandingAgentSpawnsAtLoad = [..._outstandingAgentSpawns];
493
690
  _lastTabContext = s.lastTabContextDigest ?? null;
494
691
  _seenImageHashes = [...s.seenImageHashes ?? []];
692
+ _verificationRuns = [...s.verificationRuns ?? []];
495
693
  _compactedAt = s.compactedAt ?? 0;
496
694
  }
497
695
  registerReset(() => {
696
+ clearAllReadRangeEvasion();
498
697
  _files = /* @__PURE__ */ new Map();
499
698
  _filesAtLoad = /* @__PURE__ */ new Map();
500
699
  _filesFullReadCountAtLoad = /* @__PURE__ */ new Map();
@@ -507,6 +706,8 @@ registerReset(() => {
507
706
  _curlDownloadsAtLoad = /* @__PURE__ */ new Map();
508
707
  _fileLineRanges = /* @__PURE__ */ new Map();
509
708
  _fileLineRangesAtLoad = /* @__PURE__ */ new Map();
709
+ _fileLineRangesReset = /* @__PURE__ */ new Set();
710
+ _fileLineRangeIdentities = /* @__PURE__ */ new Map();
510
711
  _fileServedOutputs = /* @__PURE__ */ new Map();
511
712
  _fileServedOutputsAtLoad = /* @__PURE__ */ new Map();
512
713
  _cliReads = /* @__PURE__ */ new Set();
@@ -520,13 +721,24 @@ registerReset(() => {
520
721
  _outstandingAgentSpawnsAtLoad = [];
521
722
  _lastTabContext = null;
522
723
  _seenImageHashes = [];
724
+ _verificationRuns = [];
523
725
  _compactedAt = 0;
524
726
  _sessionId = null;
525
727
  _transcriptPath = null;
526
728
  }, { perRequest: true });
527
729
 
528
730
  export {
731
+ evaluateRangeEvasion,
732
+ getReadRangeDenials,
733
+ lockReadRange,
734
+ recordRecentReadRange,
735
+ getRecentReadRanges,
736
+ clearReadRangeIfChanged,
737
+ coerceReadRangeEvasion,
738
+ mergeReadRangeEvasion,
739
+ rangeLockMessage,
529
740
  recordFileRead,
741
+ unrecordRefusedRead,
530
742
  recordSymbolRead,
531
743
  filesReadCountAtLoad,
532
744
  filesFullReadCountAtLoad,
@@ -534,6 +746,9 @@ export {
534
746
  getSessionFiles,
535
747
  wasFileReadThisSession,
536
748
  wasFileFullyReadThisSession,
749
+ epochReadCounts,
750
+ wasEditedSinceCompaction,
751
+ mergeEpochBase,
537
752
  getCompactedAt,
538
753
  markCompacted,
539
754
  getSessionFileEntry,
@@ -567,6 +782,8 @@ export {
567
782
  getGlobMatchCount,
568
783
  setLastTabContext,
569
784
  lastTabContextMatches,
785
+ MAX_VERIFICATION_RUNS,
786
+ recordVerificationRun,
570
787
  MAX_SEEN_IMAGE_HASHES,
571
788
  hasSeenImage,
572
789
  recordSeenImage,
@@ -586,6 +803,7 @@ export {
586
803
  consumedFileServedOutputKeys,
587
804
  MAX_RANGES_PER_FILE,
588
805
  recordFileLineRange,
806
+ dropFileLineRangesIfChanged,
589
807
  getFileLineRanges,
590
808
  resetFileLineRanges,
591
809
  MAX_SERVED_OUTPUTS_PER_FILE,