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
@@ -2,7 +2,11 @@ import { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
3
  import {
4
4
  KNOWN_HARNESS_NAMES
5
- } from "./token-goat-chunk-U4N3BN2W.mjs";
5
+ } from "./token-goat-chunk-QYHO5CPH.mjs";
6
+ import {
7
+ findProject,
8
+ shortFingerprint
9
+ } from "./token-goat-chunk-4GBVPH3D.mjs";
6
10
  import {
7
11
  registerReset
8
12
  } from "./token-goat-chunk-YZAAZU2S.mjs";
@@ -17,269 +21,23 @@ import {
17
21
  import {
18
22
  atomicWriteText,
19
23
  decodeSource,
20
- extractErrorMessage,
21
- runGit
22
- } from "./token-goat-chunk-GOLEOXZP.mjs";
23
- import {
24
- canonicalize,
25
- foldPath
26
- } from "./token-goat-chunk-AM5V6GSX.mjs";
24
+ extractErrorMessage
25
+ } from "./token-goat-chunk-HU4TORC5.mjs";
27
26
  import {
28
- normalizeDarwinSystemAlias
29
- } from "./token-goat-chunk-FNFBKLRD.mjs";
27
+ echoedValue
28
+ } from "./token-goat-chunk-GGGG7RWB.mjs";
30
29
  import {
31
30
  configPath,
32
31
  projectConfigPath
33
- } from "./token-goat-chunk-VES7EL7O.mjs";
32
+ } from "./token-goat-chunk-CO3UYX6G.mjs";
34
33
  import {
35
34
  init_define_import_meta_env
36
35
  } from "./token-goat-chunk-A37V4PBF.mjs";
37
36
 
38
- // src/fingerprint.ts
37
+ // src/config_project.ts
39
38
  init_define_import_meta_env();
40
- import { createHash } from "node:crypto";
41
39
  import * as fs from "node:fs";
42
- function fingerprintContent(content) {
43
- const hash = createHash("sha256");
44
- hash.update(typeof content === "string" ? Buffer.from(content, "utf-8") : content);
45
- return hash.digest("hex");
46
- }
47
- function shortFingerprint(content) {
48
- return fingerprintContent(content).slice(0, 16);
49
- }
50
- function indexedFileIsGone(filePath) {
51
- try {
52
- return !fs.statSync(filePath).isFile();
53
- } catch (err) {
54
- const code = err?.code;
55
- return code === "ENOENT" || code === "ENOTDIR";
56
- }
57
- }
58
- function fingerprintFile(filePath) {
59
- let data;
60
- try {
61
- data = fs.readFileSync(filePath);
62
- } catch {
63
- return null;
64
- }
65
- return fingerprintContent(data);
66
- }
67
- function fileIsAbsent(filePath) {
68
- try {
69
- return fs.statSync(filePath, { throwIfNoEntry: false }) === void 0;
70
- } catch {
71
- return false;
72
- }
73
- }
74
-
75
- // src/ocr_languages.ts
76
- init_define_import_meta_env();
77
- var DEFAULT_OCR_LANG = "eng";
78
- var SUPPORTED_OCR_LANG_CODES = [
79
- "eng",
80
- "fra",
81
- "spa",
82
- "deu",
83
- "ita",
84
- "por",
85
- "nld",
86
- "pol",
87
- "rus",
88
- "tur",
89
- "swe",
90
- "ara",
91
- "chi_sim",
92
- "chi_tra",
93
- "jpn",
94
- "kor"
95
- ];
96
- var SUPPORTED_SET = new Set(SUPPORTED_OCR_LANG_CODES);
97
- function isSupportedOcrLang(lang) {
98
- return SUPPORTED_SET.has(lang);
99
- }
100
- function resolveOcrLangs(input) {
101
- const result = [DEFAULT_OCR_LANG];
102
- if (!input) return result;
103
- const tokens = input.split(/[+,;\s]+/).map((s) => s.trim().toLowerCase());
104
- for (const token of tokens) {
105
- if (token && isSupportedOcrLang(token) && !result.includes(token)) {
106
- result.push(token);
107
- }
108
- }
109
- return result;
110
- }
111
-
112
- // src/project.ts
113
- init_define_import_meta_env();
114
- import * as fs2 from "node:fs";
115
- import * as os from "node:os";
116
40
  import * as path from "node:path";
