token-goat 2.9.29 → 2.9.30
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 +34 -40
- package/SECURITY.md +28 -10
- package/THIRD_PARTY_NOTICES.md +40 -3
- package/dist/native/linux-arm64/tg-hook +0 -0
- package/dist/native/linux-x64/tg-hook +0 -0
- package/dist/ort-wasm-simd-threaded.mjs +59 -0
- package/dist/token-goat-chunk-3CHUNJ2O.mjs +53 -0
- package/dist/token-goat-chunk-3Z5S4KGM.mjs +918 -0
- package/dist/token-goat-chunk-4FVIAHSI.mjs +211 -0
- package/dist/{token-goat-chunk-PGTH2U4C.mjs → token-goat-chunk-4JJS54TR.mjs} +4 -3
- package/dist/token-goat-chunk-4LCCNRTG.mjs +313 -0
- package/dist/{token-goat-chunk-L7H3U27L.mjs → token-goat-chunk-54K3EUV3.mjs} +1 -1
- package/dist/token-goat-chunk-56EIMS3E.mjs +159 -0
- package/dist/token-goat-chunk-5BCIJXMO.mjs +609 -0
- package/dist/token-goat-chunk-5KDMCMF2.mjs +362 -0
- package/dist/token-goat-chunk-5RG5SBL6.mjs +221 -0
- package/dist/{token-goat-chunk-RRNZMM3A.mjs → token-goat-chunk-5Y6ZYL6U.mjs} +21 -2
- package/dist/{token-goat-chunk-GLXRTTLE.mjs → token-goat-chunk-64FBPDTK.mjs} +12 -8
- package/dist/{token-goat-chunk-G23DWHV4.mjs → token-goat-chunk-664RTTUV.mjs} +28 -15
- package/dist/token-goat-chunk-67TQJ76B.mjs +57 -0
- package/dist/token-goat-chunk-6QMAS7AB.mjs +42 -0
- package/dist/token-goat-chunk-7BXSNBY5.mjs +253 -0
- package/dist/{token-goat-chunk-IVWI2OCK.mjs → token-goat-chunk-7F2UBPD3.mjs} +40 -11
- package/dist/{token-goat-chunk-TGOXNAHW.mjs → token-goat-chunk-7IN2MKDS.mjs} +682 -501
- package/dist/token-goat-chunk-7NADEDKJ.mjs +124 -0
- package/dist/{token-goat-chunk-KOPIR3KZ.mjs → token-goat-chunk-A3CK6N5Z.mjs} +618 -72
- package/dist/{token-goat-chunk-O6I2XU4P.mjs → token-goat-chunk-AFLYLHDH.mjs} +44 -31
- package/dist/{token-goat-chunk-TCQANXYC.mjs → token-goat-chunk-AM5V6GSX.mjs} +18 -2
- package/dist/{token-goat-chunk-E4ZOJIZK.mjs → token-goat-chunk-APGZXSPS.mjs} +39 -146
- package/dist/{token-goat-chunk-D4ETHJD6.mjs → token-goat-chunk-ARDNEC2U.mjs} +9 -7
- package/dist/token-goat-chunk-ATCPY7EQ.mjs +134 -0
- package/dist/token-goat-chunk-B2RZZCSS.mjs +354 -0
- package/dist/{token-goat-chunk-BGYOZ6DO.mjs → token-goat-chunk-B45VMIIH.mjs} +6 -7
- package/dist/token-goat-chunk-BPGCUMR7.mjs +30 -0
- package/dist/{token-goat-chunk-TDUT2CL6.mjs → token-goat-chunk-C63JQV5K.mjs} +45 -6
- package/dist/{token-goat-chunk-ZER6EAXZ.mjs → token-goat-chunk-COBQGL5S.mjs} +2661 -2440
- package/dist/token-goat-chunk-COTXZHTS.mjs +39 -0
- package/dist/{token-goat-chunk-RQGGMPCR.mjs → token-goat-chunk-CYCEYAEO.mjs} +76 -16
- package/dist/token-goat-chunk-CZ3LUXYZ.mjs +4115 -0
- package/dist/{token-goat-chunk-ASLWOQ3I.mjs → token-goat-chunk-DZX2YDPT.mjs} +2 -1
- package/dist/token-goat-chunk-E3OQX2XT.mjs +824 -0
- package/dist/token-goat-chunk-EFHMMIGP.mjs +68 -0
- package/dist/token-goat-chunk-ETBHVDB6.mjs +328 -0
- package/dist/token-goat-chunk-EVMZJ27Z.mjs +147 -0
- package/dist/token-goat-chunk-EXKGNHEG.mjs +2096 -0
- package/dist/{token-goat-chunk-RTMVLHWX.mjs → token-goat-chunk-FBNQNSJK.mjs} +2555 -5127
- package/dist/{token-goat-chunk-NMTKNYGF.mjs → token-goat-chunk-FNFBKLRD.mjs} +74 -9
- package/dist/{token-goat-chunk-RLP6J7WR.mjs → token-goat-chunk-FZ3NEW5O.mjs} +33 -19
- package/dist/token-goat-chunk-G5PJ7AGM.mjs +36 -0
- package/dist/{token-goat-chunk-DNRPXHYH.mjs → token-goat-chunk-GFBSN7BD.mjs} +29 -401
- package/dist/{token-goat-chunk-XYWPOSCN.mjs → token-goat-chunk-GOLEOXZP.mjs} +763 -211
- package/dist/token-goat-chunk-H2K3QP6H.mjs +24 -0
- package/dist/token-goat-chunk-H2PHHGM5.mjs +188 -0
- package/dist/token-goat-chunk-HAVAOB3S.mjs +87 -0
- package/dist/token-goat-chunk-HG45AF2B.mjs +2228 -0
- package/dist/{token-goat-chunk-HUYUHODZ.mjs → token-goat-chunk-HOJLCGDP.mjs} +12965 -11748
- package/dist/{token-goat-chunk-435UQTKD.mjs → token-goat-chunk-HYBFA5X3.mjs} +118 -19
- package/dist/token-goat-chunk-IVALLT3S.mjs +48 -0
- package/dist/{token-goat-chunk-UXSXQQIY.mjs → token-goat-chunk-IVKIZWAB.mjs} +3 -3
- package/dist/{token-goat-chunk-52NSDWYY.mjs → token-goat-chunk-JB6QN6VF.mjs} +6 -4
- package/dist/{token-goat-chunk-HNFZZHFW.mjs → token-goat-chunk-JDHE26RP.mjs} +10 -3
- package/dist/{token-goat-chunk-FTP4U4QA.mjs → token-goat-chunk-JDU3EXSU.mjs} +12 -11
- package/dist/token-goat-chunk-JDWCRD6M.mjs +253 -0
- package/dist/{token-goat-chunk-6AK46ZT7.mjs → token-goat-chunk-JN4XMLSD.mjs} +5 -1
- package/dist/token-goat-chunk-JTQQGXZH.mjs +733 -0
- package/dist/token-goat-chunk-KKUFXAPD.mjs +25 -0
- package/dist/token-goat-chunk-KQNZDCBD.mjs +47 -0
- package/dist/{token-goat-chunk-2WOUYWLM.mjs → token-goat-chunk-KWPNJBDH.mjs} +27 -3
- package/dist/{token-goat-chunk-DPNLUJWB.mjs → token-goat-chunk-LOBGHXPB.mjs} +30 -8
- package/dist/token-goat-chunk-MKPF4CL5.mjs +37 -0
- package/dist/token-goat-chunk-MNWKPRAL.mjs +13 -0
- package/dist/{token-goat-chunk-DNSPXQXQ.mjs → token-goat-chunk-N2NMER56.mjs} +25 -15
- package/dist/token-goat-chunk-NYKZLXMK.mjs +218 -0
- package/dist/token-goat-chunk-OJBNYTJP.mjs +3087 -0
- package/dist/token-goat-chunk-P4QMM4WK.mjs +295 -0
- package/dist/token-goat-chunk-PD5VKH46.mjs +184 -0
- package/dist/token-goat-chunk-PEPTHRFF.mjs +129 -0
- package/dist/{token-goat-chunk-NMBWLRHW.mjs → token-goat-chunk-PU7V3FD6.mjs} +8721 -11104
- package/dist/token-goat-chunk-PY533T7F.mjs +46 -0
- package/dist/token-goat-chunk-Q5VTGRH7.mjs +761 -0
- package/dist/{token-goat-chunk-67BNIKKO.mjs → token-goat-chunk-QCLDAQTS.mjs} +31 -20
- package/dist/{token-goat-chunk-SE6E4BKJ.mjs → token-goat-chunk-QSBRTGM2.mjs} +9 -2
- package/dist/{token-goat-chunk-VZQEWRVH.mjs → token-goat-chunk-RC35RBUL.mjs} +4 -4
- package/dist/{token-goat-chunk-VMFWNHGR.mjs → token-goat-chunk-RHXKCZX3.mjs} +873 -504
- package/dist/{token-goat-chunk-PYSUADX4.mjs → token-goat-chunk-RZRTDST7.mjs} +17 -7
- package/dist/{token-goat-chunk-ONXH6NZ5.mjs → token-goat-chunk-SJHWVNDQ.mjs} +15 -21
- package/dist/token-goat-chunk-SMVRN2HU.mjs +188 -0
- package/dist/{token-goat-chunk-QO3LXVVR.mjs → token-goat-chunk-SQSSNQPE.mjs} +2295 -1718
- package/dist/{token-goat-chunk-2AWQ2R5X.mjs → token-goat-chunk-T4FK7VOV.mjs} +2 -2
- package/dist/token-goat-chunk-U4N3BN2W.mjs +92 -0
- package/dist/token-goat-chunk-UA5V6P3P.mjs +122 -0
- package/dist/{token-goat-chunk-UA5BKWWQ.mjs → token-goat-chunk-UQ5UK2T7.mjs} +15 -4
- package/dist/token-goat-chunk-USILZ3T3.mjs +53 -0
- package/dist/{token-goat-chunk-MELOKFDJ.mjs → token-goat-chunk-VES7EL7O.mjs} +7 -1
- package/dist/{token-goat-chunk-7CPMDFGB.mjs → token-goat-chunk-VL3YIFHZ.mjs} +389 -14
- package/dist/{token-goat-chunk-E5TK5DNO.mjs → token-goat-chunk-VPBBTSJR.mjs} +356 -129
- package/dist/{token-goat-chunk-YOOVFPTP.mjs → token-goat-chunk-W3QCJS6G.mjs} +10 -2
- package/dist/{token-goat-chunk-LZNBQTDK.mjs → token-goat-chunk-WY3L4DVT.mjs} +11 -7
- package/dist/token-goat-chunk-XKI4SEMX.mjs +66 -0
- package/dist/token-goat-chunk-XU4P6Z24.mjs +133 -0
- package/dist/{token-goat-chunk-H5J3C56Q.mjs → token-goat-chunk-Y776AENC.mjs} +112 -26
- package/dist/token-goat-chunk-YARZFCZS.mjs +359 -0
- package/dist/{token-goat-chunk-S4VSXURL.mjs → token-goat-chunk-Z44YO3QZ.mjs} +4 -0
- package/dist/token-goat-chunk-Z57YODA2.mjs +35 -0
- package/dist/{token-goat-chunk-PQ53BSCF.mjs → token-goat-chunk-ZCDTCVIS.mjs} +48 -29
- package/dist/token-goat-chunk-ZEJVMUQH.mjs +48 -0
- package/dist/{token-goat-chunk-BGHSSJUA.mjs → token-goat-chunk-ZFOBKUWH.mjs} +1 -1
- package/dist/token-goat-chunk-ZMBL6UVV.mjs +110 -0
- package/dist/{token-goat-chunk-PKRWMHVP.mjs → token-goat-chunk-ZQSJMO2R.mjs} +338 -170
- package/dist/token-goat-hook-client.cjs +87 -23
- package/dist/token-goat-hook-client.mjs +3 -3
- package/dist/token-goat-hook.mjs +46 -30
- package/dist/token-goat.core.mjs +6 -6
- package/docs/C4_RUNTIME_ARCHITECTURE.md +1 -1
- package/docs/cli.md +80 -46
- package/docs/install.md +47 -14
- package/docs/security.md +7 -7
- package/package.json +17 -7
- package/scripts/verify-native-dist.mjs +386 -0
- package/dist/token-goat-chunk-2NNUA2LR.mjs +0 -143
- package/dist/token-goat-chunk-44BM7DDQ.mjs +0 -294
- package/dist/token-goat-chunk-C5YPNZKQ.mjs +0 -318
- package/dist/token-goat-chunk-CIQ2FTY3.mjs +0 -1990
- package/dist/token-goat-chunk-EUSDXRXU.mjs +0 -1429
- package/dist/token-goat-chunk-G7IWXCFF.mjs +0 -456
- package/dist/token-goat-chunk-JTKCQMQA.mjs +0 -418
- package/dist/token-goat-chunk-ORNFMIER.mjs +0 -295
- package/dist/token-goat-chunk-VQ6F6T7X.mjs +0 -36
- package/dist/token-goat-chunk-YAGLESS3.mjs +0 -23
- package/dist/token-goat-chunk-YKSRAWUU.mjs +0 -33
- package/dist/token-goat-chunk-Z6KBMOBI.mjs +0 -53
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
import { createRequire as __cjsRequire } from 'node:module';
|
|
2
|
-
const require = __cjsRequire(import.meta.url);
|
|
3
|
-
import {
|
|
4
|
-
accumulateResidentLine,
|
|
5
|
-
claudeConfigDir,
|
|
6
|
-
createResidentContextStats,
|
|
7
|
-
summarizeResidentContext
|
|
8
|
-
} from "./token-goat-chunk-ONXH6NZ5.mjs";
|
|
9
|
-
import {
|
|
10
|
-
commandHash,
|
|
11
|
-
getBashOutput,
|
|
12
|
-
normalizeCommandForCacheKey
|
|
13
|
-
} from "./token-goat-chunk-O6I2XU4P.mjs";
|
|
14
|
-
import {
|
|
15
|
-
estimateTokens
|
|
16
|
-
} from "./token-goat-chunk-52NSDWYY.mjs";
|
|
17
|
-
import {
|
|
18
|
-
init_define_import_meta_env
|
|
19
|
-
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
20
|
-
|
|
21
|
-
// src/waste.ts
|
|
22
|
-
init_define_import_meta_env();
|
|
23
|
-
import * as fs from "node:fs";
|
|
24
|
-
import * as path from "node:path";
|
|
25
|
-
import { StringDecoder } from "node:string_decoder";
|
|
26
|
-
function projectTranscriptsDir(projectRoot) {
|
|
27
|
-
const rootStr = path.resolve(projectRoot);
|
|
28
|
-
const slug = rootStr.replace(/[^A-Za-z0-9]/g, "-");
|
|
29
|
-
return path.join(claudeConfigDir(), "projects", slug);
|
|
30
|
-
}
|
|
31
|
-
function findLatestTranscript(projectRoot) {
|
|
32
|
-
const dir = projectTranscriptsDir(projectRoot);
|
|
33
|
-
let entries;
|
|
34
|
-
try {
|
|
35
|
-
entries = fs.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
|
|
36
|
-
} catch {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
let best = null;
|
|
40
|
-
for (const name of entries) {
|
|
41
|
-
const full = path.join(dir, name);
|
|
42
|
-
let stat;
|
|
43
|
-
try {
|
|
44
|
-
stat = fs.statSync(full);
|
|
45
|
-
} catch {
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
if (best === null || stat.mtimeMs > best.mtimeMs) best = { file: full, mtimeMs: stat.mtimeMs };
|
|
49
|
-
}
|
|
50
|
-
return best?.file ?? null;
|
|
51
|
-
}
|
|
52
|
-
function safeStringify(value) {
|
|
53
|
-
try {
|
|
54
|
-
return JSON.stringify(value) ?? "";
|
|
55
|
-
} catch {
|
|
56
|
-
return "";
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
var FILE_PATH_TOOLS = /* @__PURE__ */ new Set(["Read", "Edit", "Write", "NotebookEdit", "MultiEdit"]);
|
|
60
|
-
function extractFilePath(name, input) {
|
|
61
|
-
if (!FILE_PATH_TOOLS.has(name)) return null;
|
|
62
|
-
if (input === null || typeof input !== "object") return null;
|
|
63
|
-
const o = input;
|
|
64
|
-
const fp = o["file_path"];
|
|
65
|
-
if (typeof fp === "string") return fp;
|
|
66
|
-
const notebookPath = o["notebook_path"];
|
|
67
|
-
return typeof notebookPath === "string" ? notebookPath : null;
|
|
68
|
-
}
|
|
69
|
-
function extractCommand(name, input) {
|
|
70
|
-
if (name !== "Bash") return null;
|
|
71
|
-
if (input === null || typeof input !== "object") return null;
|
|
72
|
-
const cmd = input["command"];
|
|
73
|
-
return typeof cmd === "string" ? cmd : null;
|
|
74
|
-
}
|
|
75
|
-
function summarizeInput(name, input, filePath, command) {
|
|
76
|
-
if (filePath !== null) return filePath;
|
|
77
|
-
if (command !== null) return command.length > 100 ? `${command.slice(0, 100)}\u2026` : command;
|
|
78
|
-
if (input === null || typeof input !== "object") return "";
|
|
79
|
-
const o = input;
|
|
80
|
-
for (const key of ["pattern", "query", "path", "description", "prompt"]) {
|
|
81
|
-
const v = o[key];
|
|
82
|
-
if (typeof v === "string") return v.length > 100 ? `${v.slice(0, 100)}\u2026` : v;
|
|
83
|
-
}
|
|
84
|
-
const s = safeStringify(input);
|
|
85
|
-
return s.length > 100 ? `${s.slice(0, 100)}\u2026` : s;
|
|
86
|
-
}
|
|
87
|
-
function extractResultText(content) {
|
|
88
|
-
if (typeof content === "string") return content;
|
|
89
|
-
if (Array.isArray(content)) {
|
|
90
|
-
return content.map((b) => {
|
|
91
|
-
if (b !== null && typeof b === "object" && b["type"] === "text") {
|
|
92
|
-
const t = b["text"];
|
|
93
|
-
return typeof t === "string" ? t : "";
|
|
94
|
-
}
|
|
95
|
-
return "";
|
|
96
|
-
}).join("\n");
|
|
97
|
-
}
|
|
98
|
-
return "";
|
|
99
|
-
}
|
|
100
|
-
function* readFileLines(filePath) {
|
|
101
|
-
const fd = fs.openSync(filePath, "r");
|
|
102
|
-
try {
|
|
103
|
-
const buf = Buffer.allocUnsafe(1 << 20);
|
|
104
|
-
const decoder = new StringDecoder("utf8");
|
|
105
|
-
let carry = "";
|
|
106
|
-
for (; ; ) {
|
|
107
|
-
const bytes = fs.readSync(fd, buf, 0, buf.length, null);
|
|
108
|
-
if (bytes === 0) break;
|
|
109
|
-
const lines = (carry + decoder.write(buf.subarray(0, bytes))).split("\n");
|
|
110
|
-
carry = lines.pop() ?? "";
|
|
111
|
-
for (const line of lines) yield line;
|
|
112
|
-
}
|
|
113
|
-
carry += decoder.end();
|
|
114
|
-
if (carry !== "") yield carry;
|
|
115
|
-
} finally {
|
|
116
|
-
fs.closeSync(fd);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
function parseTranscript(transcriptPath) {
|
|
120
|
-
const calls = [];
|
|
121
|
-
const resultTextById = /* @__PURE__ */ new Map();
|
|
122
|
-
const assistantTurns = [];
|
|
123
|
-
const resident = createResidentContextStats();
|
|
124
|
-
let seq = 0;
|
|
125
|
-
for (const line of readFileLines(transcriptPath)) {
|
|
126
|
-
const trimmed = line.trim();
|
|
127
|
-
if (trimmed === "") continue;
|
|
128
|
-
let obj;
|
|
129
|
-
try {
|
|
130
|
-
obj = JSON.parse(trimmed);
|
|
131
|
-
} catch {
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
if (obj === null || typeof obj !== "object") continue;
|
|
135
|
-
const o = obj;
|
|
136
|
-
accumulateResidentLine(resident, o, trimmed.length);
|
|
137
|
-
const message = o["message"];
|
|
138
|
-
if (message === null || typeof message !== "object") continue;
|
|
139
|
-
const messageObj = message;
|
|
140
|
-
const content = messageObj["content"];
|
|
141
|
-
if (!Array.isArray(content)) continue;
|
|
142
|
-
const role = typeof messageObj["role"] === "string" ? messageObj["role"] : null;
|
|
143
|
-
const cwd = typeof o["cwd"] === "string" ? o["cwd"] : null;
|
|
144
|
-
let turnTextTokens = 0;
|
|
145
|
-
let sawTextBlock = false;
|
|
146
|
-
for (const block of content) {
|
|
147
|
-
if (block === null || typeof block !== "object") continue;
|
|
148
|
-
const b = block;
|
|
149
|
-
if (b["type"] === "tool_use" && typeof b["id"] === "string" && typeof b["name"] === "string") {
|
|
150
|
-
const name = b["name"];
|
|
151
|
-
const input = b["input"];
|
|
152
|
-
const filePath = extractFilePath(name, input);
|
|
153
|
-
const command = extractCommand(name, input);
|
|
154
|
-
calls.push({
|
|
155
|
-
seq: seq++,
|
|
156
|
-
id: b["id"],
|
|
157
|
-
name,
|
|
158
|
-
input,
|
|
159
|
-
filePath,
|
|
160
|
-
command,
|
|
161
|
-
summary: summarizeInput(name, input, filePath, command),
|
|
162
|
-
cwd
|
|
163
|
-
});
|
|
164
|
-
} else if (b["type"] === "tool_result" && typeof b["tool_use_id"] === "string") {
|
|
165
|
-
const id = b["tool_use_id"];
|
|
166
|
-
if (!resultTextById.has(id)) {
|
|
167
|
-
resultTextById.set(id, extractResultText(b["content"]));
|
|
168
|
-
}
|
|
169
|
-
} else if (role === "assistant" && b["type"] === "text" && typeof b["text"] === "string") {
|
|
170
|
-
sawTextBlock = true;
|
|
171
|
-
turnTextTokens += estimateTokens(b["text"]);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
if (role === "assistant" && sawTextBlock) assistantTurns.push(turnTextTokens);
|
|
175
|
-
}
|
|
176
|
-
return { calls, resultTextById, assistantTurns, resident };
|
|
177
|
-
}
|
|
178
|
-
function costPerCall(parsed) {
|
|
179
|
-
return parsed.calls.map((c) => ({
|
|
180
|
-
...c,
|
|
181
|
-
tokens: estimateTokens(parsed.resultTextById.get(c.id) ?? "")
|
|
182
|
-
}));
|
|
183
|
-
}
|
|
184
|
-
function aggregateSortedDesc(pairs) {
|
|
185
|
-
return [...pairs].map(([key, tokens]) => ({ key, tokens })).sort((a, b) => b.tokens - a.tokens);
|
|
186
|
-
}
|
|
187
|
-
function tokensByTool(costs) {
|
|
188
|
-
const m = /* @__PURE__ */ new Map();
|
|
189
|
-
for (const c of costs) m.set(c.name, (m.get(c.name) ?? 0) + c.tokens);
|
|
190
|
-
return aggregateSortedDesc(m);
|
|
191
|
-
}
|
|
192
|
-
function tokensByFile(costs) {
|
|
193
|
-
const m = /* @__PURE__ */ new Map();
|
|
194
|
-
for (const c of costs) {
|
|
195
|
-
if (c.filePath === null) continue;
|
|
196
|
-
m.set(c.filePath, (m.get(c.filePath) ?? 0) + c.tokens);
|
|
197
|
-
}
|
|
198
|
-
return aggregateSortedDesc(m);
|
|
199
|
-
}
|
|
200
|
-
function topExpensiveCalls(costs, n) {
|
|
201
|
-
return [...costs].sort((a, b) => b.tokens - a.tokens).slice(0, n);
|
|
202
|
-
}
|
|
203
|
-
function neverTouchedAgain(costs) {
|
|
204
|
-
const readsByFile = /* @__PURE__ */ new Map();
|
|
205
|
-
for (const c of costs) {
|
|
206
|
-
if (c.name !== "Read" || c.filePath === null) continue;
|
|
207
|
-
const arr = readsByFile.get(c.filePath) ?? [];
|
|
208
|
-
arr.push(c);
|
|
209
|
-
readsByFile.set(c.filePath, arr);
|
|
210
|
-
}
|
|
211
|
-
const out = [];
|
|
212
|
-
for (const [filePath, reads] of readsByFile) {
|
|
213
|
-
const firstSeq = Math.min(...reads.map((r) => r.seq));
|
|
214
|
-
const touchedLater = costs.some((c) => {
|
|
215
|
-
if (c.seq <= firstSeq) return false;
|
|
216
|
-
if (c.filePath === filePath) return true;
|
|
217
|
-
return safeStringify(c.input).includes(filePath);
|
|
218
|
-
});
|
|
219
|
-
if (!touchedLater) {
|
|
220
|
-
out.push({ filePath, tokens: reads.reduce((n, r) => n + r.tokens, 0) });
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
return out.sort((a, b) => b.tokens - a.tokens);
|
|
224
|
-
}
|
|
225
|
-
async function repeatedUncompressedBashCommands(costs) {
|
|
226
|
-
const groups = /* @__PURE__ */ new Map();
|
|
227
|
-
for (const c of costs) {
|
|
228
|
-
if (c.name !== "Bash" || c.command === null) continue;
|
|
229
|
-
const norm = normalizeCommandForCacheKey(c.command);
|
|
230
|
-
const arr = groups.get(norm) ?? [];
|
|
231
|
-
arr.push(c);
|
|
232
|
-
groups.set(norm, arr);
|
|
233
|
-
}
|
|
234
|
-
const out = [];
|
|
235
|
-
for (const [norm, calls] of groups) {
|
|
236
|
-
if (calls.length < 2) continue;
|
|
237
|
-
let cached = false;
|
|
238
|
-
for (const c of calls) {
|
|
239
|
-
try {
|
|
240
|
-
const hash = await commandHash(c.command, c.cwd);
|
|
241
|
-
if (getBashOutput(hash) !== null) {
|
|
242
|
-
cached = true;
|
|
243
|
-
break;
|
|
244
|
-
}
|
|
245
|
-
} catch {
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
if (cached) continue;
|
|
249
|
-
const totalTokens = calls.reduce((n, c) => n + c.tokens, 0);
|
|
250
|
-
out.push({
|
|
251
|
-
normalized: norm,
|
|
252
|
-
count: calls.length,
|
|
253
|
-
totalTokens,
|
|
254
|
-
avgTokens: Math.round(totalTokens / calls.length)
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
return out.sort((a, b) => b.totalTokens - a.totalTokens);
|
|
258
|
-
}
|
|
259
|
-
function assistantOutputCost(assistantTurns) {
|
|
260
|
-
const turnCount = assistantTurns.length;
|
|
261
|
-
let generatedTokens = 0;
|
|
262
|
-
let resendCeilingTokens = 0;
|
|
263
|
-
for (let i = 0; i < turnCount; i++) {
|
|
264
|
-
const tokens = assistantTurns[i] ?? 0;
|
|
265
|
-
generatedTokens += tokens;
|
|
266
|
-
resendCeilingTokens += tokens * (turnCount - 1 - i);
|
|
267
|
-
}
|
|
268
|
-
return { turnCount, generatedTokens, resendCeilingTokens };
|
|
269
|
-
}
|
|
270
|
-
async function buildWasteReport(transcriptPath, opts = {}) {
|
|
271
|
-
const parsed = parseTranscript(transcriptPath);
|
|
272
|
-
const costs = costPerCall(parsed);
|
|
273
|
-
const topN = opts.topN ?? 10;
|
|
274
|
-
return {
|
|
275
|
-
transcriptPath,
|
|
276
|
-
totalTokens: costs.reduce((n, c) => n + c.tokens, 0),
|
|
277
|
-
tokensByTool: tokensByTool(costs),
|
|
278
|
-
tokensByFile: tokensByFile(costs),
|
|
279
|
-
topCalls: topExpensiveCalls(costs, topN).map((c) => ({ seq: c.seq, name: c.name, summary: c.summary, tokens: c.tokens })),
|
|
280
|
-
neverTouchedAgain: neverTouchedAgain(costs),
|
|
281
|
-
repeatedUncompressedBash: await repeatedUncompressedBashCommands(costs),
|
|
282
|
-
assistantOutput: assistantOutputCost(parsed.assistantTurns),
|
|
283
|
-
residentContext: summarizeResidentContext(parsed.resident)
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
export {
|
|
288
|
-
projectTranscriptsDir,
|
|
289
|
-
findLatestTranscript,
|
|
290
|
-
safeStringify,
|
|
291
|
-
extractResultText,
|
|
292
|
-
readFileLines,
|
|
293
|
-
buildWasteReport
|
|
294
|
-
};
|
|
@@ -1,318 +0,0 @@
|
|
|
1
|
-
import { createRequire as __cjsRequire } from 'node:module';
|
|
2
|
-
const require = __cjsRequire(import.meta.url);
|
|
3
|
-
import {
|
|
4
|
-
findLatestTranscript,
|
|
5
|
-
readFileLines
|
|
6
|
-
} from "./token-goat-chunk-44BM7DDQ.mjs";
|
|
7
|
-
import {
|
|
8
|
-
copilotCliUserRoot,
|
|
9
|
-
readCopilotMcpTools
|
|
10
|
-
} from "./token-goat-chunk-EUSDXRXU.mjs";
|
|
11
|
-
import {
|
|
12
|
-
canonicalize
|
|
13
|
-
} from "./token-goat-chunk-TCQANXYC.mjs";
|
|
14
|
-
import {
|
|
15
|
-
init_define_import_meta_env
|
|
16
|
-
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
17
|
-
|
|
18
|
-
// src/copilot_waste.ts
|
|
19
|
-
init_define_import_meta_env();
|
|
20
|
-
import * as fs from "node:fs";
|
|
21
|
-
import * as path from "node:path";
|
|
22
|
-
var HOOK_RECORD_TYPES = /* @__PURE__ */ new Set(["hook.start", "hook.end"]);
|
|
23
|
-
function splitInjectedBlocks(transformed) {
|
|
24
|
-
const out = [];
|
|
25
|
-
const blockRe = /<([a-z_][a-z0-9_]*)>([\s\S]*?)<\/\1>/gi;
|
|
26
|
-
let m;
|
|
27
|
-
while ((m = blockRe.exec(transformed)) !== null) {
|
|
28
|
-
const tag = (m[1] ?? "").toLowerCase();
|
|
29
|
-
const body = m[2] ?? "";
|
|
30
|
-
let kind = tag;
|
|
31
|
-
if (tag === "system_reminder") {
|
|
32
|
-
const inner = /^\s*<([a-z_][a-z0-9_]*)>/i.exec(body);
|
|
33
|
-
kind = inner !== null ? `reminder:${(inner[1] ?? "").toLowerCase()}` : "reminder:text";
|
|
34
|
-
}
|
|
35
|
-
out.push({ kind, body: m[0] });
|
|
36
|
-
}
|
|
37
|
-
return out;
|
|
38
|
-
}
|
|
39
|
-
function pathsMatch(a, b) {
|
|
40
|
-
if (a === void 0 || b === void 0 || a === "" || b === "") return false;
|
|
41
|
-
return canonicalize(a) === canonicalize(b);
|
|
42
|
-
}
|
|
43
|
-
function readCopilotWorkspace(sessionDir) {
|
|
44
|
-
const wsFile = path.join(sessionDir, "workspace.yaml");
|
|
45
|
-
let raw;
|
|
46
|
-
try {
|
|
47
|
-
raw = fs.readFileSync(wsFile, "utf8");
|
|
48
|
-
} catch {
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
let cwd;
|
|
52
|
-
let gitRoot;
|
|
53
|
-
let id;
|
|
54
|
-
for (const line of raw.split("\n")) {
|
|
55
|
-
const trimmed = line.trim();
|
|
56
|
-
if (trimmed.startsWith("cwd:")) {
|
|
57
|
-
cwd = trimmed.slice(4).trim().replace(/^['"]|['"]$/g, "");
|
|
58
|
-
} else if (trimmed.startsWith("git_root:")) {
|
|
59
|
-
gitRoot = trimmed.slice(9).trim().replace(/^['"]|['"]$/g, "");
|
|
60
|
-
} else if (trimmed.startsWith("id:")) {
|
|
61
|
-
id = trimmed.slice(3).trim().replace(/^['"]|['"]$/g, "");
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return { cwd, gitRoot, id };
|
|
65
|
-
}
|
|
66
|
-
function isProcessAlive(pid) {
|
|
67
|
-
if (pid <= 0 || !Number.isInteger(pid)) return false;
|
|
68
|
-
try {
|
|
69
|
-
process.kill(pid, 0);
|
|
70
|
-
return true;
|
|
71
|
-
} catch (err) {
|
|
72
|
-
return err?.code === "EPERM";
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
function isCopilotSessionActive(sessionDir, sessionId) {
|
|
76
|
-
const sid = sessionId ?? path.basename(sessionDir);
|
|
77
|
-
const envSessionId = process.env["COPILOT_AGENT_SESSION_ID"];
|
|
78
|
-
if (envSessionId !== void 0 && envSessionId.trim() === sid) {
|
|
79
|
-
return true;
|
|
80
|
-
}
|
|
81
|
-
try {
|
|
82
|
-
const files = fs.readdirSync(sessionDir);
|
|
83
|
-
for (const f of files) {
|
|
84
|
-
if (f.startsWith("inuse.") && f.endsWith(".lock")) {
|
|
85
|
-
const pidStr = f.slice(6, -5);
|
|
86
|
-
const pid = parseInt(pidStr, 10);
|
|
87
|
-
if (!Number.isNaN(pid) && isProcessAlive(pid)) {
|
|
88
|
-
return true;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
} catch {
|
|
93
|
-
}
|
|
94
|
-
try {
|
|
95
|
-
const opLock = path.join(path.dirname(sessionDir), ".session-operation-locks", `${sid}.lock`);
|
|
96
|
-
if (fs.existsSync(opLock)) {
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
} catch {
|
|
100
|
-
}
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
function isCopilotTranscript(filePath) {
|
|
104
|
-
if (path.basename(filePath) === "events.jsonl") return true;
|
|
105
|
-
try {
|
|
106
|
-
const fd = fs.openSync(filePath, "r");
|
|
107
|
-
const buf = Buffer.alloc(512);
|
|
108
|
-
const bytesRead = fs.readSync(fd, buf, 0, 512, 0);
|
|
109
|
-
fs.closeSync(fd);
|
|
110
|
-
const prefix = buf.toString("utf8", 0, bytesRead);
|
|
111
|
-
return /"type"\s*:\s*"(?:session\.|user\.message|assistant\.message|tool\.)/.test(prefix);
|
|
112
|
-
} catch {
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
function findActiveCopilotSession(projectRoot) {
|
|
117
|
-
return findLatestCopilotSession({ projectRoot, onlyActive: true });
|
|
118
|
-
}
|
|
119
|
-
function findLatestCopilotSession(projectRootOrOpts) {
|
|
120
|
-
const opts = typeof projectRootOrOpts === "string" ? { projectRoot: projectRootOrOpts } : projectRootOrOpts ?? {};
|
|
121
|
-
const root = path.join(copilotCliUserRoot(), "session-state");
|
|
122
|
-
const envSessionId = process.env["COPILOT_AGENT_SESSION_ID"];
|
|
123
|
-
if (envSessionId !== void 0 && envSessionId.trim() !== "") {
|
|
124
|
-
const sid = envSessionId.trim();
|
|
125
|
-
const sessionDir = path.join(root, sid);
|
|
126
|
-
const candidate = path.join(sessionDir, "events.jsonl");
|
|
127
|
-
if (fs.existsSync(candidate)) {
|
|
128
|
-
try {
|
|
129
|
-
if (fs.statSync(candidate).isFile()) {
|
|
130
|
-
if (opts.projectRoot !== void 0) {
|
|
131
|
-
const ws = readCopilotWorkspace(sessionDir);
|
|
132
|
-
if (ws && (pathsMatch(ws.cwd, opts.projectRoot) || pathsMatch(ws.gitRoot, opts.projectRoot))) {
|
|
133
|
-
return candidate;
|
|
134
|
-
}
|
|
135
|
-
} else {
|
|
136
|
-
return candidate;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
} catch {
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
let entries;
|
|
144
|
-
try {
|
|
145
|
-
entries = fs.readdirSync(root);
|
|
146
|
-
} catch {
|
|
147
|
-
return null;
|
|
148
|
-
}
|
|
149
|
-
let bestActive = null;
|
|
150
|
-
let bestActiveMtime = -Infinity;
|
|
151
|
-
let bestInactive = null;
|
|
152
|
-
let bestInactiveMtime = -Infinity;
|
|
153
|
-
for (const entry of entries) {
|
|
154
|
-
if (entry.startsWith(".")) continue;
|
|
155
|
-
const sessionDir = path.join(root, entry);
|
|
156
|
-
const candidate = path.join(sessionDir, "events.jsonl");
|
|
157
|
-
let st;
|
|
158
|
-
try {
|
|
159
|
-
st = fs.statSync(candidate);
|
|
160
|
-
if (!st.isFile()) continue;
|
|
161
|
-
} catch {
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
if (opts.projectRoot !== void 0) {
|
|
165
|
-
const ws = readCopilotWorkspace(sessionDir);
|
|
166
|
-
if (!ws || !pathsMatch(ws.cwd, opts.projectRoot) && !pathsMatch(ws.gitRoot, opts.projectRoot)) {
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
const active = isCopilotSessionActive(sessionDir, entry);
|
|
171
|
-
if (active) {
|
|
172
|
-
if (st.mtimeMs > bestActiveMtime) {
|
|
173
|
-
bestActiveMtime = st.mtimeMs;
|
|
174
|
-
bestActive = candidate;
|
|
175
|
-
}
|
|
176
|
-
} else if (opts.onlyActive !== true) {
|
|
177
|
-
if (st.mtimeMs > bestInactiveMtime) {
|
|
178
|
-
bestInactiveMtime = st.mtimeMs;
|
|
179
|
-
bestInactive = candidate;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
return bestActive ?? (opts.onlyActive === true ? null : bestInactive);
|
|
184
|
-
}
|
|
185
|
-
function findProjectSession(projectRoot) {
|
|
186
|
-
const copilotSession = findActiveCopilotSession(projectRoot) ?? findLatestCopilotSession(projectRoot);
|
|
187
|
-
const claudeTranscript = findLatestTranscript(projectRoot);
|
|
188
|
-
if (copilotSession !== null && claudeTranscript !== null) {
|
|
189
|
-
if (findActiveCopilotSession(projectRoot) !== null) {
|
|
190
|
-
return { path: copilotSession, kind: "copilot" };
|
|
191
|
-
}
|
|
192
|
-
let cpTime = 0;
|
|
193
|
-
let clTime = 0;
|
|
194
|
-
try {
|
|
195
|
-
cpTime = fs.statSync(copilotSession).mtimeMs;
|
|
196
|
-
} catch {
|
|
197
|
-
}
|
|
198
|
-
try {
|
|
199
|
-
clTime = fs.statSync(claudeTranscript).mtimeMs;
|
|
200
|
-
} catch {
|
|
201
|
-
}
|
|
202
|
-
return cpTime >= clTime ? { path: copilotSession, kind: "copilot" } : { path: claudeTranscript, kind: "claude" };
|
|
203
|
-
}
|
|
204
|
-
if (copilotSession !== null) {
|
|
205
|
-
return { path: copilotSession, kind: "copilot" };
|
|
206
|
-
}
|
|
207
|
-
if (claudeTranscript !== null) {
|
|
208
|
-
return { path: claudeTranscript, kind: "claude" };
|
|
209
|
-
}
|
|
210
|
-
return null;
|
|
211
|
-
}
|
|
212
|
-
function readNumber(source, key) {
|
|
213
|
-
const value = source[key];
|
|
214
|
-
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
215
|
-
}
|
|
216
|
-
function unusedMcpServers(report) {
|
|
217
|
-
const calls = report.mcpCalls;
|
|
218
|
-
return calls === null ? null : report.mcpTools.servers.filter((server) => (calls[server.serverName] ?? 0) === 0);
|
|
219
|
-
}
|
|
220
|
-
var MCP_DISABLE_NOTE = "'copilot mcp enable <name>' restores one, and '--disable-mcp-server <name>' drops one for a single run instead.";
|
|
221
|
-
function buildCopilotWasteReport(eventsPath) {
|
|
222
|
-
const report = {
|
|
223
|
-
sessionPath: eventsPath,
|
|
224
|
-
sessionId: path.basename(path.dirname(eventsPath)),
|
|
225
|
-
turns: 0,
|
|
226
|
-
tokens: null,
|
|
227
|
-
blocks: [],
|
|
228
|
-
compactions: [],
|
|
229
|
-
hookRecordBytes: 0,
|
|
230
|
-
// Measured off the file rather than off a string of its contents: an event log large enough for this number to matter is exactly the one readFileSync cannot return, since V8 caps a string at about 512 MB.
|
|
231
|
-
totalEventBytes: fs.statSync(eventsPath).size,
|
|
232
|
-
// Read through the real resolution chain rather than passed in. Copilot's own COPILOT_CACHE_HOME override is what tests point at a fixture, so the shipping path is the tested path and there is no seam here that only a test ever supplies.
|
|
233
|
-
mcpTools: readCopilotMcpTools(),
|
|
234
|
-
mcpCalls: null
|
|
235
|
-
};
|
|
236
|
-
const classes = /* @__PURE__ */ new Map();
|
|
237
|
-
const seen = /* @__PURE__ */ new Map();
|
|
238
|
-
const mcpCalls = /* @__PURE__ */ new Map();
|
|
239
|
-
let toolExecutions = 0;
|
|
240
|
-
for (const line of readFileLines(eventsPath)) {
|
|
241
|
-
const trimmed = line.trim();
|
|
242
|
-
if (trimmed === "") continue;
|
|
243
|
-
let event;
|
|
244
|
-
try {
|
|
245
|
-
event = JSON.parse(trimmed);
|
|
246
|
-
} catch {
|
|
247
|
-
continue;
|
|
248
|
-
}
|
|
249
|
-
const type = typeof event["type"] === "string" ? event["type"] : "";
|
|
250
|
-
const data = event["data"] ?? {};
|
|
251
|
-
if (HOOK_RECORD_TYPES.has(type)) {
|
|
252
|
-
report.hookRecordBytes += Buffer.byteLength(trimmed, "utf-8");
|
|
253
|
-
continue;
|
|
254
|
-
}
|
|
255
|
-
if (type === "session.shutdown") {
|
|
256
|
-
report.tokens = {
|
|
257
|
-
systemTokens: readNumber(data, "systemTokens"),
|
|
258
|
-
toolDefinitionsTokens: readNumber(data, "toolDefinitionsTokens"),
|
|
259
|
-
conversationTokens: readNumber(data, "conversationTokens"),
|
|
260
|
-
currentTokens: readNumber(data, "currentTokens")
|
|
261
|
-
};
|
|
262
|
-
continue;
|
|
263
|
-
}
|
|
264
|
-
if (type === "session.compaction_complete") {
|
|
265
|
-
const summary = typeof data["summaryContent"] === "string" ? data["summaryContent"] : "";
|
|
266
|
-
report.compactions.push({
|
|
267
|
-
trigger: typeof data["trigger"] === "string" ? data["trigger"] : "unknown",
|
|
268
|
-
summaryBytes: Buffer.byteLength(summary, "utf-8"),
|
|
269
|
-
preTokens: readNumber(data, "preCompactionTokens"),
|
|
270
|
-
postTokens: readNumber(data, "postCompactionTokens")
|
|
271
|
-
});
|
|
272
|
-
continue;
|
|
273
|
-
}
|
|
274
|
-
if (type === "tool.execution_start") {
|
|
275
|
-
toolExecutions += 1;
|
|
276
|
-
const server = data["mcpServerName"];
|
|
277
|
-
if (typeof server === "string") mcpCalls.set(server, (mcpCalls.get(server) ?? 0) + 1);
|
|
278
|
-
continue;
|
|
279
|
-
}
|
|
280
|
-
if (type !== "user.message") continue;
|
|
281
|
-
const transformed = typeof data["transformedContent"] === "string" ? data["transformedContent"] : "";
|
|
282
|
-
if (transformed === "") continue;
|
|
283
|
-
report.turns += 1;
|
|
284
|
-
for (const block of splitInjectedBlocks(transformed)) {
|
|
285
|
-
const bytes = Buffer.byteLength(block.body, "utf-8");
|
|
286
|
-
let cls = classes.get(block.kind);
|
|
287
|
-
if (cls === void 0) {
|
|
288
|
-
cls = { kind: block.kind, count: 0, bytes: 0, repeatBytes: 0, repeatCount: 0 };
|
|
289
|
-
classes.set(block.kind, cls);
|
|
290
|
-
}
|
|
291
|
-
cls.count += 1;
|
|
292
|
-
cls.bytes += bytes;
|
|
293
|
-
let bucket = seen.get(block.kind);
|
|
294
|
-
if (bucket === void 0) {
|
|
295
|
-
bucket = /* @__PURE__ */ new Set();
|
|
296
|
-
seen.set(block.kind, bucket);
|
|
297
|
-
}
|
|
298
|
-
if (bucket.has(block.body)) {
|
|
299
|
-
cls.repeatBytes += bytes;
|
|
300
|
-
cls.repeatCount += 1;
|
|
301
|
-
} else {
|
|
302
|
-
bucket.add(block.body);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
report.blocks = [...classes.values()].sort((a, b) => b.bytes - a.bytes);
|
|
307
|
-
report.mcpCalls = toolExecutions === 0 ? null : Object.fromEntries(mcpCalls);
|
|
308
|
-
return report;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
export {
|
|
312
|
-
isCopilotTranscript,
|
|
313
|
-
findLatestCopilotSession,
|
|
314
|
-
findProjectSession,
|
|
315
|
-
unusedMcpServers,
|
|
316
|
-
MCP_DISABLE_NOTE,
|
|
317
|
-
buildCopilotWasteReport
|
|
318
|
-
};
|