token-goat 2.9.3 → 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.
@@ -18,9 +18,11 @@ import {
18
18
  storeWebOutput,
19
19
  summarizeResidentContext,
20
20
  taskListPruneHint
21
- } from "./token-goat-chunk-A4VBNWSL.mjs";
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-3XVGPLDS.mjs";
129
+ } from "./token-goat-chunk-U4FTM2SB.mjs";
116
130
  import {
131
+ bashOutputCapBytes,
117
132
  canRunWrappedShell,
118
133
  deliveredOutputBytes
119
- } from "./token-goat-chunk-OKJX2JWW.mjs";
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-KKHKXUVS.mjs";
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-XSSQFI4C.mjs");
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(/^(?:cd\s+(?:"[^"]*"|'[^']*'|\S+)\s*&&\s*)+/, "");
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(/^(?:cd\s+(?:"[^"]*"|'[^']*'|\S+)\s*&&\s*)+/);
7429
+ const prefixMatch = rawCmd.match(CD_PREFIX_RE);
7397
7430
  if (prefixMatch === null) return [];
7398
7431
  const dirs = [];
7399
- const segmentPattern = /cd\s+(?:"([^"]*)"|'([^']*)'|(\S+))\s*&&/g;
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
- const dirs = extractCdPrefixDirs(rawCmd);
7409
- if (dirs.length === 0) return filePath;
7410
- let targetDir = cwd;
7411
- for (const dir of dirs) {
7412
- targetDir = resolveIndexPath(dir, targetDir);
7413
- }
7414
- return resolveIndexPath(filePath, targetDir);
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
- return extractCatFile(cmd)?.filePath ?? extractHeadFile(cmd)?.filePath ?? extractTailFile(cmd)?.filePath ?? extractLineRangeRead(cmd)?.filePath ?? null;
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
- async function maybeCollapseIdenticalRead(cmd, output, exitCode, cwd, cacheMinBytes) {
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 storedId = await storeBashOutput(cmd, output, exitCode ?? 0, cwd);
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
- if (!isMonitoring && !isBuildCommand(cmd) && !isCurlGetCommand(cmd)) {
9351
- const identical = await maybeCollapseIdenticalRead(cmd, output, exitCode, cwd, cacheMinBytes);
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 {