token-goat 2.9.4 → 2.9.5
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 +7 -4
- package/dist/{token-goat-chunk-GEXHIIKM.mjs → token-goat-chunk-2FVSKFZU.mjs} +5 -5
- package/dist/{token-goat-chunk-US57I7GN.mjs → token-goat-chunk-4P6GTCMM.mjs} +220 -23
- package/dist/{token-goat-chunk-6MU6FHE3.mjs → token-goat-chunk-5T2K7DEE.mjs} +62 -44
- package/dist/{token-goat-chunk-OZAEJQ7L.mjs → token-goat-chunk-DRL5USXI.mjs} +3 -3
- package/dist/{token-goat-chunk-EQKNR7CN.mjs → token-goat-chunk-GFBXHGFY.mjs} +5 -5
- package/dist/{token-goat-chunk-EPX5DWGM.mjs → token-goat-chunk-HTJP6FHK.mjs} +2 -1
- package/dist/{token-goat-chunk-FSC44QOH.mjs → token-goat-chunk-JO5JX72D.mjs} +2 -2
- package/dist/{token-goat-chunk-5BHXZEOI.mjs → token-goat-chunk-KFFAZ5DJ.mjs} +2 -2
- package/dist/{token-goat-chunk-CBO45DYZ.mjs → token-goat-chunk-U4FTM2SB.mjs} +9797 -9400
- package/dist/{token-goat-chunk-63XEQMNU.mjs → token-goat-chunk-ZLP6TCGN.mjs} +215 -27
- package/dist/{token-goat-chunk-3ZR4NLB3.mjs → token-goat-chunk-ZOKNDG6V.mjs} +220 -99
- package/dist/token-goat-hook.mjs +5 -5
- package/dist/token-goat.core.mjs +5 -5
- package/docs/security.md +6 -2
- package/package.json +1 -1
|
@@ -18,9 +18,11 @@ import {
|
|
|
18
18
|
storeWebOutput,
|
|
19
19
|
summarizeResidentContext,
|
|
20
20
|
taskListPruneHint
|
|
21
|
-
} from "./token-goat-chunk-
|
|
21
|
+
} from "./token-goat-chunk-JO5JX72D.mjs";
|
|
22
22
|
import {
|
|
23
23
|
BASH_OUTPUT_SUBDIR,
|
|
24
|
+
OUTLINE_MAX_REPLACEMENT_RATIO,
|
|
25
|
+
OUTLINE_MIN_HEADINGS,
|
|
24
26
|
UNTRUSTED_TOOL_TAG,
|
|
25
27
|
WEB_FETCH_KEY_SEP,
|
|
26
28
|
appendDirtyPath,
|
|
@@ -38,8 +40,14 @@ import {
|
|
|
38
40
|
ensureWorkerAlive,
|
|
39
41
|
estimateTokens,
|
|
40
42
|
extractCompactFromMarker,
|
|
43
|
+
extractMarkdownHeadings,
|
|
41
44
|
fenceUntrusted,
|
|
45
|
+
fenceUntrustedFileContent,
|
|
42
46
|
fenceWithMatches,
|
|
47
|
+
foldDelivery,
|
|
48
|
+
foldDetail,
|
|
49
|
+
foldingEnabled,
|
|
50
|
+
formatHeadingTreeParts,
|
|
43
51
|
formatProjectMap,
|
|
44
52
|
formatShrinkSummary,
|
|
45
53
|
getBashOutput,
|
|
@@ -61,10 +69,12 @@ import {
|
|
|
61
69
|
imageQualifiesForShrink,
|
|
62
70
|
incrementSkillHit,
|
|
63
71
|
indexRecallEntry,
|
|
72
|
+
indexServedBody,
|
|
64
73
|
installedSkillPath,
|
|
65
74
|
isBashEntryStale,
|
|
66
75
|
isBuildCommand,
|
|
67
76
|
isScopedGitStatusOrDiffStatCommand,
|
|
77
|
+
isStructuralRewriteAccepted,
|
|
68
78
|
isTestRunnerCommand,
|
|
69
79
|
lastTabContextMatches,
|
|
70
80
|
listSiblingSessionStates,
|
|
@@ -78,6 +88,9 @@ import {
|
|
|
78
88
|
matchesDenyPattern,
|
|
79
89
|
meetsSavingsFloor,
|
|
80
90
|
metadataEndpointRefusal,
|
|
91
|
+
planMarkdownOutline,
|
|
92
|
+
planServedElisions,
|
|
93
|
+
planSourceSkeleton,
|
|
81
94
|
probeImageMeta,
|
|
82
95
|
recordBashOutput,
|
|
83
96
|
recordBashRerun,
|
|
@@ -98,6 +111,7 @@ import {
|
|
|
98
111
|
removeOutstandingAgentSpawn,
|
|
99
112
|
saveSessionState,
|
|
100
113
|
scanAndRecord,
|
|
114
|
+
servedRunNotice,
|
|
101
115
|
sessionOutputBodyBytes,
|
|
102
116
|
sessionSidecarPath,
|
|
103
117
|
setLastTabContext,
|
|
@@ -112,11 +126,12 @@ import {
|
|
|
112
126
|
wasCliReadThisSession,
|
|
113
127
|
wasFileReadThisSession,
|
|
114
128
|
wasHintShown
|
|
115
|
-
} from "./token-goat-chunk-
|
|
129
|
+
} from "./token-goat-chunk-U4FTM2SB.mjs";
|
|
116
130
|
import {
|
|
131
|
+
bashOutputCapBytes,
|
|
117
132
|
canRunWrappedShell,
|
|
118
133
|
deliveredOutputBytes
|
|
119
|
-
} from "./token-goat-chunk-
|
|
134
|
+
} from "./token-goat-chunk-HTJP6FHK.mjs";
|
|
120
135
|
import {
|
|
121
136
|
BODY_FIRST_TOOL_RESPONSE_KEYS,
|
|
122
137
|
ENV_KEYS,
|
|
@@ -142,6 +157,7 @@ import {
|
|
|
142
157
|
extractToolResponseField,
|
|
143
158
|
extractToolResultText,
|
|
144
159
|
filterByName,
|
|
160
|
+
findProject,
|
|
145
161
|
foldPath,
|
|
146
162
|
getCwd,
|
|
147
163
|
getDb,
|
|
@@ -172,8 +188,9 @@ import {
|
|
|
172
188
|
shortFingerprint,
|
|
173
189
|
splitOwnTrailingNotices,
|
|
174
190
|
stripAnsiEscapes,
|
|
191
|
+
toDisplayPath,
|
|
175
192
|
toKB
|
|
176
|
-
} from "./token-goat-chunk-
|
|
193
|
+
} from "./token-goat-chunk-ZOKNDG6V.mjs";
|
|
177
194
|
import {
|
|
178
195
|
init_define_import_meta_env
|
|
179
196
|
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
@@ -607,7 +624,7 @@ function buildSafeToDiscardSection(files) {
|
|
|
607
624
|
const entry = getBashOutput(id);
|
|
608
625
|
if (entry === null) continue;
|
|
609
626
|
const flatCommand = entry.command.replace(/[\t\r\n]+/g, " ");
|
|
610
|
-
rerunRows.push("- `" + flatCommand + "` \u2014 an older run of this exact command was superseded; recall the surviving copy with `bash-output " + id + "`");
|
|
627
|
+
rerunRows.push("- `" + flatCommand + "` \u2014 an older run of this exact command was superseded; recall the surviving copy with `bash-output " + id + " --full`");
|
|
611
628
|
}
|
|
612
629
|
const supersededReadRows = [];
|
|
613
630
|
for (const f of files) {
|
|
@@ -622,7 +639,7 @@ function buildSafeToDiscardSection(files) {
|
|
|
622
639
|
const entry = getBashOutput(id);
|
|
623
640
|
if (entry === null) continue;
|
|
624
641
|
const flatCommand = entry.command.replace(/[\t\r\n]+/g, " ");
|
|
625
|
-
cachedOutputRows.push("- `" + flatCommand + "` \u2014 recallable via `bash-output " + id + "`");
|
|
642
|
+
cachedOutputRows.push("- `" + flatCommand + "` \u2014 recallable via `bash-output " + id + " --full`");
|
|
626
643
|
}
|
|
627
644
|
const total = rerunRows.length + supersededReadRows.length + cachedOutputRows.length;
|
|
628
645
|
if (total === 0) return [];
|
|
@@ -703,7 +720,7 @@ init_define_import_meta_env();
|
|
|
703
720
|
var TRACKED_SKILL = "token-goat";
|
|
704
721
|
var MAX_COMMANDS_SHOWN = 8;
|
|
705
722
|
async function currentCommandNames() {
|
|
706
|
-
const { buildProgram } = await import("./token-goat-chunk-
|
|
723
|
+
const { buildProgram } = await import("./token-goat-chunk-2FVSKFZU.mjs");
|
|
707
724
|
return flattenCommandNames(buildCommandManifest(buildProgram()));
|
|
708
725
|
}
|
|
709
726
|
async function recordSkillVersionSnapshot(sessionId, skillName) {
|
|
@@ -7205,7 +7222,7 @@ function preFetchHandler(event) {
|
|
|
7205
7222
|
if (cachedBytes >= loadConfig().hints.web_dedup_min_bytes) {
|
|
7206
7223
|
recordStat("webfetch:recall", cachedBytes, savedTokensFromBytes(cachedBytes));
|
|
7207
7224
|
return denyOutput(
|
|
7208
|
-
"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."
|
|
7225
|
+
"Already fetched this URL with this prompt; the response is cached. Use `token-goat web-output " + cacheId + " --full` to recall it (append `--grep PATTERN` to filter or `--section Heading` for a markdown section) instead of re-fetching."
|
|
7209
7226
|
);
|
|
7210
7227
|
}
|
|
7211
7228
|
}
|
|
@@ -7340,6 +7357,21 @@ async function preSkillHandler(event) {
|
|
|
7340
7357
|
return denyOutput(
|
|
7341
7358
|
"Skill `" + skillName + "` is large (" + bodyBytes + " bytes) and has a compact slice available. Use `token-goat skill-section " + skillName + " '<heading>'` to load a specific section, `token-goat skill-body " + skillName + " --compact` to load the compact slice, or `token-goat skill-body " + skillName + "` for the full body."
|
|
7342
7359
|
);
|
|
7360
|
+
} else if (bodyBytes > OVERSIZED_FIRST_LOAD_THRESHOLD_BYTES) {
|
|
7361
|
+
const headings = extractMarkdownHeadings(body);
|
|
7362
|
+
if (headings.length >= OUTLINE_MIN_HEADINGS) {
|
|
7363
|
+
const { sectionsList } = formatHeadingTreeParts(headings, skillName);
|
|
7364
|
+
const treeBytes = Buffer.byteLength(sectionsList, "utf-8");
|
|
7365
|
+
if (treeBytes <= bodyBytes * OUTLINE_MAX_REPLACEMENT_RATIO) {
|
|
7366
|
+
const savedBytes = bodyBytes - treeBytes;
|
|
7367
|
+
recordStat("skill_heading_tree_inlined", savedBytes, savedTokensFromBytes(savedBytes));
|
|
7368
|
+
const totalHeadings = extractMarkdownHeadings(body, Number.MAX_SAFE_INTEGER).length;
|
|
7369
|
+
const headingCountPhrase = totalHeadings > headings.length ? "its heading tree shows " + headings.length + " of " + totalHeadings + " headings below instead of the full body; the remaining sections are reachable only through `token-goat skill-body " + skillName + "`." : "its heading tree (" + headings.length + " headings) is inlined below instead of the full body.";
|
|
7370
|
+
return denyOutput(
|
|
7371
|
+
"Skill `" + skillName + "` is large (" + bodyBytes + " bytes) with no compact slice; " + headingCountPhrase + " Use `token-goat skill-section " + skillName + " '<heading>'` to load a specific section, or `token-goat skill-body " + skillName + "` for the full body.\n\n" + fenceUntrustedFileContent(sectionsList)
|
|
7372
|
+
);
|
|
7373
|
+
}
|
|
7374
|
+
}
|
|
7343
7375
|
}
|
|
7344
7376
|
} catch {
|
|
7345
7377
|
}
|
|
@@ -7376,8 +7408,9 @@ registerHook("post_tool_use", postSkillHandler, { toolName: "Skill" });
|
|
|
7376
7408
|
// src/hooks_bash.ts
|
|
7377
7409
|
init_define_import_meta_env();
|
|
7378
7410
|
import { statSync as statSync3, existsSync, openSync, readSync, closeSync } from "node:fs";
|
|
7411
|
+
var CD_PREFIX_RE = /^(?:cd\s+(?:"[^"]*"|'[^']*'|\S+)[ \t]*(?:&&|;|\r?\n)\s*)+/;
|
|
7379
7412
|
function stripCdPrefix(cmd) {
|
|
7380
|
-
const stripped = cmd.replace(
|
|
7413
|
+
const stripped = cmd.replace(CD_PREFIX_RE, "");
|
|
7381
7414
|
return stripped.trim() || cmd;
|
|
7382
7415
|
}
|
|
7383
7416
|
function stripTrailingStderrRedirect(cmd) {
|
|
@@ -7393,10 +7426,10 @@ function isDirectTestRunnerCommand(cmd) {
|
|
|
7393
7426
|
return /^(?:pytest|(?:npx\s+)?(?:jest|vitest)|go\s+test|cargo\s+test)\b/i.test(cmd);
|
|
7394
7427
|
}
|
|
7395
7428
|
function extractCdPrefixDirs(rawCmd) {
|
|
7396
|
-
const prefixMatch = rawCmd.match(
|
|
7429
|
+
const prefixMatch = rawCmd.match(CD_PREFIX_RE);
|
|
7397
7430
|
if (prefixMatch === null) return [];
|
|
7398
7431
|
const dirs = [];
|
|
7399
|
-
const segmentPattern = /cd\s+(?:"([^"]*)"|'([^']*)'|(\S+))\
|
|
7432
|
+
const segmentPattern = /cd\s+(?:"([^"]*)"|'([^']*)'|(\S+))[ \t]*(?:&&|;|\r?\n)/g;
|
|
7400
7433
|
let match;
|
|
7401
7434
|
while ((match = segmentPattern.exec(prefixMatch[0])) !== null) {
|
|
7402
7435
|
const dir = match[1] ?? match[2] ?? match[3];
|
|
@@ -7405,13 +7438,13 @@ function extractCdPrefixDirs(rawCmd) {
|
|
|
7405
7438
|
return dirs;
|
|
7406
7439
|
}
|
|
7407
7440
|
function resolveCdHintPath(rawCmd, filePath, cwd) {
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
return
|
|
7441
|
+
if (extractCdPrefixDirs(rawCmd).length === 0) return filePath;
|
|
7442
|
+
return resolveIndexPath(filePath, cdPrefixCwd(rawCmd, cwd));
|
|
7443
|
+
}
|
|
7444
|
+
function cdPrefixCwd(rawCmd, cwd) {
|
|
7445
|
+
let dir = cwd;
|
|
7446
|
+
for (const target of extractCdPrefixDirs(rawCmd)) dir = resolveIndexPath(target, dir);
|
|
7447
|
+
return dir;
|
|
7415
7448
|
}
|
|
7416
7449
|
function surgicalHintFor(hintPath, isEnv, isConfig, isDoc) {
|
|
7417
7450
|
return isEnv ? 'Use `token-goat config-get "' + hintPath + '" KEY_NAME` to read a specific variable.' : isConfig ? 'Use `token-goat config-get "' + hintPath + '" KEY_NAME` or `token-goat section "' + hintPath + '::sectionName"` to read a specific value.' : isDoc ? 'Use `token-goat section "' + hintPath + '::SectionHeading"` to read one section.' : 'Use `token-goat read "' + hintPath + '::SymbolName"` to read one function or class.';
|
|
@@ -8573,16 +8606,110 @@ function maybeCompressRewrite(event, rawCmd, cmd) {
|
|
|
8573
8606
|
return { hookType: "rewriteInput", updatedInput: { ...event.toolInput, command: wrapped } };
|
|
8574
8607
|
}
|
|
8575
8608
|
function pureFileReadPath(cmd) {
|
|
8576
|
-
|
|
8609
|
+
const single = extractCatFile(cmd)?.filePath ?? extractHeadFile(cmd)?.filePath ?? extractTailFile(cmd)?.filePath ?? extractLineRangeRead(cmd)?.filePath;
|
|
8610
|
+
if (single !== void 0) return single;
|
|
8611
|
+
return singleFileCompoundReadPath(cmd);
|
|
8612
|
+
}
|
|
8613
|
+
function singleFileCompoundReadPath(cmd) {
|
|
8614
|
+
const reads = extractLineRangeReadsCompound(cmd);
|
|
8615
|
+
if (reads === null || reads.length !== 1) return null;
|
|
8616
|
+
return reads[0]?.filePath ?? null;
|
|
8617
|
+
}
|
|
8618
|
+
function deliveredLineNumbers(cmd, lineCount) {
|
|
8619
|
+
const ranged = extractLineRangeRead(cmd);
|
|
8620
|
+
if (ranged !== null) {
|
|
8621
|
+
const nums = [];
|
|
8622
|
+
for (const [lo, hi] of ranged.ranges) for (let n = lo; n <= hi; n++) nums.push(n);
|
|
8623
|
+
if (nums.length === 0) return null;
|
|
8624
|
+
while (nums.length < lineCount) nums.push((nums[nums.length - 1] ?? 0) + 1);
|
|
8625
|
+
return nums;
|
|
8626
|
+
}
|
|
8627
|
+
if (extractCatFile(cmd) !== null || extractHeadFile(cmd) !== null) return Array.from({ length: lineCount }, (_, i) => i + 1);
|
|
8628
|
+
if (singleFileCompoundReadPath(cmd) !== null) return Array.from({ length: lineCount }, () => null);
|
|
8629
|
+
return null;
|
|
8630
|
+
}
|
|
8631
|
+
function elideServedShellLines(cmd, output, priorIds) {
|
|
8632
|
+
if (priorIds.length === 0) return null;
|
|
8633
|
+
const lines = output.split("\n");
|
|
8634
|
+
const numbers = deliveredLineNumbers(cmd, lines.length);
|
|
8635
|
+
if (numbers === null) return null;
|
|
8636
|
+
const rows = lines.map((text, i) => ({ no: numbers[i] ?? null, text, raw: text }));
|
|
8637
|
+
const bodies = [];
|
|
8638
|
+
for (let i = priorIds.length - 1; i >= 0; i--) {
|
|
8639
|
+
const id = priorIds[i];
|
|
8640
|
+
if (id === void 0) continue;
|
|
8641
|
+
const prior = getBashOutput(id);
|
|
8642
|
+
if (prior !== null) bodies.push(indexServedBody(id, prior.output));
|
|
8643
|
+
}
|
|
8644
|
+
const cuts = planServedElisions(rows, bodies);
|
|
8645
|
+
if (cuts.length === 0) return null;
|
|
8646
|
+
const out = [];
|
|
8647
|
+
let at = 0;
|
|
8648
|
+
for (const cut of cuts) {
|
|
8649
|
+
for (let i = at; i < cut.start; i++) out.push(rows[i]?.raw ?? "");
|
|
8650
|
+
const first2 = rows[cut.start];
|
|
8651
|
+
const last = rows[cut.start + cut.len - 1];
|
|
8652
|
+
if (first2 === void 0 || last === void 0) return null;
|
|
8653
|
+
out.push(servedRunNotice(first2.no, last.no, cut.id, cut.len));
|
|
8654
|
+
at = cut.start + cut.len;
|
|
8655
|
+
}
|
|
8656
|
+
for (let i = at; i < rows.length; i++) out.push(rows[i]?.raw ?? "");
|
|
8657
|
+
return out.join("\n");
|
|
8577
8658
|
}
|
|
8578
|
-
|
|
8659
|
+
function foldShellReadBodies(cmd, output, fileKey, cwd) {
|
|
8660
|
+
if (!foldingEnabled()) return null;
|
|
8661
|
+
if (redactSecrets(output).count > 0) return null;
|
|
8662
|
+
const lines = output.split("\n");
|
|
8663
|
+
const numbers = deliveredLineNumbers(cmd, lines.length);
|
|
8664
|
+
if (numbers === null) return null;
|
|
8665
|
+
const rows = [];
|
|
8666
|
+
for (let i = 0; i < lines.length; i++) {
|
|
8667
|
+
const no = numbers[i];
|
|
8668
|
+
if (no === void 0 || no === null) return null;
|
|
8669
|
+
rows.push({ no, text: lines[i] ?? "", raw: lines[i] ?? "" });
|
|
8670
|
+
}
|
|
8671
|
+
const headRead = extractHeadFile(cmd);
|
|
8672
|
+
const windowed = extractLineRangeRead(cmd) !== null || headRead !== null && lines.length >= headRead.n;
|
|
8673
|
+
const folded = foldDelivery(rows, fileKey, displaySafePath(toDisplayPath(findProject(cwd ?? process.cwd())?.root, fileKey)), windowed);
|
|
8674
|
+
if (folded === null) return null;
|
|
8675
|
+
return { text: folded.numbered.join("\n"), folds: folded.folds };
|
|
8676
|
+
}
|
|
8677
|
+
var WHOLE_FILE_DUMP_RE = /^(?:(?:cat|type|Get-Content|gc)|(?:head|tail)[ \t]+-n[ \t]+\d+)[ \t]+(?:"([A-Za-z0-9._\-/\\:+@ ]+)"|'([A-Za-z0-9._\-/\\:+@ ]+)'|([A-Za-z0-9._\-/\\:+@]+))(?:[ \t]+2>(?:&1|\/dev\/null))?[ \t]*$/i;
|
|
8678
|
+
function isWholeFileDump(cmd, extractedPath) {
|
|
8679
|
+
const m = WHOLE_FILE_DUMP_RE.exec(cmd);
|
|
8680
|
+
if (m === null) return false;
|
|
8681
|
+
const shaped = m[1] ?? m[2] ?? m[3];
|
|
8682
|
+
return shaped !== void 0 && shaped === extractedPath;
|
|
8683
|
+
}
|
|
8684
|
+
function foldShellReadStructure(cmd, filePath, output, fileKey, cwd) {
|
|
8685
|
+
if (!isWholeFileDump(cmd, filePath)) return null;
|
|
8686
|
+
const originalBytes = Buffer.byteLength(output, "utf-8");
|
|
8687
|
+
let onDisk;
|
|
8688
|
+
try {
|
|
8689
|
+
onDisk = statSync3(fileKey).size;
|
|
8690
|
+
} catch {
|
|
8691
|
+
return null;
|
|
8692
|
+
}
|
|
8693
|
+
if (onDisk !== originalBytes) return null;
|
|
8694
|
+
if (redactSecrets(output).count > 0) return null;
|
|
8695
|
+
const lines = output.split("\n");
|
|
8696
|
+
if (lines.length > 1 && lines[lines.length - 1] === "") lines.pop();
|
|
8697
|
+
const rows = lines.map((text2, i) => ({ no: i + 1, text: text2, raw: text2 }));
|
|
8698
|
+
const shown = displaySafePath(toDisplayPath(findProject(cwd ?? process.cwd())?.root, fileKey));
|
|
8699
|
+
const fold = planMarkdownOutline(rows, fileKey, shown, originalBytes) ?? planSourceSkeleton(rows, fileKey, shown, originalBytes);
|
|
8700
|
+
if (fold === null) return null;
|
|
8701
|
+
const text = fold.numbered.join("\n");
|
|
8702
|
+
if (!isStructuralRewriteAccepted(originalBytes, Buffer.byteLength(text, "utf-8"), fold.ratioCap)) return null;
|
|
8703
|
+
return { text, kind: fold.kind === "read:markdown_outline" ? "bash_compress:markdown-outline" : "bash_compress:source-skeleton", detail: fold.detail };
|
|
8704
|
+
}
|
|
8705
|
+
async function maybeCollapseIdenticalRead(cmd, rawCmd, output, exitCode, cwd, cacheMinBytes) {
|
|
8579
8706
|
if (process.env["TOKEN_GOAT_BASH_COMPRESS"] === "0") return null;
|
|
8580
8707
|
if (exitCode !== null && exitCode !== 0) return null;
|
|
8581
8708
|
const filePath = pureFileReadPath(cmd);
|
|
8582
8709
|
if (filePath === null) return null;
|
|
8583
8710
|
const originalBytes = Buffer.byteLength(output, "utf-8");
|
|
8584
8711
|
if (originalBytes < Math.max(cacheMinBytes, IDENTICAL_READ_MIN_BODY_BYTES)) return null;
|
|
8585
|
-
const fileKey = resolveIndexPath(filePath, cwd ?? process.cwd());
|
|
8712
|
+
const fileKey = resolveIndexPath(filePath, cdPrefixCwd(rawCmd, cwd ?? process.cwd()));
|
|
8586
8713
|
const priorIds = getFileServedOutputs(fileKey);
|
|
8587
8714
|
let containerId = null;
|
|
8588
8715
|
let identical = false;
|
|
@@ -8597,12 +8724,28 @@ async function maybeCollapseIdenticalRead(cmd, output, exitCode, cwd, cacheMinBy
|
|
|
8597
8724
|
}
|
|
8598
8725
|
const sessionKey = shortFingerprint(stripOutputPipeline(cmd));
|
|
8599
8726
|
if (containerId === null) {
|
|
8600
|
-
const
|
|
8727
|
+
const elided = elideServedShellLines(cmd, output, priorIds);
|
|
8728
|
+
const priced = (text) => isRewriteWorthwhile({ originalBytes, rewrittenBytes: Buffer.byteLength(text, "utf-8"), noticeBytes: 0, minNetSavingsBytes: resolveMinNetSavingsBytes() });
|
|
8729
|
+
let rewrite = null;
|
|
8730
|
+
if (elided !== null) {
|
|
8731
|
+
if (priced(elided)) rewrite = { text: elided, reason: "already-served file lines withheld", kind: "bash_compress:served-elide" };
|
|
8732
|
+
} else {
|
|
8733
|
+
const structural = foldShellReadStructure(cmd, filePath, output, fileKey, cwd);
|
|
8734
|
+
if (structural !== null && priced(structural.text)) {
|
|
8735
|
+
rewrite = { text: structural.text, reason: structural.kind === "bash_compress:markdown-outline" ? "document replaced with its heading tree" : "source replaced with its structural skeleton", kind: structural.kind, detail: structural.detail };
|
|
8736
|
+
}
|
|
8737
|
+
if (rewrite === null) {
|
|
8738
|
+
const folded = foldShellReadBodies(cmd, output, fileKey, cwd);
|
|
8739
|
+
if (folded !== null && priced(folded.text)) rewrite = { text: folded.text, reason: "code bodies folded", kind: "bash_compress:body-fold", detail: foldDetail(fileKey, folded.folds) };
|
|
8740
|
+
}
|
|
8741
|
+
}
|
|
8742
|
+
const storedId = await storeBashOutput(cmd, rewrite?.text ?? output, exitCode ?? 0, cwd);
|
|
8601
8743
|
recordBashOutput(sessionKey, storedId, originalBytes);
|
|
8602
8744
|
recordFileServedOutput(fileKey, storedId);
|
|
8603
|
-
return null;
|
|
8745
|
+
if (rewrite === null) return null;
|
|
8746
|
+
return emitRewrite(rewrite.text, rewrite.reason, { kind: rewrite.kind, originalBytes: deliveredOutputBytes(originalBytes), ...rewrite.detail === void 0 ? {} : { detail: rewrite.detail } });
|
|
8604
8747
|
}
|
|
8605
|
-
const pointer = identical ? "[token-goat] Identical to an earlier run of this command in this session; the file has not changed since. " + originalBytes + " bytes withheld -- recall them with `token-goat bash-output " + containerId + "`." : "[token-goat] These " + originalBytes + " bytes already appear verbatim inside a wider read of " + filePath + " served earlier in this session. Withheld -- recall the full earlier output with `token-goat bash-output " + containerId + "`.";
|
|
8748
|
+
const pointer = identical ? "[token-goat] Identical to an earlier run of this command in this session; the file has not changed since. " + originalBytes + " bytes withheld -- recall them with `token-goat bash-output " + containerId + " --full`." : "[token-goat] These " + originalBytes + " bytes already appear verbatim inside a wider read of " + filePath + " served earlier in this session. Withheld -- recall the full earlier output with `token-goat bash-output " + containerId + " --full`.";
|
|
8606
8749
|
if (!isRewriteWorthwhile({ originalBytes, rewrittenBytes: Buffer.byteLength(pointer, "utf-8"), noticeBytes: 0, minNetSavingsBytes: resolveMinNetSavingsBytes() })) return null;
|
|
8607
8750
|
return emitRewrite(pointer, identical ? "identical file re-read collapsed" : "already-served file lines collapsed", { kind: identical ? "bash_compress:identical-reread" : "bash_compress:contained-reread", originalBytes: deliveredOutputBytes(originalBytes) });
|
|
8608
8751
|
}
|
|
@@ -8663,6 +8806,48 @@ function maybeStripAnsiOnly(output) {
|
|
|
8663
8806
|
}
|
|
8664
8807
|
return emitRewrite(stripped, "ansi escapes stripped", { kind: "bash_compress:ansi", originalBytes: deliveredOutputBytes(originalBytes) }, "counted-elsewhere");
|
|
8665
8808
|
}
|
|
8809
|
+
function maybeFoldCurlHtml(cmd, output, id) {
|
|
8810
|
+
if (!isCurlGetCommand(cmd) || !looksLikeHtml(output)) return null;
|
|
8811
|
+
let cfg;
|
|
8812
|
+
try {
|
|
8813
|
+
cfg = loadConfig().bash_compress;
|
|
8814
|
+
} catch {
|
|
8815
|
+
return null;
|
|
8816
|
+
}
|
|
8817
|
+
if (!cfg.enabled || cfg.disabled_filters.includes("curl-html")) return null;
|
|
8818
|
+
let cleaned;
|
|
8819
|
+
try {
|
|
8820
|
+
cleaned = extractCleanText(output);
|
|
8821
|
+
} catch {
|
|
8822
|
+
return null;
|
|
8823
|
+
}
|
|
8824
|
+
const originalBytes = Buffer.byteLength(output, "utf-8");
|
|
8825
|
+
const notice = `[token-goat: curl HTML body cleaned via extractCleanText; use \`token-goat bash-output ${id} --full\` to recall the raw markup]
|
|
8826
|
+
`;
|
|
8827
|
+
const noticeBytes = Buffer.byteLength(notice, "utf-8");
|
|
8828
|
+
const clipNote = "\n[token-goat: cleaned text clipped to the harness delivery cap; the notice at the top of this output recalls the full raw markup]";
|
|
8829
|
+
const cap = bashOutputCapBytes();
|
|
8830
|
+
const fenceOverhead = Buffer.byteLength(fenceUntrusted(cleaned, UNTRUSTED_TOOL_TAG), "utf-8") - Buffer.byteLength(cleaned, "utf-8");
|
|
8831
|
+
const room = cap === null ? null : cap - noticeBytes - fenceOverhead - Buffer.byteLength(clipNote, "utf-8");
|
|
8832
|
+
let body = cleaned;
|
|
8833
|
+
let clipped = "";
|
|
8834
|
+
if (room !== null && room > 0 && Buffer.byteLength(cleaned, "utf-8") > room) {
|
|
8835
|
+
const sliced = Buffer.from(cleaned, "utf-8").subarray(0, room).toString("utf-8");
|
|
8836
|
+
const lastNewline = sliced.lastIndexOf("\n");
|
|
8837
|
+
body = lastNewline > 0 ? sliced.slice(0, lastNewline) : sliced;
|
|
8838
|
+
clipped = clipNote;
|
|
8839
|
+
}
|
|
8840
|
+
const fenced = fenceUntrusted(body, UNTRUSTED_TOOL_TAG) + clipped;
|
|
8841
|
+
if (!isRewriteWorthwhile({
|
|
8842
|
+
originalBytes,
|
|
8843
|
+
rewrittenBytes: Buffer.byteLength(fenced, "utf-8"),
|
|
8844
|
+
noticeBytes,
|
|
8845
|
+
minNetSavingsBytes: resolveMinNetSavingsBytes()
|
|
8846
|
+
})) {
|
|
8847
|
+
return null;
|
|
8848
|
+
}
|
|
8849
|
+
return emitRewrite(notice + fenced, "curl HTML body cleaned", { kind: "bash_compress:curl-html", originalBytes: deliveredOutputBytes(originalBytes) });
|
|
8850
|
+
}
|
|
8666
8851
|
function unwrapCompressCommand(executed) {
|
|
8667
8852
|
const t = executed.trim();
|
|
8668
8853
|
if (!/^(?:token-goat|tg)\s+compress\b/.test(t)) return null;
|
|
@@ -9347,8 +9532,9 @@ async function postBashHandler(event) {
|
|
|
9347
9532
|
recordBashOutput(gitScopedCacheHash, gitScopedCacheId, Buffer.byteLength(output, "utf-8"));
|
|
9348
9533
|
}
|
|
9349
9534
|
const isMonitoring = getMonitoringRecallHint(cmd) !== null;
|
|
9350
|
-
|
|
9351
|
-
|
|
9535
|
+
const isFileRead = pureFileReadPath(cmd) !== null;
|
|
9536
|
+
if (isFileRead || !isMonitoring && !isBuildCommand(cmd) && !isCurlGetCommand(cmd)) {
|
|
9537
|
+
const identical = await maybeCollapseIdenticalRead(cmd, rawCmd, output, exitCode, cwd, cacheMinBytes);
|
|
9352
9538
|
if (identical !== null) return identical;
|
|
9353
9539
|
const compound = await maybeCompressCompoundOutput(cmd, output, exitCode, cwd, cacheMinBytes);
|
|
9354
9540
|
if (compound !== null) return compound;
|
|
@@ -9366,9 +9552,11 @@ async function postBashHandler(event) {
|
|
|
9366
9552
|
const delta = summarizeOutputDelta(priorEntry.output, output);
|
|
9367
9553
|
if (delta !== null) {
|
|
9368
9554
|
recordStat("session_hint", 0, 0);
|
|
9369
|
-
return contextOutput(delta + " \u2014 full output: bash-output " + id);
|
|
9555
|
+
return contextOutput(delta + " \u2014 full output: bash-output " + id + " --full");
|
|
9370
9556
|
}
|
|
9371
9557
|
}
|
|
9558
|
+
const curlHtmlFold = maybeFoldCurlHtml(cmd, output, id);
|
|
9559
|
+
if (curlHtmlFold !== null) return curlHtmlFold;
|
|
9372
9560
|
const ansiOnly = maybeStripAnsiOnly(output);
|
|
9373
9561
|
if (ansiOnly !== null) return ansiOnly;
|
|
9374
9562
|
} catch {
|