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
@@ -1,1597 +0,0 @@
1
- import { createRequire as __cjsRequire } from 'node:module';
2
- const require = __cjsRequire(import.meta.url);
3
- import {
4
- readTranscriptTail
5
- } from "./token-goat-chunk-SJHWVNDQ.mjs";
6
- import {
7
- claudeConfigDir
8
- } from "./token-goat-chunk-BPGCUMR7.mjs";
9
- import {
10
- GENERIC_SERVED_OUTPUT_KEY,
11
- MAX_BASH_START_CWDS,
12
- MAX_GENERIC_SERVED_OUTPUTS,
13
- MAX_OUTSTANDING_AGENT_SPAWNS,
14
- MAX_RANGES_PER_FILE,
15
- MAX_SEEN_IMAGE_HASHES,
16
- MAX_SERVED_OUTPUTS_PER_FILE,
17
- bashStartCwdsAtLoad,
18
- consumedBashStartCwdKeys,
19
- consumedCurlDownloadKeys,
20
- consumedFileLineRangeKeys,
21
- consumedFileServedOutputKeys,
22
- consumedOutstandingAgentSpawnKeys,
23
- consumedPendingLargeFileHintKeys,
24
- curlDownloadsAtLoad,
25
- exportSessionState,
26
- filesFullReadCountAtLoad,
27
- filesReadCountAtLoad,
28
- importSessionState,
29
- markCompacted,
30
- migrateCurlDownloadKey,
31
- migrateWebFetchKey,
32
- outstandingAgentSpawnKey,
33
- outstandingAgentSpawnsAtLoad,
34
- pendingLargeFileHintsAtLoad
35
- } from "./token-goat-chunk-C63JQV5K.mjs";
36
- import {
37
- DEFAULT_MAX_AGE_MS,
38
- DEFAULT_MAX_COUNT
39
- } from "./token-goat-chunk-ARDNEC2U.mjs";
40
- import {
41
- recordStat
42
- } from "./token-goat-chunk-3Z5S4KGM.mjs";
43
- import {
44
- redactSecrets,
45
- redactSerializedJson
46
- } from "./token-goat-chunk-4FVIAHSI.mjs";
47
- import {
48
- isAutoTriggerMultiplierExplicit,
49
- loadConfig,
50
- shortFingerprint
51
- } from "./token-goat-chunk-7IN2MKDS.mjs";
52
- import {
53
- detectHarness
54
- } from "./token-goat-chunk-U4N3BN2W.mjs";
55
- import {
56
- registerReset
57
- } from "./token-goat-chunk-YZAAZU2S.mjs";
58
- import {
59
- LOCK_WAIT_MS_HARDENED,
60
- atomicWriteText,
61
- ensureDirSync,
62
- isCodeFenceDelimiter,
63
- normalizePathForwardSlash,
64
- sanitizeIdForFilename,
65
- stripLower,
66
- withFileLock,
67
- withRetryOnLock
68
- } from "./token-goat-chunk-GOLEOXZP.mjs";
69
- import {
70
- foldPath
71
- } from "./token-goat-chunk-AM5V6GSX.mjs";
72
- import {
73
- normalizePath
74
- } from "./token-goat-chunk-FNFBKLRD.mjs";
75
- import {
76
- dataDir,
77
- tokenGoatHome
78
- } from "./token-goat-chunk-VES7EL7O.mjs";
79
- import {
80
- init_define_import_meta_env
81
- } from "./token-goat-chunk-A37V4PBF.mjs";
82
-
83
- // src/sessions_dir.ts
84
- init_define_import_meta_env();
85
- import * as path from "node:path";
86
- var SESSIONS_SUBDIR = "sessions";
87
- function sessionsDir() {
88
- return path.join(tokenGoatHome(), SESSIONS_SUBDIR);
89
- }
90
-
91
- // src/session_store.ts
92
- init_define_import_meta_env();
93
- import { createHash } from "node:crypto";
94
- import * as fs from "node:fs";
95
- import * as path2 from "node:path";
96
- var MAX_FILES = 500;
97
- var AGENT_SALT_MARKER = sanitizeIdForFilename(":agent:");
98
- var AGENT_SALT_SEPARATOR = ":agent:";
99
- var AGENT_DIGEST_CHARS = 12;
100
- var SALTED_SESSION_MAX = 64 - AGENT_SALT_MARKER.length - AGENT_DIGEST_CHARS;
101
- function saltedStemPrefix(sessionId) {
102
- return `${sanitizeIdForFilename(sessionId, SALTED_SESSION_MAX)}${AGENT_SALT_MARKER}`;
103
- }
104
- function sessionFileStem(sessionId) {
105
- const sep = sessionId.indexOf(AGENT_SALT_SEPARATOR);
106
- if (sep < 0) return sanitizeIdForFilename(sessionId, 64);
107
- const agentId = sessionId.slice(sep + AGENT_SALT_SEPARATOR.length);
108
- const digest = createHash("sha256").update(agentId).digest("hex").slice(0, AGENT_DIGEST_CHARS);
109
- return `${saltedStemPrefix(sessionId.slice(0, sep))}${digest}`;
110
- }
111
- function sessionPath(sessionId) {
112
- return sessionSidecarPath(sessionId, ".json");
113
- }
114
- function sessionSidecarPath(sessionId, suffix) {
115
- if (!sessionId) return null;
116
- const safe = sessionFileStem(sessionId);
117
- if (!safe) return null;
118
- if (suffix.includes("/") || suffix.includes("\\") || suffix.includes("..")) return null;
119
- if (suffix !== ".json" && suffix.endsWith(".json")) return null;
120
- const dir = sessionsDir();
121
- const candidate = path2.join(dir, `${safe}${suffix}`);
122
- try {
123
- const rel = path2.relative(dir, candidate);
124
- if (rel.startsWith("..")) return null;
125
- } catch {
126
- return null;
127
- }
128
- return candidate;
129
- }
130
- function asStringPairs(raw) {
131
- if (!Array.isArray(raw)) return [];
132
- const out = [];
133
- for (const item of raw) {
134
- if (Array.isArray(item) && typeof item[0] === "string" && typeof item[1] === "string") {
135
- out.push([item[0], item[1]]);
136
- }
137
- }
138
- return out;
139
- }
140
- function asFileEntry(raw) {
141
- if (raw === null || typeof raw !== "object") return null;
142
- const o = raw;
143
- if (typeof o["path"] !== "string" || typeof o["readCount"] !== "number" || typeof o["lastReadAt"] !== "number" || typeof o["wasEdited"] !== "boolean" || typeof o["sizeBytes"] !== "number") {
144
- return null;
145
- }
146
- let entry = {
147
- path: o["path"],
148
- readCount: o["readCount"],
149
- lastReadAt: o["lastReadAt"],
150
- wasEdited: o["wasEdited"],
151
- sizeBytes: o["sizeBytes"]
152
- };
153
- if (o["wasTruncated"] === true) entry = { ...entry, wasTruncated: true };
154
- if (typeof o["lastFullReadAt"] === "number") entry = { ...entry, lastFullReadAt: o["lastFullReadAt"] };
155
- if (typeof o["fullReadCount"] === "number") entry = { ...entry, fullReadCount: o["fullReadCount"] };
156
- if (Array.isArray(o["symbols_read"])) {
157
- const symbols = o["symbols_read"].filter((s) => typeof s === "string");
158
- if (symbols.length > 0) entry = { ...entry, symbols_read: symbols };
159
- }
160
- const rangeIdentity = o["rangeFileIdentity"];
161
- if (rangeIdentity !== null && typeof rangeIdentity === "object" && typeof rangeIdentity["size"] === "number" && typeof rangeIdentity["mtimeMs"] === "number") {
162
- entry = { ...entry, rangeFileIdentity: { size: rangeIdentity["size"], mtimeMs: rangeIdentity["mtimeMs"] } };
163
- }
164
- return entry;
165
- }
166
- function asPyFileEntry(dictKey, raw) {
167
- if (raw === null || typeof raw !== "object") return null;
168
- const v = raw;
169
- const p = typeof v["rel_or_abs"] === "string" ? v["rel_or_abs"] : dictKey;
170
- if (!p) return null;
171
- const readCount = typeof v["read_count"] === "number" ? Math.max(1, Math.round(v["read_count"])) : 1;
172
- const lastReadTs = typeof v["last_read_ts"] === "number" ? v["last_read_ts"] : 0;
173
- const sizeBytes = typeof v["read_size"] === "number" ? Math.round(v["read_size"]) : 0;
174
- const wasEdited = typeof v["last_edit_ts"] === "number" && v["last_edit_ts"] > 0;
175
- return { path: p, readCount, lastReadAt: lastReadTs * 1e3, wasEdited, sizeBytes };
176
- }
177
- function asLineRanges(raw) {
178
- if (!Array.isArray(raw)) return [];
179
- const out = [];
180
- for (const pair of raw) {
181
- if (!Array.isArray(pair) || pair.length !== 2) continue;
182
- const filePath = pair[0];
183
- const ranges = pair[1];
184
- if (typeof filePath !== "string" || !Array.isArray(ranges)) continue;
185
- const valid = [];
186
- for (const r of ranges) {
187
- if (Array.isArray(r) && r.length === 2 && typeof r[0] === "number" && typeof r[1] === "number") valid.push([r[0], r[1]]);
188
- }
189
- out.push([filePath, valid]);
190
- }
191
- return out;
192
- }
193
- function asServedOutputs(raw) {
194
- if (!Array.isArray(raw)) return [];
195
- const out = [];
196
- for (const pair of raw) {
197
- if (!Array.isArray(pair) || pair.length !== 2) continue;
198
- const filePath = pair[0];
199
- const ids = pair[1];
200
- if (typeof filePath !== "string" || !Array.isArray(ids)) continue;
201
- out.push([filePath, ids.filter((id) => typeof id === "string")]);
202
- }
203
- return out;
204
- }
205
- function migrateKeys(pairs, migrate) {
206
- return pairs.map(([key, value]) => [migrate(key), value]);
207
- }
208
- function coerce(raw) {
209
- const o = raw !== null && typeof raw === "object" ? raw : {};
210
- const files = [];
211
- if (Array.isArray(o["files"])) {
212
- for (const f of o["files"]) {
213
- const e = asFileEntry(f);
214
- if (e !== null) files.push(e);
215
- }
216
- } else if (o["files"] !== null && typeof o["files"] === "object") {
217
- for (const [key, val] of Object.entries(o["files"])) {
218
- const e = asPyFileEntry(key, val);
219
- if (e !== null) files.push(e);
220
- }
221
- }
222
- const hintsShown = Array.isArray(o["hintsShown"]) ? o["hintsShown"].filter((h) => typeof h === "string") : Array.isArray(o["hints_seen"]) ? o["hints_seen"].filter((h) => typeof h === "string") : [];
223
- const scheduledPromptCounts = Array.isArray(o["scheduledPromptCounts"]) ? o["scheduledPromptCounts"].filter(
224
- (p) => Array.isArray(p) && p.length === 2 && typeof p[0] === "string" && typeof p[1] === "number" && Number.isSafeInteger(p[1]) && p[1] >= 0
225
- ) : [];
226
- const cliReads = Array.isArray(o["cliReads"]) ? o["cliReads"].filter((h) => typeof h === "string") : [];
227
- const bashReruns = Array.isArray(o["bashReruns"]) ? o["bashReruns"].filter((h) => typeof h === "string") : [];
228
- const seenImageHashes = Array.isArray(o["seenImageHashes"]) ? o["seenImageHashes"].filter((h) => typeof h === "string") : [];
229
- const pendingLargeFileHints = Array.isArray(o["pendingLargeFileHints"]) ? o["pendingLargeFileHints"].filter(
230
- (p) => Array.isArray(p) && p.length === 2 && typeof p[0] === "string" && typeof p[1] === "number"
231
- ) : [];
232
- const grepQueries = Array.isArray(o["grepQueries"]) ? o["grepQueries"].filter(
233
- (p) => Array.isArray(p) && p.length === 2 && typeof p[0] === "string" && typeof p[1] === "number"
234
- ) : [];
235
- const globQueries = Array.isArray(o["globQueries"]) ? o["globQueries"].filter(
236
- (p) => Array.isArray(p) && p.length === 2 && typeof p[0] === "string" && typeof p[1] === "number"
237
- ) : [];
238
- const outstandingAgentSpawns = Array.isArray(o["outstandingAgentSpawns"]) ? o["outstandingAgentSpawns"].filter(
239
- (p) => Array.isArray(p) && p.length === 2 && typeof p[0] === "string" && typeof p[1] === "number"
240
- ) : [];
241
- return {
242
- files,
243
- hintsShown,
244
- ...scheduledPromptCounts.length > 0 ? { scheduledPromptCounts } : {},
245
- webFetches: migrateKeys(asStringPairs(o["webFetches"]), migrateWebFetchKey),
246
- bashOutputs: asStringPairs(o["bashOutputs"]),
247
- curlDownloads: migrateKeys(asStringPairs(o["curlDownloads"]), migrateCurlDownloadKey),
248
- fileLineRanges: asLineRanges(o["fileLineRanges"]),
249
- fileServedOutputs: asServedOutputs(o["fileServedOutputs"]),
250
- cliReads,
251
- bashReruns,
252
- pendingLargeFileHints,
253
- bashStartCwds: asStringPairs(o["bashStartCwds"]),
254
- grepQueries,
255
- globQueries,
256
- outstandingAgentSpawns,
257
- ...typeof o["lastTabContextDigest"] === "string" ? { lastTabContextDigest: o["lastTabContextDigest"] } : {},
258
- ...seenImageHashes.length > 0 ? { seenImageHashes } : {},
259
- ...typeof o["compactedAt"] === "number" ? { compactedAt: o["compactedAt"] } : {},
260
- ...typeof o["created_ts"] === "number" ? { created_ts: o["created_ts"] } : {}
261
- };
262
- }
263
- function mergeFileEntry(a, b) {
264
- const newest = a.lastReadAt >= b.lastReadAt ? a : b;
265
- const baseline = filesReadCountAtLoad().get(b.path) ?? 0;
266
- const newReadsThisProcess = Math.max(0, b.readCount - baseline);
267
- let merged = {
268
- path: a.path,
269
- readCount: a.readCount + newReadsThisProcess,
270
- lastReadAt: Math.max(a.lastReadAt, b.lastReadAt),
271
- wasEdited: a.wasEdited || b.wasEdited,
272
- sizeBytes: newest.sizeBytes
273
- };
274
- if (newest.rangeFileIdentity) merged = { ...merged, rangeFileIdentity: newest.rangeFileIdentity };
275
- if (a.wasTruncated || b.wasTruncated) merged = { ...merged, wasTruncated: true };
276
- const lastFullReadAt = Math.max(a.lastFullReadAt ?? 0, b.lastFullReadAt ?? 0);
277
- if (lastFullReadAt > 0) merged = { ...merged, lastFullReadAt };
278
- const fullReadBaseline = filesFullReadCountAtLoad().get(b.path) ?? 0;
279
- const newFullReadsThisProcess = Math.max(0, (b.fullReadCount ?? 0) - fullReadBaseline);
280
- const fullReadCount = (a.fullReadCount ?? 0) + newFullReadsThisProcess;
281
- if (fullReadCount > 0) merged = { ...merged, fullReadCount };
282
- const symbols = Array.from(/* @__PURE__ */ new Set([...a.symbols_read ?? [], ...b.symbols_read ?? []]));
283
- if (symbols.length > 0) merged = { ...merged, symbols_read: symbols };
284
- return merged;
285
- }
286
- function mergePairs(disk, mem) {
287
- return Array.from(new Map([...disk, ...mem]).entries());
288
- }
289
- function mergeMaxNumberPairs(disk, mem) {
290
- const merged = new Map(disk);
291
- for (const [key, value] of mem) {
292
- merged.set(key, Math.max(merged.get(key) ?? 0, value));
293
- }
294
- return Array.from(merged.entries());
295
- }
296
- function mergeLineRanges(disk, mem) {
297
- const byPath = /* @__PURE__ */ new Map();
298
- for (const [filePath, ranges] of disk) byPath.set(filePath, [...ranges]);
299
- for (const [filePath, ranges] of mem) {
300
- const prev = byPath.get(filePath) ?? [];
301
- const seen = new Set(prev.map(([s, e]) => s + ":" + e));
302
- for (const [s, e] of ranges) {
303
- const key = s + ":" + e;
304
- if (seen.has(key) || prev.length >= MAX_RANGES_PER_FILE) continue;
305
- prev.push([s, e]);
306
- seen.add(key);
307
- }
308
- byPath.set(filePath, prev);
309
- }
310
- return Array.from(byPath.entries());
311
- }
312
- var GENERIC_SERVED_OUTPUT_FOLDED_KEY = foldPath(normalizePath(GENERIC_SERVED_OUTPUT_KEY));
313
- function mergeServedOutputs(disk, mem) {
314
- const byPath = /* @__PURE__ */ new Map();
315
- for (const [filePath, ids] of disk) byPath.set(filePath, [...ids]);
316
- for (const [filePath, ids] of mem) {
317
- const prev = byPath.get(filePath) ?? [];
318
- const merged = [...prev.filter((id) => !ids.includes(id)), ...ids];
319
- const cap = filePath === GENERIC_SERVED_OUTPUT_FOLDED_KEY ? MAX_GENERIC_SERVED_OUTPUTS : MAX_SERVED_OUTPUTS_PER_FILE;
320
- if (merged.length > cap) merged.splice(0, merged.length - cap);
321
- byPath.set(filePath, merged);
322
- }
323
- return Array.from(byPath.entries());
324
- }
325
- function mergePendingLargeFileHints(disk, mem) {
326
- const merged = new Map(disk);
327
- for (const key of consumedPendingLargeFileHintKeys()) merged.delete(key);
328
- const atLoad = pendingLargeFileHintsAtLoad();
329
- for (const [key, size] of mem) {
330
- if (atLoad.get(key) === size) continue;
331
- merged.set(key, size);
332
- }
333
- return Array.from(merged.entries());
334
- }
335
- function mergeCurlDownloads(disk, mem) {
336
- const merged = new Map(disk);
337
- for (const key of consumedCurlDownloadKeys()) merged.delete(key);
338
- const atLoad = curlDownloadsAtLoad();
339
- for (const [url, savedPath] of mem) {
340
- if (atLoad.get(url) === savedPath) continue;
341
- merged.set(url, savedPath);
342
- }
343
- return Array.from(merged.entries());
344
- }
345
- function mergeBashStartCwds(disk, mem) {
346
- const merged = new Map(disk);
347
- for (const key of consumedBashStartCwdKeys()) merged.delete(key);
348
- const atLoad = bashStartCwdsAtLoad();
349
- for (const [toolUseId, cwd] of mem) {
350
- if (atLoad.get(toolUseId) === cwd) continue;
351
- merged.set(toolUseId, cwd);
352
- }
353
- const entries = Array.from(merged.entries());
354
- return entries.length > MAX_BASH_START_CWDS ? entries.slice(entries.length - MAX_BASH_START_CWDS) : entries;
355
- }
356
- function mergeOutstandingAgentSpawns(disk, mem) {
357
- const merged = new Map(disk.map(([prompt, ts]) => [outstandingAgentSpawnKey(prompt, ts), [prompt, ts]]));
358
- for (const key of consumedOutstandingAgentSpawnKeys()) merged.delete(key);
359
- const atLoad = new Set(outstandingAgentSpawnsAtLoad().map((e) => outstandingAgentSpawnKey(e.prompt, e.ts)));
360
- for (const [prompt, ts] of mem) {
361
- const key = outstandingAgentSpawnKey(prompt, ts);
362
- if (atLoad.has(key)) continue;
363
- merged.set(key, [prompt, ts]);
364
- }
365
- const result = Array.from(merged.values()).sort((a, b) => a[1] - b[1]);
366
- if (result.length > MAX_OUTSTANDING_AGENT_SPAWNS) {
367
- result.splice(0, result.length - MAX_OUTSTANDING_AGENT_SPAWNS);
368
- }
369
- return result;
370
- }
371
- function mergeSessionState(disk, mem) {
372
- const byPath = /* @__PURE__ */ new Map();
373
- for (const e of disk.files) byPath.set(foldPath(e.path), e);
374
- for (const e of mem.files) {
375
- const key = foldPath(e.path);
376
- const prev = byPath.get(key);
377
- byPath.set(key, prev ? mergeFileEntry(prev, e) : e);
378
- }
379
- const compactedAt = Math.max(disk.compactedAt ?? 0, mem.compactedAt ?? 0);
380
- const rawDiskRanges = (disk.compactedAt ?? 0) === compactedAt ? disk.fileLineRanges ?? [] : [];
381
- const memRanges = (mem.compactedAt ?? 0) === compactedAt ? mem.fileLineRanges ?? [] : [];
382
- const rawDiskServed = (disk.compactedAt ?? 0) === compactedAt ? disk.fileServedOutputs ?? [] : [];
383
- const memServed = (mem.compactedAt ?? 0) === compactedAt ? mem.fileServedOutputs ?? [] : [];
384
- const rawDiskCliReads = (disk.compactedAt ?? 0) === compactedAt ? disk.cliReads ?? [] : [];
385
- const memCliReads = (mem.compactedAt ?? 0) === compactedAt ? mem.cliReads ?? [] : [];
386
- const clearedRangeFiles = new Set(consumedFileLineRangeKeys());
387
- const clearedServedFiles = new Set(consumedFileServedOutputKeys());
388
- const diskRanges = rawDiskRanges.filter(([path4]) => !clearedRangeFiles.has(path4));
389
- const diskServed = rawDiskServed.filter(([path4]) => !clearedServedFiles.has(path4));
390
- return {
391
- files: Array.from(byPath.values()),
392
- hintsShown: Array.from(/* @__PURE__ */ new Set([...disk.hintsShown, ...mem.hintsShown])),
393
- scheduledPromptCounts: mergeMaxNumberPairs(disk.scheduledPromptCounts ?? [], mem.scheduledPromptCounts ?? []),
394
- webFetches: mergePairs(disk.webFetches, mem.webFetches),
395
- bashOutputs: mergePairs(disk.bashOutputs, mem.bashOutputs),
396
- curlDownloads: mergeCurlDownloads(disk.curlDownloads, mem.curlDownloads),
397
- fileLineRanges: mergeLineRanges(diskRanges, memRanges),
398
- fileServedOutputs: mergeServedOutputs(diskServed, memServed),
399
- ...compactedAt > 0 ? { compactedAt } : {},
400
- // Same epoch filter as the line ranges above: a CLI read recorded before the winning compaction is not in the model's context any more, so merging it back would resurrect the "you already ran this" note the compaction just cleared.
401
- cliReads: Array.from(/* @__PURE__ */ new Set([...rawDiskCliReads, ...memCliReads])),
402
- bashReruns: Array.from(/* @__PURE__ */ new Set([...disk.bashReruns ?? [], ...mem.bashReruns ?? []])),
403
- pendingLargeFileHints: mergePendingLargeFileHints(disk.pendingLargeFileHints ?? [], mem.pendingLargeFileHints ?? []),
404
- bashStartCwds: mergeBashStartCwds(disk.bashStartCwds ?? [], mem.bashStartCwds ?? []),
405
- grepQueries: mergePairs(disk.grepQueries ?? [], mem.grepQueries ?? []),
406
- globQueries: mergePairs(disk.globQueries ?? [], mem.globQueries ?? []),
407
- outstandingAgentSpawns: mergeOutstandingAgentSpawns(disk.outstandingAgentSpawns ?? [], mem.outstandingAgentSpawns ?? []),
408
- // An accumulating collection, so union rather than pick a winner: two hook processes can each see a screenshot the other never did. Disk first, then mem, so the order stays oldest-to-newest and the cap evicts the oldest -- the same policy recordSeenImage applies in memory, applied again here because a union of two capped lists can exceed the cap.
409
- seenImageHashes: mergeSeenImageHashes(disk.seenImageHashes ?? [], mem.seenImageHashes ?? []),
410
- // Last-seen scalar, not an accumulating collection: prefer mem's value (this process's freshest observation) over disk's, since a newer write always supersedes an older one.
411
- ...mem.lastTabContextDigest !== void 0 ? { lastTabContextDigest: mem.lastTabContextDigest } : disk.lastTabContextDigest !== void 0 ? { lastTabContextDigest: disk.lastTabContextDigest } : {},
412
- // Prefer the value already on disk: it marks the original creation time, and must never be bumped forward to the merge's "now". `mem` never carries one (it is not tracked in memory), so this is really "keep whatever disk has".
413
- ...disk.created_ts !== void 0 ? { created_ts: disk.created_ts } : mem.created_ts !== void 0 ? { created_ts: mem.created_ts } : {}
414
- };
415
- }
416
- function mergeSeenImageHashes(disk, mem) {
417
- const merged = Array.from(/* @__PURE__ */ new Set([...disk, ...mem]));
418
- return merged.length > MAX_SEEN_IMAGE_HASHES ? merged.slice(merged.length - MAX_SEEN_IMAGE_HASHES) : merged;
419
- }
420
- function capFiles(s, max) {
421
- if (s.files.length <= max) return s;
422
- const kept = [...s.files].sort((a, b) => a.wasEdited === b.wasEdited ? b.lastReadAt - a.lastReadAt : a.wasEdited ? -1 : 1).slice(0, max);
423
- return { ...s, files: kept };
424
- }
425
- function readDiskState(p) {
426
- try {
427
- return readDiskStateForMerge(p);
428
- } catch {
429
- return null;
430
- }
431
- }
432
- function readDiskStateForMerge(p) {
433
- let text = "";
434
- try {
435
- withRetryOnLock(() => {
436
- text = fs.readFileSync(p, "utf8");
437
- });
438
- } catch (err) {
439
- if (err.code === "ENOENT") return null;
440
- throw err;
441
- }
442
- try {
443
- return coerce(JSON.parse(text));
444
- } catch {
445
- return null;
446
- }
447
- }
448
- function readSessionStateFile(sessionId) {
449
- const p = sessionPath(sessionId);
450
- if (!p) return null;
451
- return readDiskState(p);
452
- }
453
- function listSiblingSessionStates(sessionId) {
454
- if (!sessionId) return [];
455
- const prefix = saltedStemPrefix(sessionId);
456
- if (prefix === AGENT_SALT_MARKER) return [];
457
- const dir = sessionsDir();
458
- const out = [];
459
- try {
460
- if (!fs.existsSync(dir)) return out;
461
- for (const file of fs.readdirSync(dir)) {
462
- if (!file.endsWith(".json") || !file.startsWith(prefix)) continue;
463
- const state = readDiskState(path2.join(dir, file));
464
- if (state !== null) out.push(state);
465
- }
466
- } catch {
467
- return out;
468
- }
469
- return out;
470
- }
471
- var EMPTY_SESSION = { files: [], hintsShown: [], webFetches: [], bashOutputs: [], curlDownloads: [] };
472
- function loadSessionState(sessionId) {
473
- const p = sessionPath(sessionId);
474
- importSessionState((p ? readDiskState(p) : null) ?? EMPTY_SESSION);
475
- applySessionCompaction(sessionId);
476
- }
477
- var COMPACTED_AT_SUFFIX = ".compacted-at";
478
- function applySessionCompaction(stateKey) {
479
- const at = stateKey.indexOf(":agent:");
480
- if (at < 0) return;
481
- const p = sessionSidecarPath(stateKey.slice(0, at), COMPACTED_AT_SUFFIX);
482
- if (!p) return;
483
- try {
484
- const epoch = Number(fs.readFileSync(p, "utf8").trim());
485
- if (Number.isFinite(epoch) && epoch > 0) markCompacted(epoch);
486
- } catch {
487
- }
488
- }
489
- function saveSessionState(sessionId) {
490
- const p = sessionPath(sessionId);
491
- if (!p) return;
492
- try {
493
- const dir = path2.dirname(p);
494
- if (!fs.existsSync(dir)) ensureDirSync(dir);
495
- const mem = exportSessionState();
496
- const writeMerged = () => {
497
- const disk = readDiskStateForMerge(p);
498
- const merged = capFiles(disk ? mergeSessionState(disk, mem) : mem, MAX_FILES);
499
- if (merged.created_ts === void 0) merged.created_ts = Date.now() / 1e3;
500
- let json;
501
- try {
502
- json = redactSerializedJson(JSON.stringify(merged)).text;
503
- } catch {
504
- return true;
505
- }
506
- atomicWriteText(p, json);
507
- return true;
508
- };
509
- if (withFileLock(`${p}.lock`, writeMerged, { waitMs: LOCK_WAIT_MS_HARDENED }) === void 0) writeMerged();
510
- } catch {
511
- }
512
- }
513
-
514
- // src/skill_cache.ts
515
- init_define_import_meta_env();
516
- import * as fs2 from "fs/promises";
517
- import { resolve } from "path";
518
- import { readdirSync as readdirSync2, readFileSync as readFileSync2, existsSync as existsSync2, statSync, unlinkSync } from "node:fs";
519
- var COMPACT_END_MARKER = "<!-- COMPACT_END -->";
520
- var SKILLS_OUTPUT_SUBDIR = "skills";
521
- var _skillOutputsDirOverride = null;
522
- var _skillsSourceDirOverride = null;
523
- var _pluginsManifestPathOverride = null;
524
- registerReset(() => {
525
- _skillOutputsDirOverride = null;
526
- _skillsSourceDirOverride = null;
527
- _pluginsManifestPathOverride = null;
528
- inProcessHitLockQueues.clear();
529
- });
530
- function skillOutputsDir() {
531
- if (_skillOutputsDirOverride) return _skillOutputsDirOverride;
532
- return resolve(dataDir(), SKILLS_OUTPUT_SUBDIR);
533
- }
534
- function skillsSourceDir() {
535
- if (_skillsSourceDirOverride) return _skillsSourceDirOverride;
536
- return resolve(claudeConfigDir(), "skills");
537
- }
538
- async function ensureSkillsDir() {
539
- try {
540
- ensureDirSync(skillOutputsDir());
541
- } catch {
542
- }
543
- }
544
- function contentHash(content) {
545
- return shortFingerprint(content);
546
- }
547
- function safeSessionFragment(sessionId) {
548
- let result = "";
549
- for (const c of sessionId) {
550
- if (result.length >= 16) break;
551
- if (/[a-zA-Z0-9_-]/.test(c)) {
552
- result += c;
553
- }
554
- }
555
- return result.slice(0, 16);
556
- }
557
- function safeSkillName(skillName) {
558
- if (!skillName || skillName.length > 128) return null;
559
- if (!/^[A-Za-z0-9_:-]+$/.test(skillName)) return null;
560
- return skillName.replace(/[^a-zA-Z0-9_:-]/g, "_");
561
- }
562
- function sanitizeSkillId(name) {
563
- return name.replace(/:/g, "~");
564
- }
565
- function sessionSkillPrefix(sessionId, skillName) {
566
- const safeSession = safeSessionFragment(sessionId);
567
- return `${safeSession}-${sanitizeSkillId(skillName)}-`;
568
- }
569
- function outputIdFor(sessionId, skillName, contentSha) {
570
- return `${sessionSkillPrefix(sessionId, skillName)}${contentSha}`;
571
- }
572
- var LOAD_RECORD_EXT = ".load";
573
- function readLoadRecordsSync(dir) {
574
- const records = [];
575
- let files;
576
- try {
577
- files = readdirSync2(dir);
578
- } catch {
579
- return records;
580
- }
581
- for (const file of files) {
582
- if (!file.endsWith(LOAD_RECORD_EXT)) continue;
583
- try {
584
- const parsed = JSON.parse(readFileSync2(resolve(dir, file), "utf-8"));
585
- if (typeof parsed.skillName !== "string" || typeof parsed.bodyOutputId !== "string") continue;
586
- records.push({ ...parsed, loadId: file.slice(0, -LOAD_RECORD_EXT.length) });
587
- } catch {
588
- continue;
589
- }
590
- }
591
- return records;
592
- }
593
- async function sessionLoads(safeSession, name) {
594
- const metas = await listOutputs();
595
- const own = metas.filter((m) => m.skillName === name && m.outputId.startsWith(`${safeSession}-`));
596
- const storedIds = new Set(metas.map((m) => m.outputId));
597
- const recorded = readLoadRecordsSync(skillOutputsDir()).filter(
598
- (r) => r.skillName === name && r.loadId.startsWith(`${safeSession}-`) && storedIds.has(r.bodyOutputId)
599
- );
600
- return [...own, ...recorded].map((e) => ({ bodyBytes: e.bodyBytes, ts: e.ts }));
601
- }
602
- function* contentLineEntries(lines) {
603
- let inCodeBlock = false;
604
- for (let i = 0; i < lines.length; i++) {
605
- const stripped = lines[i].trim();
606
- if (isCodeFenceDelimiter(stripped)) {
607
- inCodeBlock = !inCodeBlock;
608
- continue;
609
- }
610
- if (!inCodeBlock) yield [i, stripped];
611
- }
612
- }
613
- function headingLevel(stripped) {
614
- const match = stripped.match(/^(#{1,6}) (.*)$/);
615
- if (!match) return 0;
616
- return match[2].trim() ? match[1].length : 0;
617
- }
618
- function collectSectionBody(lines, startIdx, matchedLevel) {
619
- const bodyLines = [];
620
- let inBodyCodeBlock = false;
621
- for (let j = startIdx; j < lines.length; j++) {
622
- const stripped = lines[j].trim();
623
- if (isCodeFenceDelimiter(stripped)) {
624
- inBodyCodeBlock = !inBodyCodeBlock;
625
- }
626
- if (!inBodyCodeBlock) {
627
- const level = headingLevel(stripped);
628
- if (level >= 1 && level <= matchedLevel) break;
629
- }
630
- bodyLines.push(lines[j]);
631
- }
632
- return bodyLines;
633
- }
634
- function extractCompactFromMarker(body) {
635
- if (!body || !body.includes(COMPACT_END_MARKER)) {
636
- return null;
637
- }
638
- const lines = body.split("\n");
639
- for (const [i, stripped] of contentLineEntries(lines)) {
640
- if (stripped === COMPACT_END_MARKER) {
641
- const preMarker = lines.slice(0, i).join("\n").trim();
642
- return preMarker || null;
643
- }
644
- }
645
- return null;
646
- }
647
- function parseSectionOrdinal(heading) {
648
- const match = heading.match(/^(.*?)(?:#(\d+))?$/);
649
- if (!match) return [heading, 1];
650
- const baseHeading = match[1] || heading;
651
- const ordinal = match[2] ? Math.max(1, parseInt(match[2], 10)) : 1;
652
- return [baseHeading, ordinal];
653
- }
654
- function extractNamedSection(body, heading) {
655
- if (!body || !heading) return null;
656
- const [baseHeading, ordinal] = parseSectionOrdinal(heading);
657
- const headingLower = stripLower(baseHeading);
658
- const lines = body.split("\n");
659
- let matchCount = 0;
660
- let startIdx = -1;
661
- let matchedLevel = 0;
662
- for (const [i, stripped] of contentLineEntries(lines)) {
663
- const level = headingLevel(stripped);
664
- if (level >= 1 && level <= 3) {
665
- const headingText = stripLower(stripped.slice(level));
666
- if (headingText === headingLower) {
667
- matchCount++;
668
- if (matchCount === ordinal) {
669
- startIdx = i + 1;
670
- matchedLevel = level;
671
- break;
672
- }
673
- }
674
- }
675
- }
676
- if (startIdx === -1) return null;
677
- const text = collectSectionBody(lines, startIdx, matchedLevel).join("\n").trim();
678
- return text || null;
679
- }
680
- function extractChecklistSection(body) {
681
- if (!body) return null;
682
- const checklistHeadings = ["checklist", "check list", "to-do", "todo"];
683
- const lines = body.split("\n");
684
- let bestPriority = checklistHeadings.length;
685
- let bestStart = -1;
686
- for (const [i, stripped] of contentLineEntries(lines)) {
687
- if (stripped.startsWith("## ") && stripped.length > 3) {
688
- const headingText = stripLower(stripped.slice(3));
689
- for (let p = 0; p < checklistHeadings.length; p++) {
690
- if (headingText.startsWith(checklistHeadings[p])) {
691
- if (p < bestPriority) {
692
- bestPriority = p;
693
- bestStart = i;
694
- }
695
- break;
696
- }
697
- }
698
- }
699
- }
700
- if (bestStart === -1) return null;
701
- const bodyLines = collectSectionBody(lines, bestStart + 1, 2);
702
- let text = bodyLines.join("\n").trim();
703
- const maxChars = 2e3;
704
- if (text.length > maxChars) {
705
- const cut = maxChars - 1;
706
- text = text.slice(0, cut).trimEnd() + "\u2026";
707
- }
708
- return text || null;
709
- }
710
- async function listOutputs() {
711
- try {
712
- const dir = skillOutputsDir();
713
- const entries = await fs2.readdir(dir, { withFileTypes: true });
714
- const metas = [];
715
- for (const entry of entries) {
716
- if (!entry.isFile() || !entry.name.endsWith(".meta")) {
717
- continue;
718
- }
719
- try {
720
- const content = await fs2.readFile(resolve(dir, entry.name), "utf-8");
721
- const meta = JSON.parse(content);
722
- metas.push(meta);
723
- } catch {
724
- continue;
725
- }
726
- }
727
- return metas;
728
- } catch {
729
- return [];
730
- }
731
- }
732
- async function hasSessionOutput(sessionId, skillName) {
733
- try {
734
- if (!sessionId) return false;
735
- const name = safeSkillName(skillName);
736
- if (!name) return false;
737
- return (await sessionLoads(safeSessionFragment(sessionId), name)).length > 0;
738
- } catch {
739
- return false;
740
- }
741
- }
742
- async function sessionOutputBodyBytes(sessionId, skillName) {
743
- try {
744
- if (!sessionId) return null;
745
- const name = safeSkillName(skillName);
746
- if (!name) return null;
747
- const matches = await sessionLoads(safeSessionFragment(sessionId), name);
748
- if (matches.length === 0) return null;
749
- matches.sort((a, b) => b.ts - a.ts);
750
- return matches[0].bodyBytes;
751
- } catch {
752
- return null;
753
- }
754
- }
755
- async function findCrossSessionEntry(skillName, contentSha) {
756
- const name = safeSkillName(skillName);
757
- if (!name || !contentSha) return null;
758
- try {
759
- const metas = await listOutputs();
760
- for (const meta of metas) {
761
- if (meta.skillName !== name || meta.contentSha !== contentSha) {
762
- continue;
763
- }
764
- const dir = skillOutputsDir();
765
- const bodyPath = resolve(dir, `${meta.outputId}.txt`);
766
- try {
767
- const bodyExists = await fs2.access(bodyPath).then(() => true).catch(() => false);
768
- if (bodyExists) {
769
- return meta;
770
- }
771
- } catch {
772
- continue;
773
- }
774
- }
775
- } catch {
776
- return null;
777
- }
778
- return null;
779
- }
780
- async function storeOutput(sessionId, skillName, body, opts) {
781
- try {
782
- await ensureSkillsDir();
783
- const name = safeSkillName(skillName);
784
- if (!name) return null;
785
- const sha = contentHash(body);
786
- const outId = outputIdFor(sessionId, skillName, sha);
787
- const dir = skillOutputsDir();
788
- const ts = Date.now();
789
- const existing = await findCrossSessionEntry(skillName, sha);
790
- if (existing) {
791
- if (existing.outputId !== outId && sessionId) {
792
- const record = {
793
- skillName: name,
794
- contentSha: sha,
795
- bodyOutputId: existing.outputId,
796
- bodyBytes: Buffer.byteLength(body, "utf-8"),
797
- ts
798
- };
799
- await atomicWriteText(resolve(dir, `${outId}${LOAD_RECORD_EXT}`), JSON.stringify(record, null, 2));
800
- }
801
- return existing;
802
- }
803
- const bodyBytes = Buffer.byteLength(body, "utf-8");
804
- const truncated = bodyBytes > 256 * 1024;
805
- const redactedBody = redactSecrets(body);
806
- if (redactedBody.count > 0) recordStat("secret_redacted", 0, redactedBody.count, void 0, SKILLS_OUTPUT_SUBDIR);
807
- let storedBody = redactedBody.text;
808
- if (truncated) {
809
- const buf = Buffer.from(redactedBody.text, "utf-8");
810
- let truncStart = Math.max(0, buf.length - 262144);
811
- if (truncStart < buf.length) {
812
- const byte = buf[truncStart];
813
- if ((byte & 192) === 128) {
814
- let charStart = truncStart - 1;
815
- while (charStart >= 0 && (buf[charStart] & 192) === 128) {
816
- charStart--;
817
- }
818
- if (charStart >= 0) {
819
- const leadByte = buf[charStart];
820
- let charLen = 1;
821
- if ((leadByte & 128) === 0) charLen = 1;
822
- else if ((leadByte & 224) === 192) charLen = 2;
823
- else if ((leadByte & 240) === 224) charLen = 3;
824
- else if ((leadByte & 248) === 240) charLen = 4;
825
- truncStart = charStart + charLen;
826
- }
827
- }
828
- }
829
- const truncBuf = buf.slice(truncStart);
830
- storedBody = truncBuf.toString("utf-8");
831
- }
832
- await atomicWriteText(resolve(dir, `${outId}.txt`), storedBody);
833
- const meta = {
834
- outputId: outId,
835
- skillName: name,
836
- contentSha: sha,
837
- bodyBytes,
838
- ts,
839
- truncated,
840
- sourcePath: opts?.sourcePath || ""
841
- };
842
- await atomicWriteText(resolve(dir, `${outId}.meta`), JSON.stringify(meta, null, 2));
843
- pruneSkillOutputs();
844
- return meta;
845
- } catch {
846
- return null;
847
- }
848
- }
849
- async function storeCompact(sessionId, skillName, compactText, sourceSha) {
850
- try {
851
- await ensureSkillsDir();
852
- const name = safeSkillName(skillName);
853
- if (!name) return;
854
- const safeSession = safeSessionFragment(sessionId);
855
- const fileId = `${safeSession}@${sanitizeSkillId(name)}@compact`;
856
- const dir = skillOutputsDir();
857
- const redacted = redactSecrets(compactText);
858
- if (redacted.count > 0) recordStat("secret_redacted", 0, redacted.count, void 0, SKILLS_OUTPUT_SUBDIR);
859
- let text = redacted.text;
860
- if (sourceSha) {
861
- text = `<!-- source_sha: ${sourceSha.slice(0, 12)} -->
862
- ${text}`;
863
- }
864
- await atomicWriteText(resolve(dir, fileId), text);
865
- } catch {
866
- }
867
- }
868
- function compactSessionSuffix(skillName) {
869
- const name = safeSkillName(skillName);
870
- return name ? `@${sanitizeSkillId(name)}@compact` : null;
871
- }
872
- function matchesCompactSuffix(entryName, isFile, suffix) {
873
- return isFile && entryName.endsWith("@compact") && entryName.endsWith(suffix);
874
- }
875
- function getCompactAnySessionSync(skillName) {
876
- try {
877
- const suffix = compactSessionSuffix(skillName);
878
- if (!suffix) return null;
879
- const dir = skillOutputsDir();
880
- const entries = readdirSync2(dir, { withFileTypes: true });
881
- for (const entry of entries) {
882
- if (!matchesCompactSuffix(entry.name, entry.isFile(), suffix)) continue;
883
- try {
884
- const text = readFileSync2(resolve(dir, entry.name), "utf-8");
885
- if (text.trim()) return text;
886
- } catch {
887
- continue;
888
- }
889
- }
890
- return null;
891
- } catch {
892
- return null;
893
- }
894
- }
895
- function extractSourceShaFromCompact(compactText) {
896
- if (!compactText) return null;
897
- const match = compactText.match(/<!--\s*source_sha:\s*([a-f0-9]{12})\s*-->/);
898
- return match ? match[1] : null;
899
- }
900
- function isCompactStale(compactText, skillName, currentBodySha) {
901
- if (!compactText) return null;
902
- const embeddedSha = extractSourceShaFromCompact(compactText);
903
- if (!embeddedSha) return null;
904
- const currentSha = currentBodySha.slice(0, 12);
905
- return embeddedSha !== currentSha;
906
- }
907
- async function readSkillHits(skillName) {
908
- try {
909
- const name = safeSkillName(skillName);
910
- if (!name) return { count: 0, lastTs: 0 };
911
- const dir = skillOutputsDir();
912
- const hitsFile = resolve(dir, `${sanitizeSkillId(name)}.hits`);
913
- const content = await fs2.readFile(hitsFile, "utf-8").catch(() => null);
914
- if (content) {
915
- const parsed = JSON.parse(content);
916
- return parsed;
917
- }
918
- } catch {
919
- }
920
- return { count: 0, lastTs: 0 };
921
- }
922
- var SKILL_HIT_LOCK_RETRY_MS = 5;
923
- var SKILL_HIT_LOCK_STALE_MS = 5e3;
924
- var SKILL_HIT_LOCK_MAX_ATTEMPTS = Math.ceil(SKILL_HIT_LOCK_STALE_MS / SKILL_HIT_LOCK_RETRY_MS);
925
- var inProcessHitLockQueues = /* @__PURE__ */ new Map();
926
- function runExclusiveInProcess(key, fn) {
927
- const prior = inProcessHitLockQueues.get(key) ?? Promise.resolve();
928
- const run = prior.then(fn, fn);
929
- inProcessHitLockQueues.set(
930
- key,
931
- run.then(
932
- () => void 0,
933
- () => void 0
934
- )
935
- );
936
- return run;
937
- }
938
- async function acquireSkillHitLock(lockPath) {
939
- for (let attempt = 0; attempt < SKILL_HIT_LOCK_MAX_ATTEMPTS; attempt++) {
940
- try {
941
- await fs2.mkdir(lockPath);
942
- return true;
943
- } catch (err) {
944
- if (err.code !== "EEXIST") return false;
945
- try {
946
- const st = await fs2.stat(lockPath);
947
- if (Date.now() - st.mtimeMs > SKILL_HIT_LOCK_STALE_MS) {
948
- await fs2.rmdir(lockPath).catch(() => {
949
- });
950
- continue;
951
- }
952
- } catch {
953
- continue;
954
- }
955
- await new Promise((r) => setTimeout(r, SKILL_HIT_LOCK_RETRY_MS));
956
- }
957
- }
958
- return false;
959
- }
960
- async function incrementSkillHit(skillName) {
961
- try {
962
- const name = safeSkillName(skillName);
963
- if (!name) return;
964
- await ensureSkillsDir();
965
- const dir = skillOutputsDir();
966
- const hitsFile = resolve(dir, `${sanitizeSkillId(name)}.hits`);
967
- const lockPath = `${hitsFile}.lock`;
968
- await runExclusiveInProcess(lockPath, async () => {
969
- const locked = await acquireSkillHitLock(lockPath);
970
- if (!locked) return;
971
- try {
972
- const hits = await readSkillHits(name);
973
- hits.count++;
974
- hits.lastTs = Date.now();
975
- await atomicWriteText(hitsFile, JSON.stringify(hits, null, 2));
976
- } finally {
977
- await fs2.rmdir(lockPath).catch(() => {
978
- });
979
- }
980
- });
981
- } catch {
982
- }
983
- }
984
- function formatAge(ageMs) {
985
- const s = Math.floor(ageMs / 1e3);
986
- if (s < 60) return `${s}s`;
987
- const m = Math.floor(s / 60);
988
- if (m < 60) return `${m}m`;
989
- const h = Math.floor(m / 60);
990
- if (h < 24) return `${h}h`;
991
- const d = Math.floor(h / 24);
992
- return `${d}d`;
993
- }
994
- async function listSkills(sessionId) {
995
- try {
996
- const metas = (await listOutputs()).sort((a, b) => b.ts - a.ts);
997
- const results = [];
998
- const seen = /* @__PURE__ */ new Set();
999
- for (const meta of metas) {
1000
- if (sessionId && !meta.outputId.startsWith(`${safeSessionFragment(sessionId)}-`)) {
1001
- continue;
1002
- }
1003
- if (seen.has(meta.skillName)) continue;
1004
- seen.add(meta.skillName);
1005
- const dir = skillOutputsDir();
1006
- const sanitizedName = sanitizeSkillId(meta.skillName);
1007
- const outputSuffix = `-${sanitizedName}-${meta.contentSha}`;
1008
- const safeSession = meta.outputId.endsWith(outputSuffix) ? meta.outputId.slice(0, meta.outputId.length - outputSuffix.length) : safeSessionFragment(meta.outputId.split("-")[0]);
1009
- const compactFileId = `${safeSession}@${sanitizedName}@compact`;
1010
- let compactLen = 0;
1011
- let compactText = "";
1012
- try {
1013
- const stat2 = await fs2.stat(resolve(dir, compactFileId));
1014
- compactLen = stat2.size;
1015
- compactText = await fs2.readFile(resolve(dir, compactFileId), "utf-8").catch(() => "");
1016
- } catch {
1017
- compactLen = 0;
1018
- }
1019
- const hasMarker = extractCompactFromMarker(
1020
- await fs2.readFile(resolve(dir, `${meta.outputId}.txt`), "utf-8").catch(() => "")
1021
- ) !== null;
1022
- const compactStale = isCompactStale(compactText, meta.skillName, meta.contentSha);
1023
- const { count: hitCount } = await readSkillHits(meta.skillName);
1024
- const ageMs = Date.now() - meta.ts;
1025
- results.push({
1026
- name: meta.skillName,
1027
- bodyLen: meta.bodyBytes,
1028
- compactLen,
1029
- hasMarker,
1030
- compactStale,
1031
- hitCount,
1032
- ageMs
1033
- });
1034
- }
1035
- return results;
1036
- } catch {
1037
- return [];
1038
- }
1039
- }
1040
- async function getSkillFilePath(skillName) {
1041
- try {
1042
- const name = safeSkillName(skillName);
1043
- if (!name) return null;
1044
- const metas = await listOutputs();
1045
- for (const meta of metas) {
1046
- if (meta.skillName === name && meta.sourcePath) {
1047
- return meta.sourcePath;
1048
- }
1049
- }
1050
- return await installedSkillPath(name);
1051
- } catch {
1052
- return null;
1053
- }
1054
- }
1055
- async function resolvePluginSkillPath(pluginName, skillSlug) {
1056
- const manifestPath = _pluginsManifestPathOverride ?? resolve(claudeConfigDir(), "plugins", "installed_plugins.json");
1057
- let raw;
1058
- try {
1059
- raw = await fs2.readFile(manifestPath, "utf8");
1060
- } catch {
1061
- return null;
1062
- }
1063
- let parsed;
1064
- try {
1065
- parsed = JSON.parse(raw);
1066
- } catch {
1067
- return null;
1068
- }
1069
- if (typeof parsed !== "object" || parsed === null) return null;
1070
- const plugins = parsed["plugins"];
1071
- if (typeof plugins !== "object" || plugins === null) return null;
1072
- for (const [key, entries] of Object.entries(plugins)) {
1073
- if (key !== pluginName && !key.startsWith(pluginName + "@")) continue;
1074
- if (!Array.isArray(entries)) continue;
1075
- for (const entry of entries) {
1076
- if (typeof entry !== "object" || entry === null) continue;
1077
- const installPath = entry["installPath"];
1078
- if (typeof installPath !== "string" || installPath === "") continue;
1079
- const diskPath = resolve(installPath, "skills", skillSlug, "SKILL.md");
1080
- try {
1081
- await fs2.access(diskPath);
1082
- return diskPath;
1083
- } catch {
1084
- continue;
1085
- }
1086
- }
1087
- }
1088
- return null;
1089
- }
1090
- async function installedSkillPath(skillName) {
1091
- const name = safeSkillName(skillName);
1092
- if (!name) return null;
1093
- const colonIdx = name.indexOf(":");
1094
- if (colonIdx > 0 && colonIdx < name.length - 1) {
1095
- const pluginPath = await resolvePluginSkillPath(name.slice(0, colonIdx), name.slice(colonIdx + 1));
1096
- if (pluginPath !== null) return pluginPath;
1097
- }
1098
- const diskPath = resolve(skillsSourceDir(), name, "SKILL.md");
1099
- try {
1100
- await fs2.access(diskPath);
1101
- return diskPath;
1102
- } catch {
1103
- return null;
1104
- }
1105
- }
1106
- function pruneSkillOutputs(maxCount = DEFAULT_MAX_COUNT, maxAgeMs = DEFAULT_MAX_AGE_MS) {
1107
- const dir = skillOutputsDir();
1108
- let removed = 0;
1109
- try {
1110
- if (!existsSync2(dir)) return 0;
1111
- const cutoff = Date.now() - maxAgeMs;
1112
- const entries = [];
1113
- for (const file of readdirSync2(dir)) {
1114
- if (!file.endsWith(".meta")) continue;
1115
- const outputId = file.slice(0, -".meta".length);
1116
- let ts;
1117
- try {
1118
- const parsed = JSON.parse(readFileSync2(resolve(dir, file), "utf-8"));
1119
- ts = parsed.ts;
1120
- } catch {
1121
- try {
1122
- ts = statSync(resolve(dir, file)).mtimeMs;
1123
- } catch {
1124
- continue;
1125
- }
1126
- }
1127
- entries.push({ outputId, ts });
1128
- }
1129
- const removeEntry = (outputId) => {
1130
- for (const ext of [".meta", ".txt"]) {
1131
- try {
1132
- unlinkSync(resolve(dir, `${outputId}${ext}`));
1133
- } catch {
1134
- }
1135
- }
1136
- removed++;
1137
- };
1138
- const kept = [];
1139
- for (const entry of entries) {
1140
- if (entry.ts < cutoff) {
1141
- removeEntry(entry.outputId);
1142
- } else {
1143
- kept.push(entry);
1144
- }
1145
- }
1146
- if (kept.length > maxCount) {
1147
- kept.sort((a, b) => a.ts - b.ts);
1148
- for (const entry of kept.slice(0, kept.length - maxCount)) {
1149
- removeEntry(entry.outputId);
1150
- }
1151
- }
1152
- for (const record of readLoadRecordsSync(dir)) {
1153
- if (record.ts >= cutoff && existsSync2(resolve(dir, `${record.bodyOutputId}.meta`))) continue;
1154
- try {
1155
- unlinkSync(resolve(dir, `${record.loadId}${LOAD_RECORD_EXT}`));
1156
- } catch {
1157
- }
1158
- }
1159
- } catch {
1160
- return removed;
1161
- }
1162
- return removed;
1163
- }
1164
-
1165
- // src/compact.ts
1166
- init_define_import_meta_env();
1167
- import * as fs3 from "node:fs";
1168
- import * as path3 from "node:path";
1169
- var CONTEXT_AUTOCOMPACT_TOKENS = 66e4;
1170
- var CATALOG_TOKENS = 10800;
1171
- var CONTEXT_TIER_WARM = 0.5;
1172
- var CONTEXT_TIER_HOT = 0.7;
1173
- var CONTEXT_TIER_CRITICAL = 0.85;
1174
- var NOISE_EXTS = /* @__PURE__ */ new Set([
1175
- ".pyc",
1176
- ".pyo",
1177
- ".pyd",
1178
- ".class",
1179
- ".o",
1180
- ".obj",
1181
- ".a",
1182
- ".lib",
1183
- ".dll",
1184
- ".so",
1185
- ".dylib",
1186
- ".log",
1187
- ".jsonl",
1188
- ".tmp",
1189
- ".temp",
1190
- ".swp",
1191
- ".swo",
1192
- ".bak",
1193
- ".pid",
1194
- ".lock",
1195
- ".map",
1196
- ".wasm",
1197
- ".gz",
1198
- ".zip",
1199
- ".tar",
1200
- ".tgz",
1201
- ".db",
1202
- ".sqlite",
1203
- ".sqlite3",
1204
- ".db3",
1205
- ".d.ts",
1206
- ".snap",
1207
- ".eot",
1208
- ".ttf",
1209
- ".woff",
1210
- ".woff2",
1211
- ".ico",
1212
- ".pdb",
1213
- ".exe",
1214
- ".bin"
1215
- ]);
1216
- var NOISE_BASENAMES = /* @__PURE__ */ new Set([
1217
- ".ds_store",
1218
- "thumbs.db",
1219
- "desktop.ini",
1220
- "package-lock.json",
1221
- "yarn.lock",
1222
- "pnpm-lock.yaml",
1223
- "poetry.lock",
1224
- "uv.lock",
1225
- "pdm.lock",
1226
- "cargo.lock",
1227
- "composer.lock",
1228
- "gemfile.lock",
1229
- "coverage.xml",
1230
- ".coverage",
1231
- "lcov.info"
1232
- ]);
1233
- var NOISE_SEGMENTS = [
1234
- "/__pycache__/",
1235
- "/.git/",
1236
- "/node_modules/",
1237
- "/.venv/",
1238
- "/venv/",
1239
- "/dist/",
1240
- "/build/",
1241
- "/.mypy_cache/",
1242
- "/.pytest_cache/",
1243
- "/.ruff_cache/",
1244
- "/appdata/local/temp/",
1245
- "/appdata/roaming/",
1246
- "/tmp/",
1247
- "/.next/",
1248
- "/.nuxt/",
1249
- "/.svelte-kit/",
1250
- "/.turbo/",
1251
- "/.parcel-cache/",
1252
- "/.cache/",
1253
- "/.tox/",
1254
- "/coverage/",
1255
- "/.nyc_output/",
1256
- "/site-packages/",
1257
- ".egg-info/",
1258
- "/target/",
1259
- "/__snapshots__/"
1260
- ];
1261
- var HARNESS_MULTIPLIER_DEFAULTS = {
1262
- claudecode: 2,
1263
- codex: 1.5,
1264
- opencode: 2.5,
1265
- gemini: 3,
1266
- openclaw: 1,
1267
- pi: 1,
1268
- hermes: 1,
1269
- generic: 1
1270
- };
1271
- function tierForFraction(fill) {
1272
- if (fill >= CONTEXT_TIER_CRITICAL) return "critical";
1273
- if (fill >= CONTEXT_TIER_HOT) return "hot";
1274
- if (fill >= CONTEXT_TIER_WARM) return "warm";
1275
- return "cool";
1276
- }
1277
- function pressureRawTotal(cache) {
1278
- const skillTokens = cache.loadedSkillTotalTokens ?? 0;
1279
- const bashCount = (cache.bashOutputs ?? []).length;
1280
- const webCount = (cache.webFetches ?? []).length;
1281
- const files = cache.files ?? [];
1282
- const readCount = files.length;
1283
- return skillTokens + CATALOG_TOKENS + bashCount * 500 + webCount * 1e3 + readCount * 200;
1284
- }
1285
- var PROMPT_MEASURE_TAIL_BYTES = 1048576;
1286
- function extractUsageTotal(record) {
1287
- if (record === null || typeof record !== "object") return null;
1288
- if (record["type"] !== "assistant") return null;
1289
- const message = record["message"];
1290
- if (message === null || typeof message !== "object") return null;
1291
- const usage = message["usage"];
1292
- if (usage === null || typeof usage !== "object") return null;
1293
- const u = usage;
1294
- const input = u["input_tokens"];
1295
- const cacheCreation = u["cache_creation_input_tokens"];
1296
- const cacheRead = u["cache_read_input_tokens"];
1297
- const output = u["output_tokens"];
1298
- if (typeof input !== "number" || typeof cacheCreation !== "number" || typeof cacheRead !== "number" || typeof output !== "number") {
1299
- return null;
1300
- }
1301
- return input + cacheCreation + cacheRead + output;
1302
- }
1303
- function measurePromptTokens(transcriptPath) {
1304
- try {
1305
- const lines = readTranscriptTail(transcriptPath, PROMPT_MEASURE_TAIL_BYTES);
1306
- for (let i = lines.length - 1; i >= 0; i--) {
1307
- const line = lines[i];
1308
- if (line === void 0) continue;
1309
- const trimmed = line.trim();
1310
- if (trimmed === "") continue;
1311
- let parsed;
1312
- try {
1313
- parsed = JSON.parse(trimmed);
1314
- } catch {
1315
- continue;
1316
- }
1317
- const total = extractUsageTotal(parsed);
1318
- if (total !== null) return total;
1319
- }
1320
- return null;
1321
- } catch {
1322
- return null;
1323
- }
1324
- }
1325
- function getEffectiveAutoTriggerWindow() {
1326
- const ca = loadConfig().compact_assist;
1327
- const isConfigDefault = !isAutoTriggerMultiplierExplicit();
1328
- const multiplier = getAutoTriggerMultiplier(
1329
- ca.harness === "auto" ? { configExplicitMultiplier: ca.auto_trigger_multiplier, isConfigDefault } : { configExplicitMultiplier: ca.auto_trigger_multiplier, harness: ca.harness, isConfigDefault }
1330
- );
1331
- return CONTEXT_AUTOCOMPACT_TOKENS * multiplier;
1332
- }
1333
- function getContextPressure(cache, transcriptPath) {
1334
- try {
1335
- const window = getEffectiveAutoTriggerWindow();
1336
- if (transcriptPath) {
1337
- const measured = measurePromptTokens(transcriptPath);
1338
- if (measured !== null) {
1339
- const fill2 = measured / window;
1340
- return { fillFraction: fill2, tier: tierForFraction(fill2) };
1341
- }
1342
- }
1343
- if (!cache) {
1344
- return { fillFraction: 0, tier: "cool" };
1345
- }
1346
- const total = pressureRawTotal(cache);
1347
- const fill = total / window;
1348
- return {
1349
- fillFraction: fill,
1350
- tier: tierForFraction(fill)
1351
- };
1352
- } catch {
1353
- return { fillFraction: 0, tier: "cool" };
1354
- }
1355
- }
1356
- function getAutoTriggerMultiplier(opts) {
1357
- const config = opts?.configExplicitMultiplier ?? 2;
1358
- let isDefault = opts?.isConfigDefault;
1359
- if (isDefault === void 0) {
1360
- isDefault = config === 2;
1361
- }
1362
- if (!isDefault && opts?.configExplicitMultiplier !== void 0) {
1363
- return Math.max(1, Math.min(10, opts.configExplicitMultiplier));
1364
- }
1365
- const harness = opts?.harness ?? detectHarness();
1366
- return Math.max(1, Math.min(10, HARNESS_MULTIPLIER_DEFAULTS[harness] ?? 1));
1367
- }
1368
- function isNoisePath(inputPath) {
1369
- if (!inputPath) {
1370
- return false;
1371
- }
1372
- const p = normalizePathForwardSlash(inputPath, true);
1373
- for (const segment of NOISE_SEGMENTS) {
1374
- if (p.includes(segment)) {
1375
- return true;
1376
- }
1377
- }
1378
- const slashIdx = p.lastIndexOf("/");
1379
- const basename = slashIdx >= 0 ? p.slice(slashIdx + 1) : p;
1380
- if (NOISE_BASENAMES.has(basename)) {
1381
- return true;
1382
- }
1383
- if (basename.startsWith(".improve-state-") || basename.startsWith("improve_commit_msg_")) {
1384
- return true;
1385
- }
1386
- const dotIdx = basename.lastIndexOf(".");
1387
- if (dotIdx >= 0) {
1388
- const ext = basename.slice(dotIdx);
1389
- if (NOISE_EXTS.has(ext)) {
1390
- return true;
1391
- }
1392
- }
1393
- for (const ext of NOISE_EXTS) {
1394
- if (ext.includes(".") && ext.split(".").length > 2) {
1395
- if (basename.endsWith(ext)) {
1396
- return true;
1397
- }
1398
- }
1399
- }
1400
- return false;
1401
- }
1402
- function findLatestSessionId() {
1403
- try {
1404
- const sessionsDir2 = path3.join(tokenGoatHome(), "sessions");
1405
- if (!fs3.existsSync(sessionsDir2)) {
1406
- return null;
1407
- }
1408
- const files = fs3.readdirSync(sessionsDir2);
1409
- const jsonFiles = files.filter((f) => f.endsWith(".json") && !f.includes(AGENT_SALT_MARKER));
1410
- if (jsonFiles.length === 0) {
1411
- return null;
1412
- }
1413
- const firstFile = jsonFiles[0];
1414
- if (!firstFile) {
1415
- return null;
1416
- }
1417
- let latestFile = firstFile;
1418
- let latestMtime = fs3.statSync(path3.join(sessionsDir2, firstFile)).mtimeMs;
1419
- for (const file of jsonFiles) {
1420
- const mtime = fs3.statSync(path3.join(sessionsDir2, file)).mtimeMs;
1421
- if (mtime > latestMtime) {
1422
- latestFile = file;
1423
- latestMtime = mtime;
1424
- }
1425
- }
1426
- return latestFile.replace(/\.json$/, "");
1427
- } catch {
1428
- return null;
1429
- }
1430
- }
1431
- function eventCount(cache) {
1432
- const files = cache.files ?? [];
1433
- const editedCount = files.filter((f) => f.wasEdited).length;
1434
- const bashCount = (cache.bashOutputs ?? []).length;
1435
- const webCount = (cache.webFetches ?? []).length;
1436
- const skillHistory = cache.skillHistory ?? {};
1437
- return files.length + editedCount + bashCount + webCount + Object.keys(skillHistory).length;
1438
- }
1439
- function writeSessionManifest(projectHash, sessionId, manifestJson) {
1440
- const safeSessionId = sessionFileStem(sessionId);
1441
- if (!safeSessionId) return;
1442
- const sessionsDir2 = path3.join(dataDir(), "projects", projectHash, "sessions");
1443
- if (!fs3.existsSync(sessionsDir2)) {
1444
- ensureDirSync(sessionsDir2);
1445
- }
1446
- const dest = path3.join(sessionsDir2, `${safeSessionId}.json`);
1447
- atomicWriteText(dest, JSON.stringify(manifestJson));
1448
- }
1449
- function readAllSessionManifests(projectHash, maxAgeSecs = 3600) {
1450
- const sessionsDir2 = path3.join(dataDir(), "projects", projectHash, "sessions");
1451
- if (!fs3.existsSync(sessionsDir2)) {
1452
- return [];
1453
- }
1454
- const now = Date.now() / 1e3;
1455
- const results = [];
1456
- try {
1457
- const files = fs3.readdirSync(sessionsDir2);
1458
- for (const file of files) {
1459
- if (!file.endsWith(".json")) {
1460
- continue;
1461
- }
1462
- try {
1463
- const fullPath = path3.join(sessionsDir2, file);
1464
- const stat2 = fs3.statSync(fullPath);
1465
- if (now - stat2.mtimeMs / 1e3 > maxAgeSecs) {
1466
- try {
1467
- fs3.unlinkSync(fullPath);
1468
- } catch {
1469
- }
1470
- continue;
1471
- }
1472
- const text = fs3.readFileSync(fullPath, "utf8");
1473
- const data = JSON.parse(text);
1474
- if (typeof data === "object" && data !== null && "files" in data) {
1475
- results.push(data);
1476
- }
1477
- } catch {
1478
- }
1479
- }
1480
- } catch {
1481
- }
1482
- return results;
1483
- }
1484
- function _editedFileCount(cache) {
1485
- return (cache.files ?? []).filter((f) => f.wasEdited).length;
1486
- }
1487
- function _computeActivityMultiplier(ageSecs, editedCount) {
1488
- const TEN_MIN_SECS = 600;
1489
- const SIXTY_MIN_SECS = 3600;
1490
- const EDITS_PER_MIN_DENSITY_THRESHOLD = 0.3;
1491
- let tier;
1492
- if (ageSecs < TEN_MIN_SECS) {
1493
- tier = "young";
1494
- } else if (ageSecs < SIXTY_MIN_SECS) {
1495
- tier = "active";
1496
- } else {
1497
- tier = "mature";
1498
- }
1499
- const baseFactor = {
1500
- young: 0.6,
1501
- active: 1,
1502
- mature: 1.4
1503
- };
1504
- let factor = baseFactor[tier];
1505
- if (ageSecs >= TEN_MIN_SECS) {
1506
- const editsPerMin = ageSecs > 0 ? editedCount / (ageSecs / 60) : 0;
1507
- if (editsPerMin < EDITS_PER_MIN_DENSITY_THRESHOLD) {
1508
- factor = Math.min(factor, 1);
1509
- }
1510
- }
1511
- return factor;
1512
- }
1513
- function loadSessionCache(sessionId) {
1514
- const disk = readSessionStateFile(sessionId);
1515
- if (!disk) {
1516
- return null;
1517
- }
1518
- return {
1519
- files: disk.files,
1520
- webFetches: disk.webFetches,
1521
- bashOutputs: disk.bashOutputs,
1522
- ...disk.created_ts !== void 0 ? { created_ts: disk.created_ts } : {}
1523
- };
1524
- }
1525
- function computeAdaptiveBudget(cache, ageSecs = 0, opts) {
1526
- const base = 200;
1527
- const maxTotal = 800;
1528
- const minTotal = 200;
1529
- const editedCount = _editedFileCount(cache);
1530
- const editedBonus = Math.min(200, editedCount * 50);
1531
- const files = cache.files ?? [];
1532
- const symbolFiles = files.filter((f) => {
1533
- const entry = f;
1534
- return (entry["symbols_read"] ?? []).length > 0;
1535
- }).length;
1536
- const symbolsBonus = Math.min(150, symbolFiles * 30);
1537
- const bashCount = (cache.bashOutputs ?? []).length;
1538
- const bashBonus = bashCount > 0 ? Math.min(100, Math.max(20, bashCount * 5)) : 0;
1539
- const webBonus = (cache.webFetches ?? []).length > 0 ? 15 : 0;
1540
- const diffBonus = opts?.hasPendingDiff ? 50 : 0;
1541
- const uncommittedBonus = opts?.hasUncommittedChanges ? 10 : 0;
1542
- const staleFrac = Math.max(0, Math.min(1, opts?.staleCompactFraction ?? 0));
1543
- const staleBonus = Math.min(60, Math.round(staleFrac * 60));
1544
- const rawTotal = base + editedBonus + symbolsBonus + bashBonus + webBonus + diffBonus + uncommittedBonus + staleBonus;
1545
- const factor = _computeActivityMultiplier(ageSecs, editedCount);
1546
- const total = Math.round(rawTotal * factor);
1547
- let capMax = maxTotal;
1548
- if (opts?.contextPressure) {
1549
- if (opts.contextPressure.tier === "critical") {
1550
- capMax = Math.min(capMax, 300);
1551
- } else if (opts.contextPressure.tier === "hot") {
1552
- capMax = Math.min(capMax, 500);
1553
- }
1554
- }
1555
- return Math.max(minTotal, Math.min(capMax, total));
1556
- }
1557
-
1558
- export {
1559
- SESSIONS_SUBDIR,
1560
- sessionsDir,
1561
- AGENT_SALT_MARKER,
1562
- sessionSidecarPath,
1563
- readSessionStateFile,
1564
- listSiblingSessionStates,
1565
- loadSessionState,
1566
- COMPACTED_AT_SUFFIX,
1567
- saveSessionState,
1568
- CONTEXT_AUTOCOMPACT_TOKENS,
1569
- getContextPressure,
1570
- isNoisePath,
1571
- findLatestSessionId,
1572
- eventCount,
1573
- writeSessionManifest,
1574
- readAllSessionManifests,
1575
- loadSessionCache,
1576
- computeAdaptiveBudget,
1577
- SKILLS_OUTPUT_SUBDIR,
1578
- skillOutputsDir,
1579
- contentHash,
1580
- safeSkillName,
1581
- extractCompactFromMarker,
1582
- extractNamedSection,
1583
- extractChecklistSection,
1584
- listOutputs,
1585
- hasSessionOutput,
1586
- sessionOutputBodyBytes,
1587
- storeOutput,
1588
- storeCompact,
1589
- getCompactAnySessionSync,
1590
- isCompactStale,
1591
- incrementSkillHit,
1592
- formatAge,
1593
- listSkills,
1594
- getSkillFilePath,
1595
- installedSkillPath,
1596
- pruneSkillOutputs
1597
- };