token-goat 2.8.3 → 2.8.4
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 +14 -12
- package/dist/{token-goat-chunk-PWVXXPCC.mjs → token-goat-chunk-4OTIB7SB.mjs} +450 -138
- package/dist/{token-goat-chunk-EFF2XCLB.mjs → token-goat-chunk-CZALRRGN.mjs} +175 -56
- package/dist/{token-goat-chunk-6ODZ6PZK.mjs → token-goat-chunk-E76UNTVK.mjs} +189 -69
- package/dist/{token-goat-chunk-4OM2Q2SX.mjs → token-goat-chunk-HNODKNWO.mjs} +5 -5
- package/dist/{token-goat-chunk-4HIMCBYK.mjs → token-goat-chunk-IZRXU64B.mjs} +51 -4
- package/dist/{token-goat-chunk-DP3NNGGV.mjs → token-goat-chunk-L6YP6FKQ.mjs} +5 -5
- package/dist/{token-goat-chunk-C5IL6MJH.mjs → token-goat-chunk-LZOAPGWR.mjs} +6 -5
- package/dist/{token-goat-chunk-VAGPOZHO.mjs → token-goat-chunk-NZFTWBGV.mjs} +2 -2
- package/dist/{token-goat-chunk-TX4JFJTD.mjs → token-goat-chunk-RE7S7H26.mjs} +110 -50
- package/dist/{token-goat-chunk-222VPFP2.mjs → token-goat-chunk-U4UI5F3S.mjs} +953 -188
- package/dist/{token-goat-chunk-NKNCHJ4H.mjs → token-goat-chunk-ZQ3PUOP3.mjs} +310 -109
- package/dist/token-goat-hook.mjs +5 -5
- package/dist/token-goat.core.mjs +5 -5
- package/package.json +3 -1
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
summarizeOutputDelta,
|
|
24
24
|
summarizeResidentContext,
|
|
25
25
|
taskListPruneHint
|
|
26
|
-
} from "./token-goat-chunk-
|
|
26
|
+
} from "./token-goat-chunk-IZRXU64B.mjs";
|
|
27
27
|
import {
|
|
28
28
|
BODY_FIRST_TOOL_RESPONSE_KEYS,
|
|
29
29
|
OUTPUT_FIRST_TOOL_RESPONSE_KEYS,
|
|
@@ -45,7 +45,6 @@ import {
|
|
|
45
45
|
enqueueDirtyPathSafe,
|
|
46
46
|
ensureWorkerAlive,
|
|
47
47
|
estimateTokens,
|
|
48
|
-
estimateTokensFromLength,
|
|
49
48
|
extractCompactFromMarker,
|
|
50
49
|
extractToolResponseField,
|
|
51
50
|
extractToolResultText,
|
|
@@ -89,6 +88,7 @@ import {
|
|
|
89
88
|
matchesDenyPattern,
|
|
90
89
|
meetsSavingsFloor,
|
|
91
90
|
passOutput,
|
|
91
|
+
probeImageMeta,
|
|
92
92
|
recordBashOutput,
|
|
93
93
|
recordBashRerun,
|
|
94
94
|
recordCliRead,
|
|
@@ -117,10 +117,12 @@ import {
|
|
|
117
117
|
storeBlob,
|
|
118
118
|
storeOutput,
|
|
119
119
|
takePendingLargeFileHint,
|
|
120
|
+
visionTokens,
|
|
121
|
+
visionTokensSaved,
|
|
120
122
|
wasCliReadThisSession,
|
|
121
123
|
wasFileReadThisSession,
|
|
122
124
|
wasHintShown
|
|
123
|
-
} from "./token-goat-chunk-
|
|
125
|
+
} from "./token-goat-chunk-4OTIB7SB.mjs";
|
|
124
126
|
import {
|
|
125
127
|
canRunWrappedShell,
|
|
126
128
|
compressOutput,
|
|
@@ -131,7 +133,7 @@ import {
|
|
|
131
133
|
isRewriteWorthwhile,
|
|
132
134
|
resolveMinNetSavingsBytes,
|
|
133
135
|
shlexSplit
|
|
134
|
-
} from "./token-goat-chunk-
|
|
136
|
+
} from "./token-goat-chunk-CZALRRGN.mjs";
|
|
135
137
|
import {
|
|
136
138
|
PER_FILE_COUNTERFACTUAL_CEILING,
|
|
137
139
|
VERSION,
|
|
@@ -150,9 +152,10 @@ import {
|
|
|
150
152
|
redactSecrets,
|
|
151
153
|
resolveIndexPath,
|
|
152
154
|
runGit,
|
|
155
|
+
savedTokensFromBytes,
|
|
153
156
|
shortFingerprint,
|
|
154
157
|
toKB
|
|
155
|
-
} from "./token-goat-chunk-
|
|
158
|
+
} from "./token-goat-chunk-E76UNTVK.mjs";
|
|
156
159
|
|
|
157
160
|
// src/hooks_grep.ts
|
|
158
161
|
function grepIntInput(toolInput, key) {
|
|
@@ -167,7 +170,7 @@ function grepIntInput(toolInput, key) {
|
|
|
167
170
|
function grepSignature(toolInput) {
|
|
168
171
|
const pattern = toolInput["pattern"];
|
|
169
172
|
if (typeof pattern !== "string" || pattern === "") return null;
|
|
170
|
-
const
|
|
173
|
+
const path3 = typeof toolInput["path"] === "string" ? toolInput["path"] : "";
|
|
171
174
|
const outputMode = typeof toolInput["output_mode"] === "string" ? toolInput["output_mode"] : "files_with_matches";
|
|
172
175
|
const glob = typeof toolInput["glob"] === "string" ? toolInput["glob"] : "";
|
|
173
176
|
const type = typeof toolInput["type"] === "string" ? toolInput["type"] : "";
|
|
@@ -182,7 +185,7 @@ function grepSignature(toolInput) {
|
|
|
182
185
|
const offset = grepIntInput(toolInput, "offset");
|
|
183
186
|
return JSON.stringify([
|
|
184
187
|
pattern,
|
|
185
|
-
|
|
188
|
+
path3,
|
|
186
189
|
outputMode,
|
|
187
190
|
glob,
|
|
188
191
|
type,
|
|
@@ -218,8 +221,7 @@ function foldGrepContentHandler(event) {
|
|
|
218
221
|
if (toolInput["-n"] === false) return passOutput();
|
|
219
222
|
const rawText = extractToolResponseField(event.raw, OUTPUT_FIRST_TOOL_RESPONSE_KEYS);
|
|
220
223
|
if (!rawText) return passOutput();
|
|
221
|
-
const
|
|
222
|
-
const text = redacted.text;
|
|
224
|
+
const text = redactSecrets(rawText).text;
|
|
223
225
|
const rawLines = text.split(/\r\n|\r|\n/);
|
|
224
226
|
const parsed = [];
|
|
225
227
|
for (const line of rawLines) {
|
|
@@ -260,10 +262,7 @@ function foldGrepContentHandler(event) {
|
|
|
260
262
|
})) {
|
|
261
263
|
return passOutput();
|
|
262
264
|
}
|
|
263
|
-
|
|
264
|
-
recordStat("grep:fold", bytesDelta, Math.round(bytesDelta / 4));
|
|
265
|
-
if (redacted.count > 0) recordStat("secret_redacted", 0, redacted.count, void 0, "grep");
|
|
266
|
-
return { hookType: "rewriteOutput", updatedOutput: rewritten };
|
|
265
|
+
return emitRewrite(rewritten, "grep", { kind: "grep:fold", originalBytes });
|
|
267
266
|
} catch {
|
|
268
267
|
return passOutput();
|
|
269
268
|
}
|
|
@@ -281,8 +280,8 @@ registerHook("post_tool_use", postGrepHandler, { toolName: "Grep" });
|
|
|
281
280
|
function globSignature(toolInput) {
|
|
282
281
|
const pattern = toolInput["pattern"];
|
|
283
282
|
if (typeof pattern !== "string" || pattern === "") return null;
|
|
284
|
-
const
|
|
285
|
-
return JSON.stringify([pattern,
|
|
283
|
+
const path3 = typeof toolInput["path"] === "string" ? toolInput["path"] : "";
|
|
284
|
+
return JSON.stringify([pattern, path3]);
|
|
286
285
|
}
|
|
287
286
|
var { post: postGlobHandler, pre: preGlobDedupHandler } = makeDedupHintHandlers({
|
|
288
287
|
toolName: "Glob",
|
|
@@ -430,6 +429,9 @@ function renderReadRow(entry) {
|
|
|
430
429
|
const edited = entry.wasEdited ? ", edited" : "";
|
|
431
430
|
return `- ${entry.path} (${kb}kb, ${entry.readCount} ${plural}${edited})`;
|
|
432
431
|
}
|
|
432
|
+
function renderSymbolReadRow(entry) {
|
|
433
|
+
return `- ${entry.path} (symbols: ${(entry.symbols_read ?? []).join(", ")})`;
|
|
434
|
+
}
|
|
433
435
|
function mergeManifestFiles(parent, siblingFiles) {
|
|
434
436
|
const byPath = /* @__PURE__ */ new Map();
|
|
435
437
|
for (const f of parent) byPath.set(foldPath(f.path), f);
|
|
@@ -457,6 +459,7 @@ function buildManifest(sessionId, cwd) {
|
|
|
457
459
|
const files = siblingFiles.length > 0 ? mergeManifestFiles(ownFiles, siblingFiles) : ownFiles;
|
|
458
460
|
const editedFiles = files.filter((f) => f.wasEdited);
|
|
459
461
|
const readFiles = files.filter((f) => f.readCount > 0 && !f.wasEdited);
|
|
462
|
+
const symbolOnlyFiles = files.filter((f) => f.readCount === 0 && !f.wasEdited && (f.symbols_read?.length ?? 0) > 0);
|
|
460
463
|
const webFetches = [...getSessionWebFetches().entries()];
|
|
461
464
|
const lines = [];
|
|
462
465
|
lines.push("## Session context");
|
|
@@ -469,6 +472,7 @@ function buildManifest(sessionId, cwd) {
|
|
|
469
472
|
editedFiles.map((entry) => `- ${entry.path}`),
|
|
470
473
|
MAX_ROWS
|
|
471
474
|
);
|
|
475
|
+
appendCappedSection(lines, "### Surgically read files (symbol/section reads, never read whole)", symbolOnlyFiles.map(renderSymbolReadRow), MAX_ROWS);
|
|
472
476
|
appendCappedSection(
|
|
473
477
|
lines,
|
|
474
478
|
"### Web URLs fetched",
|
|
@@ -613,7 +617,7 @@ function postCompactHandler(event) {
|
|
|
613
617
|
0,
|
|
614
618
|
0,
|
|
615
619
|
void 0,
|
|
616
|
-
`trigger=${trigger} bytes=${bytes} est_tokens=${
|
|
620
|
+
`trigger=${trigger} bytes=${bytes} est_tokens=${savedTokensFromBytes(bytes)} manifest_paths=${survived}/${sample.length}`
|
|
617
621
|
);
|
|
618
622
|
return passOutput();
|
|
619
623
|
}
|
|
@@ -626,7 +630,7 @@ import crypto from "node:crypto";
|
|
|
626
630
|
var TRACKED_SKILL = "token-goat";
|
|
627
631
|
var MAX_COMMANDS_SHOWN = 8;
|
|
628
632
|
async function currentCommandNames() {
|
|
629
|
-
const { buildProgram } = await import("./token-goat-chunk-
|
|
633
|
+
const { buildProgram } = await import("./token-goat-chunk-HNODKNWO.mjs");
|
|
630
634
|
return flattenCommandNames(buildCommandManifest(buildProgram()));
|
|
631
635
|
}
|
|
632
636
|
async function recordSkillVersionSnapshot(sessionId, skillName) {
|
|
@@ -1470,11 +1474,11 @@ function mapInner(r, f) {
|
|
|
1470
1474
|
function mapOuter(r, f) {
|
|
1471
1475
|
return r.matched ? f(r) : r;
|
|
1472
1476
|
}
|
|
1473
|
-
function ab(pa, pb,
|
|
1474
|
-
return (data, i) => mapOuter(pa(data, i), (ma) => mapInner(pb(data, ma.position), (vb, j) =>
|
|
1477
|
+
function ab(pa, pb, join2) {
|
|
1478
|
+
return (data, i) => mapOuter(pa(data, i), (ma) => mapInner(pb(data, ma.position), (vb, j) => join2(ma.value, vb, data, i, j)));
|
|
1475
1479
|
}
|
|
1476
|
-
function abc(pa, pb, pc,
|
|
1477
|
-
return (data, i) => mapOuter(pa(data, i), (ma) => mapOuter(pb(data, ma.position), (mb) => mapInner(pc(data, mb.position), (vc, j) =>
|
|
1480
|
+
function abc(pa, pb, pc, join2) {
|
|
1481
|
+
return (data, i) => mapOuter(pa(data, i), (ma) => mapOuter(pb(data, ma.position), (mb) => mapInner(pc(data, mb.position), (vc, j) => join2(ma.value, mb.value, vc, data, i, j))));
|
|
1478
1482
|
}
|
|
1479
1483
|
function ahead(p) {
|
|
1480
1484
|
return (data, i) => mapOuter(p(data, i), (m1) => ({
|
|
@@ -5391,8 +5395,8 @@ function trimCharacterEnd(str, char) {
|
|
|
5391
5395
|
function unicodeEscape(str) {
|
|
5392
5396
|
return str.replace(/[\s\S]/g, (c) => "\\u" + c.charCodeAt().toString(16).padStart(4, "0"));
|
|
5393
5397
|
}
|
|
5394
|
-
function get(obj,
|
|
5395
|
-
for (const key of
|
|
5398
|
+
function get(obj, path3) {
|
|
5399
|
+
for (const key of path3) {
|
|
5396
5400
|
if (!obj) {
|
|
5397
5401
|
return void 0;
|
|
5398
5402
|
}
|
|
@@ -6593,8 +6597,8 @@ function withBrackets(str, brackets) {
|
|
|
6593
6597
|
const rbr = typeof brackets[1] === "string" ? brackets[1] : "]";
|
|
6594
6598
|
return lbr + str + rbr;
|
|
6595
6599
|
}
|
|
6596
|
-
function pathRewrite(
|
|
6597
|
-
const modifiedPath = typeof rewriter === "function" ? rewriter(
|
|
6600
|
+
function pathRewrite(path3, rewriter, baseUrl, metadata, elem) {
|
|
6601
|
+
const modifiedPath = typeof rewriter === "function" ? rewriter(path3, metadata, elem) : path3;
|
|
6598
6602
|
return modifiedPath[0] === "/" && baseUrl ? trimCharacterEnd(baseUrl, "/") + modifiedPath : modifiedPath;
|
|
6599
6603
|
}
|
|
6600
6604
|
function formatImage(elem, walk, builder, formatOptions) {
|
|
@@ -6918,9 +6922,9 @@ function handleDeprecatedOptions(options) {
|
|
|
6918
6922
|
options.selectors.push(...tagDefinitions);
|
|
6919
6923
|
options.selectors = mergeDuplicatesPreferLast(options.selectors, ((s) => s.selector));
|
|
6920
6924
|
}
|
|
6921
|
-
function set(obj,
|
|
6922
|
-
const valueKey =
|
|
6923
|
-
for (const key of
|
|
6925
|
+
function set(obj, path3, value) {
|
|
6926
|
+
const valueKey = path3.pop();
|
|
6927
|
+
for (const key of path3) {
|
|
6924
6928
|
let nested = obj[key];
|
|
6925
6929
|
if (!nested) {
|
|
6926
6930
|
nested = {};
|
|
@@ -7011,7 +7015,7 @@ function preFetchHandler(event) {
|
|
|
7011
7015
|
if (cached !== null) {
|
|
7012
7016
|
const cachedBytes = Buffer.byteLength(cached, "utf-8");
|
|
7013
7017
|
if (cachedBytes >= loadConfig().hints.web_dedup_min_bytes) {
|
|
7014
|
-
recordStat("webfetch:recall", cachedBytes,
|
|
7018
|
+
recordStat("webfetch:recall", cachedBytes, savedTokensFromBytes(cachedBytes));
|
|
7015
7019
|
return denyOutput(
|
|
7016
7020
|
"Already fetched this URL with this prompt; the response is cached. Use `token-goat web-output " + cacheId + "` to recall it (append `--grep PATTERN` to filter or `--section Heading` for a markdown section) instead of re-fetching."
|
|
7017
7021
|
);
|
|
@@ -7084,9 +7088,7 @@ function postFetchHandler(event) {
|
|
|
7084
7088
|
noticeBytes,
|
|
7085
7089
|
minNetSavingsBytes: resolveMinNetSavingsBytes()
|
|
7086
7090
|
})) {
|
|
7087
|
-
|
|
7088
|
-
recordStat("webfetch:compress", bytesDelta, Math.round(bytesDelta / 4));
|
|
7089
|
-
return emitRewrite(storedRedacted.text + notice, "fetch");
|
|
7091
|
+
return emitRewrite(storedRedacted.text + notice, "fetch", { kind: "webfetch:compress", originalBytes });
|
|
7090
7092
|
}
|
|
7091
7093
|
}
|
|
7092
7094
|
if (storedRedacted.count > 0) {
|
|
@@ -7146,7 +7148,7 @@ async function preSkillHandler(event) {
|
|
|
7146
7148
|
if (await hasSessionOutput(event.sessionId, skillName)) {
|
|
7147
7149
|
const cachedBytes = await sessionOutputBodyBytes(event.sessionId, skillName);
|
|
7148
7150
|
const denyCredit = cachedBytes !== null ? Math.min(cachedBytes, PER_FILE_COUNTERFACTUAL_CEILING) : 0;
|
|
7149
|
-
recordStat("session_hint", denyCredit,
|
|
7151
|
+
recordStat("session_hint", denyCredit, savedTokensFromBytes(denyCredit));
|
|
7150
7152
|
return denyOutput(
|
|
7151
7153
|
"Skill `" + skillName + "` was already loaded this session and is cached. Use `token-goat skill-body " + skillName + " --compact` to recall the compact slice (or `token-goat skill-body " + skillName + "` for the full body) instead of re-loading it."
|
|
7152
7154
|
);
|
|
@@ -7161,7 +7163,7 @@ async function preSkillHandler(event) {
|
|
|
7161
7163
|
const compactBytes = Buffer.byteLength(compact, "utf-8");
|
|
7162
7164
|
if (compactBytes * 2 <= bodyBytes && compactBytes <= COMPACT_INLINE_MAX_BYTES) {
|
|
7163
7165
|
const savedBytes = bodyBytes - compactBytes;
|
|
7164
|
-
recordStat("skill_compact_inlined", savedBytes,
|
|
7166
|
+
recordStat("skill_compact_inlined", savedBytes, savedTokensFromBytes(savedBytes));
|
|
7165
7167
|
return denyOutput(
|
|
7166
7168
|
"Skill `" + skillName + "` is large (" + bodyBytes + " bytes); its compact slice (" + compactBytes + " bytes) is inlined below instead of the full body. Run `token-goat skill-body " + skillName + "` if you need the full body.\n\n" + compact
|
|
7167
7169
|
);
|
|
@@ -7485,16 +7487,16 @@ function isTempPath(fp) {
|
|
|
7485
7487
|
return /^\/tmp\//i.test(norm) || /\/var\/folders\//i.test(norm) || /AppData\/Local\/Temp\//i.test(norm) || norm.startsWith("/c/Users/") && norm.includes("/AppData/Local/Temp/") || isUnderSystemTemp(fp);
|
|
7486
7488
|
}
|
|
7487
7489
|
function isOrchestratorStateFile(filePath) {
|
|
7488
|
-
const
|
|
7489
|
-
return /^\.improve-state-/.test(
|
|
7490
|
+
const basename3 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
|
|
7491
|
+
return /^\.improve-state-/.test(basename3);
|
|
7490
7492
|
}
|
|
7491
7493
|
function extractCatSourceFile(cmd) {
|
|
7492
7494
|
const m = /^cat\s+(\S+\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|css|scss|sass|less))\s*$/.exec(cmd);
|
|
7493
7495
|
return m?.[1] ?? null;
|
|
7494
7496
|
}
|
|
7495
7497
|
function classifyFileExtensions(filePath) {
|
|
7496
|
-
const
|
|
7497
|
-
const isEnvFile = /^\.env(\.\w+)?$/i.test(
|
|
7498
|
+
const basename3 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
|
|
7499
|
+
const isEnvFile = /^\.env(\.\w+)?$/i.test(basename3);
|
|
7498
7500
|
const hasKnownExt = /\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|css|scss|sass|less|md|mdx|rst|txt|json|yaml|yml|toml|xml|conf|cfg|ini|properties|sql|ps1|psm1|env)$/i.test(filePath);
|
|
7499
7501
|
if (!hasKnownExt && !isEnvFile) return null;
|
|
7500
7502
|
const isSql = /\.sql$/i.test(filePath);
|
|
@@ -7516,12 +7518,14 @@ function classifyCatPath(filePath, cmd0) {
|
|
|
7516
7518
|
return { filePath, ...flags, cmd0 };
|
|
7517
7519
|
}
|
|
7518
7520
|
function extractCatFile(cmd) {
|
|
7519
|
-
const m = /^(cat|bat|type|Get-Content|gc)(?:\s+(?:-[a-zA-Z]+|--[a-zA-Z-]+))*\s+(?:"([^"]+)"|'([^']+)'|(\S+?))(?:\s+-[a-zA-Z]
|
|
7521
|
+
const m = /^(cat|bat|type|Get-Content|gc)(?:\s+(?:-[a-zA-Z]+|--[a-zA-Z-]+))*\s+(?:"([^"]+)"|'([^']+)'|(\S+?))(?:\s+-[a-zA-Z].*?)?(?:\s+2>(&1|\/dev\/null))?\s*$/i.exec(cmd);
|
|
7520
7522
|
if (!m) return null;
|
|
7521
7523
|
const cmd0 = m[1];
|
|
7522
7524
|
const filePath = m[2] ?? m[3] ?? m[4];
|
|
7523
7525
|
if (filePath === void 0) return null;
|
|
7524
|
-
|
|
7526
|
+
const r = classifyCatPath(filePath, cmd0);
|
|
7527
|
+
if (r === null) return null;
|
|
7528
|
+
return { ...r, advisoryOnly: m[5] === "/dev/null" };
|
|
7525
7529
|
}
|
|
7526
7530
|
function extractCatFilesMulti(cmd) {
|
|
7527
7531
|
if (/[|<>;&`]/.test(cmd) || cmd.includes("$(")) return null;
|
|
@@ -7769,11 +7773,12 @@ function extractPythonFileRead(cmd) {
|
|
|
7769
7773
|
return null;
|
|
7770
7774
|
}
|
|
7771
7775
|
function extractHeadFile(cmd) {
|
|
7772
|
-
const
|
|
7773
|
-
|
|
7774
|
-
|
|
7776
|
+
const direct = /^head(?:\s+-n\s+(\d+)|\s+-(\d+))?\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s*$/.exec(cmd);
|
|
7777
|
+
const piped = direct === null ? /^cat(?:\s+(?:-[a-zA-Z]+|--[a-zA-Z-]+))*\s+(?:"([^"]+)"|'([^']+)'|(\S+?))(?:\s+2>(?:&1|\/dev\/null))?\s*\|\s*head(?:\s+-n\s+(\d+)|\s+-(\d+))?\s*$/.exec(cmd) : null;
|
|
7778
|
+
if (direct === null && piped === null) return null;
|
|
7779
|
+
const n = parseInt((direct !== null ? direct[1] ?? direct[2] : piped[4] ?? piped[5]) ?? "0", 10);
|
|
7775
7780
|
if (n <= 10) return null;
|
|
7776
|
-
const filePath =
|
|
7781
|
+
const filePath = direct !== null ? direct[3] ?? direct[4] ?? direct[5] : piped[1] ?? piped[2] ?? piped[3];
|
|
7777
7782
|
if (filePath === void 0) return null;
|
|
7778
7783
|
if (isTempPath(filePath)) return null;
|
|
7779
7784
|
if (!/\.(?:ts|tsx|js|jsx|py|go|java|rs|rb|cs|md|mdx|rst|txt|json|yaml|yml|toml|sql|sh)$/i.test(filePath)) return null;
|
|
@@ -7781,12 +7786,12 @@ function extractHeadFile(cmd) {
|
|
|
7781
7786
|
return { filePath, isDoc, isConfig, isSql, n };
|
|
7782
7787
|
}
|
|
7783
7788
|
function extractSedRange(cmd) {
|
|
7784
|
-
const m = /^sed\s+-n\s+['"](?:\d+,\d+p)(?:;\d+,\d+p)*['"]\s+(?:"([^"]+)"|'([^']+)'|(\S+))(?:\s+2
|
|
7789
|
+
const m = /^sed\s+-n\s+(?:['"]((?:\d+,\d+p)(?:;\d+,\d+p)*)['"]|(\d+,\d+p))\s+(?:"([^"]+)"|'([^']+)'|(\S+))(?:\s+2>(?:\/dev\/null|&1))?\s*$/.exec(cmd);
|
|
7785
7790
|
if (!m) return null;
|
|
7786
|
-
const
|
|
7787
|
-
if (
|
|
7791
|
+
const addressList = m[1] ?? m[2];
|
|
7792
|
+
if (addressList === void 0) return null;
|
|
7788
7793
|
const ranges = [];
|
|
7789
|
-
for (const clause of
|
|
7794
|
+
for (const clause of addressList.split(";")) {
|
|
7790
7795
|
const cm = /^(\d+),(\d+)p$/.exec(clause ?? "");
|
|
7791
7796
|
if (!cm) return null;
|
|
7792
7797
|
const start = parseInt(cm[1], 10);
|
|
@@ -7795,19 +7800,21 @@ function extractSedRange(cmd) {
|
|
|
7795
7800
|
ranges.push([start, end]);
|
|
7796
7801
|
}
|
|
7797
7802
|
if (ranges.length === 0) return null;
|
|
7798
|
-
const filePath = m[
|
|
7803
|
+
const filePath = m[3] ?? m[4] ?? m[5];
|
|
7799
7804
|
if (filePath === void 0) return null;
|
|
7800
7805
|
if (isTempPath(filePath)) return null;
|
|
7801
7806
|
return { filePath, ranges };
|
|
7802
7807
|
}
|
|
7803
7808
|
function extractAwkRange(cmd) {
|
|
7804
|
-
const m = /^awk\s+(?:'([^']+)'|"([^"]+)")\s+(?:"([^"]+)"|'([^']+)'|(\S+))(?:\s+2
|
|
7809
|
+
const m = /^awk\s+(?:'([^']+)'|"([^"]+)")\s+(?:"([^"]+)"|'([^']+)'|(\S+))(?:\s+2>(?:\/dev\/null|&1))?\s*$/.exec(cmd);
|
|
7805
7810
|
if (!m) return null;
|
|
7806
7811
|
const program = m[1] ?? m[2];
|
|
7807
7812
|
if (program === void 0) return null;
|
|
7808
|
-
const cmp = /^\s*NR\s*>=\s*(\d+)\s*&&\s*NR\s*<=\s*(\d+)\s*$/.exec(program);
|
|
7809
|
-
const rng = /^\s*NR\s*==\s*(\d+)\s*,\s*NR\s*==\s*(\d+)\s*$/.exec(program);
|
|
7813
|
+
const cmp = /^\s*NR\s*>=\s*(\d+)\s*&&\s*NR\s*<=\s*(\d+)\s*(\{.*\})?\s*$/.exec(program);
|
|
7814
|
+
const rng = /^\s*NR\s*==\s*(\d+)\s*,\s*NR\s*==\s*(\d+)\s*(\{.*\})?\s*$/.exec(program);
|
|
7810
7815
|
const hit = cmp ?? rng;
|
|
7816
|
+
const action = hit?.[3];
|
|
7817
|
+
if (action !== void 0 && !/^\{\s*(?:print(?:\s+NR\s*"[^"%]*"\s*\$0|\s+\$0)?|printf\s*"%d[^%"]*%s\\n"\s*,\s*NR\s*,\s*\$0)\s*\}$/.test(action)) return null;
|
|
7811
7818
|
if (!hit) return null;
|
|
7812
7819
|
const start = parseInt(hit[1], 10);
|
|
7813
7820
|
const end = parseInt(hit[2], 10);
|
|
@@ -7817,13 +7824,47 @@ function extractAwkRange(cmd) {
|
|
|
7817
7824
|
if (isTempPath(filePath)) return null;
|
|
7818
7825
|
return { filePath, ranges: [[start, end]] };
|
|
7819
7826
|
}
|
|
7827
|
+
function normalizeCatSedPipe(cmd) {
|
|
7828
|
+
const m = /^cat(?:\s+(?:-[a-zA-Z]+|--[a-zA-Z-]+))*\s+(?:"([^"]+)"|'([^']+)'|(\S+?))(?:\s+2>(?:&1|\/dev\/null))?\s*\|\s*sed\s+-n\s+('[^']+'|"[^"]+"|\d+,\d+p)\s*$/.exec(cmd);
|
|
7829
|
+
if (!m) return null;
|
|
7830
|
+
const filePath = m[1] ?? m[2] ?? m[3];
|
|
7831
|
+
if (filePath === void 0 || filePath.includes('"')) return null;
|
|
7832
|
+
return "sed -n " + m[4] + ' "' + filePath + '"';
|
|
7833
|
+
}
|
|
7820
7834
|
function extractLineRangeRead(cmd) {
|
|
7835
|
+
const catSed = normalizeCatSedPipe(cmd);
|
|
7836
|
+
if (catSed !== null) cmd = catSed;
|
|
7821
7837
|
const sed = extractSedRange(cmd);
|
|
7822
7838
|
if (sed !== null) return { ...sed, tool: "sed" };
|
|
7823
7839
|
const awk = extractAwkRange(cmd);
|
|
7824
7840
|
if (awk !== null) return { ...awk, tool: "awk" };
|
|
7825
7841
|
return null;
|
|
7826
7842
|
}
|
|
7843
|
+
var FORMATTING_STAGE_RE = /^(?:fold|cut|cat|nl|head|tail)(?:\s+(?:-\S+|[\d,+-]+))*\s*$/;
|
|
7844
|
+
function extractLineRangeReadsCompound(cmd) {
|
|
7845
|
+
const cleaned = cmd.replace(/\s2>(?:&1|\/dev\/null)/g, "");
|
|
7846
|
+
const segments = splitShellSegments(cleaned);
|
|
7847
|
+
if (segments.length < 2) return null;
|
|
7848
|
+
const reads = [];
|
|
7849
|
+
for (const seg of segments) {
|
|
7850
|
+
const r = extractLineRangeRead(seg);
|
|
7851
|
+
if (r !== null) {
|
|
7852
|
+
reads.push(r);
|
|
7853
|
+
continue;
|
|
7854
|
+
}
|
|
7855
|
+
if (/^echo\b[^<>]*$/.test(seg)) continue;
|
|
7856
|
+
if (FORMATTING_STAGE_RE.test(seg)) continue;
|
|
7857
|
+
return null;
|
|
7858
|
+
}
|
|
7859
|
+
if (reads.length === 0) return null;
|
|
7860
|
+
const merged = /* @__PURE__ */ new Map();
|
|
7861
|
+
for (const r of reads) {
|
|
7862
|
+
const prev = merged.get(r.filePath);
|
|
7863
|
+
if (prev !== void 0) prev.ranges.push(...r.ranges);
|
|
7864
|
+
else merged.set(r.filePath, { filePath: r.filePath, ranges: [...r.ranges], tool: r.tool });
|
|
7865
|
+
}
|
|
7866
|
+
return [...merged.values()];
|
|
7867
|
+
}
|
|
7827
7868
|
var SYMBOL_BEARING_LANGUAGES = /* @__PURE__ */ new Set([
|
|
7828
7869
|
"python",
|
|
7829
7870
|
"typescript",
|
|
@@ -7923,11 +7964,12 @@ function extractTailFile(cmd) {
|
|
|
7923
7964
|
if (/-f\b/.test(cmd)) return null;
|
|
7924
7965
|
if (/-c\b/.test(cmd)) return null;
|
|
7925
7966
|
if (/-n\s*\+/.test(cmd)) return null;
|
|
7926
|
-
const
|
|
7927
|
-
|
|
7928
|
-
|
|
7967
|
+
const direct = /^tail(?:\s+-n\s+(\d+)|\s+-(\d+))?\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s*$/.exec(cmd);
|
|
7968
|
+
const piped = direct === null ? /^cat(?:\s+(?:-[a-zA-Z]+|--[a-zA-Z-]+))*\s+(?:"([^"]+)"|'([^']+)'|(\S+?))(?:\s+2>(?:&1|\/dev\/null))?\s*\|\s*tail(?:\s+-n\s+(\d+)|\s+-(\d+))?\s*$/.exec(cmd) : null;
|
|
7969
|
+
if (direct === null && piped === null) return null;
|
|
7970
|
+
const n = parseInt((direct !== null ? direct[1] ?? direct[2] : piped[4] ?? piped[5]) ?? "0", 10);
|
|
7929
7971
|
if (n <= 10) return null;
|
|
7930
|
-
const filePath =
|
|
7972
|
+
const filePath = direct !== null ? direct[3] ?? direct[4] ?? direct[5] : piped[1] ?? piped[2] ?? piped[3];
|
|
7931
7973
|
if (!filePath) return null;
|
|
7932
7974
|
if (isTempPath(filePath)) return null;
|
|
7933
7975
|
if (!/\.(?:ts|tsx|js|jsx|py|go|java|rs|rb|cs|md|mdx|rst|txt|json|yaml|yml|toml|sql|sh)$/i.test(filePath)) return null;
|
|
@@ -8381,10 +8423,19 @@ async function maybeCompressCompoundOutput(cmd, output, exitCode, cwd, cacheMinB
|
|
|
8381
8423
|
});
|
|
8382
8424
|
const minNet = resolveMinNetSavingsBytes();
|
|
8383
8425
|
if (!compressed.worthApplying(minNet)) return null;
|
|
8384
|
-
const id = await
|
|
8385
|
-
recordStat("bash_compress:generic", compressed.bytesSaved, compressed.tokensSaved);
|
|
8426
|
+
const id = await commandHash(cmd, cwd);
|
|
8386
8427
|
const body = compressed.withMarker(minNet) + "\n[token-goat] full output: bash-output " + id + " --full";
|
|
8387
|
-
|
|
8428
|
+
const emittedBytes = Buffer.byteLength(body, "utf-8");
|
|
8429
|
+
if (!isRewriteWorthwhile({
|
|
8430
|
+
originalBytes: compressed.originalBytes,
|
|
8431
|
+
rewrittenBytes: emittedBytes,
|
|
8432
|
+
noticeBytes: 0,
|
|
8433
|
+
minNetSavingsBytes: minNet
|
|
8434
|
+
})) {
|
|
8435
|
+
return null;
|
|
8436
|
+
}
|
|
8437
|
+
await storeBashOutput(cmd, output, exitCode ?? 0, cwd);
|
|
8438
|
+
return emitRewrite(body, "bash", { kind: "bash_compress:generic", originalBytes: compressed.originalBytes });
|
|
8388
8439
|
}
|
|
8389
8440
|
function unwrapCompressCommand(executed) {
|
|
8390
8441
|
const t = executed.trim();
|
|
@@ -8610,25 +8661,28 @@ function preBashHandlerInner(event) {
|
|
|
8610
8661
|
"Use `token-goat outline <file>` to see symbol names and line counts without loading files."
|
|
8611
8662
|
);
|
|
8612
8663
|
}
|
|
8613
|
-
const
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
const hintPath = cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath;
|
|
8664
|
+
const singleLineRangeRead = extractLineRangeRead(cmd);
|
|
8665
|
+
const sedReads = singleLineRangeRead !== null ? [singleLineRangeRead] : extractLineRangeReadsCompound(cmd);
|
|
8666
|
+
if (sedReads !== null) {
|
|
8617
8667
|
recordStat("session_hint", 0, 0);
|
|
8618
|
-
const
|
|
8619
|
-
const
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
const
|
|
8623
|
-
|
|
8624
|
-
|
|
8625
|
-
|
|
8626
|
-
|
|
8627
|
-
|
|
8668
|
+
const hints = [];
|
|
8669
|
+
for (const { filePath, ranges, tool } of sedReads) {
|
|
8670
|
+
const hintPath = cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath;
|
|
8671
|
+
const sedDedupKey = resolveIndexPath(hintPath, preHookCwd ?? process.cwd());
|
|
8672
|
+
const overlapHints = [];
|
|
8673
|
+
const freshRanges = [];
|
|
8674
|
+
for (const [start, end] of ranges) {
|
|
8675
|
+
const priorOverlap = findRangeOverlap(getFileLineRanges(sedDedupKey), start, end);
|
|
8676
|
+
recordFileLineRange(sedDedupKey, start, end);
|
|
8677
|
+
if (priorOverlap !== null) {
|
|
8678
|
+
overlapHints.push(sedOverlapHint(hintPath, priorOverlap, start, end));
|
|
8679
|
+
} else {
|
|
8680
|
+
freshRanges.push([start, end]);
|
|
8681
|
+
}
|
|
8628
8682
|
}
|
|
8683
|
+
hints.push(...overlapHints);
|
|
8684
|
+
if (freshRanges.length > 0) hints.push(sedRangeHint(hintPath, freshRanges, tool));
|
|
8629
8685
|
}
|
|
8630
|
-
const hints = [...overlapHints];
|
|
8631
|
-
if (freshRanges.length > 0) hints.push(sedRangeHint(hintPath, freshRanges, tool));
|
|
8632
8686
|
return contextOutput(hints.join(" "));
|
|
8633
8687
|
}
|
|
8634
8688
|
const catJsonPipe = extractCatJsonPipe(cmd);
|
|
@@ -8642,7 +8696,7 @@ function preBashHandlerInner(event) {
|
|
|
8642
8696
|
}
|
|
8643
8697
|
const catResult = extractCatFile(cmd);
|
|
8644
8698
|
if (catResult !== null) {
|
|
8645
|
-
const { filePath, isDoc, isEnv, isConfig, isSql, cmd0 } = catResult;
|
|
8699
|
+
const { filePath, isDoc, isEnv, isConfig, isSql, cmd0, advisoryOnly } = catResult;
|
|
8646
8700
|
const hintPath = cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath;
|
|
8647
8701
|
recordStat("session_hint", 0, 0);
|
|
8648
8702
|
if (isSql) {
|
|
@@ -8651,7 +8705,7 @@ function preBashHandlerInner(event) {
|
|
|
8651
8705
|
);
|
|
8652
8706
|
}
|
|
8653
8707
|
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc);
|
|
8654
|
-
return cdStripped ? contextOutput("`" + cmd0 + "` loads the entire file into context. " + hint) : denyOutput("`" + cmd0 + "` loads the entire file into context. " + hint);
|
|
8708
|
+
return cdStripped || advisoryOnly ? contextOutput("`" + cmd0 + "` loads the entire file into context. " + hint) : denyOutput("`" + cmd0 + "` loads the entire file into context. " + hint);
|
|
8655
8709
|
}
|
|
8656
8710
|
const catMulti = extractCatFilesMulti(cmd);
|
|
8657
8711
|
if (catMulti !== null) {
|
|
@@ -8790,13 +8844,13 @@ function preBashHandlerInner(event) {
|
|
|
8790
8844
|
const monBytes = monEntry.sizeBytes;
|
|
8791
8845
|
const catFile = extractCatSourceFile(cmd);
|
|
8792
8846
|
if (catFile !== null) {
|
|
8793
|
-
recordStat("bash_compress:recall", monBytes,
|
|
8847
|
+
recordStat("bash_compress:recall", monBytes, savedTokensFromBytes(monBytes));
|
|
8794
8848
|
return contextOutput(
|
|
8795
8849
|
"Prior output from `" + cmd + "`" + pipelineDivergenceNote(cmd, monEntry.command) + " is cached. Use `token-goat bash-output " + monOutputId + "` to recall the full file, or `token-goat read '" + catFile + "::SymbolName'` to extract only the symbol you need."
|
|
8796
8850
|
);
|
|
8797
8851
|
}
|
|
8798
8852
|
const cmdSummary = cmd.length > 60 ? cmd.slice(0, 57) + "..." : cmd;
|
|
8799
|
-
recordStat("bash_compress:recall", monBytes,
|
|
8853
|
+
recordStat("bash_compress:recall", monBytes, savedTokensFromBytes(monBytes));
|
|
8800
8854
|
return contextOutput(
|
|
8801
8855
|
"Prior output from `" + cmdSummary + "`" + pipelineDivergenceNote(cmd, monEntry.command) + " is cached.\nUse `token-goat bash-output " + monOutputId + " " + monitoringHint + "` to re-inspect without re-running."
|
|
8802
8856
|
);
|
|
@@ -8823,7 +8877,7 @@ function preBashHandlerInner(event) {
|
|
|
8823
8877
|
const curlEntry = curlEntryRaw !== null && !isBashEntryStale(curlEntryRaw, cmd, preHookCwd) ? curlEntryRaw : null;
|
|
8824
8878
|
if (curlOutputId !== null && curlEntry !== null && curlEntry.sizeBytes >= loadConfig().hints.bash_dedup_min_bytes && meetsSavingsFloor(curlEntry.sizeBytes)) {
|
|
8825
8879
|
const curlBytes = curlEntry.sizeBytes;
|
|
8826
|
-
recordStat("bash_compress:recall", curlBytes,
|
|
8880
|
+
recordStat("bash_compress:recall", curlBytes, savedTokensFromBytes(curlBytes));
|
|
8827
8881
|
const curlPreview = cmd.length > 60 ? cmd.slice(0, 57) + "..." : cmd;
|
|
8828
8882
|
return contextOutput(
|
|
8829
8883
|
"curl response cached (`" + curlPreview + "`)." + pipelineDivergenceNote(cmd, curlEntry.command) + " Use `token-goat bash-output " + curlOutputId + "` to recall it. Append `--grep PATTERN` to filter or `--section HeadingName` for a markdown section."
|
|
@@ -8837,7 +8891,7 @@ function preBashHandlerInner(event) {
|
|
|
8837
8891
|
const ghEntry = ghEntryRaw !== null && !isBashEntryStale(ghEntryRaw, cmd, preHookCwd) ? ghEntryRaw : null;
|
|
8838
8892
|
if (ghOutputId !== null && ghEntry !== null && ghEntry.sizeBytes >= loadConfig().hints.bash_dedup_min_bytes && meetsSavingsFloor(ghEntry.sizeBytes)) {
|
|
8839
8893
|
const ghBytes = ghEntry.sizeBytes;
|
|
8840
|
-
recordStat("bash_compress:recall", ghBytes,
|
|
8894
|
+
recordStat("bash_compress:recall", ghBytes, savedTokensFromBytes(ghBytes));
|
|
8841
8895
|
const ghPreview = cmd.length > 60 ? cmd.slice(0, 57) + "..." : cmd;
|
|
8842
8896
|
return contextOutput(
|
|
8843
8897
|
"gh api response cached (`" + ghPreview + "`)." + pipelineDivergenceNote(cmd, ghEntry.command) + " Use `token-goat bash-output " + ghOutputId + "` to recall it. Append `--jq '.field'` on the original call, or `--grep PATTERN` / `--max-matches N` here, to narrow it."
|
|
@@ -8851,7 +8905,7 @@ function preBashHandlerInner(event) {
|
|
|
8851
8905
|
const gitScopedEntry = gitScopedEntryRaw !== null && !isBashEntryStale(gitScopedEntryRaw, cmd, preHookCwd) ? gitScopedEntryRaw : null;
|
|
8852
8906
|
if (gitScopedOutputId !== null && gitScopedEntry !== null && gitScopedEntry.sizeBytes >= loadConfig().hints.bash_dedup_min_bytes && meetsSavingsFloor(gitScopedEntry.sizeBytes)) {
|
|
8853
8907
|
const gitScopedBytes = gitScopedEntry.sizeBytes;
|
|
8854
|
-
recordStat("bash_compress:recall", gitScopedBytes,
|
|
8908
|
+
recordStat("bash_compress:recall", gitScopedBytes, savedTokensFromBytes(gitScopedBytes));
|
|
8855
8909
|
const gitScopedPreview = cmd.length > 60 ? cmd.slice(0, 57) + "..." : cmd;
|
|
8856
8910
|
return contextOutput(
|
|
8857
8911
|
"Output from `" + gitScopedPreview + "`" + pipelineDivergenceNote(cmd, gitScopedEntry.command) + " is cached and unchanged (no edits to that path or HEAD since). Use `token-goat bash-output " + gitScopedOutputId + "` to recall it instead of re-running."
|
|
@@ -8893,7 +8947,7 @@ function preBashHandlerInner(event) {
|
|
|
8893
8947
|
const entry = entryRaw !== null && !isBashEntryStale(entryRaw, cmd, preHookCwd) ? entryRaw : null;
|
|
8894
8948
|
if (outputId !== null && entry !== null && entry.sizeBytes >= loadConfig().hints.bash_dedup_min_bytes && meetsSavingsFloor(entry.sizeBytes)) {
|
|
8895
8949
|
const bytes = entry.sizeBytes;
|
|
8896
|
-
recordStat("bash_compress:recall", bytes,
|
|
8950
|
+
recordStat("bash_compress:recall", bytes, savedTokensFromBytes(bytes));
|
|
8897
8951
|
return contextOutput(buildRecallHint(cmd, outputId));
|
|
8898
8952
|
}
|
|
8899
8953
|
return maybeCompressRewrite(event, rawCmd, cmd) ?? passOutput();
|
|
@@ -9189,12 +9243,23 @@ function getTaskId(toolInput) {
|
|
|
9189
9243
|
const value = toolInput["task_id"];
|
|
9190
9244
|
return typeof value === "string" && value !== "" ? value : void 0;
|
|
9191
9245
|
}
|
|
9246
|
+
function extractTaskOutputText(raw) {
|
|
9247
|
+
const tr = raw["tool_response"];
|
|
9248
|
+
if (tr !== null && typeof tr === "object" && !Array.isArray(tr)) {
|
|
9249
|
+
const task = tr["task"];
|
|
9250
|
+
if (task !== null && typeof task === "object" && !Array.isArray(task)) {
|
|
9251
|
+
const output = task["output"];
|
|
9252
|
+
if (typeof output === "string") return output;
|
|
9253
|
+
}
|
|
9254
|
+
}
|
|
9255
|
+
return extractToolResultText(raw);
|
|
9256
|
+
}
|
|
9192
9257
|
function postTaskOutputHandler(event) {
|
|
9193
9258
|
try {
|
|
9194
9259
|
if (getToolName(event) !== "TaskOutput" || !event.sessionId) return passOutput();
|
|
9195
9260
|
const taskId = getTaskId(getToolInput(event));
|
|
9196
9261
|
if (taskId === void 0) return passOutput();
|
|
9197
|
-
const rawOutput =
|
|
9262
|
+
const rawOutput = extractTaskOutputText(event.raw);
|
|
9198
9263
|
if (!rawOutput) return passOutput();
|
|
9199
9264
|
const output = redactSecrets(rawOutput).text;
|
|
9200
9265
|
if (!output) return passOutput();
|
|
@@ -9366,10 +9431,10 @@ ${PLAN_OMIT_POINTER}`;
|
|
|
9366
9431
|
})) {
|
|
9367
9432
|
return passOutput();
|
|
9368
9433
|
}
|
|
9369
|
-
return {
|
|
9370
|
-
|
|
9371
|
-
|
|
9372
|
-
};
|
|
9434
|
+
return emitRewrite(`${prefix}${notice}`, "exitplanmode", {
|
|
9435
|
+
kind: "plan_echo_collapse",
|
|
9436
|
+
originalBytes: Buffer.byteLength(output, "utf-8")
|
|
9437
|
+
});
|
|
9373
9438
|
} catch {
|
|
9374
9439
|
return passOutput();
|
|
9375
9440
|
}
|
|
@@ -9437,6 +9502,9 @@ function storeMcpOutput(sessionId, toolName, toolInput, resultText) {
|
|
|
9437
9502
|
${redactedOutput}`, entry.storedAt);
|
|
9438
9503
|
return id;
|
|
9439
9504
|
}
|
|
9505
|
+
function mcpOutputBytes(id) {
|
|
9506
|
+
return getBashOutput(id)?.sizeBytes ?? 0;
|
|
9507
|
+
}
|
|
9440
9508
|
function getMcpOutput(sessionId, toolName, toolInput, ttlMs = Number.POSITIVE_INFINITY) {
|
|
9441
9509
|
if (!sessionId) return null;
|
|
9442
9510
|
const id = mcpOutputId(sessionId, mcpHash(toolName, toolInput));
|
|
@@ -9759,6 +9827,8 @@ function preMcpHandler(event) {
|
|
|
9759
9827
|
const ttlMs = loadConfig().hints.mcp_dedup_ttl_secs * 1e3;
|
|
9760
9828
|
const id = getMcpOutput(event.sessionId, toolName, toolInput, ttlMs);
|
|
9761
9829
|
if (!id) return passOutput();
|
|
9830
|
+
const denyCredit = Math.min(mcpOutputBytes(id), PER_FILE_COUNTERFACTUAL_CEILING);
|
|
9831
|
+
recordStat("mcp:recall", denyCredit, savedTokensFromBytes(denyCredit));
|
|
9762
9832
|
return denyOutput(
|
|
9763
9833
|
"Identical read-only MCP call already cached this session. Use `token-goat bash-output " + id + "` to recall the result (add `--grep PATTERN`, `--tail N`, or `--head N` to slice) instead of repeating the call."
|
|
9764
9834
|
);
|
|
@@ -9803,8 +9873,9 @@ function postMcpHandler(event) {
|
|
|
9803
9873
|
const redactedBody = redactSecrets(compressed).text;
|
|
9804
9874
|
const notice = `[token-goat: compressed, full via mcp-output ${id}]
|
|
9805
9875
|
`;
|
|
9876
|
+
const originalBytes = Buffer.byteLength(resultText, "utf-8");
|
|
9806
9877
|
const worthwhile = isRewriteWorthwhile({
|
|
9807
|
-
originalBytes
|
|
9878
|
+
originalBytes,
|
|
9808
9879
|
rewrittenBytes: Buffer.byteLength(redactedBody, "utf-8"),
|
|
9809
9880
|
noticeBytes: Buffer.byteLength(notice, "utf-8"),
|
|
9810
9881
|
minNetSavingsBytes: resolveMinNetSavingsBytes()
|
|
@@ -9812,7 +9883,8 @@ function postMcpHandler(event) {
|
|
|
9812
9883
|
if (worthwhile) {
|
|
9813
9884
|
return emitRewrite(
|
|
9814
9885
|
injectionMatches.length > 0 ? `${notice}${fenceUntrustedContent(redactedBody, injectionMatches, UNTRUSTED_TOOL_TAG)}` : `${notice}${redactedBody}`,
|
|
9815
|
-
"mcp"
|
|
9886
|
+
"mcp",
|
|
9887
|
+
{ kind: "mcp:compress", originalBytes }
|
|
9816
9888
|
);
|
|
9817
9889
|
}
|
|
9818
9890
|
}
|
|
@@ -9934,13 +10006,15 @@ async function shrinkImageBlock(block) {
|
|
|
9934
10006
|
const mediaType = typeof source?.media_type === "string" ? source.media_type : "image/png";
|
|
9935
10007
|
const data = typeof source?.data === "string" ? source.data : "";
|
|
9936
10008
|
const originalDataUrl = `data:${mediaType};base64,${data}`;
|
|
9937
|
-
if (source?.type !== "base64" || data === "") return { text: originalDataUrl, changed: false, savedBytes: 0 };
|
|
10009
|
+
if (source?.type !== "base64" || data === "") return { text: originalDataUrl, changed: false, savedBytes: 0, savedTokens: 0 };
|
|
9938
10010
|
let buffer;
|
|
9939
10011
|
try {
|
|
9940
10012
|
buffer = Buffer.from(data, "base64");
|
|
9941
10013
|
} catch {
|
|
9942
|
-
return { text: originalDataUrl, changed: false, savedBytes: 0 };
|
|
10014
|
+
return { text: originalDataUrl, changed: false, savedBytes: 0, savedTokens: 0 };
|
|
9943
10015
|
}
|
|
10016
|
+
const meta = await probeImageMeta(buffer);
|
|
10017
|
+
const tier = loadConfig().image_shrink.vision_tier;
|
|
9944
10018
|
const fingerprint = screenshotFingerprint(data);
|
|
9945
10019
|
const alreadyShown = hasSeenImage(fingerprint);
|
|
9946
10020
|
recordSeenImage(fingerprint);
|
|
@@ -9955,16 +10029,23 @@ async function shrinkImageBlock(block) {
|
|
|
9955
10029
|
return {
|
|
9956
10030
|
text: SCREENSHOT_REPEAT_NOTICE,
|
|
9957
10031
|
changed: true,
|
|
9958
|
-
|
|
10032
|
+
// Decoded image bytes, not base64 characters. Both branches of this function file under the same `image_shrink` kind, and the shrink branch below credits `result.originalBytes - result.shrunkBytes` -- decoded bytes, the convention documented at image_shrink.ts's own recordStat call. Base64 inflates by 4/3, so crediting `originalDataUrl.length` here booked roughly 33% more for a repeat screenshot than an identical shrink of the same image, and the ledger summed the two units into one row. The gate above deliberately still measures the data URL: it decides whether the rewrite pays off on the wire, where base64 characters are what is actually sent.
|
|
10033
|
+
savedBytes: buffer.length - Buffer.byteLength(SCREENSHOT_REPEAT_NOTICE, "utf-8"),
|
|
10034
|
+
// The whole image is withheld here rather than resized, so the visual tokens saved are its
|
|
10035
|
+
// entire billed cost less the notice standing in for it. Billed cost, not raw patch count:
|
|
10036
|
+
// an oversized screenshot would have been capped by the API's own downscale, so pricing the
|
|
10037
|
+
// untouched dimensions would credit a bill that was never going to be sent.
|
|
10038
|
+
savedTokens: Math.max(0, visionTokens(meta?.width ?? 0, meta?.height ?? 0, tier) - savedTokensFromBytes(Buffer.byteLength(SCREENSHOT_REPEAT_NOTICE, "utf-8")))
|
|
9959
10039
|
};
|
|
9960
10040
|
}
|
|
9961
10041
|
}
|
|
9962
10042
|
const result = await imageQualifiesForShrink(buffer) ? await shrinkImage(buffer, { sizeThresholdBytes: 0 }) : null;
|
|
9963
|
-
if (result === null) return { text: originalDataUrl, changed: false, savedBytes: 0 };
|
|
10043
|
+
if (result === null) return { text: originalDataUrl, changed: false, savedBytes: 0, savedTokens: 0 };
|
|
9964
10044
|
const saved = result.originalBytes - result.shrunkBytes;
|
|
9965
10045
|
const { summary, dataUrl } = formatShrinkSummary(result, "an inline browser screenshot");
|
|
10046
|
+
const savedTokens = visionTokensSaved(result.originalWidth, result.originalHeight, result.width, result.height, tier);
|
|
9966
10047
|
return { text: `${summary}
|
|
9967
|
-
${dataUrl}`, changed: true, savedBytes: saved };
|
|
10048
|
+
${dataUrl}`, changed: true, savedBytes: saved, savedTokens };
|
|
9968
10049
|
}
|
|
9969
10050
|
var TAB_CONTEXT_UNCHANGED_NOTICE = "(tabs unchanged since last check)";
|
|
9970
10051
|
function dedupTabContext(text) {
|
|
@@ -9992,15 +10073,19 @@ async function postBrowserImageHandler(event) {
|
|
|
9992
10073
|
const parts = [];
|
|
9993
10074
|
for (const block of blocks) {
|
|
9994
10075
|
if (block.type === "image") {
|
|
9995
|
-
const { text, changed, savedBytes } = await shrinkImageBlock(block);
|
|
10076
|
+
const { text, changed, savedBytes, savedTokens } = await shrinkImageBlock(block);
|
|
9996
10077
|
if (changed) {
|
|
9997
10078
|
anyChanged = true;
|
|
9998
|
-
recordStat("image_shrink", savedBytes,
|
|
10079
|
+
recordStat("image_shrink", savedBytes, savedTokens, void 0, toolName);
|
|
9999
10080
|
}
|
|
10000
10081
|
parts.push(text);
|
|
10001
10082
|
} else if (block.type === "text" && typeof block.text === "string") {
|
|
10002
10083
|
const { text, changed } = dedupTabContext(block.text);
|
|
10003
|
-
if (changed)
|
|
10084
|
+
if (changed) {
|
|
10085
|
+
anyChanged = true;
|
|
10086
|
+
const savedBytes = Buffer.byteLength(block.text, "utf-8") - Buffer.byteLength(text, "utf-8");
|
|
10087
|
+
if (savedBytes > 0) recordStat("browser_tab_dedup", savedBytes, savedTokensFromBytes(savedBytes), void 0, toolName);
|
|
10088
|
+
}
|
|
10004
10089
|
parts.push(text);
|
|
10005
10090
|
} else {
|
|
10006
10091
|
try {
|
|
@@ -10011,7 +10096,7 @@ async function postBrowserImageHandler(event) {
|
|
|
10011
10096
|
}
|
|
10012
10097
|
}
|
|
10013
10098
|
if (!anyChanged) return passOutput();
|
|
10014
|
-
return
|
|
10099
|
+
return emitRewrite(parts.join("\n"), toolName);
|
|
10015
10100
|
} catch {
|
|
10016
10101
|
return passOutput();
|
|
10017
10102
|
}
|
|
@@ -10020,6 +10105,9 @@ registerHook("post_tool_use", postBrowserImageHandler, { toolPattern: "^mcp__" }
|
|
|
10020
10105
|
|
|
10021
10106
|
// src/hooks_agent_spawn.ts
|
|
10022
10107
|
import { createHash as createHash2 } from "node:crypto";
|
|
10108
|
+
import * as fs from "node:fs";
|
|
10109
|
+
import * as os from "node:os";
|
|
10110
|
+
import * as path2 from "node:path";
|
|
10023
10111
|
var BRIEFING_TARGET_TOKENS = 550;
|
|
10024
10112
|
function buildSubagentBriefing() {
|
|
10025
10113
|
try {
|
|
@@ -10091,8 +10179,11 @@ function findDuplicateOutstandingPrompt(prompt) {
|
|
|
10091
10179
|
function truncateForWarning(text, max) {
|
|
10092
10180
|
return text.length > max ? text.slice(0, max) + "..." : text;
|
|
10093
10181
|
}
|
|
10182
|
+
function isAgentTool(toolName) {
|
|
10183
|
+
return toolName === "Agent" || toolName === "task" || toolName === "Task";
|
|
10184
|
+
}
|
|
10094
10185
|
function preAgentHandler(event) {
|
|
10095
|
-
if (event.toolName
|
|
10186
|
+
if (!isAgentTool(event.toolName)) return passOutput();
|
|
10096
10187
|
const toolInput = event.toolInput;
|
|
10097
10188
|
const prompt = toolInput["prompt"];
|
|
10098
10189
|
if (typeof prompt !== "string" || prompt.trim() === "") return passOutput();
|
|
@@ -10114,7 +10205,7 @@ function preAgentHandler(event) {
|
|
|
10114
10205
|
return passOutput();
|
|
10115
10206
|
}
|
|
10116
10207
|
}
|
|
10117
|
-
var FENCE_LINE_RE = /^ {0,3}(`{3,}|~{3,})(
|
|
10208
|
+
var FENCE_LINE_RE = /^ {0,3}(`{3,}|~{3,})([^\n]*)$/;
|
|
10118
10209
|
function findFencedBlockLines(lines) {
|
|
10119
10210
|
const blocks = [];
|
|
10120
10211
|
let fenceStart = -1;
|
|
@@ -10214,9 +10305,100 @@ function collapseBlankRunsInFences(text) {
|
|
|
10214
10305
|
out.push(...lines.slice(cursor));
|
|
10215
10306
|
return changedAny ? out.join("\n") : text;
|
|
10216
10307
|
}
|
|
10308
|
+
var SPAWN_RESTRICT_HINT_KEY = "agent-spawn-restrict-hint";
|
|
10309
|
+
var SPAWN_RESTRICT_MAX_NAMES = 3;
|
|
10310
|
+
var ROSTER_WALK_MAX_DEPTH = 4;
|
|
10311
|
+
var ROSTER_WALK_MAX_FILES = 400;
|
|
10312
|
+
function parseAgentDefinition(text, fallbackName) {
|
|
10313
|
+
const fmMatch = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(text);
|
|
10314
|
+
if (!fmMatch) return null;
|
|
10315
|
+
const fm = fmMatch[1];
|
|
10316
|
+
const nameMatch = /^name:(.*)$/m.exec(fm);
|
|
10317
|
+
const rawName = nameMatch ? nameMatch[1].trim().replace(/^["']|["']$/g, "") : "";
|
|
10318
|
+
const name2 = rawName !== "" ? rawName : fallbackName;
|
|
10319
|
+
const toolsMatch = /^tools:(.*)$/m.exec(fm);
|
|
10320
|
+
if (!toolsMatch) return { name: name2, restricted: false };
|
|
10321
|
+
const inline = toolsMatch[1].trim();
|
|
10322
|
+
if (inline === "*" || inline === '"*"' || inline === "'*'") return { name: name2, restricted: false };
|
|
10323
|
+
if (inline !== "") return { name: name2, restricted: true };
|
|
10324
|
+
const after = fm.slice(toolsMatch.index + toolsMatch[0].length);
|
|
10325
|
+
for (const line of after.split(/\r?\n/)) {
|
|
10326
|
+
if (/^\s+-\s*\S/.test(line)) return { name: name2, restricted: true };
|
|
10327
|
+
if (/^\S/.test(line)) break;
|
|
10328
|
+
}
|
|
10329
|
+
return { name: name2, restricted: false };
|
|
10330
|
+
}
|
|
10331
|
+
function findRestrictedAgentNames(roots) {
|
|
10332
|
+
const scanRoots = roots ?? [path2.join(os.homedir(), ".claude", "agents")];
|
|
10333
|
+
const names = /* @__PURE__ */ new Set();
|
|
10334
|
+
const visited = /* @__PURE__ */ new Set();
|
|
10335
|
+
let filesSeen = 0;
|
|
10336
|
+
const walk = (dir, depth) => {
|
|
10337
|
+
if (depth > ROSTER_WALK_MAX_DEPTH || filesSeen >= ROSTER_WALK_MAX_FILES) return;
|
|
10338
|
+
let real;
|
|
10339
|
+
try {
|
|
10340
|
+
real = fs.realpathSync(dir);
|
|
10341
|
+
} catch {
|
|
10342
|
+
return;
|
|
10343
|
+
}
|
|
10344
|
+
const key = process.platform === "win32" ? real.toLocaleLowerCase() : real;
|
|
10345
|
+
if (visited.has(key)) return;
|
|
10346
|
+
visited.add(key);
|
|
10347
|
+
let entries;
|
|
10348
|
+
try {
|
|
10349
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
10350
|
+
} catch {
|
|
10351
|
+
return;
|
|
10352
|
+
}
|
|
10353
|
+
for (const entry of entries) {
|
|
10354
|
+
if (filesSeen >= ROSTER_WALK_MAX_FILES) return;
|
|
10355
|
+
const full = path2.join(dir, entry.name);
|
|
10356
|
+
let stat;
|
|
10357
|
+
try {
|
|
10358
|
+
stat = fs.statSync(full);
|
|
10359
|
+
} catch {
|
|
10360
|
+
continue;
|
|
10361
|
+
}
|
|
10362
|
+
if (stat.isDirectory()) {
|
|
10363
|
+
walk(full, depth + 1);
|
|
10364
|
+
continue;
|
|
10365
|
+
}
|
|
10366
|
+
if (!stat.isFile() || !entry.name.toLowerCase().endsWith(".md")) continue;
|
|
10367
|
+
filesSeen++;
|
|
10368
|
+
let text;
|
|
10369
|
+
try {
|
|
10370
|
+
text = fs.readFileSync(full, "utf-8");
|
|
10371
|
+
} catch {
|
|
10372
|
+
continue;
|
|
10373
|
+
}
|
|
10374
|
+
const parsed = parseAgentDefinition(text, path2.basename(entry.name, path2.extname(entry.name)));
|
|
10375
|
+
if (parsed !== null && parsed.restricted) names.add(parsed.name);
|
|
10376
|
+
}
|
|
10377
|
+
};
|
|
10378
|
+
for (const root of scanRoots) walk(root, 0);
|
|
10379
|
+
return Array.from(names).sort();
|
|
10380
|
+
}
|
|
10381
|
+
function buildUnrestrictedSpawnAdvisory(toolInput) {
|
|
10382
|
+
try {
|
|
10383
|
+
if (getHarnessName() === "copilot_cli") return "";
|
|
10384
|
+
const rawType = toolInput["subagent_type"];
|
|
10385
|
+
const spawnType = typeof rawType === "string" ? rawType.trim() : "";
|
|
10386
|
+
if (spawnType !== "" && spawnType !== "general-purpose") return "";
|
|
10387
|
+
if (wasHintShown(SPAWN_RESTRICT_HINT_KEY)) return "";
|
|
10388
|
+
const names = findRestrictedAgentNames();
|
|
10389
|
+
if (names.length === 0) return "";
|
|
10390
|
+
markHintShown(SPAWN_RESTRICT_HINT_KEY);
|
|
10391
|
+
recordStat("session_hint", 0, 0, void 0, "agent-spawn-restrict");
|
|
10392
|
+
const shown = names.slice(0, SPAWN_RESTRICT_MAX_NAMES).join(", ");
|
|
10393
|
+
const more = names.length > SPAWN_RESTRICT_MAX_NAMES ? ` and ${names.length - SPAWN_RESTRICT_MAX_NAMES} more` : "";
|
|
10394
|
+
return `[token-goat] This spawn ran as general-purpose (the default when subagent_type is omitted), which is unrestricted: its lane starts by paying for every tool and MCP schema on the machine. Tools-restricted agent definitions exist here: ${shown}${more}. A future spawn that fits one of them can pass that name as subagent_type to start with a much smaller prefix. Advisory only: this spawn has already run, and this notice saved nothing.`;
|
|
10395
|
+
} catch {
|
|
10396
|
+
return "";
|
|
10397
|
+
}
|
|
10398
|
+
}
|
|
10217
10399
|
function postAgentHandler(event) {
|
|
10218
10400
|
try {
|
|
10219
|
-
if (event.toolName
|
|
10401
|
+
if (!isAgentTool(event.toolName) || !event.sessionId) return passOutput();
|
|
10220
10402
|
const finishedPrompt = event.toolInput["prompt"];
|
|
10221
10403
|
if (typeof finishedPrompt === "string" && finishedPrompt !== "") {
|
|
10222
10404
|
removeOutstandingAgentSpawn(finishedPrompt);
|
|
@@ -10224,13 +10406,17 @@ function postAgentHandler(event) {
|
|
|
10224
10406
|
const redactedReport = redactSecrets(extractToolResultText(event.raw));
|
|
10225
10407
|
const resultText = redactedReport.text;
|
|
10226
10408
|
const agentReportCfg = loadConfig().agent_report;
|
|
10227
|
-
|
|
10228
|
-
|
|
10229
|
-
|
|
10409
|
+
const spawnAdvisory = buildUnrestrictedSpawnAdvisory(event.toolInput);
|
|
10410
|
+
if (!resultText || resultText.length < agentReportCfg.min_bytes) {
|
|
10411
|
+
return spawnAdvisory === "" ? passOutput() : contextOutput(spawnAdvisory);
|
|
10412
|
+
}
|
|
10413
|
+
const id = storeMcpOutput(event.sessionId, event.toolName ?? "Agent", event.toolInput, resultText);
|
|
10414
|
+
if (id === null) return spawnAdvisory === "" ? passOutput() : contextOutput(spawnAdvisory);
|
|
10230
10415
|
if (redactedReport.count > 0) recordStat("secret_redacted", 0, redactedReport.count, void 0, "agent");
|
|
10231
10416
|
recordStat("session_hint", 0, 0);
|
|
10232
10417
|
const recallHint = `token-goat mcp-output ${id} --full`;
|
|
10233
|
-
const notice = `[token-goat] This subagent report (${toKB(resultText.length)}KB) is cached for later recall: ${recallHint}
|
|
10418
|
+
const notice = `[token-goat] This subagent report (${toKB(resultText.length)}KB) is cached for later recall: ${recallHint}${spawnAdvisory === "" ? "" : `
|
|
10419
|
+
${spawnAdvisory}`}`;
|
|
10234
10420
|
const collapsed = collapseFencedBlocks(resultText, recallHint, agentReportCfg.fence_collapse_min_lines, agentReportCfg.fence_collapse_keep_lines);
|
|
10235
10421
|
const deduped = dedupeFencedBlocks(collapsed, resultText, recallHint);
|
|
10236
10422
|
const final = collapseBlankRunsInFences(deduped);
|
|
@@ -10247,8 +10433,8 @@ function postAgentHandler(event) {
|
|
|
10247
10433
|
|
|
10248
10434
|
${notice}`;
|
|
10249
10435
|
const savedBytes = originalBytes - Buffer.byteLength(updatedOutput, "utf-8");
|
|
10250
|
-
if (savedBytes > 0) recordStat("agent_report_compact", savedBytes,
|
|
10251
|
-
return
|
|
10436
|
+
if (savedBytes > 0) recordStat("agent_report_compact", savedBytes, savedTokensFromBytes(savedBytes));
|
|
10437
|
+
return emitRewrite(updatedOutput, "agent", void 0, "counted-elsewhere");
|
|
10252
10438
|
}
|
|
10253
10439
|
recordStat("agent_report_compact_declined", 0, 0);
|
|
10254
10440
|
}
|
|
@@ -10258,7 +10444,11 @@ ${notice}`;
|
|
|
10258
10444
|
}
|
|
10259
10445
|
}
|
|
10260
10446
|
registerHook("pre_tool_use", preAgentHandler, { toolName: "Agent" });
|
|
10447
|
+
registerHook("pre_tool_use", preAgentHandler, { toolName: "task" });
|
|
10448
|
+
registerHook("pre_tool_use", preAgentHandler, { toolName: "Task" });
|
|
10261
10449
|
registerHook("post_tool_use", postAgentHandler, { toolName: "Agent" });
|
|
10450
|
+
registerHook("post_tool_use", postAgentHandler, { toolName: "task" });
|
|
10451
|
+
registerHook("post_tool_use", postAgentHandler, { toolName: "Task" });
|
|
10262
10452
|
|
|
10263
10453
|
// src/relay.ts
|
|
10264
10454
|
function isHookEventName(name2) {
|
|
@@ -10274,7 +10464,11 @@ function buildEvent(eventName, payload) {
|
|
|
10274
10464
|
const sessionId = typeof rawSession === "string" ? rawSession : "";
|
|
10275
10465
|
const rawAgentId = obj["agent_id"] ?? obj["agentId"];
|
|
10276
10466
|
const agentId = typeof rawAgentId === "string" && rawAgentId !== "" ? rawAgentId : void 0;
|
|
10277
|
-
|
|
10467
|
+
const rawTraceparent = obj["traceparent"] ?? obj["traceParent"] ?? process.env["TRACEPARENT"] ?? process.env["traceparent"];
|
|
10468
|
+
const traceparent = typeof rawTraceparent === "string" && rawTraceparent.trim() !== "" ? rawTraceparent.trim() : void 0;
|
|
10469
|
+
const rawTracestate = obj["tracestate"] ?? obj["traceState"] ?? process.env["TRACESTATE"] ?? process.env["tracestate"];
|
|
10470
|
+
const tracestate = typeof rawTracestate === "string" && rawTracestate.trim() !== "" ? rawTracestate.trim() : void 0;
|
|
10471
|
+
return { eventName, toolName, toolInput, sessionId, agentId, traceparent, tracestate, raw: obj };
|
|
10278
10472
|
}
|
|
10279
10473
|
function sessionStateKey(event) {
|
|
10280
10474
|
return event.agentId !== void 0 ? `${event.sessionId}:agent:${event.agentId}` : event.sessionId;
|
|
@@ -10285,6 +10479,7 @@ function harnessForNormalization() {
|
|
|
10285
10479
|
if (detected === "gemini") return "gemini";
|
|
10286
10480
|
if (detected === "grok") return "grok";
|
|
10287
10481
|
if (detected === "kimi") return "kimi";
|
|
10482
|
+
if (detected === "qwen") return "qwen";
|
|
10288
10483
|
if (detected === "copilot_cli") return "copilot_cli";
|
|
10289
10484
|
return "claude";
|
|
10290
10485
|
}
|
|
@@ -10299,6 +10494,12 @@ async function relayInProcess(eventName, rawPayload) {
|
|
|
10299
10494
|
if (!process.env["CLAUDE_CODE_SESSION_ID"] && event.sessionId) {
|
|
10300
10495
|
process.env["CLAUDE_CODE_SESSION_ID"] = event.sessionId;
|
|
10301
10496
|
}
|
|
10497
|
+
if (!process.env["TRACEPARENT"] && event.traceparent) {
|
|
10498
|
+
process.env["TRACEPARENT"] = event.traceparent;
|
|
10499
|
+
}
|
|
10500
|
+
if (!process.env["TRACESTATE"] && event.tracestate) {
|
|
10501
|
+
process.env["TRACESTATE"] = event.tracestate;
|
|
10502
|
+
}
|
|
10302
10503
|
const stateKey = sessionStateKey(event);
|
|
10303
10504
|
try {
|
|
10304
10505
|
loadSessionState(stateKey);
|