117
- var PROJECT_MARKERS = [
118
- ".git",
119
- "package.json",
120
- "pyproject.toml",
121
- "Cargo.toml",
122
- "go.mod",
123
- "shopify.app.toml",
124
- "_config.yml",
125
- "deno.json",
126
- "deno.jsonc"
127
- ];
128
- var REPO_CONTAINER_THRESHOLD = 3;
129
- function projectHash(canonicalRoot) {
130
- return shortFingerprint(foldPath(canonicalRoot));
131
- }
132
- function makeProjectAt(root) {
133
- let canonical;
134
- try {
135
- canonical = canonicalize(root);
136
- } catch (exc) {
137
- throw new Error(
138
- `makeProjectAt: could not resolve path: ${extractErrorMessage(exc)}`,
139
- { cause: exc }
140
- );
141
- }
142
- try {
143
- const stat = fs2.statSync(canonical);
144
- if (!stat.isDirectory()) {
145
- throw new Error(`makeProjectAt: path is not a directory: ${canonical}`);
146
- }
147
- } catch (exc) {
148
- if (exc instanceof Error && exc.message.includes("ENOENT")) {
149
- throw new Error(`makeProjectAt: path does not exist: ${canonical}`, { cause: exc });
150
- }
151
- throw exc;
152
- }
153
- const hash = projectHash(canonical);
154
- return { root: canonical, hash, marker: "manual" };
155
- }
156
- function isRepoContainer(pathStr) {
157
- let nestedRepos = 0;
158
- try {
159
- const entries = fs2.readdirSync(pathStr, { withFileTypes: true });
160
- for (const entry of entries) {
161
- if (entry.isDirectory()) {
162
- const gitPath = path.join(pathStr, entry.name, ".git");
163
- if (fs2.existsSync(gitPath)) {
164
- let isGitDir = false;
165
- try {
166
- isGitDir = fs2.statSync(gitPath).isDirectory();
167
- } catch {
168
- }
169
- if (isGitDir) {
170
- nestedRepos++;
171
- if (nestedRepos >= REPO_CONTAINER_THRESHOLD) {
172
- return true;
173
- }
174
- }
175
- }
176
- }
177
- }
178
- } catch {
179
- return false;
180
- }
181
- return false;
182
- }
183
- function markerExists(current, marker) {
184
- const markerPath = path.join(current, marker);
185
- try {
186
- if (!fs2.existsSync(markerPath)) {
187
- return false;
188
- }
189
- const stat = fs2.lstatSync(markerPath);
190
- if (!stat.isSymbolicLink()) {
191
- return true;
192
- }
193
- const resolved = fs2.realpathSync(markerPath);
194
- const rel = path.relative(path.resolve(current), path.resolve(resolved));
195
- return !rel.startsWith("..") && !path.isAbsolute(rel);
196
- } catch {
197
- return false;
198
- }
199
- }
200
- function findProject(cwd) {
201
- let p;
202
- try {
203
- p = canonicalize(cwd);
204
- } catch {
205
- return null;
206
- }
207
- let sysTemp = null;
208
- try {
209
- sysTemp = canonicalize(os.tmpdir());
210
- } catch {
211
- }
212
- let current = p;
213
- while (true) {
214
- if (sysTemp && normalizeDarwinSystemAlias(foldPath(current)) === normalizeDarwinSystemAlias(foldPath(sysTemp))) {
215
- break;
216
- }
217
- for (const marker of PROJECT_MARKERS) {
218
- if (markerExists(current, marker)) {
219
- if (!isRepoContainer(current)) {
220
- return {
221
- root: current,
222
- hash: projectHash(current),
223
- marker
224
- };
225
- }
226
- break;
227
- }
228
- }
229
- const parent = path.dirname(current);
230
- if (parent === current) {
231
- break;
232
- }
233
- current = parent;
234
- }
235
- return null;
236
- }
237
- var _displayRootCache = null;
238
- function getDisplayRoot(explicitRoot) {
239
- if (explicitRoot !== void 0) return explicitRoot;
240
- const cwd = process.cwd();
241
- if (_displayRootCache !== null && _displayRootCache.cwd === cwd) return _displayRootCache.root;
242
- const project = findProject(cwd);
243
- const root = project !== null ? project.root : void 0;
244
- _displayRootCache = { cwd, root };
245
- return root;
246
- }
247
- registerReset(() => {
248
- _displayRootCache = null;
249
- }, { perRequest: true });
250
- function isUnderSystemTemp(filePath) {
251
- let sysTemp;
252
- try {
253
- sysTemp = canonicalize(os.tmpdir());
254
- } catch {
255
- return false;
256
- }
257
- let target;
258
- try {
259
- target = canonicalize(filePath);
260
- } catch {
261
- return false;
262
- }
263
- const foldedTemp = normalizeDarwinSystemAlias(foldPath(sysTemp));
264
- const foldedTarget = normalizeDarwinSystemAlias(foldPath(target));
265
- return foldedTarget === foldedTemp || foldedTarget.startsWith(`${foldedTemp}/`);
266
- }
267
- function resolveProjectRoot(opts) {
268
- const base = opts?.project !== void 0 ? path.resolve(opts.project) : process.cwd();
269
- const toplevel = runGit(["rev-parse", "--show-toplevel"], { cwd: base });
270
- if (toplevel.exitCode === 0) {
271
- const trimmed = toplevel.stdout.trim();
272
- if (trimmed.length > 0) return canonicalize(trimmed);
273
- }
274
- const project = findProject(base);
275
- if (project !== null) return project.root;
276
- return canonicalize(base);
277
- }
278
-
279
- // src/config_project.ts
280
- init_define_import_meta_env();
281
- import * as fs3 from "node:fs";
282
- import * as path2 from "node:path";
283
41
 
284
42
  // node_modules/smol-toml/dist/index.js
285
43
  init_define_import_meta_env();
