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.
- package/README.md +24 -7
- package/SECURITY.md +10 -2
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/dist/native/linux-arm64/tg-hook +0 -0
- package/dist/native/linux-x64/tg-hook +0 -0
- package/dist/native/win32-arm64/tg-hook.exe +0 -0
- package/dist/native/win32-x64/tg-hook.exe +0 -0
- package/dist/{token-goat-chunk-QSBRTGM2.mjs → token-goat-chunk-25TRAWET.mjs} +17 -7
- package/dist/token-goat-chunk-2KXO6V2B.mjs +203 -0
- package/dist/{token-goat-chunk-IVKIZWAB.mjs → token-goat-chunk-2M7E4PF7.mjs} +41 -12
- package/dist/{token-goat-chunk-SMVRN2HU.mjs → token-goat-chunk-2UPZGKKY.mjs} +2 -2
- package/dist/token-goat-chunk-2WLPWKHV.mjs +135 -0
- package/dist/token-goat-chunk-2XBMMDBD.mjs +126 -0
- package/dist/token-goat-chunk-32ZOY4N5.mjs +23 -0
- package/dist/token-goat-chunk-3EZRNKTK.mjs +188 -0
- package/dist/token-goat-chunk-4ADZHI7J.mjs +63 -0
- package/dist/token-goat-chunk-4GBVPH3D.mjs +242 -0
- package/dist/{token-goat-chunk-KWPNJBDH.mjs → token-goat-chunk-4GILLLWY.mjs} +7 -4
- package/dist/{token-goat-chunk-67TQJ76B.mjs → token-goat-chunk-4NAPX7BA.mjs} +36 -3
- package/dist/{token-goat-chunk-PU7V3FD6.mjs → token-goat-chunk-4SC7UJYV.mjs} +3354 -2553
- package/dist/token-goat-chunk-4SMRO4TO.mjs +366 -0
- package/dist/{token-goat-chunk-AFLYLHDH.mjs → token-goat-chunk-4ZBFWLXF.mjs} +283 -427
- package/dist/{token-goat-chunk-JDWCRD6M.mjs → token-goat-chunk-57BIAXJL.mjs} +73 -40
- package/dist/token-goat-chunk-5AC66HNP.mjs +115 -0
- package/dist/token-goat-chunk-5IMLKSOP.mjs +145 -0
- package/dist/token-goat-chunk-646E6U44.mjs +398 -0
- package/dist/token-goat-chunk-65N4RHP4.mjs +33 -0
- package/dist/{token-goat-chunk-JN4XMLSD.mjs → token-goat-chunk-6DG6HWP3.mjs} +8 -2
- package/dist/token-goat-chunk-6ENQSNRZ.mjs +573 -0
- package/dist/token-goat-chunk-6ZY4GNF2.mjs +91 -0
- package/dist/{token-goat-chunk-VPBBTSJR.mjs → token-goat-chunk-76FGDLRO.mjs} +262 -255
- package/dist/{token-goat-chunk-7IN2MKDS.mjs → token-goat-chunk-7EZTZOMC.mjs} +121 -289
- package/dist/{token-goat-chunk-OJBNYTJP.mjs → token-goat-chunk-7MAOA6X4.mjs} +640 -80
- package/dist/token-goat-chunk-7R2ZVVAX.mjs +218 -0
- package/dist/{token-goat-chunk-PD5VKH46.mjs → token-goat-chunk-AUNGYYQX.mjs} +2 -2
- package/dist/{token-goat-chunk-HG45AF2B.mjs → token-goat-chunk-AUQYRMMH.mjs} +794 -37
- package/dist/token-goat-chunk-B3PEKBYG.mjs +128 -0
- package/dist/{token-goat-chunk-A3CK6N5Z.mjs → token-goat-chunk-BA4HI6BW.mjs} +1309 -110
- package/dist/{token-goat-chunk-JB6QN6VF.mjs → token-goat-chunk-BFCWY73P.mjs} +59 -23
- package/dist/{token-goat-chunk-B45VMIIH.mjs → token-goat-chunk-BWGFUZZE.mjs} +7 -5
- package/dist/{token-goat-chunk-P4QMM4WK.mjs → token-goat-chunk-C75NA7PL.mjs} +50 -23
- package/dist/token-goat-chunk-CBIQ7WTD.mjs +87 -0
- package/dist/token-goat-chunk-CETM74KC.mjs +307 -0
- package/dist/{token-goat-chunk-H2PHHGM5.mjs → token-goat-chunk-CIMQ2BB3.mjs} +57 -12
- package/dist/{token-goat-chunk-N2NMER56.mjs → token-goat-chunk-CJLS3CDL.mjs} +46 -4
- package/dist/{token-goat-chunk-VES7EL7O.mjs → token-goat-chunk-CO3UYX6G.mjs} +1 -1
- package/dist/token-goat-chunk-COPNXRJM.mjs +154 -0
- package/dist/token-goat-chunk-CSJ5L3NL.mjs +479 -0
- package/dist/{token-goat-chunk-4LCCNRTG.mjs → token-goat-chunk-CVCEVUGK.mjs} +8 -6
- package/dist/{token-goat-chunk-B2RZZCSS.mjs → token-goat-chunk-CXHOFN7R.mjs} +37 -20
- package/dist/token-goat-chunk-D735C4QM.mjs +603 -0
- package/dist/{token-goat-chunk-APGZXSPS.mjs → token-goat-chunk-DGS2LVNA.mjs} +47 -30
- package/dist/{token-goat-chunk-54K3EUV3.mjs → token-goat-chunk-DOWRSMWO.mjs} +1 -1
- package/dist/token-goat-chunk-E45INMY3.mjs +71 -0
- package/dist/token-goat-chunk-EC5F37F4.mjs +244 -0
- package/dist/{token-goat-chunk-XTQAOTSO.mjs → token-goat-chunk-EDBDRVH5.mjs} +4 -1
- package/dist/{token-goat-chunk-5BCIJXMO.mjs → token-goat-chunk-EELTBKDM.mjs} +14 -10
- package/dist/{token-goat-chunk-W3QCJS6G.mjs → token-goat-chunk-F4HYL64R.mjs} +8 -6
- package/dist/token-goat-chunk-F6PAUIEK.mjs +624 -0
- package/dist/token-goat-chunk-FDSREWLX.mjs +692 -0
- package/dist/token-goat-chunk-GGGG7RWB.mjs +236 -0
- package/dist/{token-goat-chunk-C63JQV5K.mjs → token-goat-chunk-GL3ETKSC.mjs} +233 -15
- package/dist/token-goat-chunk-GVB2MUST.mjs +1298 -0
- package/dist/token-goat-chunk-GWFLAUMQ.mjs +307 -0
- package/dist/{token-goat-chunk-ZEJVMUQH.mjs → token-goat-chunk-HLPW63A7.mjs} +2 -2
- package/dist/{token-goat-chunk-GOLEOXZP.mjs → token-goat-chunk-HU4TORC5.mjs} +44 -26
- package/dist/{token-goat-chunk-Z57YODA2.mjs → token-goat-chunk-I7VKZZAW.mjs} +2 -2
- package/dist/{token-goat-chunk-NYKZLXMK.mjs → token-goat-chunk-IA6XSPE3.mjs} +6 -6
- package/dist/{token-goat-chunk-E3OQX2XT.mjs → token-goat-chunk-IAGXMHIK.mjs} +81 -42
- package/dist/{token-goat-chunk-HYBFA5X3.mjs → token-goat-chunk-IK2DRMFX.mjs} +24 -68
- package/dist/{token-goat-chunk-COBQGL5S.mjs → token-goat-chunk-INNPTRUX.mjs} +249 -299
- package/dist/{token-goat-chunk-7NADEDKJ.mjs → token-goat-chunk-JJ5BX4F4.mjs} +6 -1
- package/dist/{token-goat-chunk-PEPTHRFF.mjs → token-goat-chunk-JVFSHVFJ.mjs} +6 -6
- package/dist/token-goat-chunk-KGEFFSHD.mjs +50 -0
- package/dist/token-goat-chunk-KQBLRPBN.mjs +433 -0
- package/dist/{token-goat-chunk-Q5VTGRH7.mjs → token-goat-chunk-LCNQ2CNN.mjs} +55 -319
- package/dist/token-goat-chunk-M3LFJDPU.mjs +58 -0
- package/dist/token-goat-chunk-M52KPT3C.mjs +87 -0
- package/dist/token-goat-chunk-MBEG42H7.mjs +150 -0
- package/dist/{token-goat-chunk-EVC4TOLE.mjs → token-goat-chunk-METECDOH.mjs} +3 -12
- package/dist/{token-goat-chunk-T4FK7VOV.mjs → token-goat-chunk-MHX7XCC3.mjs} +2 -1
- package/dist/token-goat-chunk-N5LSOVSU.mjs +137 -0
- package/dist/{token-goat-chunk-LOBGHXPB.mjs → token-goat-chunk-N5MMR2IW.mjs} +1 -1
- package/dist/token-goat-chunk-N7B3JQ2C.mjs +160 -0
- package/dist/{token-goat-chunk-RHXKCZX3.mjs → token-goat-chunk-NISNMD7T.mjs} +2182 -2959
- package/dist/token-goat-chunk-NMD42SFQ.mjs +434 -0
- package/dist/token-goat-chunk-NOSS5VWY.mjs +174 -0
- package/dist/{token-goat-chunk-KQNZDCBD.mjs → token-goat-chunk-NSNSPPBG.mjs} +25 -3
- package/dist/token-goat-chunk-OBOFIHKN.mjs +38 -0
- package/dist/token-goat-chunk-OBRFJ5O5.mjs +76 -0
- package/dist/{token-goat-chunk-JTQQGXZH.mjs → token-goat-chunk-ODBT2USM.mjs} +124 -57
- package/dist/token-goat-chunk-OS5EM5PP.mjs +107 -0
- package/dist/token-goat-chunk-OUKDTVNU.mjs +1178 -0
- package/dist/token-goat-chunk-OXFMP75N.mjs +566 -0
- package/dist/{token-goat-chunk-DZX2YDPT.mjs → token-goat-chunk-OYIYKDDT.mjs} +2 -2
- package/dist/{token-goat-chunk-QCLDAQTS.mjs → token-goat-chunk-P6HQSWQ3.mjs} +50 -53
- package/dist/token-goat-chunk-P6VGNTQT.mjs +71 -0
- package/dist/{token-goat-chunk-7BXSNBY5.mjs → token-goat-chunk-PFWIMH2E.mjs} +23 -15
- package/dist/token-goat-chunk-PJZXIS3G.mjs +39 -0
- package/dist/token-goat-chunk-PUO5INXP.mjs +606 -0
- package/dist/{token-goat-chunk-GFBSN7BD.mjs → token-goat-chunk-PX5PQWTY.mjs} +22 -250
- package/dist/token-goat-chunk-Q5QDBC6O.mjs +111 -0
- package/dist/token-goat-chunk-QFNQXSDT.mjs +40 -0
- package/dist/{token-goat-chunk-FBNQNSJK.mjs → token-goat-chunk-QH7YVYOY.mjs} +2198 -2782
- package/dist/{token-goat-chunk-64FBPDTK.mjs → token-goat-chunk-QLMUHEPB.mjs} +12 -9
- package/dist/{token-goat-chunk-RZRTDST7.mjs → token-goat-chunk-QQOJIHL3.mjs} +37 -11
- package/dist/{token-goat-chunk-XKI4SEMX.mjs → token-goat-chunk-QQTWJY2O.mjs} +19 -0
- package/dist/{token-goat-chunk-U4N3BN2W.mjs → token-goat-chunk-QYHO5CPH.mjs} +3 -2
- package/dist/token-goat-chunk-RELAVWAD.mjs +27 -0
- package/dist/{token-goat-chunk-ETBHVDB6.mjs → token-goat-chunk-RSLPOBKQ.mjs} +22 -11
- package/dist/token-goat-chunk-SAMFDIS2.mjs +712 -0
- package/dist/token-goat-chunk-SDELXLDM.mjs +48 -0
- package/dist/{token-goat-chunk-4JJS54TR.mjs → token-goat-chunk-SOAFZNNZ.mjs} +161 -38
- package/dist/{token-goat-chunk-664RTTUV.mjs → token-goat-chunk-SRTHT7KV.mjs} +21 -16
- package/dist/{token-goat-chunk-4FVIAHSI.mjs → token-goat-chunk-T5P5ACNQ.mjs} +7 -6
- package/dist/token-goat-chunk-T5YGG4V3.mjs +45 -0
- package/dist/token-goat-chunk-TAUZ4OFS.mjs +110 -0
- package/dist/{token-goat-chunk-COTXZHTS.mjs → token-goat-chunk-TM6MC754.mjs} +4 -3
- package/dist/token-goat-chunk-TWUKBOMP.mjs +72 -0
- package/dist/{token-goat-chunk-3CHUNJ2O.mjs → token-goat-chunk-U2KSB4SG.mjs} +8 -5
- package/dist/{token-goat-chunk-3Z5S4KGM.mjs → token-goat-chunk-U4WHMG6Y.mjs} +29 -49
- package/dist/{token-goat-chunk-PY533T7F.mjs → token-goat-chunk-UGJHGR5A.mjs} +21 -15
- package/dist/{token-goat-chunk-CYCEYAEO.mjs → token-goat-chunk-UOUDVA5U.mjs} +1 -1
- package/dist/{token-goat-chunk-JDHE26RP.mjs → token-goat-chunk-URRJ33GR.mjs} +16 -5
- package/dist/{token-goat-chunk-56EIMS3E.mjs → token-goat-chunk-UZOROKIS.mjs} +23 -17
- package/dist/{token-goat-chunk-CZ3LUXYZ.mjs → token-goat-chunk-VIHGGGEI.mjs} +427 -308
- package/dist/token-goat-chunk-VTJLAA7P.mjs +21 -0
- package/dist/token-goat-chunk-VWDXLT4F.mjs +678 -0
- package/dist/{token-goat-chunk-YARZFCZS.mjs → token-goat-chunk-VWZA2H4P.mjs} +62 -53
- package/dist/{token-goat-chunk-SQSSNQPE.mjs → token-goat-chunk-VXEXMJDL.mjs} +3979 -4745
- package/dist/token-goat-chunk-WBA5SKT4.mjs +547 -0
- package/dist/{token-goat-chunk-SJHWVNDQ.mjs → token-goat-chunk-WDH44VAU.mjs} +7 -4
- package/dist/token-goat-chunk-X2SUP3EI.mjs +107 -0
- package/dist/token-goat-chunk-X3BELZSJ.mjs +125 -0
- package/dist/{token-goat-chunk-FZ3NEW5O.mjs → token-goat-chunk-XLRBNZCC.mjs} +13 -8
- package/dist/{token-goat-chunk-Z44YO3QZ.mjs → token-goat-chunk-XRKUX6CM.mjs} +1 -1
- package/dist/token-goat-chunk-XX6O25RN.mjs +82 -0
- package/dist/token-goat-chunk-XZSUHBVS.mjs +65 -0
- package/dist/{token-goat-chunk-WY3L4DVT.mjs → token-goat-chunk-Y2ZTQOCL.mjs} +13 -8
- package/dist/{token-goat-chunk-UQ5UK2T7.mjs → token-goat-chunk-YB4VMGK2.mjs} +1 -1
- package/dist/token-goat-chunk-YBIIY3HR.mjs +51 -0
- package/dist/{token-goat-chunk-ZQSJMO2R.mjs → token-goat-chunk-YHVDKQBR.mjs} +105 -105
- package/dist/{token-goat-chunk-H2K3QP6H.mjs → token-goat-chunk-YJB5XLWI.mjs} +8 -6
- package/dist/{token-goat-chunk-ZCDTCVIS.mjs → token-goat-chunk-YKB6AQG3.mjs} +5 -2
- package/dist/token-goat-chunk-YMTG2YA5.mjs +35 -0
- package/dist/token-goat-chunk-YPBZYQRV.mjs +40 -0
- package/dist/{token-goat-chunk-HOJLCGDP.mjs → token-goat-chunk-YY6AVUFL.mjs} +2033 -2251
- package/dist/token-goat-chunk-Z7N5OMMP.mjs +98 -0
- package/dist/token-goat-chunk-ZNMNUPAZ.mjs +938 -0
- package/dist/token-goat-hook-client.cjs +1 -1
- package/dist/token-goat-hook-client.mjs +3 -3
- package/dist/token-goat-hook.mjs +67 -38
- package/dist/token-goat.core.mjs +4 -4
- package/docs/C4_RUNTIME_ARCHITECTURE.md +1 -1
- package/docs/cli.md +40 -21
- package/docs/install.md +19 -9
- package/docs/security.md +5 -3
- package/package.json +2 -1
- package/dist/token-goat-chunk-5KDMCMF2.mjs +0 -362
- package/dist/token-goat-chunk-6QMAS7AB.mjs +0 -42
- package/dist/token-goat-chunk-7F2UBPD3.mjs +0 -397
- package/dist/token-goat-chunk-ARDNEC2U.mjs +0 -248
- package/dist/token-goat-chunk-ATCPY7EQ.mjs +0 -134
- package/dist/token-goat-chunk-EFHMMIGP.mjs +0 -68
- package/dist/token-goat-chunk-IVALLT3S.mjs +0 -48
- package/dist/token-goat-chunk-JDU3EXSU.mjs +0 -65
- package/dist/token-goat-chunk-MKPF4CL5.mjs +0 -37
- package/dist/token-goat-chunk-RC35RBUL.mjs +0 -312
- package/dist/token-goat-chunk-USILZ3T3.mjs +0 -53
- package/dist/token-goat-chunk-VL3YIFHZ.mjs +0 -2975
- package/dist/token-goat-chunk-XU4P6Z24.mjs +0 -133
- package/dist/token-goat-chunk-Y776AENC.mjs +0 -1597
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
import { createRequire as __cjsRequire } from 'node:module';
|
|
2
|
+
const require = __cjsRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
GENERIC_SERVED_OUTPUT_KEY,
|
|
5
|
+
MAX_BASH_START_CWDS,
|
|
6
|
+
MAX_GENERIC_SERVED_OUTPUTS,
|
|
7
|
+
MAX_OUTSTANDING_AGENT_SPAWNS,
|
|
8
|
+
MAX_RANGES_PER_FILE,
|
|
9
|
+
MAX_SEEN_IMAGE_HASHES,
|
|
10
|
+
MAX_SERVED_OUTPUTS_PER_FILE,
|
|
11
|
+
MAX_VERIFICATION_RUNS,
|
|
12
|
+
bashStartCwdsAtLoad,
|
|
13
|
+
coerceReadRangeEvasion,
|
|
14
|
+
consumedBashStartCwdKeys,
|
|
15
|
+
consumedCurlDownloadKeys,
|
|
16
|
+
consumedFileLineRangeKeys,
|
|
17
|
+
consumedFileServedOutputKeys,
|
|
18
|
+
consumedOutstandingAgentSpawnKeys,
|
|
19
|
+
consumedPendingLargeFileHintKeys,
|
|
20
|
+
curlDownloadsAtLoad,
|
|
21
|
+
exportSessionState,
|
|
22
|
+
filesFullReadCountAtLoad,
|
|
23
|
+
filesReadCountAtLoad,
|
|
24
|
+
importSessionState,
|
|
25
|
+
markCompacted,
|
|
26
|
+
mergeEpochBase,
|
|
27
|
+
mergeReadRangeEvasion,
|
|
28
|
+
migrateCurlDownloadKey,
|
|
29
|
+
migrateWebFetchKey,
|
|
30
|
+
outstandingAgentSpawnKey,
|
|
31
|
+
outstandingAgentSpawnsAtLoad,
|
|
32
|
+
pendingLargeFileHintsAtLoad
|
|
33
|
+
} from "./token-goat-chunk-GL3ETKSC.mjs";
|
|
34
|
+
import {
|
|
35
|
+
redactSerializedJson
|
|
36
|
+
} from "./token-goat-chunk-T5P5ACNQ.mjs";
|
|
37
|
+
import {
|
|
38
|
+
atomicWriteText,
|
|
39
|
+
ensureDirSync,
|
|
40
|
+
sanitizeIdForFilename,
|
|
41
|
+
withFileLock,
|
|
42
|
+
withRetryOnLock
|
|
43
|
+
} from "./token-goat-chunk-HU4TORC5.mjs";
|
|
44
|
+
import {
|
|
45
|
+
foldPath
|
|
46
|
+
} from "./token-goat-chunk-AM5V6GSX.mjs";
|
|
47
|
+
import {
|
|
48
|
+
normalizePath
|
|
49
|
+
} from "./token-goat-chunk-FNFBKLRD.mjs";
|
|
50
|
+
import {
|
|
51
|
+
tokenGoatHome
|
|
52
|
+
} from "./token-goat-chunk-CO3UYX6G.mjs";
|
|
53
|
+
import {
|
|
54
|
+
init_define_import_meta_env
|
|
55
|
+
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
56
|
+
|
|
57
|
+
// src/sessions_dir.ts
|
|
58
|
+
init_define_import_meta_env();
|
|
59
|
+
import * as path from "node:path";
|
|
60
|
+
var SESSIONS_SUBDIR = "sessions";
|
|
61
|
+
function sessionsDir() {
|
|
62
|
+
return path.join(tokenGoatHome(), SESSIONS_SUBDIR);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// src/session_store.ts
|
|
66
|
+
init_define_import_meta_env();
|
|
67
|
+
import { createHash } from "node:crypto";
|
|
68
|
+
import * as fs from "node:fs";
|
|
69
|
+
import * as path2 from "node:path";
|
|
70
|
+
var MAX_FILES = 500;
|
|
71
|
+
var AGENT_SALT_MARKER = sanitizeIdForFilename(":agent:");
|
|
72
|
+
var AGENT_SALT_SEPARATOR = ":agent:";
|
|
73
|
+
var AGENT_DIGEST_CHARS = 12;
|
|
74
|
+
var SALTED_SESSION_MAX = 64 - AGENT_SALT_MARKER.length - AGENT_DIGEST_CHARS;
|
|
75
|
+
function saltedStemPrefix(sessionId) {
|
|
76
|
+
return `${sanitizeIdForFilename(sessionId, SALTED_SESSION_MAX)}${AGENT_SALT_MARKER}`;
|
|
77
|
+
}
|
|
78
|
+
function sessionFileStem(sessionId) {
|
|
79
|
+
const sep = sessionId.indexOf(AGENT_SALT_SEPARATOR);
|
|
80
|
+
if (sep < 0) return sanitizeIdForFilename(sessionId, 64);
|
|
81
|
+
const agentId = sessionId.slice(sep + AGENT_SALT_SEPARATOR.length);
|
|
82
|
+
const digest = createHash("sha256").update(agentId).digest("hex").slice(0, AGENT_DIGEST_CHARS);
|
|
83
|
+
return `${saltedStemPrefix(sessionId.slice(0, sep))}${digest}`;
|
|
84
|
+
}
|
|
85
|
+
function sessionPath(sessionId) {
|
|
86
|
+
return sessionSidecarPath(sessionId, ".json");
|
|
87
|
+
}
|
|
88
|
+
function sessionSidecarPath(sessionId, suffix) {
|
|
89
|
+
if (!sessionId) return null;
|
|
90
|
+
const safe = sessionFileStem(sessionId);
|
|
91
|
+
if (!safe) return null;
|
|
92
|
+
if (suffix.includes("/") || suffix.includes("\\") || suffix.includes("..")) return null;
|
|
93
|
+
if (suffix !== ".json" && suffix.endsWith(".json")) return null;
|
|
94
|
+
const dir = sessionsDir();
|
|
95
|
+
const candidate = path2.join(dir, `${safe}${suffix}`);
|
|
96
|
+
try {
|
|
97
|
+
const rel = path2.relative(dir, candidate);
|
|
98
|
+
if (rel.startsWith("..")) return null;
|
|
99
|
+
} catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
return candidate;
|
|
103
|
+
}
|
|
104
|
+
function asStringPairs(raw) {
|
|
105
|
+
if (!Array.isArray(raw)) return [];
|
|
106
|
+
const out = [];
|
|
107
|
+
for (const item of raw) {
|
|
108
|
+
if (Array.isArray(item) && typeof item[0] === "string" && typeof item[1] === "string") {
|
|
109
|
+
out.push([item[0], item[1]]);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
function asFileEntry(raw) {
|
|
115
|
+
if (raw === null || typeof raw !== "object") return null;
|
|
116
|
+
const o = raw;
|
|
117
|
+
if (typeof o["path"] !== "string" || typeof o["readCount"] !== "number" || typeof o["lastReadAt"] !== "number" || typeof o["wasEdited"] !== "boolean" || typeof o["sizeBytes"] !== "number") {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
let entry = {
|
|
121
|
+
path: o["path"],
|
|
122
|
+
readCount: o["readCount"],
|
|
123
|
+
lastReadAt: o["lastReadAt"],
|
|
124
|
+
wasEdited: o["wasEdited"],
|
|
125
|
+
sizeBytes: o["sizeBytes"]
|
|
126
|
+
};
|
|
127
|
+
if (o["wasTruncated"] === true) entry = { ...entry, wasTruncated: true };
|
|
128
|
+
if (typeof o["lastFullReadAt"] === "number") entry = { ...entry, lastFullReadAt: o["lastFullReadAt"] };
|
|
129
|
+
if (typeof o["fullReadCount"] === "number") entry = { ...entry, fullReadCount: o["fullReadCount"] };
|
|
130
|
+
if (typeof o["lastEditedAt"] === "number") entry = { ...entry, lastEditedAt: o["lastEditedAt"] };
|
|
131
|
+
if (Array.isArray(o["symbols_read"])) {
|
|
132
|
+
const symbols = o["symbols_read"].filter((s) => typeof s === "string");
|
|
133
|
+
if (symbols.length > 0) entry = { ...entry, symbols_read: symbols };
|
|
134
|
+
}
|
|
135
|
+
const rangeIdentity = o["rangeFileIdentity"];
|
|
136
|
+
if (rangeIdentity !== null && typeof rangeIdentity === "object" && typeof rangeIdentity["size"] === "number" && typeof rangeIdentity["mtimeMs"] === "number") {
|
|
137
|
+
entry = { ...entry, rangeFileIdentity: { size: rangeIdentity["size"], mtimeMs: rangeIdentity["mtimeMs"] } };
|
|
138
|
+
}
|
|
139
|
+
const epochBase = o["epochBase"];
|
|
140
|
+
if (epochBase !== null && typeof epochBase === "object") {
|
|
141
|
+
const b = epochBase;
|
|
142
|
+
if (typeof b["at"] === "number" && typeof b["readCount"] === "number" && typeof b["fullReadCount"] === "number") {
|
|
143
|
+
entry = { ...entry, epochBase: { at: b["at"], readCount: b["readCount"], fullReadCount: b["fullReadCount"] } };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return entry;
|
|
147
|
+
}
|
|
148
|
+
function asPyFileEntry(dictKey, raw) {
|
|
149
|
+
if (raw === null || typeof raw !== "object") return null;
|
|
150
|
+
const v = raw;
|
|
151
|
+
const p = typeof v["rel_or_abs"] === "string" ? v["rel_or_abs"] : dictKey;
|
|
152
|
+
if (!p) return null;
|
|
153
|
+
const readCount = typeof v["read_count"] === "number" ? Math.max(1, Math.round(v["read_count"])) : 1;
|
|
154
|
+
const lastReadTs = typeof v["last_read_ts"] === "number" ? v["last_read_ts"] : 0;
|
|
155
|
+
const sizeBytes = typeof v["read_size"] === "number" ? Math.round(v["read_size"]) : 0;
|
|
156
|
+
const wasEdited = typeof v["last_edit_ts"] === "number" && v["last_edit_ts"] > 0;
|
|
157
|
+
return { path: p, readCount, lastReadAt: lastReadTs * 1e3, wasEdited, sizeBytes };
|
|
158
|
+
}
|
|
159
|
+
function asRangeIdentities(raw) {
|
|
160
|
+
if (!Array.isArray(raw)) return [];
|
|
161
|
+
const out = [];
|
|
162
|
+
for (const pair of raw) {
|
|
163
|
+
if (!Array.isArray(pair) || pair.length !== 2 || typeof pair[0] !== "string") continue;
|
|
164
|
+
const id = pair[1];
|
|
165
|
+
if (id !== null && typeof id === "object" && typeof id["size"] === "number" && typeof id["mtimeMs"] === "number") out.push([pair[0], { size: id["size"], mtimeMs: id["mtimeMs"] }]);
|
|
166
|
+
}
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
function asLineRanges(raw) {
|
|
170
|
+
if (!Array.isArray(raw)) return [];
|
|
171
|
+
const out = [];
|
|
172
|
+
for (const pair of raw) {
|
|
173
|
+
if (!Array.isArray(pair) || pair.length !== 2) continue;
|
|
174
|
+
const filePath = pair[0];
|
|
175
|
+
const ranges = pair[1];
|
|
176
|
+
if (typeof filePath !== "string" || !Array.isArray(ranges)) continue;
|
|
177
|
+
const valid = [];
|
|
178
|
+
for (const r of ranges) {
|
|
179
|
+
if (Array.isArray(r) && r.length === 2 && typeof r[0] === "number" && typeof r[1] === "number") valid.push([r[0], r[1]]);
|
|
180
|
+
}
|
|
181
|
+
out.push([filePath, valid]);
|
|
182
|
+
}
|
|
183
|
+
return out;
|
|
184
|
+
}
|
|
185
|
+
function asServedOutputs(raw) {
|
|
186
|
+
if (!Array.isArray(raw)) return [];
|
|
187
|
+
const out = [];
|
|
188
|
+
for (const pair of raw) {
|
|
189
|
+
if (!Array.isArray(pair) || pair.length !== 2) continue;
|
|
190
|
+
const filePath = pair[0];
|
|
191
|
+
const ids = pair[1];
|
|
192
|
+
if (typeof filePath !== "string" || !Array.isArray(ids)) continue;
|
|
193
|
+
out.push([filePath, ids.filter((id) => typeof id === "string")]);
|
|
194
|
+
}
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
function migrateKeys(pairs, migrate) {
|
|
198
|
+
return pairs.map(([key, value]) => [migrate(key), value]);
|
|
199
|
+
}
|
|
200
|
+
function coerce(raw) {
|
|
201
|
+
const o = raw !== null && typeof raw === "object" ? raw : {};
|
|
202
|
+
const files = [];
|
|
203
|
+
if (Array.isArray(o["files"])) {
|
|
204
|
+
for (const f of o["files"]) {
|
|
205
|
+
const e = asFileEntry(f);
|
|
206
|
+
if (e !== null) files.push(e);
|
|
207
|
+
}
|
|
208
|
+
} else if (o["files"] !== null && typeof o["files"] === "object") {
|
|
209
|
+
for (const [key, val] of Object.entries(o["files"])) {
|
|
210
|
+
const e = asPyFileEntry(key, val);
|
|
211
|
+
if (e !== null) files.push(e);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
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") : [];
|
|
215
|
+
const scheduledPromptCounts = Array.isArray(o["scheduledPromptCounts"]) ? o["scheduledPromptCounts"].filter(
|
|
216
|
+
(p) => Array.isArray(p) && p.length === 2 && typeof p[0] === "string" && typeof p[1] === "number" && Number.isSafeInteger(p[1]) && p[1] >= 0
|
|
217
|
+
) : [];
|
|
218
|
+
const cliReads = Array.isArray(o["cliReads"]) ? o["cliReads"].filter((h) => typeof h === "string") : [];
|
|
219
|
+
const bashReruns = Array.isArray(o["bashReruns"]) ? o["bashReruns"].filter((h) => typeof h === "string") : [];
|
|
220
|
+
const seenImageHashes = Array.isArray(o["seenImageHashes"]) ? o["seenImageHashes"].filter((h) => typeof h === "string") : [];
|
|
221
|
+
const verificationRuns = Array.isArray(o["verificationRuns"]) ? o["verificationRuns"].filter(
|
|
222
|
+
(r) => r !== null && typeof r === "object" && typeof r.command === "string" && typeof r.at === "number" && (r.exitCode === null || typeof r.exitCode === "number")
|
|
223
|
+
) : [];
|
|
224
|
+
const pendingLargeFileHints = Array.isArray(o["pendingLargeFileHints"]) ? o["pendingLargeFileHints"].filter(
|
|
225
|
+
(p) => Array.isArray(p) && p.length === 2 && typeof p[0] === "string" && typeof p[1] === "number"
|
|
226
|
+
) : [];
|
|
227
|
+
const grepQueries = Array.isArray(o["grepQueries"]) ? o["grepQueries"].filter(
|
|
228
|
+
(p) => Array.isArray(p) && p.length === 2 && typeof p[0] === "string" && typeof p[1] === "number"
|
|
229
|
+
) : [];
|
|
230
|
+
const globQueries = Array.isArray(o["globQueries"]) ? o["globQueries"].filter(
|
|
231
|
+
(p) => Array.isArray(p) && p.length === 2 && typeof p[0] === "string" && typeof p[1] === "number"
|
|
232
|
+
) : [];
|
|
233
|
+
const outstandingAgentSpawns = Array.isArray(o["outstandingAgentSpawns"]) ? o["outstandingAgentSpawns"].filter(
|
|
234
|
+
(p) => Array.isArray(p) && p.length === 2 && typeof p[0] === "string" && typeof p[1] === "number"
|
|
235
|
+
) : [];
|
|
236
|
+
const rangeIdentities = asRangeIdentities(o["fileLineRangeIdentities"]);
|
|
237
|
+
return {
|
|
238
|
+
files,
|
|
239
|
+
hintsShown,
|
|
240
|
+
...scheduledPromptCounts.length > 0 ? { scheduledPromptCounts } : {},
|
|
241
|
+
webFetches: migrateKeys(asStringPairs(o["webFetches"]), migrateWebFetchKey),
|
|
242
|
+
bashOutputs: asStringPairs(o["bashOutputs"]),
|
|
243
|
+
curlDownloads: migrateKeys(asStringPairs(o["curlDownloads"]), migrateCurlDownloadKey),
|
|
244
|
+
fileLineRanges: asLineRanges(o["fileLineRanges"]),
|
|
245
|
+
readRangeEvasion: coerceReadRangeEvasion(o["readRangeEvasion"]),
|
|
246
|
+
...rangeIdentities.length > 0 ? { fileLineRangeIdentities: rangeIdentities } : {},
|
|
247
|
+
fileServedOutputs: asServedOutputs(o["fileServedOutputs"]),
|
|
248
|
+
cliReads,
|
|
249
|
+
bashReruns,
|
|
250
|
+
pendingLargeFileHints,
|
|
251
|
+
bashStartCwds: asStringPairs(o["bashStartCwds"]),
|
|
252
|
+
grepQueries,
|
|
253
|
+
globQueries,
|
|
254
|
+
outstandingAgentSpawns,
|
|
255
|
+
...typeof o["lastTabContextDigest"] === "string" ? { lastTabContextDigest: o["lastTabContextDigest"] } : {},
|
|
256
|
+
...seenImageHashes.length > 0 ? { seenImageHashes } : {},
|
|
257
|
+
...verificationRuns.length > 0 ? { verificationRuns } : {},
|
|
258
|
+
...typeof o["compactedAt"] === "number" ? { compactedAt: o["compactedAt"] } : {},
|
|
259
|
+
...typeof o["created_ts"] === "number" ? { created_ts: o["created_ts"] } : {}
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
function mergeFileEntry(a, b) {
|
|
263
|
+
const newest = a.lastReadAt >= b.lastReadAt ? a : b;
|
|
264
|
+
const baseline = filesReadCountAtLoad().get(b.path) ?? 0;
|
|
265
|
+
const newReadsThisProcess = Math.max(0, b.readCount - baseline);
|
|
266
|
+
let merged = {
|
|
267
|
+
path: a.path,
|
|
268
|
+
readCount: a.readCount + newReadsThisProcess,
|
|
269
|
+
lastReadAt: Math.max(a.lastReadAt, b.lastReadAt),
|
|
270
|
+
wasEdited: a.wasEdited || b.wasEdited,
|
|
271
|
+
sizeBytes: newest.sizeBytes
|
|
272
|
+
};
|
|
273
|
+
if (newest.rangeFileIdentity) merged = { ...merged, rangeFileIdentity: newest.rangeFileIdentity };
|
|
274
|
+
if (a.wasTruncated || b.wasTruncated) merged = { ...merged, wasTruncated: true };
|
|
275
|
+
const lastFullReadAt = Math.max(a.lastFullReadAt ?? 0, b.lastFullReadAt ?? 0);
|
|
276
|
+
if (lastFullReadAt > 0) merged = { ...merged, lastFullReadAt };
|
|
277
|
+
const fullReadBaseline = filesFullReadCountAtLoad().get(b.path) ?? 0;
|
|
278
|
+
const newFullReadsThisProcess = Math.max(0, (b.fullReadCount ?? 0) - fullReadBaseline);
|
|
279
|
+
const fullReadCount = (a.fullReadCount ?? 0) + newFullReadsThisProcess;
|
|
280
|
+
if (fullReadCount > 0) merged = { ...merged, fullReadCount };
|
|
281
|
+
const lastEditedAt = Math.max(a.lastEditedAt ?? 0, b.lastEditedAt ?? 0);
|
|
282
|
+
if (lastEditedAt > 0) merged = { ...merged, lastEditedAt };
|
|
283
|
+
const epochBase = mergeEpochBase(a.epochBase, b.epochBase);
|
|
284
|
+
if (epochBase !== void 0) merged = { ...merged, epochBase };
|
|
285
|
+
const symbols = Array.from(/* @__PURE__ */ new Set([...a.symbols_read ?? [], ...b.symbols_read ?? []]));
|
|
286
|
+
if (symbols.length > 0) merged = { ...merged, symbols_read: symbols };
|
|
287
|
+
return merged;
|
|
288
|
+
}
|
|
289
|
+
function mergePairs(disk, mem) {
|
|
290
|
+
return Array.from(new Map([...disk, ...mem]).entries());
|
|
291
|
+
}
|
|
292
|
+
function mergeMaxNumberPairs(disk, mem) {
|
|
293
|
+
const merged = new Map(disk);
|
|
294
|
+
for (const [key, value] of mem) {
|
|
295
|
+
merged.set(key, Math.max(merged.get(key) ?? 0, value));
|
|
296
|
+
}
|
|
297
|
+
return Array.from(merged.entries());
|
|
298
|
+
}
|
|
299
|
+
function mergeLineRanges(disk, mem) {
|
|
300
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
301
|
+
for (const [filePath, ranges] of disk) byPath.set(filePath, [...ranges]);
|
|
302
|
+
for (const [filePath, ranges] of mem) {
|
|
303
|
+
const prev = byPath.get(filePath) ?? [];
|
|
304
|
+
const seen = new Set(prev.map(([s, e]) => s + ":" + e));
|
|
305
|
+
for (const [s, e] of ranges) {
|
|
306
|
+
const key = s + ":" + e;
|
|
307
|
+
if (seen.has(key) || prev.length >= MAX_RANGES_PER_FILE) continue;
|
|
308
|
+
prev.push([s, e]);
|
|
309
|
+
seen.add(key);
|
|
310
|
+
}
|
|
311
|
+
byPath.set(filePath, prev);
|
|
312
|
+
}
|
|
313
|
+
return Array.from(byPath.entries());
|
|
314
|
+
}
|
|
315
|
+
var GENERIC_SERVED_OUTPUT_FOLDED_KEY = foldPath(normalizePath(GENERIC_SERVED_OUTPUT_KEY));
|
|
316
|
+
function mergeServedOutputs(disk, mem) {
|
|
317
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
318
|
+
for (const [filePath, ids] of disk) byPath.set(filePath, [...ids]);
|
|
319
|
+
for (const [filePath, ids] of mem) {
|
|
320
|
+
const prev = byPath.get(filePath) ?? [];
|
|
321
|
+
const merged = [...prev.filter((id) => !ids.includes(id)), ...ids];
|
|
322
|
+
const cap = filePath === GENERIC_SERVED_OUTPUT_FOLDED_KEY ? MAX_GENERIC_SERVED_OUTPUTS : MAX_SERVED_OUTPUTS_PER_FILE;
|
|
323
|
+
if (merged.length > cap) merged.splice(0, merged.length - cap);
|
|
324
|
+
byPath.set(filePath, merged);
|
|
325
|
+
}
|
|
326
|
+
return Array.from(byPath.entries());
|
|
327
|
+
}
|
|
328
|
+
function mergePendingLargeFileHints(disk, mem) {
|
|
329
|
+
const merged = new Map(disk);
|
|
330
|
+
for (const key of consumedPendingLargeFileHintKeys()) merged.delete(key);
|
|
331
|
+
const atLoad = pendingLargeFileHintsAtLoad();
|
|
332
|
+
for (const [key, size] of mem) {
|
|
333
|
+
if (atLoad.get(key) === size) continue;
|
|
334
|
+
merged.set(key, size);
|
|
335
|
+
}
|
|
336
|
+
return Array.from(merged.entries());
|
|
337
|
+
}
|
|
338
|
+
function mergeCurlDownloads(disk, mem) {
|
|
339
|
+
const merged = new Map(disk);
|
|
340
|
+
for (const key of consumedCurlDownloadKeys()) merged.delete(key);
|
|
341
|
+
const atLoad = curlDownloadsAtLoad();
|
|
342
|
+
for (const [url, savedPath] of mem) {
|
|
343
|
+
if (atLoad.get(url) === savedPath) continue;
|
|
344
|
+
merged.set(url, savedPath);
|
|
345
|
+
}
|
|
346
|
+
return Array.from(merged.entries());
|
|
347
|
+
}
|
|
348
|
+
function mergeBashStartCwds(disk, mem) {
|
|
349
|
+
const merged = new Map(disk);
|
|
350
|
+
for (const key of consumedBashStartCwdKeys()) merged.delete(key);
|
|
351
|
+
const atLoad = bashStartCwdsAtLoad();
|
|
352
|
+
for (const [toolUseId, cwd] of mem) {
|
|
353
|
+
if (atLoad.get(toolUseId) === cwd) continue;
|
|
354
|
+
merged.set(toolUseId, cwd);
|
|
355
|
+
}
|
|
356
|
+
const entries = Array.from(merged.entries());
|
|
357
|
+
return entries.length > MAX_BASH_START_CWDS ? entries.slice(entries.length - MAX_BASH_START_CWDS) : entries;
|
|
358
|
+
}
|
|
359
|
+
function mergeOutstandingAgentSpawns(disk, mem) {
|
|
360
|
+
const merged = new Map(disk.map(([prompt, ts]) => [outstandingAgentSpawnKey(prompt, ts), [prompt, ts]]));
|
|
361
|
+
for (const key of consumedOutstandingAgentSpawnKeys()) merged.delete(key);
|
|
362
|
+
const atLoad = new Set(outstandingAgentSpawnsAtLoad().map((e) => outstandingAgentSpawnKey(e.prompt, e.ts)));
|
|
363
|
+
for (const [prompt, ts] of mem) {
|
|
364
|
+
const key = outstandingAgentSpawnKey(prompt, ts);
|
|
365
|
+
if (atLoad.has(key)) continue;
|
|
366
|
+
merged.set(key, [prompt, ts]);
|
|
367
|
+
}
|
|
368
|
+
const result = Array.from(merged.values()).sort((a, b) => a[1] - b[1]);
|
|
369
|
+
if (result.length > MAX_OUTSTANDING_AGENT_SPAWNS) {
|
|
370
|
+
result.splice(0, result.length - MAX_OUTSTANDING_AGENT_SPAWNS);
|
|
371
|
+
}
|
|
372
|
+
return result;
|
|
373
|
+
}
|
|
374
|
+
function mergeSessionState(disk, mem) {
|
|
375
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
376
|
+
for (const e of disk.files) byPath.set(foldPath(e.path), e);
|
|
377
|
+
for (const e of mem.files) {
|
|
378
|
+
const key = foldPath(e.path);
|
|
379
|
+
const prev = byPath.get(key);
|
|
380
|
+
byPath.set(key, prev ? mergeFileEntry(prev, e) : e);
|
|
381
|
+
}
|
|
382
|
+
const compactedAt = Math.max(disk.compactedAt ?? 0, mem.compactedAt ?? 0);
|
|
383
|
+
const rawDiskRanges = (disk.compactedAt ?? 0) === compactedAt ? disk.fileLineRanges ?? [] : [];
|
|
384
|
+
const memRanges = (mem.compactedAt ?? 0) === compactedAt ? mem.fileLineRanges ?? [] : [];
|
|
385
|
+
const rawDiskServed = (disk.compactedAt ?? 0) === compactedAt ? disk.fileServedOutputs ?? [] : [];
|
|
386
|
+
const memServed = (mem.compactedAt ?? 0) === compactedAt ? mem.fileServedOutputs ?? [] : [];
|
|
387
|
+
const rawDiskCliReads = (disk.compactedAt ?? 0) === compactedAt ? disk.cliReads ?? [] : [];
|
|
388
|
+
const memCliReads = (mem.compactedAt ?? 0) === compactedAt ? mem.cliReads ?? [] : [];
|
|
389
|
+
const latestRuns = /* @__PURE__ */ new Map();
|
|
390
|
+
for (const run of [...disk.verificationRuns ?? [], ...mem.verificationRuns ?? []]) {
|
|
391
|
+
const seen = latestRuns.get(run.command);
|
|
392
|
+
if (seen === void 0 || run.at >= seen.at) latestRuns.set(run.command, run);
|
|
393
|
+
}
|
|
394
|
+
const verificationRuns = Array.from(latestRuns.values()).sort((a, b) => a.at - b.at).slice(-MAX_VERIFICATION_RUNS);
|
|
395
|
+
const clearedRangeFiles = new Set(consumedFileLineRangeKeys());
|
|
396
|
+
const clearedServedFiles = new Set(consumedFileServedOutputKeys());
|
|
397
|
+
const diskRanges = rawDiskRanges.filter(([path3]) => !clearedRangeFiles.has(path3));
|
|
398
|
+
const diskServed = rawDiskServed.filter(([path3]) => !clearedServedFiles.has(path3));
|
|
399
|
+
const fileLineRanges = mergeLineRanges(diskRanges, memRanges);
|
|
400
|
+
const diskRangeEvasion = (disk.compactedAt ?? 0) === compactedAt ? disk.readRangeEvasion ?? [] : [];
|
|
401
|
+
const memRangeEvasion = (mem.compactedAt ?? 0) === compactedAt ? mem.readRangeEvasion ?? [] : [];
|
|
402
|
+
const rangedFiles = new Set(fileLineRanges.map(([path3]) => path3));
|
|
403
|
+
const rawDiskIdentities = (disk.compactedAt ?? 0) === compactedAt ? disk.fileLineRangeIdentities ?? [] : [];
|
|
404
|
+
const memIdentities = (mem.compactedAt ?? 0) === compactedAt ? mem.fileLineRangeIdentities ?? [] : [];
|
|
405
|
+
const fileLineRangeIdentities = mergePairs(rawDiskIdentities.filter(([path3]) => !clearedRangeFiles.has(path3)), memIdentities).filter(([path3]) => rangedFiles.has(path3));
|
|
406
|
+
return {
|
|
407
|
+
files: Array.from(byPath.values()),
|
|
408
|
+
hintsShown: Array.from(/* @__PURE__ */ new Set([...disk.hintsShown, ...mem.hintsShown])),
|
|
409
|
+
scheduledPromptCounts: mergeMaxNumberPairs(disk.scheduledPromptCounts ?? [], mem.scheduledPromptCounts ?? []),
|
|
410
|
+
webFetches: mergePairs(disk.webFetches, mem.webFetches),
|
|
411
|
+
bashOutputs: mergePairs(disk.bashOutputs, mem.bashOutputs),
|
|
412
|
+
curlDownloads: mergeCurlDownloads(disk.curlDownloads, mem.curlDownloads),
|
|
413
|
+
fileLineRanges,
|
|
414
|
+
readRangeEvasion: mergeReadRangeEvasion(diskRangeEvasion, memRangeEvasion),
|
|
415
|
+
...fileLineRangeIdentities.length > 0 ? { fileLineRangeIdentities } : {},
|
|
416
|
+
fileServedOutputs: mergeServedOutputs(diskServed, memServed),
|
|
417
|
+
...compactedAt > 0 ? { compactedAt } : {},
|
|
418
|
+
// 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.
|
|
419
|
+
cliReads: Array.from(/* @__PURE__ */ new Set([...rawDiskCliReads, ...memCliReads])),
|
|
420
|
+
bashReruns: Array.from(/* @__PURE__ */ new Set([...disk.bashReruns ?? [], ...mem.bashReruns ?? []])),
|
|
421
|
+
pendingLargeFileHints: mergePendingLargeFileHints(disk.pendingLargeFileHints ?? [], mem.pendingLargeFileHints ?? []),
|
|
422
|
+
bashStartCwds: mergeBashStartCwds(disk.bashStartCwds ?? [], mem.bashStartCwds ?? []),
|
|
423
|
+
grepQueries: mergePairs(disk.grepQueries ?? [], mem.grepQueries ?? []),
|
|
424
|
+
globQueries: mergePairs(disk.globQueries ?? [], mem.globQueries ?? []),
|
|
425
|
+
outstandingAgentSpawns: mergeOutstandingAgentSpawns(disk.outstandingAgentSpawns ?? [], mem.outstandingAgentSpawns ?? []),
|
|
426
|
+
// 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.
|
|
427
|
+
seenImageHashes: mergeSeenImageHashes(disk.seenImageHashes ?? [], mem.seenImageHashes ?? []),
|
|
428
|
+
...verificationRuns.length > 0 ? { verificationRuns } : {},
|
|
429
|
+
// 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.
|
|
430
|
+
...mem.lastTabContextDigest !== void 0 ? { lastTabContextDigest: mem.lastTabContextDigest } : disk.lastTabContextDigest !== void 0 ? { lastTabContextDigest: disk.lastTabContextDigest } : {},
|
|
431
|
+
// 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".
|
|
432
|
+
...disk.created_ts !== void 0 ? { created_ts: disk.created_ts } : mem.created_ts !== void 0 ? { created_ts: mem.created_ts } : {}
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
function mergeSeenImageHashes(disk, mem) {
|
|
436
|
+
const merged = Array.from(/* @__PURE__ */ new Set([...disk, ...mem]));
|
|
437
|
+
return merged.length > MAX_SEEN_IMAGE_HASHES ? merged.slice(merged.length - MAX_SEEN_IMAGE_HASHES) : merged;
|
|
438
|
+
}
|
|
439
|
+
function capFiles(s, max) {
|
|
440
|
+
if (s.files.length <= max) return s;
|
|
441
|
+
const kept = [...s.files].sort((a, b) => a.wasEdited === b.wasEdited ? b.lastReadAt - a.lastReadAt : a.wasEdited ? -1 : 1).slice(0, max);
|
|
442
|
+
return { ...s, files: kept };
|
|
443
|
+
}
|
|
444
|
+
function readDiskState(p) {
|
|
445
|
+
try {
|
|
446
|
+
return readDiskStateForMerge(p);
|
|
447
|
+
} catch {
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
function readDiskStateForMerge(p) {
|
|
452
|
+
let text = "";
|
|
453
|
+
try {
|
|
454
|
+
withRetryOnLock(() => {
|
|
455
|
+
text = fs.readFileSync(p, "utf8");
|
|
456
|
+
});
|
|
457
|
+
} catch (err) {
|
|
458
|
+
if (err.code === "ENOENT") return null;
|
|
459
|
+
throw err;
|
|
460
|
+
}
|
|
461
|
+
try {
|
|
462
|
+
return coerce(JSON.parse(text));
|
|
463
|
+
} catch {
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
function readSessionStateFile(sessionId) {
|
|
468
|
+
const p = sessionPath(sessionId);
|
|
469
|
+
if (!p) return null;
|
|
470
|
+
return readDiskState(p);
|
|
471
|
+
}
|
|
472
|
+
function listSiblingSessionStates(sessionId) {
|
|
473
|
+
if (!sessionId) return [];
|
|
474
|
+
const prefix = saltedStemPrefix(sessionId);
|
|
475
|
+
if (prefix === AGENT_SALT_MARKER) return [];
|
|
476
|
+
const dir = sessionsDir();
|
|
477
|
+
const out = [];
|
|
478
|
+
try {
|
|
479
|
+
if (!fs.existsSync(dir)) return out;
|
|
480
|
+
for (const file of fs.readdirSync(dir)) {
|
|
481
|
+
if (!file.endsWith(".json") || !file.startsWith(prefix)) continue;
|
|
482
|
+
const state = readDiskState(path2.join(dir, file));
|
|
483
|
+
if (state !== null) out.push(state);
|
|
484
|
+
}
|
|
485
|
+
} catch {
|
|
486
|
+
return out;
|
|
487
|
+
}
|
|
488
|
+
return out;
|
|
489
|
+
}
|
|
490
|
+
var EMPTY_SESSION = { files: [], hintsShown: [], webFetches: [], bashOutputs: [], curlDownloads: [] };
|
|
491
|
+
function loadSessionState(sessionId) {
|
|
492
|
+
const p = sessionPath(sessionId);
|
|
493
|
+
importSessionState((p ? readDiskState(p) : null) ?? EMPTY_SESSION);
|
|
494
|
+
applySessionCompaction(sessionId);
|
|
495
|
+
}
|
|
496
|
+
var COMPACTED_AT_SUFFIX = ".compacted-at";
|
|
497
|
+
function applySessionCompaction(stateKey) {
|
|
498
|
+
const at = stateKey.indexOf(":agent:");
|
|
499
|
+
const p = sessionSidecarPath(at < 0 ? stateKey : stateKey.slice(0, at), COMPACTED_AT_SUFFIX);
|
|
500
|
+
if (!p) return;
|
|
501
|
+
try {
|
|
502
|
+
const epoch = Number(fs.readFileSync(p, "utf8").trim());
|
|
503
|
+
if (Number.isFinite(epoch) && epoch > 0) markCompacted(epoch);
|
|
504
|
+
} catch {
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
var SESSION_SAVE_LOCK_WAIT_MS = 500;
|
|
508
|
+
function saveSessionState(sessionId) {
|
|
509
|
+
const p = sessionPath(sessionId);
|
|
510
|
+
if (!p) return;
|
|
511
|
+
try {
|
|
512
|
+
const dir = path2.dirname(p);
|
|
513
|
+
if (!fs.existsSync(dir)) ensureDirSync(dir);
|
|
514
|
+
const mem = exportSessionState();
|
|
515
|
+
const writeMerged = () => {
|
|
516
|
+
const disk = readDiskStateForMerge(p);
|
|
517
|
+
const merged = capFiles(disk ? mergeSessionState(disk, mem) : mem, MAX_FILES);
|
|
518
|
+
if (merged.created_ts === void 0) merged.created_ts = Date.now() / 1e3;
|
|
519
|
+
let json;
|
|
520
|
+
try {
|
|
521
|
+
json = redactSerializedJson(JSON.stringify(merged)).text;
|
|
522
|
+
} catch {
|
|
523
|
+
return true;
|
|
524
|
+
}
|
|
525
|
+
atomicWriteText(p, json);
|
|
526
|
+
return true;
|
|
527
|
+
};
|
|
528
|
+
let heldByAnother = false;
|
|
529
|
+
if (withFileLock(`${p}.lock`, writeMerged, { waitMs: SESSION_SAVE_LOCK_WAIT_MS, onContended: () => {
|
|
530
|
+
heldByAnother = true;
|
|
531
|
+
} }) === void 0 && !heldByAnother) writeMerged();
|
|
532
|
+
} catch {
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
export {
|
|
537
|
+
SESSIONS_SUBDIR,
|
|
538
|
+
sessionsDir,
|
|
539
|
+
AGENT_SALT_MARKER,
|
|
540
|
+
sessionFileStem,
|
|
541
|
+
sessionSidecarPath,
|
|
542
|
+
readSessionStateFile,
|
|
543
|
+
listSiblingSessionStates,
|
|
544
|
+
loadSessionState,
|
|
545
|
+
COMPACTED_AT_SUFFIX,
|
|
546
|
+
saveSessionState
|
|
547
|
+
};
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { createRequire as __cjsRequire } from 'node:module';
|
|
2
2
|
const require = __cjsRequire(import.meta.url);
|
|
3
|
-
import {
|
|
4
|
-
estimateTokensFromLength
|
|
5
|
-
} from "./token-goat-chunk-JB6QN6VF.mjs";
|
|
6
3
|
import {
|
|
7
4
|
matchRestOfLine
|
|
8
5
|
} from "./token-goat-chunk-EVMZJ27Z.mjs";
|
|
6
|
+
import {
|
|
7
|
+
estimateTokensFromLength
|
|
8
|
+
} from "./token-goat-chunk-BFCWY73P.mjs";
|
|
9
|
+
import {
|
|
10
|
+
quotedArg
|
|
11
|
+
} from "./token-goat-chunk-GGGG7RWB.mjs";
|
|
9
12
|
import {
|
|
10
13
|
displaySafeText
|
|
11
14
|
} from "./token-goat-chunk-FNFBKLRD.mjs";
|
|
@@ -174,7 +177,7 @@ function repeatedSkillBodyHint(injections) {
|
|
|
174
177
|
if (worst.count < SKILL_BODY_REPEAT_THRESHOLD || worst.bytes < LARGE_SKILL_BODY_BYTES) return null;
|
|
175
178
|
const skill = displaySafeText(worst.skill);
|
|
176
179
|
const tokens = formatTokenEstimate(estimateTokensFromLength(worst.bytes));
|
|
177
|
-
return `The \`${skill}\` skill body has been injected ${worst.count} times this session (${formatBytes(worst.bytes)} total, ~${tokens} tok est). Slash expansion and the Skill tool both send the whole body every time, and no hook can intercept either. If it is already loaded, work from it instead of re-invoking; to reread one part, use \`token-goat skill-section ${skill}
|
|
180
|
+
return `The \`${skill}\` skill body has been injected ${worst.count} times this session (${formatBytes(worst.bytes)} total, ~${tokens} tok est). Slash expansion and the Skill tool both send the whole body every time, and no hook can intercept either. If it is already loaded, work from it instead of re-invoking; to reread one part, use \`token-goat skill-section ${quotedArg(skill)} "<heading>"\`.`;
|
|
178
181
|
}
|
|
179
182
|
function readTranscriptTail(transcriptPath, maxBytes = RESIDENT_TAIL_MAX_BYTES) {
|
|
180
183
|
let fd = null;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { createRequire as __cjsRequire } from 'node:module';
|
|
2
|
+
const require = __cjsRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
init_define_import_meta_env
|
|
5
|
+
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
6
|
+
|
|
7
|
+
// src/shell.ts
|
|
8
|
+
init_define_import_meta_env();
|
|
9
|
+
import * as fs from "node:fs";
|
|
10
|
+
import * as path from "node:path";
|
|
11
|
+
var WSL_LAUNCHER_SEGMENTS = /* @__PURE__ */ new Set(["system32", "syswow64", "windowsapps"]);
|
|
12
|
+
function isExecutable(p) {
|
|
13
|
+
try {
|
|
14
|
+
return fs.statSync(p).isFile();
|
|
15
|
+
} catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function isWslLauncherDir(dir) {
|
|
20
|
+
return dir.toLowerCase().split(/[\\/]+/).some((seg) => WSL_LAUNCHER_SEGMENTS.has(seg));
|
|
21
|
+
}
|
|
22
|
+
function locateBashOnPath(pathValue, isExe = isExecutable) {
|
|
23
|
+
for (const dir of (pathValue ?? "").split(path.delimiter)) {
|
|
24
|
+
if (!dir || isWslLauncherDir(dir)) continue;
|
|
25
|
+
const cand = path.join(dir, "bash.exe");
|
|
26
|
+
if (isExe(cand)) return cand;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
function knownGitBashPaths() {
|
|
31
|
+
const localPrograms = process.env["LOCALAPPDATA"] ? path.join(process.env["LOCALAPPDATA"], "Programs") : void 0;
|
|
32
|
+
const bases = [
|
|
33
|
+
process.env["ProgramFiles"],
|
|
34
|
+
process.env["ProgramFiles(x86)"],
|
|
35
|
+
process.env["ProgramW6432"],
|
|
36
|
+
localPrograms
|
|
37
|
+
];
|
|
38
|
+
const out = [];
|
|
39
|
+
for (const base of bases) {
|
|
40
|
+
if (!base) continue;
|
|
41
|
+
out.push(path.join(base, "Git", "bin", "bash.exe"));
|
|
42
|
+
out.push(path.join(base, "Git", "usr", "bin", "bash.exe"));
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
function resolveWindowsBash() {
|
|
47
|
+
if (process.platform !== "win32") return null;
|
|
48
|
+
const override = process.env["TOKEN_GOAT_BASH"];
|
|
49
|
+
if (override && isExecutable(override)) return override;
|
|
50
|
+
const onPath = locateBashOnPath(process.env["PATH"]);
|
|
51
|
+
if (onPath) return onPath;
|
|
52
|
+
for (const cand of knownGitBashPaths()) {
|
|
53
|
+
if (isExecutable(cand)) return cand;
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
function wrappedShell() {
|
|
58
|
+
if (process.platform !== "win32") return true;
|
|
59
|
+
return resolveWindowsBash() ?? true;
|
|
60
|
+
}
|
|
61
|
+
function canRunWrappedShell() {
|
|
62
|
+
return process.platform !== "win32" || resolveWindowsBash() !== null;
|
|
63
|
+
}
|
|
64
|
+
function resolvePowerShell(env = process.env, isExe = isExecutable) {
|
|
65
|
+
const override = env["TOKEN_GOAT_POWERSHELL"] || env["TOKEN_GOAT_PWSH"];
|
|
66
|
+
if (override && isExe(override)) return override;
|
|
67
|
+
if (process.platform === "win32") {
|
|
68
|
+
for (const dir of (env["PATH"] ?? "").split(path.delimiter)) {
|
|
69
|
+
if (!dir) continue;
|
|
70
|
+
const cand = path.join(dir, "pwsh.exe");
|
|
71
|
+
if (isExe(cand)) return cand;
|
|
72
|
+
}
|
|
73
|
+
const systemRoot = env["SystemRoot"] ?? env["windir"] ?? "C:\\Windows";
|
|
74
|
+
const defaultPs = path.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
75
|
+
if (isExe(defaultPs)) return defaultPs;
|
|
76
|
+
return "powershell.exe";
|
|
77
|
+
}
|
|
78
|
+
for (const dir of (env["PATH"] ?? "").split(path.delimiter)) {
|
|
79
|
+
if (!dir) continue;
|
|
80
|
+
const cand = path.join(dir, "pwsh");
|
|
81
|
+
if (isExe(cand)) return cand;
|
|
82
|
+
}
|
|
83
|
+
for (const cand of ["/usr/bin/pwsh", "/usr/local/bin/pwsh", "/opt/microsoft/powershell/7/pwsh"]) {
|
|
84
|
+
if (isExe(cand)) return cand;
|
|
85
|
+
}
|
|
86
|
+
return "pwsh";
|
|
87
|
+
}
|
|
88
|
+
function canRunPowerShell(env = process.env, isExe = isExecutable) {
|
|
89
|
+
if (process.platform === "win32") return true;
|
|
90
|
+
try {
|
|
91
|
+
const ps = resolvePowerShell(env, isExe);
|
|
92
|
+
return isExe(ps);
|
|
93
|
+
} catch {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function powerShellCommandArgs(command) {
|
|
98
|
+
return ["-NoProfile", "-NonInteractive", "-Command", command];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export {
|
|
102
|
+
wrappedShell,
|
|
103
|
+
canRunWrappedShell,
|
|
104
|
+
resolvePowerShell,
|
|
105
|
+
canRunPowerShell,
|
|
106
|
+
powerShellCommandArgs
|
|
107
|
+
};
|