@@ -1261,6 +1019,9 @@ var PROJECT_LOCKED_KEYS = [
1261
1019
  "hints.outline_large_documents",
1262
1020
  // Same reasoning one file type over: a repository must not be able to decide, from its own checked-in config, how much of its source a reviewing agent is shown -- neither by turning the skeleton off to bury a declaration in a wall of bodies, nor by leaving it on to withhold the bodies themselves. The user's global config and TOKEN_GOAT_SKELETON_LARGE_SOURCES still set it freely.
1263
1021
  "hints.skeleton_large_sources",
1022
+ // The symbol-aware first-read advice and its size: a repository must not be able to turn off, or turn into a refusal, what an agent is told when it first opens one of the repository's own large files.
1023
+ "hints.first_read_symbol_bytes",
1024
+ "hints.first_read_symbol_policy",
1264
1025
  // The widest blast radius of anything on this list. The fold keys above decide how much of a file an agent is shown; this decides how much of the whole session survives compaction. A checked-in `.token-goat.toml` setting it to a handful of characters would ask the summarizer to discard the session's accumulated state at every compaction boundary, and the loss is silent -- what comes back is a well-formed short summary, not an error. The user's own global config still sets it freely; only the project-supplied layer is refused.
1265
1026
  "compact_assist.summary_budget_chars",
1266
1027
  "image_shrink.max_image_pixels",
@@ -1342,7 +1103,7 @@ function flattenRawValues(raw) {
1342
1103
  return out;
1343
1104
  }
1344
1105
  function readConfigSource(p) {
1345
- return decodeSource(fs3.readFileSync(p));
1106
+ return decodeSource(fs.readFileSync(p));
1346
1107
  }
1347
1108
  function readConfigToml(p) {
1348
1109
  try {
@@ -1392,8 +1153,8 @@ function configProjectRootFor(dir) {
1392
1153
  const own = findProject(dir);
1393
1154
  let project = own;
1394
1155
  while (project !== null) {
1395
- if (fs3.existsSync(projectConfigPath(project.root))) return project.root;
1396
- const parent = path2.dirname(project.root);
1156
+ if (fs.existsSync(projectConfigPath(project.root))) return project.root;
1157
+ const parent = path.dirname(project.root);
1397
1158
  project = parent === project.root ? null : findProject(parent);
1398
1159
  }
1399
1160
  return own?.root ?? dir;
@@ -1413,7 +1174,7 @@ registerReset(() => {
1413
1174
  function getProjectConfigInfo(projectRoot) {
1414
1175
  const root = projectRoot ?? resolveConfigProjectRoot();
1415
1176
  const p = projectConfigPath(root);
1416
- if (!fs3.existsSync(p)) return null;
1177
+ if (!fs.existsSync(p)) return null;
1417
1178
  const { raw, parseError } = readConfigToml(p);
1418
1179
  if (parseError !== null) return { path: p, keys: [], values: {}, parseError };
1419
1180
  return { path: p, keys: flattenRawKeys(raw), values: flattenRawValues(raw), parseError };
@@ -1440,7 +1201,44 @@ function isAutoTriggerMultiplierExplicit() {
1440
1201
 
1441
1202
  // src/config.ts
1442
1203
  init_define_import_meta_env();
1443
- import * as path3 from "node:path";
1204
+ import * as path2 from "node:path";
1205
+
1206
+ // src/ocr_languages.ts
1207
+ init_define_import_meta_env();
1208
+ var DEFAULT_OCR_LANG = "eng";
1209
+ var SUPPORTED_OCR_LANG_CODES = [
1210
+ "eng",
1211
+ "fra",
1212
+ "spa",
1213
+ "deu",
1214
+ "ita",
1215
+ "por",
1216
+ "nld",
1217
+ "pol",
1218
+ "rus",
1219
+ "tur",
1220
+ "swe",
1221
+ "ara",
1222
+ "chi_sim",
1223
+ "chi_tra",
1224
+ "jpn",
1225
+ "kor"
1226
+ ];
1227
+ var SUPPORTED_SET = new Set(SUPPORTED_OCR_LANG_CODES);
1228
+ function isSupportedOcrLang(lang) {
1229
+ return SUPPORTED_SET.has(lang);
1230
+ }
1231
+ function resolveOcrLangs(input) {
1232
+ const result = [DEFAULT_OCR_LANG];
1233
+ if (!input) return result;
1234
+ const tokens = input.split(/[+,;\s]+/).map((s) => s.trim().toLowerCase());
1235
+ for (const token of tokens) {
1236
+ if (token && isSupportedOcrLang(token) && !result.includes(token)) {
1237
+ result.push(token);
1238
+ }
1239
+ }
1240
+ return result;
1241
+ }
1444
1242
 
1445
1243
  // src/own_lookup.ts
1446
1244
  init_define_import_meta_env();
@@ -1454,6 +1252,7 @@ init_define_import_meta_env();
1454
1252
 
1455
1253
  // src/config_defaults.ts
1456
1254
  init_define_import_meta_env();
1255
+ var RETIRED_CONFIG_KEYS = /* @__PURE__ */ new Set(["stats.record_zero_savings", "image_shrink.ocr_enabled"]);
1457
1256
  var CONFIG_DEFAULTS = {
1458
1257
  compact_assist: {
1459
1258
  enabled: true,
@@ -1472,7 +1271,8 @@ var CONFIG_DEFAULTS = {
1472
1271
  cache_min_bytes: 512,
1473
1272
  cache_max_file_count: 4096,
1474
1273
  cache_max_bytes: 16 * 1024 * 1024,
1475
- cache_max_bytes_per_output: 50 * 1024 * 1024,
1274
+ // A quarter of cache_max_bytes: the loader caps the per-output value at the total, so a larger default (it was 50 MiB against a 16 MiB total) was never in effect.
1275
+ cache_max_bytes_per_output: 4 * 1024 * 1024,
1476
1276
  // Measured against the filter test-fixture corpus (424 apply() calls, 298 with bytesSaved > 0): net-of-marker savings (bytesSaved - ~70-79B marker cost) is <= 0 for 130/298 (44%) of "compressed" results and <= 100 for 170/298 (57%), while the real-win half sits at p75=393B / p90=952B net. 100 kills the marker-doesn't-even-pay-for-itself tier without touching genuine wins.
1477
1277
  min_net_savings_bytes: 100,
1478
1278
  // Extends the per-file already-served elision (which only reaches cat/head/tail/sed/awk-shaped reads) to every other Bash command's output -- npm test, git, rg, build runs -- matched against a session-wide served-output list instead of a per-file one. On by default: it goes through the same isRewriteWorthwhile net-benefit gate as every other rewrite here, so it never ships a notice that costs more than the lines it withholds.
@@ -1516,7 +1316,6 @@ var CONFIG_DEFAULTS = {
1516
1316
  // 16,000,000 used to reject a routine 24MP DSLR/phone photo outright -- the exact input this feature exists to shrink -- while the engine's own decode ceiling (MAX_DECODED_BYTES in image_engine.ts, 256MB at 4 bytes/pixel for a single frame) sits at 67,108,864px. 64,000,000 covers cameras well past 24MP (including 48MP phone sensors) with ~4.9% margin below that hard ceiling, which assertDecodableSize enforces independently of this value on every decode regardless of what this header check allows through. Measured on this engine (2026-09-17): a 24MP PNG decodes+shrinks in ~370ms, 60,000,516px in ~920ms, and 67,108,864px (the ceiling itself) in ~760ms -- all comfortably fast, so the new value is chosen for coverage headroom under the ceiling, not for decode-time cost. Decode time is not the cost that binds, though: memory is. This value is read off a header before anything is decoded, so what it really sets is how large a buffer a file's own declared dimensions may talk this hook into allocating -- at 4 bytes per pixel, raising it from 16,000,000 raised that peak from ~64MB to ~256MB, which is why it now sits flush against MAX_DECODED_BYTES rather than comfortably below it. And on the JPEG path there is no assertDecodableSize call at all: decodeJpeg hands the buffer to jpeg-js, so a JPEG decode is bounded by whatever maxResolutionInMP/maxMemoryUsageInMB that call passes -- a bound this codebase states only because decodeJpeg passes both explicitly rather than inheriting jpeg-js's own 100MP default, which is looser than the 67.1MP MAX_DECODED_BYTES allows.
1517
1317
  max_image_pixels: 64e6,
1518
1318
  screenshot_redirect: true,
1519
- ocr_enabled: true,
1520
1319
  // Confidence is Tesseract's own 0-100 mean-word-confidence score. 65 is a deliberately conservative floor: a real screenshot of terminal/code/prose text routinely scores 85+, while a photo with an incidental sign or logo in frame scores much lower and noisier -- padding the threshold below the terminal/code norm still comfortably excludes photographic false positives without needing a second heuristic.
1521
1320
  ocr_min_confidence: 65,
1522
1321
  ocr_lang: DEFAULT_OCR_LANG,
@@ -1535,9 +1334,6 @@ var CONFIG_DEFAULTS = {
1535
1334
  enabled: true,
1536
1335
  max_tokens: 25e3
1537
1336
  },
1538
- stats: {
1539
- record_zero_savings: false
1540
- },
1541
1337
  hints: {
1542
1338
  quiet_hours: "",
1543
1339
  json_sidecar: false,
@@ -1558,10 +1354,21 @@ var CONFIG_DEFAULTS = {
1558
1354
  git_hint_max_ms: 50,
1559
1355
  min_session_hint_savings_bytes: 512,
1560
1356
  pre_skill_advisory: true,
1357
+ stop_verification_advisory: true,
1358
+ agent_receipt: true,
1561
1359
  context_threshold_advisory: true,
1562
1360
  diff_hint_min_tokens_saved: 1e3,
1563
1361
  // Base for the pressure-scaled first-read deny gate in hooks_read.ts (large file, never read before). Matches that gate's long-tuned 500KB threshold at 'cool' context pressure; warm/hot/ critical scale it down from there so the same read gets redirected to a surgical read sooner once the context window is nearly full.
1564
1362
  large_read_redirect_bytes: 512e3,
1363
+ // Smallest whole-file first read of an indexed file that draws the symbol-aware advice; 0 turns the check off.
1364
+ first_read_symbol_bytes: 5e4,
1365
+ // Advice, not refusal: `warn` notes the file's size and symbols, `deny` refuses the whole-file read, `off` says nothing.
1366
+ first_read_symbol_policy: "warn",
1367
+ // A refusal that costs one retry, against a file overwritten with a placeholder that costs a re-read, a re-write and a debugging loop.
1368
+ write_blind_drop: "deny",
1369
+ // A hint costs one sentence, against a whole file sent again as output and a second copy of every symbol in the index.
1370
+ write_sibling_copy: true,
1371
+ write_sibling_copy_allow: [],
1565
1372
  reread_deny: true,
1566
1373
  // Matches hooks_read.ts's previously-hardcoded REREAD_DENY_BYTES (50 * 1024) so wiring this key up as the real gate for that logic does not silently change default behavior for existing users -- see the reread_deny/reread_deny_min_bytes fix's commit message.
1567
1374
  reread_deny_min_bytes: 51200,
@@ -1675,7 +1482,6 @@ function defaultConfig() {
1675
1482
  screenshot: getDefaultConfig("screenshot"),
1676
1483
  repomap: getDefaultConfig("repomap"),
1677
1484
  overflow_guard: getDefaultConfig("overflow_guard"),
1678
- stats: getDefaultConfig("stats"),
1679
1485
  hints: getDefaultConfig("hints"),
1680
1486
  hooks: getDefaultConfig("hooks"),
1681
1487
  webfetch: getDefaultConfig("webfetch"),
@@ -1711,6 +1517,12 @@ function validatedFloat(raw, def, min, max) {
1711
1517
  function validatedVisionTier(raw, def) {
1712
1518
  return raw === "standard" || raw === "high" ? raw : def;
1713
1519
  }
1520
+ function validatedEnum(key, raw, def) {
1521
+ return typeof raw === "string" && (ENUM_FIELD_VALUES[key] ?? []).includes(raw) ? raw : def;
1522
+ }
1523
+ function envEnum(key, name, def) {
1524
+ return validatedEnum(key, process.env[name], def);
1525
+ }
1714
1526
  function validatedNativeHooksMode(raw, def) {
1715
1527
  return raw === "auto" || raw === "off" ? raw : def;
1716
1528
  }
@@ -1787,6 +1599,7 @@ var NUMERIC_FIELD_BOUNDS = {
1787
1599
  "hints.min_session_hint_savings_bytes": { min: 0, max: 1e6 },
1788
1600
  "hints.diff_hint_min_tokens_saved": { min: 0, max: 1e5 },
1789
1601
  "hints.large_read_redirect_bytes": { min: 0, max: 1e8 },
1602
+ "hints.first_read_symbol_bytes": { min: 0, max: 1e8 },
1790
1603
  "hints.reread_deny_min_bytes": { min: 0, max: 1e8 },
1791
1604
  "hints.truncated_read_min_lines": { min: 0, max: 1e6 },
1792
1605
  "hints.protect_recent_reads": { min: 0, max: 100 },
@@ -1815,7 +1628,7 @@ var NUMERIC_FIELD_BOUNDS = {
1815
1628
  };
1816
1629
  function boundsOf(key) {
1817
1630
  const b = ownGet(NUMERIC_FIELD_BOUNDS, key);
1818
- if (!b) throw new Error(`token-goat: no NUMERIC_FIELD_BOUNDS entry for '${key}'`);
1631
+ if (!b) throw new Error(`token-goat: no NUMERIC_FIELD_BOUNDS entry for ${echoedValue(key)}`);
1819
1632
  return [b.min, b.max];
1820
1633
  }
1821
1634
  function walkGetNumeric(obj, parts) {
@@ -1845,7 +1658,9 @@ var ENUM_FIELD_VALUES = {
1845
1658
  "image_shrink.ocr_lang": [...SUPPORTED_OCR_LANG_CODES],
1846
1659
  // Deliberately has no entry above `normal`. This table is what `config set` checks; a hand-edited TOML bypasses it, which is why resolveWorkerPriority (process_priority.ts) maps an unrecognized value back to the default rather than trusting whatever the file said.
1847
1660
  "worker.priority": ["below_normal", "low", "normal"],
1848
- "hooks.native": ["auto", "off"]
1661
+ "hooks.native": ["auto", "off"],
1662
+ "hints.first_read_symbol_policy": ["warn", "deny", "off"],
1663
+ "hints.write_blind_drop": ["deny", "warn", "off"]
1849
1664
  };
1850
1665
  function validateEnumField(fieldKey, value) {
1851
1666
  if (fieldKey === "image_shrink.ocr_lang") {
@@ -2011,6 +1826,15 @@ function resolveConfigKeyLayer(key, effectiveValue, cfg, projectInfo) {
2011
1826
  if (rawValueEquals(rawValue, effectiveValue)) return { layer: "project", path: projectInfo.path };
2012
1827
  return { layer: "project-invalid", path: projectInfo.path, rawValue, effectiveValue, reason: rejectionReason(key, rawValue, effectiveValue, cfg) };
2013
1828
  }
1829
+ function globalValueRejection(key, rawValue, persistedCfg) {
1830
+ let eff = persistedCfg;
1831
+ for (const part of key.split(".")) {
1832
+ if (eff === null || typeof eff !== "object" || !Object.prototype.hasOwnProperty.call(eff, part)) return null;
1833
+ eff = eff[part];
1834
+ }
1835
+ if (rawValueEquals(rawValue, eff)) return null;
1836
+ return rejectionReason(key, rawValue, eff, persistedCfg);
1837
+ }
2014
1838
  function loadConfig(projectRoot) {
2015
1839
  const p = configPath();
2016
1840
  const root = projectRoot ?? resolveConfigProjectRoot();
@@ -2166,12 +1990,10 @@ function _buildConfig(raw, projectRaw = {}) {
2166
1990
  is_cfg.jpeg_quality = validatedInt(is_raw["jpeg_quality"], is_cfg.jpeg_quality, ...boundsOf("image_shrink.jpeg_quality"));
2167
1991
  is_cfg.max_image_pixels = validatedIntWithLegacySentinel(is_raw["max_image_pixels"], is_cfg.max_image_pixels, 16e6, ...boundsOf("image_shrink.max_image_pixels"));
2168
1992
  is_cfg.screenshot_redirect = validatedBool(is_raw["screenshot_redirect"], is_cfg.screenshot_redirect);
2169
- is_cfg.ocr_enabled = validatedBool(is_raw["ocr_enabled"], is_cfg.ocr_enabled);
2170
1993
  is_cfg.ocr_min_confidence = validatedInt(is_raw["ocr_min_confidence"], is_cfg.ocr_min_confidence, ...boundsOf("image_shrink.ocr_min_confidence"));
2171
1994
  is_cfg.ocr_lang = validatedOcrLang(is_raw["ocr_lang"], is_cfg.ocr_lang);
2172
1995
  is_cfg.vision_tier = validatedVisionTier(is_raw["vision_tier"], is_cfg.vision_tier);
2173
1996
  is_cfg.max_image_pixels = envInt("TOKEN_GOAT_MAX_IMAGE_PIXELS", is_cfg.max_image_pixels, ...boundsOf("image_shrink.max_image_pixels"));
2174
- is_cfg.ocr_enabled = envBool("TOKEN_GOAT_OCR_ENABLED", is_cfg.ocr_enabled);
2175
1997
  is_cfg.ocr_lang = validatedOcrLang(process.env["TOKEN_GOAT_OCR_LANG"], is_cfg.ocr_lang);
2176
1998
  is_cfg.vision_tier = validatedVisionTier(process.env["TOKEN_GOAT_VISION_TIER"], is_cfg.vision_tier);
2177
1999
  const sc_raw = section(raw, "screenshot");
@@ -2191,9 +2013,6 @@ function _buildConfig(raw, projectRaw = {}) {
2191
2013
  og.max_tokens = validatedInt(og_raw["max_tokens"], og.max_tokens, ...boundsOf("overflow_guard.max_tokens"));
2192
2014
  og.enabled = envBool("TOKEN_GOAT_OVERFLOW_GUARD", og.enabled);
2193
2015
  og.max_tokens = envInt("TOKEN_GOAT_OVERFLOW_MAX_TOKENS", og.max_tokens, ...boundsOf("overflow_guard.max_tokens"));
2194
- const st_raw = section(raw, "stats");
2195
- const st = getDefaultConfig("stats");
2196
- st.record_zero_savings = validatedBool(st_raw["record_zero_savings"], st.record_zero_savings);
2197
2016
  const hi_raw = section(raw, "hints");
2198
2017
  const hi = getDefaultConfig("hints");
2199
2018
  hi.quiet_hours = validatedStr(hi_raw["quiet_hours"], hi.quiet_hours);
@@ -2219,9 +2038,16 @@ function _buildConfig(raw, projectRaw = {}) {
2219
2038
  hi.git_hint_max_ms = validatedInt(hi_raw["git_hint_max_ms"], hi.git_hint_max_ms, ...boundsOf("hints.git_hint_max_ms"));
2220
2039
  hi.min_session_hint_savings_bytes = validatedInt(hi_raw["min_session_hint_savings_bytes"], hi.min_session_hint_savings_bytes, ...boundsOf("hints.min_session_hint_savings_bytes"));
2221
2040
  hi.pre_skill_advisory = validatedBool(hi_raw["pre_skill_advisory"], hi.pre_skill_advisory);
2041
+ hi.stop_verification_advisory = validatedBool(hi_raw["stop_verification_advisory"], hi.stop_verification_advisory);
2042
+ hi.agent_receipt = validatedBool(hi_raw["agent_receipt"], hi.agent_receipt);
2222
2043
  hi.context_threshold_advisory = validatedBool(hi_raw["context_threshold_advisory"], hi.context_threshold_advisory);
2223
2044
  hi.diff_hint_min_tokens_saved = validatedInt(hi_raw["diff_hint_min_tokens_saved"], hi.diff_hint_min_tokens_saved, ...boundsOf("hints.diff_hint_min_tokens_saved"));
2224
2045
  hi.large_read_redirect_bytes = validatedIntWithLegacySentinel(hi_raw["large_read_redirect_bytes"], hi.large_read_redirect_bytes, 45e3, ...boundsOf("hints.large_read_redirect_bytes"));
2046
+ hi.first_read_symbol_bytes = validatedInt(hi_raw["first_read_symbol_bytes"], hi.first_read_symbol_bytes, ...boundsOf("hints.first_read_symbol_bytes"));
2047
+ hi.first_read_symbol_policy = validatedEnum("hints.first_read_symbol_policy", hi_raw["first_read_symbol_policy"], hi.first_read_symbol_policy);
2048
+ hi.write_blind_drop = validatedEnum("hints.write_blind_drop", hi_raw["write_blind_drop"], hi.write_blind_drop);
2049
+ hi.write_sibling_copy = validatedBool(hi_raw["write_sibling_copy"], hi.write_sibling_copy);
2050
+ hi.write_sibling_copy_allow = validatedStrList(hi_raw["write_sibling_copy_allow"], hi.write_sibling_copy_allow);
2225
2051
  hi.reread_deny = validatedBool(hi_raw["reread_deny"], hi.reread_deny);
2226
2052
  hi.reread_deny_min_bytes = validatedIntWithLegacySentinel(hi_raw["reread_deny_min_bytes"], hi.reread_deny_min_bytes, 2048, ...boundsOf("hints.reread_deny_min_bytes"));
2227
2053
  hi.stable_doc_compacts = validatedBool(hi_raw["stable_doc_compacts"], hi.stable_doc_compacts);
@@ -2255,6 +2081,10 @@ function _buildConfig(raw, projectRaw = {}) {
2255
2081
  hi.min_session_hint_savings_bytes = envInt("TOKEN_GOAT_SESSION_HINT_MIN_BYTES", hi.min_session_hint_savings_bytes, ...boundsOf("hints.min_session_hint_savings_bytes"));
2256
2082
  hi.reread_deny = envBool("TOKEN_GOAT_REREAD_DENY", hi.reread_deny);
2257
2083
  hi.reread_deny_min_bytes = envInt("TOKEN_GOAT_REREAD_DENY_MIN_BYTES", hi.reread_deny_min_bytes, ...boundsOf("hints.reread_deny_min_bytes"));
2084
+ hi.first_read_symbol_bytes = envInt("TOKEN_GOAT_FIRST_READ_SYMBOL_BYTES", hi.first_read_symbol_bytes, ...boundsOf("hints.first_read_symbol_bytes"));
2085
+ hi.first_read_symbol_policy = envEnum("hints.first_read_symbol_policy", "TOKEN_GOAT_FIRST_READ_SYMBOL_POLICY", hi.first_read_symbol_policy);
2086
+ hi.write_blind_drop = envEnum("hints.write_blind_drop", "TOKEN_GOAT_WRITE_BLIND_DROP", hi.write_blind_drop);
2087
+ hi.write_sibling_copy = envBool("TOKEN_GOAT_WRITE_SIBLING_COPY", hi.write_sibling_copy);
2258
2088
  hi.protect_recent_reads = envInt("TOKEN_GOAT_PROTECT_RECENT_READS", hi.protect_recent_reads, ...boundsOf("hints.protect_recent_reads"));
2259
2089
  hi.truncated_read_min_lines = envInt("TOKEN_GOAT_TRUNCATED_READ_MIN_LINES", hi.truncated_read_min_lines, ...boundsOf("hints.truncated_read_min_lines"));
2260
2090
  hi.diff_hint_min_tokens_saved = envInt("TOKEN_GOAT_DIFF_HINT_MIN_TOKENS_SAVED", hi.diff_hint_min_tokens_saved, ...boundsOf("hints.diff_hint_min_tokens_saved"));
@@ -2268,6 +2098,8 @@ function _buildConfig(raw, projectRaw = {}) {
2268
2098
  hi.skeleton_large_sources = envBool("TOKEN_GOAT_SKELETON_LARGE_SOURCES", hi.skeleton_large_sources);
2269
2099
  hi.context_threshold_advisory = envBool("TOKEN_GOAT_CONTEXT_THRESHOLD_ADVISORY", hi.context_threshold_advisory);
2270
2100
  hi.pre_skill_advisory = envBool("TOKEN_GOAT_PRE_SKILL_ADVISORY", hi.pre_skill_advisory);
2101
+ hi.stop_verification_advisory = envBool("TOKEN_GOAT_STOP_VERIFICATION_ADVISORY", hi.stop_verification_advisory);
2102
+ hi.agent_receipt = envBool("TOKEN_GOAT_AGENT_RECEIPT", hi.agent_receipt);
2271
2103
  hi.quiet_hours = envStr("TOKEN_GOAT_QUIET_HOURS", hi.quiet_hours);
2272
2104
  const triggers_raw = hi_raw["prompt_triggers"];
2273
2105
  if (Array.isArray(triggers_raw)) {
@@ -2354,7 +2186,7 @@ function _buildConfig(raw, projectRaw = {}) {
2354
2186
  mcp.confine_reads_to_project_root = validatedBool(mcp_raw["confine_reads_to_project_root"], mcp.confine_reads_to_project_root);
2355
2187
  mcp.confine_reads_to_project_root = envBool("TOKEN_GOAT_MCP_CONFINE_READS", mcp.confine_reads_to_project_root);
2356
2188
  mcp.allowed_roots = validatedStrList(mcp_raw["allowed_roots"], mcp.allowed_roots);
2357
- mcp.allowed_roots = envStrList("TOKEN_GOAT_MCP_ALLOWED_ROOTS", mcp.allowed_roots, path3.delimiter);
2189
+ mcp.allowed_roots = envStrList("TOKEN_GOAT_MCP_ALLOWED_ROOTS", mcp.allowed_roots, path2.delimiter);
2358
2190
  const hs_raw = section(raw, "hint_stats");
2359
2191
  const hs = getDefaultConfig("hint_stats");
2360
2192
  hs.suppress_threshold_pct = validatedInt(hs_raw["suppress_threshold_pct"], hs.suppress_threshold_pct, ...boundsOf("hint_stats.suppress_threshold_pct"));
@@ -2380,7 +2212,6 @@ function _buildConfig(raw, projectRaw = {}) {
2380
2212
  screenshot: sc_cfg,
2381
2213
  repomap: rm,
2382
2214
  overflow_guard: og,
2383
- stats: st,
2384
2215
  hints: hi,
2385
2216
  hooks: hk,
2386
2217
  webfetch: wf,
@@ -2414,7 +2245,6 @@ var CONFIG_KEY_ENV_OVERRIDES = {
2414
2245
  "skill_preservation.pre_skill_enabled": ["TOKEN_GOAT_PRE_SKILL"],
2415
2246
  "skill_preservation.orphan_sweep_enabled": ["TOKEN_GOAT_ORPHAN_SWEEP"],
2416
2247
  "image_shrink.max_image_pixels": ["TOKEN_GOAT_MAX_IMAGE_PIXELS"],
2417
- "image_shrink.ocr_enabled": ["TOKEN_GOAT_OCR_ENABLED"],
2418
2248
  "image_shrink.ocr_lang": ["TOKEN_GOAT_OCR_LANG"],
2419
2249
  "image_shrink.vision_tier": ["TOKEN_GOAT_VISION_TIER"],
2420
2250
  "screenshot.block_private_targets": ["TOKEN_GOAT_SCREENSHOT_BLOCK_PRIVATE_TARGETS"],
@@ -2430,6 +2260,10 @@ var CONFIG_KEY_ENV_OVERRIDES = {
2430
2260
  "hints.write_rewrite_min_lines": ["TOKEN_GOAT_WRITE_REWRITE_MIN_LINES"],
2431
2261
  "hints.write_rewrite_unchanged_pct": ["TOKEN_GOAT_WRITE_REWRITE_UNCHANGED_PCT"],
2432
2262
  "hints.large_read_redirect_bytes": ["TOKEN_GOAT_LARGE_READ_BYTES"],
2263
+ "hints.first_read_symbol_bytes": ["TOKEN_GOAT_FIRST_READ_SYMBOL_BYTES"],
2264
+ "hints.first_read_symbol_policy": ["TOKEN_GOAT_FIRST_READ_SYMBOL_POLICY"],
2265
+ "hints.write_blind_drop": ["TOKEN_GOAT_WRITE_BLIND_DROP"],
2266
+ "hints.write_sibling_copy": ["TOKEN_GOAT_WRITE_SIBLING_COPY"],
2433
2267
  "hints.warn_unbalanced_shell_quoting": ["TOKEN_GOAT_WARN_UNBALANCED_SHELL_QUOTING"],
2434
2268
  "hints.deny_bash_double_backslash": ["TOKEN_GOAT_DENY_BASH_DOUBLE_BACKSLASH"],
2435
2269
  "hints.serve_diff_on_reread": ["TOKEN_GOAT_SERVE_DIFF_ON_REREAD"],
@@ -2457,6 +2291,8 @@ var CONFIG_KEY_ENV_OVERRIDES = {
2457
2291
  "hints.skeleton_large_sources": ["TOKEN_GOAT_SKELETON_LARGE_SOURCES"],
2458
2292
  "hints.context_threshold_advisory": ["TOKEN_GOAT_CONTEXT_THRESHOLD_ADVISORY"],
2459
2293
  "hints.pre_skill_advisory": ["TOKEN_GOAT_PRE_SKILL_ADVISORY"],
2294
+ "hints.stop_verification_advisory": ["TOKEN_GOAT_STOP_VERIFICATION_ADVISORY"],
2295
+ "hints.agent_receipt": ["TOKEN_GOAT_AGENT_RECEIPT"],
2460
2296
  "hints.quiet_hours": ["TOKEN_GOAT_QUIET_HOURS"],
2461
2297
  "hooks.latency_budget_ms": ["TOKEN_GOAT_HOOK_LATENCY_BUDGET_MS"],
2462
2298
  "hooks.server": ["TOKEN_GOAT_HOOK_SERVER"],
@@ -2568,7 +2404,6 @@ function saveConfig(config, explicitKeys = []) {
2568
2404
  jpeg_quality: is_cfg.jpeg_quality,
2569
2405
  max_image_pixels: is_cfg.max_image_pixels,
2570
2406
  screenshot_redirect: is_cfg.screenshot_redirect,
2571
- ocr_enabled: is_cfg.ocr_enabled,
2572
2407
  ocr_min_confidence: is_cfg.ocr_min_confidence,
2573
2408
  ocr_lang: is_cfg.ocr_lang,
2574
2409
  vision_tier: is_cfg.vision_tier
@@ -2585,9 +2420,6 @@ function saveConfig(config, explicitKeys = []) {
2585
2420
  enabled: config.overflow_guard.enabled,
2586
2421
  max_tokens: config.overflow_guard.max_tokens
2587
2422
  },
2588
- stats: {
2589
- record_zero_savings: config.stats.record_zero_savings
2590
- },
2591
2423
  hints: {
2592
2424
  quiet_hours: config.hints.quiet_hours,
2593
2425
  json_sidecar: config.hints.json_sidecar,
@@ -2606,9 +2438,16 @@ function saveConfig(config, explicitKeys = []) {
2606
2438
  git_hint_max_ms: config.hints.git_hint_max_ms,
2607
2439
  min_session_hint_savings_bytes: config.hints.min_session_hint_savings_bytes,
2608
2440
  pre_skill_advisory: config.hints.pre_skill_advisory,
2441
+ stop_verification_advisory: config.hints.stop_verification_advisory,
2442
+ agent_receipt: config.hints.agent_receipt,
2609
2443
  context_threshold_advisory: config.hints.context_threshold_advisory,
2610
2444
  diff_hint_min_tokens_saved: config.hints.diff_hint_min_tokens_saved,
2611
2445
  large_read_redirect_bytes: config.hints.large_read_redirect_bytes,
2446
+ first_read_symbol_bytes: config.hints.first_read_symbol_bytes,
2447
+ first_read_symbol_policy: config.hints.first_read_symbol_policy,
2448
+ write_blind_drop: config.hints.write_blind_drop,
2449
+ write_sibling_copy: config.hints.write_sibling_copy,
2450
+ write_sibling_copy_allow: config.hints.write_sibling_copy_allow,
2612
2451
  reread_deny: config.hints.reread_deny,
2613
2452
  reread_deny_min_bytes: config.hints.reread_deny_min_bytes,
2614
2453
  stable_doc_compacts: config.hints.stable_doc_compacts,
@@ -2701,27 +2540,18 @@ function saveConfig(config, explicitKeys = []) {
2701
2540
  export {
2702
2541
  parse,
2703
2542
  stringify,
2704
- fingerprintContent,
2705
- shortFingerprint,
2706
- indexedFileIsGone,
2707
- fingerprintFile,
2708
- fileIsAbsent,
2709
2543
  DEFAULT_OCR_LANG,
2710
2544
  SUPPORTED_OCR_LANG_CODES,
2711
2545
  isSupportedOcrLang,
2712
2546
  resolveOcrLangs,
2713
2547
  ownGet,
2548
+ RETIRED_CONFIG_KEYS,
2714
2549
  defaultConfig,
2715
- projectHash,
2716
- makeProjectAt,
2717
- findProject,
2718
- getDisplayRoot,
2719
- isUnderSystemTemp,
2720
- resolveProjectRoot,
2721
2550
  PROJECT_LOCKED_SECTIONS,
2722
2551
  PROJECT_LOCKED_KEYS,
2723
2552
  lastProjectConfigLockedKeys,
2724
2553
  readConfigSource,
2554
+ readConfigToml,
2725
2555
  withConfigProjectRoot,
2726
2556
  configProjectRootFor,
2727
2557
  getProjectConfigInfo,
@@ -2731,8 +2561,10 @@ export {
2731
2561
  getLastConfigParseError,
2732
2562
  getLastProjectConfigParseError,
2733
2563
  resolveConfigKeyLayer,
2564
+ globalValueRejection,
2734
2565
  loadConfig,
2735
2566
  invalidateConfigCache,
2567
+ buildPersistedConfig,
2736
2568
  loadPersistedConfig,
2737
2569
  hookServerEnabled,
2738
2570
  nativeHooksEnabled,